0

i like to get a script field if a specific value exist in a array .

Here is my mapping -

"mappings": {
    "properties": {
        "field1": {
            "type": "text",
        },
        "field2": {
            "type": "text",
        },
    }
}

Here is my example documents -

{
    "field1": "hello 1",
    "field2": ["id1","id3"]
}

{
    "field1": "hello 2",
    "field2": ["id2","id3"]
}
{
    "field1": "hello 3",

}
{
    "field1": "mobile 1",
    "field2": ["id1","id4"]
}

Here is my search query -

{
    "query": {
        "match" : {
            "title": "hello"
        }
    },
    "script_fields":{
        "testField2":{
            "script": {
                "source":"doc['field2'].value ==  params.id ? true : false",
                "params": {
                    "id": "id1"
                }
            }
        }
    }

}

what i want to achieve is to get all those document which has "hello" in their title and add a custom field (testField2) in every doc . the value of that custom field would be True if "field2" contain a specific id ("id1") else False

1 Answer 1

1

If field2 is only mapped as text, you won't be able to use its field values (via doc['field2'].value). You can only use its _source:

POST your-index/_search
{
  "query": {
    "match": {
      "field1": "hello"
    }
  },
  "script_fields": {
    "testField2": {
      "script": {
        "source": "params._source.containsKey('field2') ? params._source.field2.contains(params.id) : false",
        "params": {
          "id": "id1"
        }
      }
    }
  }
}

Note that accessing _source is not as efficient as using the doc values -- so in short, you were using the right strategy but with the wrong mapping.

In order to be able to access the doc values, either use the keyword data type or set the fielddata parameter on your existing text field to true. I'm going to go with the keyword here:

DELETE your-index

PUT your-index
{
  "mappings": {
    "properties": {
      "field1": {
        "type": "text"
      },
      "field2": {
        "type": "keyword"     <--
      }
    }
  }
}

After reindexing (which is required after a "mapping breaking change") you can then call:

POST your-index/_search
{
  "query": {
    "match": {
      "field1": "hello"
    }
  },
  "script_fields": {
    "testField2": {
      "script": {
        "source": "doc['field2'].size() != 0 ? doc['field2'].asList().contains(params.id) : false",
        "params": {
          "id": "id1"
        }
      }
    }
  }
}

Finally, notice the .asList() method. It's needed to convert the doc values which are an instance of the class ScriptDocValues.Strings to a consumable ArrayList on which you can then call the conventional .contains() method.

Sign up to request clarification or add additional context in comments.

3 Comments

thank you very much, it worked . previously i tried with keyword & nested mapping, but it wasn't working, probably because i was not using .asList() and .contain() method
@ Joe - ElasticsearchBook.com I just encountered you answer and it works! But I am confused with why size() method works on checking if the field exists. I suspect it has to do with internally how the field is mapped. But I couldn't find any resources on this.
@codeedoc yea it's an internal method on the doc value instance.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.