当前位置: 首页 > 知识库问答 >
问题:

在Elasticsearch中使用短语匹配时忽略查询字符串中筛选的单词

洪俊捷
2023-03-14

我正在使用一个定制的索引分析器来删除一组特定的停用词。然后,我用包含一些停用词的文本进行短语匹配查询。我认为停用词会从查询中过滤掉,但事实并非如此(任何不包含停用词的文档都会被排除在结果之外)。

下面是一个简单的例子,说明我要做的事情:

    #!/bin/bash
    
    export ELASTICSEARCH_ENDPOINT="http://localhost:9200"
    
    # Create index, with a custom analyzer to filter out the word 'foo'
    curl -XPUT "$ELASTICSEARCH_ENDPOINT/play" -d '{
        "settings": {
            "analysis": {
                "analyzer": {
                    "fooAnalyzer": {
                        "type": "custom",
                        "tokenizer": "letter",
                        "filter": [
                            "fooFilter"
                        ]
                    }
                },
                "filter": {
                    "fooFilter": {
                        "type": "stop",
                        "stopwords": [
                            "foo"
                        ]
                    }
                }
            }
        },
        "mappings": {
            "myDocument": {
                "properties": {
                    "myMessage": {
                        "analyzer": "fooAnalyzer",
                        "type": "string"
                    }
                }
            }
        }
    }'
    
    # Add sample document
    curl -XPOST "$ELASTICSEARCH_ENDPOINT/_bulk?refresh=true" -d '
    {"index":{"_index":"play","_type":"myDocument"}}
    {"myMessage":"bar baz"}
    '

如果我对这个索引执行phrase_match搜索,在查询中间有一个过滤的停止字,我希望它匹配(因为'foo'应该被我们的分析器过滤掉)。

    curl -XPOST "$ELASTICSEARCH_ENDPOINT/_search?pretty" -d '
    {
        "query": {
            "match": {
                "myMessage": {
                    "type": "phrase",
                    "query": "bar foo baz"
                }
            }
        }
    }
    '

然而,我没有得到任何结果。

在执行搜索之前,有没有办法指示Elasticsearch对查询字符串进行标记和过滤?

编辑1:现在我更迷茫了。我之前看到,如果我的查询在查询文本中间包含停用词,短语匹配就不起作用。现在,另外,我发现如果文档在查询文本中间包含停用词,那么短语query不起作用。这里是一个最小的例子,仍然使用上面的映射。

    POST play/myDocument
    {
      "myMessage": "fib foo bar"  <---- remember that 'foo' is a stopword and is filtered out of analysis
    }

    GET play/_search
    {
        "query": {
            "match": {
                "myMessage": {
                    "type": "phrase",
                    "query": "fib bar"
                }
            }
        }
    }

这个查询不匹配。我对此非常惊讶!我希望foo停止字被过滤掉并忽略。

有关我预期会出现这种情况的示例,请参阅以下查询:

    POST play/myDocument
    {
      "myMessage": "fib 123 bar"
    }

    GET play/_search
    {
        "query": {
            "match": {
                "myMessage": {
                    "type": "phrase",
                    "query": "fib bar"
                }
            }
        }
    }

这是匹配的,因为'123’被我的'letter'tokenizer过滤掉了。短语匹配似乎完全忽略了停止词过滤,并且表现得好像这些标记一直在分析字段中(即使它们没有出现在_analyze的标记列表中)。

我目前最好的解决方法建议:

    < li >使用我的自定义分析器针对我的文档的文本字符串调用< code>_analyzeendpoint。这将从原始文本字符串中返回标记,但为我删除讨厌的停用词 < li >将仅使用标记的文本版本保存到文档的< code >“filtered”字段中

稍后,在查询时:

  • 使用我的自定义分析器针对我的查询字符串调用_analyze终结点,以仅获取令牌
  • 使用筛选的令牌字符串针对文档的新“筛选”字段使我的短语匹配查询

共有3个答案

商勇
2023-03-14

这里有一个类似问题的替代解决方案——但是删除英文停用词,处理多值字段;在7.10版上测试。它不需要显式使用< code>char_filter,它使用< code >标准分析器和< code >英语停用词并使字段成为< code >文本,因此它应该正确处理< code>match_phrases:

