Search code examples
elasticsearchtemplatesmustache

How to get a dynamic list of term clauses from a list of strings using Moustache template


I have a list of strings e.g. items = ["red", "green", "blue", ...] and I'd like to use templating to get

"should": [
  {
    "term": {
      "mycolor": "red"
    }
  },
  {
    "term": {
      "mycolor": "green"
    }
  },
  {
    "term": {
      "mycolor": "blue"
    }
  },
  ...
]

Now I'm trying

"should": [
  {{#items}}
  {
    "term": {
      "mycolor" : "{{.}}"
    }
  },
  {{/items}}
]

but it doesn't work because there will be a trailing comma at the end. How can I get rid of the trailing comma to make this work?


Solution

  • I found the answer myself. Moustache is a "logicless" templating tool, so it seems there is no better option than what I'm trying here. Please add answers or comments if you have better ideas.

    To render multiple term clauses and handle the trailing comma, I can do

    "should": [
      {{#items}}
      {
        "term": {
          "mycolor" : "{{color}}"
        }
      }
      {{^last}},{{/last}}
      {{/items}}
    ]
    

    where items are a list of objects like this

    params: {
    "items": [{
      "color": "red"
    },
    {
      "color": "green"
    },
    {
      "color": "blue",
      "last": 1
    }]
    }
    

    Using ^ operator, the "inverted section", the template knows not to render the comma when "last" is present.

    A more advanced case

    In case you have to add this list of term clauses on top of some existing ones, e.g. adding these "mycolor" terms on top of "mysize"

    "should": [
      {
        "term": {
          "mycolor": "red"
        }
      },
      {
        "term": {
          "mycolor": "green"
        }
      },
      {
        "term": {
          "mycolor": "blue"
        }
      },
      {
        "term": {
          "mysize": "xl"
        }
      },
      {
        "term": {
          "mysize": "l"
        }
      },
      ...
    ]
    

    You will need to handle the case where either list is empty, and handle whether to add a comma between them, i.e. if "mysize" list is not empty, "mycolor" should have a trailing comma, otherwise not. In this case we need to render a single comma based on whether the 2nd list is empty or not. If we use the 2nd list sizes as the tag, it doesn't work because Moustache will render the comma N times where N is the length of sizes. AFAIK there's no way to render once based on the list being non-empty. So I have to introduce another param sizesExist and set it in your code.

    "should": [
      {{#colors}}
      {
        "term": {
          "mycolor" : "{{color}}"
        }
      }
      {{^last}},{{/last}}
      {{#sizesExist}},{{/sizesExist}}
      {{/colors}}
      {{#sizes}}
      {
        "term": {
          "mysize" : "{{size}}"
        }
      }
      {{^last}},{{/last}}
      {{/sizes}}
    ]
    
    params: {
      "colors": [...],
      "sizes": [...],
      "sizesExist": true
    }