Painless: Collection.contains() not working

I have a simple test object indexed. It has a collection "IntCollection" and the indexed object has this property set to the array {1, 2, 3, 4, 5}. Here is the (simplified) indexed object:

"_source": 
{
  // ...
  "IntCollection": [
	1,
	2,
	3,
	4,
	5
  ]
}

The goal is to filter the indexed objects by containing a specific value in "IntCollection" using Painless.
Here is my query (in Kibana):

GET /testdatatype_unit_tests/_search
{ "size": 100, 
  "query": {
  "script": {
    "script": {
      "source": "doc['IntCollection'].values.contains(1)",
      "lang": "painless"
    }
  }
}
}

No results are returned, although the indexed document has the searched value inside. Here is the query result:

{
  "took": 0,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 0,
    "max_score": null,
    "hits": []
  }
}

When I do the same request with a TermQuery, it works:

GET /testdatatype_unit_tests/_search
{ "size": 100, 
  "query": {
  "term": {
      "IntCollection": {
        "value": 1
      }
    }
  }
}

Any idea, what is wrong with the Painless script?

Edit: ES version 6.4.2 is used.

Because Elasticsearch treats those numbers as Longs by default, you need to make sure that you pass a Long to the contains method. The following should work:

GET testdatatype_unit_tests/_search
{
  "size": 100,
  "query": {
    "script": {
      "script": {
        "source": "doc['IntCollection'].values.contains(1L)",
        "lang": "painless"
      }
    }
  }
}
1 Like

It helped. Thank you!

In the same context I have another issue. My test object contains another collection with nullable integers:

"_source": 
{
  // ...
  "NullableIntCollection": [
	1,
	null,
	3,
	4,
	null
  ]
}

The task is to query, if the NullableIntCollection contains a null. For such queries, I use the negated ExistsQuery for simple properties, which works fine, but it does not apply to collection items. My thought was to use a script once again, but it is not working:

GET /testdatatype_unit_tests/_search
{ "size": 100, 
  "query": {
  "script": {
    "script": {
      "lang": "painless",
      "source": "doc['NullableIntCollection'].values.contains(null)"
    }
  }
}
}

Is something missing in the script?

doc['NullableIntCollection'].values in your script will only contain actual values. In your example it is exactly as if you had indexed the following field:

  "NullableIntCollection": [
	1,
	3,
	4
  ]

As a solution, maybe you can define a null_value in your mappings for this field? A null_value is a "default value" assigned to fields that have a value null. For example, if you know your collection will only contain positive numbers, you could define a null_value of -1 and check for that value:

PUT testdatatype_unit_tests
{
  "mappings": {
    "_doc": {
      "properties": {
        "NullableIntCollection": {
          "type": "long",
          "null_value": -1
        }
      }
    }
  }
}

PUT testdatatype_unit_tests/_doc/1
{
  "NullableIntCollection": [
    1,
    null,
    3,
    4,
    null
  ]
}

GET testdatatype_unit_tests/_search
{
  "size": 100,
  "query": {
    "script": {
      "script": {
        "source": "doc['NullableIntCollection'].values.contains(-1L)",
        "lang": "painless"
      }
    }
  }
}

Thank you very much once again.

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.