【问题标题】:Elasticsearch match exact terms with spaces across different fieldsElasticsearch 将精确术语与跨不同字段的空格匹配
【发布时间】:2020-04-16 18:54:32
【问题描述】:

我在 elastic 中的数据设置有不同的字段:类别、子类别、乐器和情绪。我的目标是只为传递给它的所有关键字提供完全匹配的结果,并且只返回与所有内容匹配的结果。到目前为止,这似乎一直有效,直到我使用由多个用空格分隔的单词组成的关键字,如下所示:

"query": {
    "bool": {
      "must": [
        {
          "match": {
            "categories": "Electronic"
          }
        },
        {
          "match": {
            "categories": "Pop"
          }
        },
        {
          "match": {
            "instruments": "Female Vocal"
          }
        }
      ]
    }
}

我在 ES 中的数据由这种类型的数据组成:

[name] => Some Data Name
[categories] => Electronic,Pop
[subcategories] => 1970s,Alternative,Experimental,Retro
[instruments] => Electronic Drums,Male Vocal,Synth
[moods] => Fun,Futuristic,Pulsing,Quirky,Rhythmic

因此,它与乐器字段的“人声”部分匹配,但不完全匹配“女声”。

这可能会通过 ES 过滤器解决吗?

编辑: 为了考虑其他字符,我稍微扩展了示例数据集:

[categories]=>R&B,Dance/House
[instruments] => Electronic Drums,Male Vocal,Synth
[moods] => Fun,Futuristic,Pulsing,Quirky,Rhythmic

因此,可能会使用与号、斜线和空格。逗号将分隔单独的术语。

已解决 我最终对分析器进行了更多研究,并意识到我可能需要创建一个自定义的分析器来说明我的关键字的边界。

myesurl/tracks/_settings    
{
      "index": {
        "analysis": {
          "tokenizer": {
            "comma": {
              "type": "pattern",
              "pattern": ","
            }
          },
          "analyzer": {
            "tracks_analyzer": {
              "type": "custom",
              "tokenizer": "comma",
              "filter": [
                "trim",
                "lowercase"
              ]
            }
          }
        }
      }
    }

然后我设置一个映射:

{
  "track": {
    "properties": {
      "categories": {
        "type": "string",
        "analyzer": "tracks_analyzer"
      },
      "subcategories": {
        "type": "string",
        "analyzer": "tracks_analyzer"
      },
      "instruments": {
        "type": "string",
        "analyzer": "tracks_analyzer"
      },
      "moods": {
        "type": "string",
        "analyzer": "tracks_analyzer"
      }
    }
  }
}

然后将内容推送到elasticsearch中。似乎按预期工作。它现在考虑关键字中的任何字符,只要关键字与由分隔的逗号创建的标记匹配。

【问题讨论】:

    标签: elasticsearch


    【解决方案1】:

    使用match 查询意味着您输入的任何字符串都将由标准分析器进行分析,从而拆分为空格并小写。如您所见,只要每个字段匹配一个单词就可以了,但是,只要您搜索的内容包含空格,就会很有趣。

    发生的情况是,在索引时,Female Vocal 将分为两个标记 femalevocal 并索引到 instruments 字段中。 Male Vocal 被索引为两个标记 malevocal 也是如此。因此也会匹配带有Male Vocal 的字段。然后,当您在Female Vocal 上使用match 时,会发生搜索词被拆分并小写为femalevocal 并且词vocal 将匹配两个文档与Male VocalFemale Vocal.

    如果你想要精确匹配,你需要两件事: 1. 在映射中声明您需要与not_analyzed 完全匹配的字符串字段 2. 使用不分析搜索词的term queries(或term filters)。

    使用这样的映射很容易得出第一点:

    curl -XPUT localhost:9200/my_index -d '{
       "mappings": {
           "my_type": {
               "properties": {
                   "categories": {
                       "type": "string",
                       "index": "not_analyzed"
                   },
                   "subcategories": {
                       "type": "string",
                       "index": "not_analyzed"
                   },
                   "instruments": {
                       "type": "string",
                       "index": "not_analyzed"
                   },
                   "moods": {
                       "type": "string",
                       "index": "not_analyzed"
                   },
                   ...
               }
           }
       }
    }'
    

    使用这样的映射,Female Vocal 将不会被分析(即不被索引为femalevocal),而是被逐字索引为Female Vocal

    然后您可以使用这样的查询来查询确切的字段值:

    curl -XPOST localhost:9200/my_index/my_type/_search -d '{
        "query": {
            "bool": {
              "must": [
                {
                  "term": {
                    "categories": "Electronic"
                  }
                },
                {
                  "term": {
                    "categories": "Pop"
                  }
                },
                {
                  "term": {
                    "instruments": "Female Vocal"
                  }
                }
              ]
            }
        }
    }'
    

    【讨论】:

    • 请注意,使用这种方法,您的搜索也会区分大小写。也可以看看关键字分析器。
    • 好吧,不过,jpea 确实说“完全匹配”。但好的一点,keyword 分析器确实是我的下一个选择,这取决于他的反馈。
    • 谢谢,我今天试试。精确匹配,甚至区分大小写,是我的目标。这些术语来自已知来源,因此在这种情况下无需考虑用户/输入错误。
    • 所以,我所追求的并不是“完全”。有没有办法让它在创建索引时忽略特定字符?我希望它使用逗号作为描述术语的一种方式,但将带有空格和斜杠的术语视为一个术语。使用此映射,它返回的结果仅包含类别、工具等中的 1 个术语。
    • 好的,那么请提供更多示例数据,其中包含您要考虑的字符以及您期望在搜索中出现的内容。我们会弄清楚的。
    【解决方案2】:

    我最终对分析器进行了更多研究,并意识到我可能需要创建一个自定义分析器来解决关键字的边界问题。

    myesurl/tracks/_settings    
    {
          "index": {
            "analysis": {
              "tokenizer": {
                "comma": {
                  "type": "pattern",
                  "pattern": ","
                }
              },
              "analyzer": {
                "tracks_analyzer": {
                  "type": "custom",
                  "tokenizer": "comma",
                  "filter": [
                    "trim",
                    "lowercase"
                  ]
                }
              }
            }
          }
        }
    

    然后我设置一个映射:

    {
      "track": {
        "properties": {
          "categories": {
            "type": "string",
            "analyzer": "tracks_analyzer"
          },
          "subcategories": {
            "type": "string",
            "analyzer": "tracks_analyzer"
          },
          "instruments": {
            "type": "string",
            "analyzer": "tracks_analyzer"
          },
          "moods": {
            "type": "string",
            "analyzer": "tracks_analyzer"
          }
        }
      }
    }
    

    然后将内容推送到elasticsearch中。似乎按预期工作。它现在考虑关键字中的任何字符,只要关键字与由分隔的逗号创建的标记匹配。

    【讨论】:

      【解决方案3】:

      一个不错的解决方案是使用matchminimum_should_match,提供您想要匹配的单词的百分比。它可以是 100% 并且将返回至少包含给定文本的结果;

      重要的是,这种方法不考虑单词的顺序。

      "query":{
        "bool":{
           "should":[
              {
                 "match":{
                    "my_text":{
                       "query":"I want to buy a new new car",
                       "minimum_should_match":"90%"
                    }
                 }
              }
           ]
        }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多