I'm building a search template in Elastic and depending on the value of the parameter the search query should perform differently.
The documentation says the syntax should look like
{{#condition}}if content{{/condition}} {{^condition}}else content{{/condition}}
This is my template:
POST _scripts/my-search-template
{
"script": {
"lang": "mustache",
"source": """
{
"track_total_hits":true,
"query": {
"bool": {
"filter": [
{{#radio}}
if ({{radio}} == "ANY") {
{"exists": { "field": "radio" }},
}
{{/radio}}
{{^radio}}
else {
{"match": { "radio": "{{radio}}" }},
}
{{/radio}}
]
}
}
}"""
}
But when execute the command:
GET anlage/_search/template
{
"id": "my-search-template",
"params": {
"radio": "ANY"
}
}
I get the error:
Unrecognized token 'if': was expecting (JSON String, Number, Array, Object or token 'null', 'true' or 'false
How do I build a template with an if else condition properly?
You don't have to mention if/else explicitly and also you cannot compare the field against a specific value. You need to do it like this instead:
POST _scripts/my-search-template
{
"script": {
"lang": "mustache",
"source": """
{
"track_total_hits":true,
"query": {
"bool": {
"filter": [
{{#radio}}
{"match": { "radio": "{{radio}}" }}
{{/radio}}
{{^radio}}
{"exists": { "field": "radio" }}
{{/radio}}
]
}
}
}"""
}
I realize that the logic above is different than the one you need, but mustache is just a templating language, not a programming language. This means that you need to think in terms of signals (if parameter is present and has a value), not in terms of logic (if parameter has a specifc value)
Related
I would like to construct an elasticsearch query in which I can search for a term and on-the-fly compute a new field for each found document, which is calculated based on some existing fields as well as the query term. Is this possible?
For example, let's say in my EL query I am searching for documents which have the keyword "amsterdam" in the "text" field.
"filter": [
{
"match_phrase": {
"text": {
"query": "amsterdam"
}
}
}]
Now I would also like to have a script field in my query, which computes some value based on other fields as well as the query.
So far, I have only found how to access the other fields of a document though, using doc['someOtherField'], for example
"script_fields" : {
"new_field" : {
"script" : {
"lang": "painless",
"source": "if (doc['citizens'].value > 10000) {
return "large";
}
return "small";"
}
}
}
How can I integrate the query term, e.g. if I wanted to add to the if statement "if the query term starts with a-e"?
You're on the right track but script_fields are primarily used to post-process your documents' attributes — they won't help you filter any docs because they're run after the query phase.
With that being said, you can use scripts to filter your documents through script queries. Before you do that, though, you should explore alternatives.
In other words, scripts should be used when all other mechanisms and techniques have been exhausted.
Back to your example. I see three possibilities off the top of my head.
Match phrase prefix queries as a group of bool-should subqueries:
POST your-index/_search
{
"query": {
"bool": {
"must": [
{
"bool": {
"should": [
{
"match_phrase_prefix": {
"text_field": "a"
}
},
{
"match_phrase_prefix": {
"text_field": "b"
}
},
{
"match_phrase_prefix": {
"text_field": "c"
}
},
... till the letter "e"
]
}
}
]
}
}
}
A regexp query:
POST your-index/_search
{
"query": {
"bool": {
"must": [
{
"regexp": {
"text_field": "[a-e].+"
}
}
]
}
}
}
Script queries using .charAt comparisons:
POST your-index/_search
{
"query": {
"bool": {
"must": [
{
"script": {
"script": {
"source": """
char c = doc['text_field.keyword'].value.charAt(0);
return c >= params.gte.charAt(0) && c <= params.lte.charAt(0);
""",
"params": {
"gte": "a",
"lte": "e"
}
}
}
}
]
}
}
}
If you're relatively new to ES and would love to see real-world examples, check out my recently released Elasticsearch Handbook. One chapter is dedicated to scripting and as it turns out, you can achieve a lot with scripts (if of course executed properly).
is there a way of creating a field in document within a painless script
if it does not exists?
i'm using something like:
if(!ctx._source.tags.contains(....)
but tags field may not be exists at document
can it be done?
thanks.
If you plan to use the _update_by_query API, I'd recommend you to do something like:
POST your_index/_update_by_query
{
"query": {
"bool": {
"must_not": {
"exists": {
"field": "tags"
}
}
}
},
"script": {
"source": "ctx._source.tags = ''"
}
}
Otherwise, just using painless, you can do something like:
{
"script": {
"source": """
if(ctx._source.tags == null) {
ctx._source.tags = null;
}
"""
}
}
I want to find the record in my elasticsearch index where it should match field "connectorSpecific.hostname.keyword" with value "tyco-fire.com" and field "hasForms" with value true.
Below is my elasticsearch query:
GET index1/_search
{
"query": {
"bool": {
"should": [
{ "match": { "connectorSpecific.hostname.keyword": "tyco-fire.com" }},
{ "match": { "hasForms": true }}
]
}
}
}
This query is returning records which also has field "hasForms" with value false. Not sure why.I am using a boolean should query.Any help is appreciated
If you want both constraints to match, then you should use bool/filter (or bool/must would work as well but since you're doing exact matching, you don't need scoring at all), like this:
GET index1/_search
{
"query": {
"bool": {
"filter": [
{ "match": { "connectorSpecific.hostname.keyword": "tyco-fire.com" }},
{ "match": { "hasForms": true }}
]
}
}
}
Per our requirement we need to find the max ID of the document before adding new document. Problem here is doc may contain string data also So had to use inline script on the elastic query to find out max id only for the document which has integer data otherwise returning 0. am using following inline script query to find max-key but not working. can you help me onthis ?.
{
"size":0,
"query":
{"bool":
{"filter":[
{"term":
{"Name":
{
"value":"Test2"
}
}}
]
}},
"aggs":{
"MaxId":{
"max":{
"field":"Key","script":{
"inline":"((doc['Key'].value).isNumber()) ? Integer.parseInt(doc['Key'].value) : 0"}}
}
}
}
The error is because the max aggregation only supports numeric fields, i.e. you cannot specify a string field (i.e. Key) in a max aggregation.
Simply remove the "field":"Key" part and only keep the script part
{
"size": 0,
"query": {
"bool": {
"filter": [
{
"term": {
"Name": "Test2"
}
}
]
}
},
"aggs": {
"MaxId": {
"max": {
"script": {
"source": "((doc['Key'].value).isNumber()) ? Integer.parseInt(doc['Key'].value) : 0"
}
}
}
}
}
I try to evaluate a web-application for my masterthesis. For this I want to make a user study, where I prepare the data in elasitc found, and send my web application to the testers. As far as I know, elastic found does not allow dynamic scripting for security reasons. I try to refomulate the following dynamic script query:
GET my_index/document/_search
{
"query": {
"match_all":{}
},
"aggs": {
"stadt": {
"sum": {
"script": "_index['textBody']['frankfurt'].tf()"
}
}
}
}
This query sums up all term frequencies in the document field textBody for the term frankfurt.
In order to reformulate the query without dynamic scripting, I've taken a look on groovy scripts without dynamic scripting, but I still get parsing errors.
My approach to this was:
GET my_index/document/_search
{
"query": {
"match_all":{}
},
"aggs": {
"stadt": {
"sum": {
"script": {
"script_id": "termFrequency",
"lang" : "groovy",
"params": {
"term" : "frankfurt"
}
}
}
}
}
}
and the file termFrequency.groovy in the scripts directory:
_index['textBody'][term].tf()
I get the following parsing error:
Parse Failure [Unexpected token START_OBJECT in [stadt].]
This is the correct syntax assuming your file is inside config/scripts directory.
{
"query": {
"match_all": {}
},
"aggs": {
"stadt": {
"sum": {
"script_file": "termFrequency",
"lang": "groovy",
"params": {
"term": "frankfurt"
}
}
}
},
"size": 0
}
Also the term should be variable rather than string so it should be
_index['textBody'][term].tf()
Hope this helps!