【问题标题】:ElasticSearch query to exclude certain resultsElasticSearch 查询以排除某些结果
【发布时间】:2021-06-08 15:10:22
【问题描述】:

我正在尝试编写一个允许过滤结果集的 ElasticSearch 查询。该应用程序为职位提供了一个过滤器,也为相同的职位提供了一个排除过滤器。所以例如在下面的数据集中,我想过滤Engineer,但也排除Software Engineer。问题是现在查询也排除了Principal Software Engineer,它不应该。

这是我正在使用的数据:

{
  "data": [
    {
      "email": "chris@example.com",
      "job_title": "Industrial Electrical Engineer"
    },
    {
      "email": "esther@example.com",
      "job_title": "Chief Revenue Officer"
    },
    {
      "email": "george@example.com",
      "job_title": "Principal Software Engineer"
    },
    {
      "email": "helena@example.com",
      "job_title": "Software Engineer"
    },
    {
      "email": "john@example.com",
      "job_title": "Engineer"
    },
    {
      "email": "judith@example.com",
      "job_title": "Design Engineer"
    },
    {
      "email": "katy@example.com",
      "job_title": "Software Designer"
    },
    {
      "email": "mark@example.com",
      "job_title": "Engineer"
    },
    {
      "email": "mary@example.com",
      "job_title": "Mechanical Design Engineer"
    },
    {
      "email": "tom@example.com",
      "job_title": "Electrical Engineer"
    },
    {
      "email": "tony@example.com",
      "job_title": "Chief Executive Officer"
    }
  ]
}

这里是 ElasticSearch 查询:

{
  "query": {
    "bool": {
      "must": [
        {
          "bool": {
            "should": [
              {
                "match": {
                  "job_title": {
                    "query": "Engineer",
                    "operator": "and"
                  }
                }
              }
            ]
          }
        }
      ],
      "filter": [
        {
          "term": {
            "user_id": 1
          }
        },
        {
          "bool": {
            "must_not": [
              {
                "match": {
                  "job_title": "Software Engineer"
                }
              }
            ]
          }
        }
      ]
    }
  }
}

【问题讨论】:

    标签: elasticsearch filter


    【解决方案1】:

    假设job_titletext 类型。如果未指定分析器,Elasticsearch 将 standard analyzer 用于 text 类型字段。这会将“软件工程师”分解为

    {
      "tokens": [
        {
          "token": "software",
          "start_offset": 0,
          "end_offset": 8,
          "type": "<ALPHANUM>",
          "position": 0
        },
        {
          "token": "engineer",
          "start_offset": 9,
          "end_offset": 17,
          "type": "<ALPHANUM>",
          "position": 1
        }
      ]
    }
    

    因此,当使用must_not 进行查询并匹配"Software Engineer" 的查询时,它不会包含任何包含softwareengineer 的结果


    如果您没有明确定义任何映射,那么您需要将 .keyword 添加到 job_title 字段。这使用关键字分析器而不是标准分析器(注意 job_title 字段后的“.keyword”)。

    将您的查询修改为

    {
      "query": {
        "bool": {
          "must": [
            {
              "bool": {
                "should": [
                  {
                    "match": {
                      "job_title": {
                        "query": "Engineer",
                        "operator": "and"
                      }
                    }
                  }
                ]
              }
            }
          ],
          "filter": [
            {
              "bool": {
                "must_not": [
                  {
                    "term": {
                      "job_title.keyword": "Software Engineer"
                    }
                  }
                ]
              }
            }
          ]
        }
      }
    }
    

    更新 1:

    如果您使用的是 elasticsearch 7.10 或更高版本,并且您希望搜索不区分大小写以及搜索确切的术语,那么您可以使用case_insensitive param with the term query.

    {
      "query": {
        "bool": {
          "must": [
            {
              "bool": {
                "should": [
                  {
                    "match": {
                      "job_title": {
                        "query": "Engineer",
                        "operator": "and"
                      }
                    }
                  }
                ]
              }
            }
          ],
          "filter": [
            {
              "bool": {
                "must_not": [
                  {
                    "term": {
                      "job_title.keyword": {
                        "value": "software engineer",
                        "case_insensitive": true
                      }
                    }
                  }
                ]
              }
            }
          ]
        }
      }
    }
    

    否则,如果你使用的是7.10以下的版本,那么你需要修改你的索引映射,如下图,然后重新索引数据

    {
      "settings": {
        "analysis": {
          "normalizer": {
            "my_normalizer": {
              "type": "custom",
              "filter": [
                "lowercase"
              ]
            }
          }
        }
      },
      "mappings": {
        "properties": {
          "job_title": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword",
                "normalizer": "my_normalizer"
              }
            }
          }
        }
      }
    }
    

    【讨论】:

    • 感谢您的回复。我确实用.keyword 尝试过这个版本,但没有收到任何结果。在您的帖子之后,我再次尝试,但这次写了Software Engineer 而不是software engineer,就像我之前所做的那样,它奏效了。
    • @Cosmin 很高兴这对你有用:-)你能不能也接受答案:-)
    • @Cosmin 它不适用于小写字母,因为关键字分析器会寻找精确匹配,而不是像标准分析器那样寻找分析的文本
    • 可以调整为不区分大小写吗?我相信用户总是会输入小写字母。
    • @Cosmin 是的,您可以这样做,但为此,您需要更改索引映射。你能告诉你使用的是哪个版本的elasticsearch吗?
    【解决方案2】:

    您可以在 'must_not' 子句中使用 match phrase 来仅排除确切的短语 'Software Engineer'。

    【讨论】:

    • 我已经试过了。 match_phrase 的问题在于,如果我想排除 Software Engineer,它也会排除 Principal Software Engineer
    • 我认为你描述的行为应该只发生在'phrase_prefix'@Cosmin
    猜你喜欢
    • 2021-07-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-21
    • 2012-08-09
    • 2014-02-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多