PUT play
    {
        "settings": {
            "analysis": {
                "analyzer": {
                    "phrase_analyzer": {
                        "type": "standard",
                        "stopwords": "_english_" //for my use case
                    }
                }
            }
        },
        "mappings": {
            // "myDocument" is not used in v7.x
            "properties": {
                "myMessage": {
                    "analyzer": "phrase_analyzer",
                    "type": "text" //changed to handle match_phrase
                }
            }
        }
    }

对于此演示数据:

    POST _bulk
    { "index": { "_index": "play", "_id": "1" } }
    { "myMessage": ["Guardian of the Galaxy"]}
    { "index": { "_index": "play", "_id": "2" } }
    { "myMessage": ["Ambassador of Peace", "Guardian of the Galaxy"]}
    { "index": { "_index": "play", "_id": "3" } }
    { "myMessage": ["Guardian of the Galaxy and Ambassador of Peace"]}
    { "index": { "_index": "play", "_id": "4" } }
    { "myMessage": ["Ambassador of Peace and Guardian of the Galaxy"]}
    { "index": { "_index": "play", "_id": "5" } }
    { "myMessage": ["Supreme Galaxy and All Living Beings Guardian"]}
    { "index": { "_index": "play", "_id": "6" } }
    { "myMessage": ["Guardian of the Sun", "Worker of the Galaxy"]}

查询 1:

    GET play/_search
    {
        "query": {
            "match_phrase": {
                "myMessage": {
                    "query": "guardian of the galaxy",
                    "slop": 99 //useful on multi-values text fields
                    //https://www.elastic.co/guide/en/elasticsearch/reference/7.10/position-increment-gap.html
                }
            }
        }
    }

应该返回文档1到5,因为每个文档至少有一个值匹配< code >“guardian”或< code >“galaxy”;doc 6将不会匹配,因为这些单词中的每一个都匹配不同的值,而不是相同的值(这就是为什么我们使用< code>slop=99)。

问题2:


    GET play/_search
    {
        "query": {
            "match_phrase": {
                "myMessage": {
                    "query": "\"guardian of the galaxy\"",
                    "slop": 99
                }
            }
        }
    }

应该只返回文档1到4,因为(转义的)双引号强制每个值进行精确的子串匹配,而文档5在不同的位置有2个单词。

问题是您使用了< code>stop token filter 1...

不允许令牌筛选器更改每个令牌的位置或字符偏移。

和一个< code>match_phrase查询,但是2...

match_phrase查询分析文本,并从分析的文本创建短语查询。

因此,在应用停止标记过滤器之前,已经计算了位置,并且match_phrase'123’工作正常,因为字母标记器>确实定义了

标记器还负责记录每个术语的顺序或位置。

在使用更大的数据种类测试了这个解决方案后,我发现了一些异常的误报——大约占4k搜索结果的0.3%。在我的特殊情况下,我在过滤器中使用了match_phrase。为了重现误报,我们可以切换第6项中值的顺序,这样"Galaxy""Guardian"这两个词看起来就很接近:

    POST _bulk
    { "index": { "_index": "play", "_id": "7" } }
    { "myMessage": ["Worker of the Galaxy", "Guardian of the Sun"]}

之前的查询1也会返回它,而它显然不应该返回它。我无法通过使用Elasticsearch API来解决它,但通过以编程方式从查询1中删除停止词来解决它(请参阅下一步)。

查询3:

    GET play/_search
    {
        "query": {
            "match_phrase": {
                "myMessage": {
                    "query": "guardian galaxy", //manually removed "of" and "the" stop words
                    "slop": 99 //useful on multi-values text fields
                    //https://www.elastic.co/guide/en/elasticsearch/reference/7.10/position-increment-gap.html
                }
            }
        }
    }
  1. Tokenizer与Token Filter:分析仪的剖析
  2. 匹配短语查询
  3. Tokenizer和位置计算:Token图
左丘元徽
2023-03-14

事实证明,如果您想使用短语匹配,令牌过滤器删除不需要的单词为时已晚。到那时,您的重要令牌的位置字段被过滤令牌的存在污染,并且短语匹配拒绝工作。

