Search code examples
elasticsearchelasticsearch-painless

Elastic painless and multivalued objects


I want to do a painless script that will get the max groupe_an value filtered by an. I have indexed as follows, without nesting. Then I add multi-valued data in an array. But I somehow lose the consistency of the order in the two list of fields in the painless script. Using the index k is not nice, I am just experimenting. I would have liked to iterate on the objects but could not find a way to do that, something like

for (item in doc['micro.ans'])

but I get

"caused_by" : {
    "type" : "illegal_argument_exception",
    "reason" : "Extraneous for each loop."
  }

I may have to index differently. What would be the good solution in terms of index and in terms of painless script ? Thank you.

    PUT test-500
    {
      "mappings": {
        "properties": {
          "micro" : {
            "properties": {
              "ans" : {
                "properties" : {
                  "an" : {
                    "type" : "keyword"
                  },
                  "groupe_an" : {
                    "type": "float"
                  }
                }
              }
            }
          }
        }
      }
    }

    PUT test-500/_doc/1
    {
      "micro" : {
        "ans" : [
          {"an": "686660", "groupe_an" : 14.0},
          {"an": "439750", "groupe_an" : 6.0}
        ]
      }
    }      

    PUT test-500/_doc/2
    {
      "micro" : {
        "ans" : [
          {"an": "286660", "groupe_an" : 100.0},
          {"an": "239750", "groupe_an" : 200.0}
        ]
      }
    }

    GET test-500/_search
    {
      "query": {
        "script_score" : {
          "query" : {
            "match_all": {}
          },
          "script" : {
            "source" : """
              double max = 0;
              int k=0;
              List ans = doc['micro.ans.an'];
              List groupe_ans = doc['micro.ans.groupe_an'];
              for (an in ans) {
                if (an == params.queried_an && groupe_ans[k]>max) {
                  max=groupe_ans[k]} k=k+1
              }
              return max
            """,
            "lang" : "painless",
            "params" : {
              "queried_an" : "239750"
            }
          }
        }
      }
    }

Solution

  • I ended up using a concatenation of the two fields into a single keyword typed field:

        PUT test-2003
        {
          "mappings": {
            "properties": {
              "micro" : {
                "properties": {
                  "an_groupe" : {
                    "type" : "keyword"
                  }
                }
              }
            }
          }
        }
    
        PUT test-2003/_doc/1
        {
          "micro" : {
            "an_groupe" : [
              "686660|14.0",
              "439750|6.0"
            ]
          }
        }      
    
        PUT test-2003/_doc/2
        {
          "micro" : {
            "an_groupe" : [
              "68666022|100.0",
              "43975022|200.0"
            ]
          }
        }
    
        GET test-2003/_search
        {
          "query": {
            "script_score" : {
              "query" : {
                "match_all": {}
              },
              "script" : {
                "source" : """
                  double max = 0;
                  for (an_groupe in doc['micro.an_groupe']) {
                    String[] splitted = an8_groupe.splitOnToken("|");
                    double groupe_val = Double.parseDouble(splitted[1]);
                    for (queried_an in params.queried_ans) {
                      if (splitted[0] == queried_an && groupe_val > max) {
                        max = groupe_val
                      }
                    }
                  }
                  return max
                """,
                "lang" : "painless",
                "params" : {
                  "queried_ans" : ["68666022", "43975022", "439750"]
                }
              }
            }
          }
        }