ElasticSearch 从指定术语中查找具有不同嵌套列表元素的索引对象

ElasticSearch find indexed objects with distinct nested list elements from specified terms

假设我们有一个 student 有一个 scores 的(nested)列表如下:

public class Student
{
    public string FullName { get; set; }
    public List<Score> Scores { get; set; } = new List<int>();
}

public class Score
{
    public int Value { get; set; }
}

如何使用 NEST(或简单查询)编写 ElasticSearch 查询来获取至少有 2 个不同分数的所有学生从 7 到 10。

因此,例如,如果学生的分数为 {2,7,10} 或 {8,10},他应该在我们的结果中,而学生的分数为 {10, 6, 5} 或 {8, 8} 或 {2, 7} 不应该出现在我们的结果中。

我想出的是:

GET /index/student/_search
{
  "query": {
    "nested": {
      "path": "scores",
      "query": {
        "bool": {
          "should":
          [
            {
              "bool": {
                "filter": [
                  {
                    "terms": {
                      "scores.value": [7, 10] 
                    }
                  },
                  {
                    "terms":{
                      "scores.value": [8, 9]
                    }
                  }
                ]
              }
            },
            {
              "bool": {
                "filter": [
                  {
                    "terms": {
                      "scores.value": [7, 8] 
                    }
                  },
                  {
                    "terms":{
                      "scores.value": [9, 10]
                    }
                  }
                ]
              }
            }
          ]
        }
      }
    }
  }
}

但 ElasticSearch 似乎不允许 TERMS 查询的连词(返回 0 次匹配)。即使它确实有效,我仍然需要更优雅的东西,因为如果我们有超过 4 个允许值,这将变得很痛苦。

更新

我尝试了以下脚本,但也得到了 0 个结果:

GET /index/student/_search
{
  "query": {
    "nested": {
      "path": "scores",
      "query": {
        "bool": {
          "filter": [
            {
              "exists": {
                "field": "scores"
              }
            },
            {
              "script": {
                "script": """
                   boolean condition = false;
                   def availableScores = [7, 8, 9, 10];
                   def scores = doc['scores.id'].values;
                   
                   for (int i = 0; i < scores.length; i++)
                    for(int j = 0; j < availableScores.length; j++)
                      if (scores[i] == availableScores[j])
                      {
                        if (condition == true) 
                          return (true);
                          
                        condition = true;
                        availableScores.remove(j);
                        break;
                      } 
                   return (false)"""
              }
            }
          ]
        }
      }
    }
  }
}

很长一段时间后,我找到了一个有效的查询:

GET /index/student/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "bool": {
            "must": [
              {
                "nested": {
                  "path": "scores",
                  "query": {
                    "terms": {
                      "scores.value": [
                        7,
                        10
                      ]
                    }
                  }
                }
              },
              {
                "nested": {
                  "path": "scores",
                  "query": {
                    "terms": {
                      "scores.value": [
                        8,
                        9
                      ]
                    }
                  }
                }
              }
            ]
          }
        },
        {
          "bool": {
            "must": [
              {
                "nested": {
                  "path": "scores",
                  "query": {
                    "terms": {
                      "scores.value": [
                        7,
                        9
                      ]
                    }
                  }
                }
              },
              {
                "nested": {
                  "path": "scores",
                  "query": {
                    "terms": {
                      "scores.value": [
                        8,
                        10
                      ]
                    }
                  }
                }
              }
            ]
          }
        }
      ]
    }
  }
}

这里的技巧是将 1 个嵌套查询分成多个,然后将 它们 放在 should 查询中。

我仍然想要一个更优雅的解决方案(我想是 script),但现在,我将它作为最终答案。