Elasticsearch Mustache optional parameters

5.1k views Asked by At

I've been struggling with Elasticsearch templates, specifically in optional parameters. I'd like to add optional filters there. This is the code snippet I was trying out:

{
  "filter" : {
    "bool" : {
      "must" : [
        {{#ProductIDs.0}}
        { "terms" : { "Product.ProductID" : [{{#ProductIDs}}{{.}},{{/ProductIDs}}] } }
        {{/ProductIDs.0}}
      ]
    }
  }
}

Of course I replaced " with \", uglified it, wrapped it up in { "template" :"_snippet_above_" }.

Now when I'm trying to call it using the following:

GET /statistic/_search/template
{
    "template": {
        "id": "test" 
    },
    "params": {
        "ProductIDs": [1,2]
    }
}

It ignores parameter that I've provided, however when I try to do that in official mustache.io demo page - it works just fine.

I tried {{#ProductIDs.length}} option too - it didn't work out. After doing some research I've found out that there is one difference between mustache.js and mustache.java. I assumed that Elasticsearch uses JAVA version and it doesn't support length parameter, so I have to rely on isEmpty. So I've rewritten my query as follows:

{
  "filter" : {
    "bool" : {
      "must" : [
        {{^ProductIDs.isEmpty}}
        { "terms" : { "Product.ProductID" : [{{#ProductIDs}}{{.}},{{/ProductIDs}}] } }
        {{/ProductIDs.isEmpty}}
      ]
    }
  }
}

Now when I query template with ProductIDs list - it works fine, however if I remove parameter, it brings no results. I assume it generates this:

{
  "filter" : {
    "bool" : {
      "must" : [
        { "terms" : { "Product.ProductID" : [] } }
      ]
    }
  }
}

If I send empty array as Parameter - it works fine.

GET /statistic/_search/template
{
    "template": {
        "id": "test" 
    },
    "params": {
        "ProductIDs": []
    }
}

I assume this happens because "ProductIDs" are undefined and not empty.

Is there a way to cath this condition in mustache.java so I can ignore these parameters?

tl;dr; The issue is that if I don't specify parameter in my search request via template, my condition is rendered as an empty array, see this:

{
  "filter" : {
    "bool" : {
      "must" : [
        { "terms" : { "Product.ProductID" : [] } }
      ]
    }
  }
}

If I pass empty array as a parameter, see this:

GET /statistic/_search/template
{
    "template": {
        "id": "test" 
    },
    "params": {
        "ProductIDs": []
    }
}

It works as expected and doesn't generate filter condition as described in my template, because array doesn't have any data in it.

I want this:

GET /statistic/_search/template
{
    "template": {
        "id": "test" 
    },
    "params": {
    }
}

To work same as this:

GET /statistic/_search/template
{
    "template": {
        "id": "test" 
    },
    "params": {
        "ProductIDs": []
    }
}
3

There are 3 answers

4
keety On BEST ANSWER

A workaround probably not the most elegant would be to change template query to be a should clause and add a match_all clause for empty list.

example:

{
    "filter" : { 
        "bool" : {
            "should" : [  
                { "terms" : { "status" : [ "{{#ProductIDs}}","{{.}}","{{/ProductIDs}}"] }} 
                {{^ProductIDs}},
                {"match_all":{}}
                {{/ProductIDs}}
            ]
        }
    }
}
0
user3272154 On

My suggestion to overcome this using a JSON template is:

{
   "query": {
      "bool": {
        "must": [
            {
                "script": {
                    "script": {
                        "inline": "1==1 {{#ProductIDs}} &&  [\"{{#ProductIDs}}\",\"{{.}}\",\"{{/ProductIDs}}\"].contains(doc['Product.ProductID'].value){{/ProductIDs}}",
                        "lang": "painless"
                    }
                }
            }
        ]
    }
}
0
slawek On

Didn't try it, but shouldn't something like this work?

{
  "filter" : {
    "bool" : {
      "must" : [
        {{#ProductIDs}}
          {{^ProductIDs.isEmpty}}
            { "terms" : { "Product.ProductID" : [{{#ProductIDs}}{{.}},{{/ProductIDs}}] } }
          {{/ProductIDs.isEmpty}}
          {{#ProductIDs.isEmpty}}
            {"match_all":{}}
          {{/ProductIDs.isEmpty}}
        {{/ProductIDs}}
        {{^ProductIDs}}
          {"match_all":{}}
        {{/ProductIDs}}
      ]
    }
  }
}

Ain't pretty, maybe there's better way.