答案 - 在我们进入令牌过滤器级别之前进行过滤。我创建了一个char_filter,删除了我们不需要的术语,短语匹配开始正常工作!

    PUT play 
    {
        "settings": {
            "analysis": {
                "analyzer": {
                    "fooAnalyzer": {
                        "type": "custom",
                        "tokenizer": "letter",
                        "char_filter": [
                            "fooFilter"
                        ]
                    }
                },
                "char_filter": {
                    "fooFilter": {
                        "type": "pattern_replace",
                        "pattern": "(foo)",
                        "replacement": ""
                    }
                }
            }
        },
        "mappings": {
            "myDocument": {
                "properties": {
                    "myMessage": {
                        "analyzer": "fooAnalyzer",
                        "type": "string"
                    }
                }
            }
        }
    }

查询:

    POST play/myDocument
    {
      "myMessage": "fib bar"
    }
    
    GET play/_search
    {
        "query": {
            "match": {
                "myMessage": {
                    "type": "phrase",
                    "query": "fib foo bar"
                }
            }
        }
    }

    POST play/myDocument
    {
      "myMessage": "fib foo bar"
    }
    
    GET play/_search
    {
        "query": {
            "match": {
                "myMessage": {
                    "type": "phrase",
                    "query": "fib bar"
                }
            }
        }
    }

两者现在都工作了!

谭泳
2023-03-14

应该有效的解决方法:

  • 使用我的自定义分析器针对我的查询字符串调用_analyze终结点,这将返回原始查询字符串中的令牌,但为我删除讨厌的停用词
  • 使用过滤的标记使我的短语匹配查询

然而,这显然需要对我的每个查询调用两次Elasticsearch。如果可能的话,我想找一个更好的解决方案。

 类似资料:
  • 问题内容: Elasticsearch的新功能,试图更好地了解这些查询之间的区别。据我所知,匹配单个项(需要小写的比赛工作?),都和文本字符串匹配。 问题答案: 查询匹配单个术语: 不分析 值。因此,不必根据所索引的内容将其小写。 如果您是在索引时间提供的,并且未分析该值,则以下查询将不会返回任何内容: 如果为查询字段定义了分析器,则查询将分析输入,并查找符合以下条件的文档: 所有条款 必须出现在

  • 这里是Elasticsearch的新内容,并试图更好地理解这些查询之间的差异。据我所知,匹配单个术语(需要小写才能有效匹配?),和都匹配一个文本字符串。

  • 这些变化都是形式上的 其中“main()”可以是任何函数的名称。我想生成不是这种形式的所有变化的差异。 git diff的-g和-s选项非常接近--它们查找与字符串或正则表达式匹配的更改。 我还注意到,标准unix diff有-I选项来“忽略所有行都匹配re的更改”。但是我找不到用unix diff工具替换Git自己的diff的正确方法。

  • 通过看到我的结果,我有点困惑如何在MatchPherage和match query中进行评分 对于匹配短语,我有如下查询 获得的reults如下 a。“信用-消费者测试文章”得分12.64 b的文件。“信用-X测试文章”得分12.64 c的文件。“Credit-XYZ测试文章”得分10.92 d的文档。“信用测试文章”得分10.22 e的文件。“Credit-Z测试文章”得分09.40的文档 前两

  • 问题内容: 我想同时执行完全的单词匹配和部分的单词/子字符串匹配。例如,如果我搜索“男士剃须刀”,那么我应该能够在结果中找到“男士剃须刀”。但是,如果我搜索“剃须刀”,那么在结果中我也应该能够找到“剃须刀”。我使用以下设置和映射: 索引设置: 对应: 插入记录: 查询: 1.按完全匹配的词组进行搜索- >“男式” 上面的查询在返回结果中返回“男士剃须刀”。 2.按部分单词匹配搜索- >“ en’s

  • 问题内容: 我是Elasticsearch的新手,正在寻找模糊查询搜索。 我用这样的对象/记录值制作了新的索引产品 现在当我在elasticsearch中执行模糊查询搜索时 它会向我返回预期的正确记录(上述产品)。 而且我知道该术语匹配,因此记录已归还。 但是从技术上讲,我怎么知道它已经匹配了。elasticsearch仅返回完整记录,像这样 是否有elasticsearch任何方式,这样我可以知