【问题标题】:Why does elasticsearch not return documents which contain almost equivalent word?为什么 elasticsearch 不返回包含几乎相同单词的文档?
【发布时间】:2020-08-27 09:37:58
【问题描述】:

我想使用elasticsearch来搜索与用户提供的搜索词相关的文档(文档文本是荷兰语,也假设用户搜索的词也是荷兰语) .

我还使用同义词为搜索的单词返回相同的文档,这些单词拼写不同但在荷兰语中表示相同的意思。我将这些同义词存储在 elasticsearch 的 config 文件夹中的 synonyms.txt 文件中。

为了测试搜索是否正常,我使用单词loopbaan 作为用户可能搜索的示例。此外,在 synonyms.txt 文件中,我将这个词与其同义词 carriere 联系起来。这是以这种格式完成的:

...
loopbaan, carriere
...

现在当我使用分析器分析loopbaan 时,如下所示:

GET /documents/_analyze
{
    "analyzer": "test_analyzer",
    "text": "loopbaan"
}

我得到以下结果:

{
    "tokens": [
        {
            "token": "loopban",
            "start_offset": 0,
            "end_offset": 8,
            "type": "<ALPHANUM>",
            "position": 0
        },
        {
            "token": "carrier",
            "start_offset": 0,
            "end_offset": 8,
            "type": "SYNONYM",
            "position": 0
        }
    ]
}

我知道loopbaan 被转换为loopban 因为我使用荷兰语词干分析器,但是 loopban 确实NOTloopbaan 的意思相同在荷兰语中,并且出现在我在 documents 索引中编入索引的任何文本中。

因此,当我使用以下查询搜索 loopbaan 时:

{
    "query": {
        "simple_query_string": {
            "query": "loopbaan",
            "fields": [
                "content^1.0"
            ],
            "analyzer": "test_analyzer",
            "flags": -1,
            "default_operator": "or",
            "analyze_wildcard": false,
            "auto_generate_synonyms_phrase_query": true,
            "fuzzy_prefix_length": 0,
            "fuzzy_max_expansions": 50,
            "fuzzy_transpositions": true,
            "boost": 1
        }
    }
}

我没有结果:

{
    "took": 1,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 0,
            "relation": "eq"
        },
        "max_score": null,
        "hits": []
    }
}

问题: 如何通过搜索词 "loopbaan" 获得预期结果(我知道至少有 5 个文档包含词 "loopbaan")?。 p>

注意:我知道在 elasticsearch 中存在 stemmer-override,但我希望搜索尽可能通用,并且每次荷兰语词干分析器做得不好时,我都不会在词干分析器覆盖中添加单词。我还希望 loopbaan 的复数形式(即loopbanen)返回与我搜索 loopbaan 时完全相同的结果。这就是我使用词干分析器的原因。

这就是我创建文档索引的方式:

PUT /documents
{
    "aliases": {},
    "mappings": {
        "properties": {
            "content": {
                "type": "text"
            },
            "title": {
                "type": "text"
            }
        }
    },
    "settings": {
        "analysis": {
            "filter": {
                "test_synonyms": {
                    "type": "synonym",
                    "synonyms_path": "synonyms.txt",
                    "lenient": "true"
                },
                "dutch_stemmer": {
                    "type": "stemmer",
                    "language": "dutch"
                },
                "dutch_stopwords": {
                    "type": "stop",
                    "stopwords": "_dutch_"
                },
                "test_ascii_folding": {
                    "type": "asciifolding"
                }
            },
            "analyzer": {
                "test_analyzer": {
                    "filter": [
                        "lowercase",
                        "test_ascii_folding",
                        "dutch_stopwords",
                        "dutch_stemmer",
                        "test_synonyms"
                    ],
                    "tokenizer": "standard"
                }
            }
        }
    }
}

更新

复制的 2 个同义词

loopbaan, carriere => loopbaan, carriere
schakelen, koppelen, toggelen => schakelen, koppelen, toggelen

要复制的 3 个文档(第一个和第三个示例应与 loopbanenloopbaan 匹配,因为它们包含 carriere):

{
   "title": "Hoezo is dit goed gedaan in het onderwijs?"
   "content": "Werken is goed voor de mensen die in Nederlands wonen. Het verbetert de economie en de welzijn van de mensen. Carrière opbouwen is ook zeer belangrijk voor de specialisatie van de nederlandse mensen in onze samenleving."
}, 
{
   "title": "Dit slaat toch nergens op dat mensen dit kunnen doen."
   "content": "Mensen moeten koppelen. Wat nou "dit" is in deze context weet ik ook niet maar ja zo kan je zien dat elke woord zomaar iets kan betekenen toch? Zou zeggen van wel maar dit heeft niks te maken met iets dus de mazzel."
},
{
   "title": "Werken moet door iedereen gedaan worden en niet alleen door paar mensen in nederland"
   "content": "Werken moet door iedereen gedaan worden en niet alleen door paar mensen in nederland. Het moet echt zo zijn dat mensen carrieres opbouwen en niet alleen thuis zitten, want dat is slecht voor gezondheid van de mensen en de economie over het algemeen."
}

【问题讨论】:

    标签: database elasticsearch search text full-text-search


    【解决方案1】:

    您正在使用一种分析器进行索引,而另一种分析器用于搜索。推荐的做你想做的事的方法可以在here

    找到

    有两种方法可以满足您的需求。

    1. 您可以使用multi-fields。您将自定义分析器用于一个字段,并且与查询时使用的分析器完全相同(在这种情况下,标准分析器是文本的默认值,可以省略)。
    {
      "mappings": {
        "properties": {
          "content": {
            "type": "text",
            "fields": {
              "stemmed": {
                "type": "text",
                "analyzer": "test_analyzer"
              }
            }
          },
          "title": {
            "type": "text"
          }
        }
      }
    }
    
    {
      "query": {
        "simple_query_string": {
          "query": "loopbaan",
          "fields": [
            "content^1.0",
            "context.stemmed^1.0"
          ],
          "analyzer": "test_analyzer",
          "flags": -1,
          "default_operator": "or",
          "analyze_wildcard": false,
          "auto_generate_synonyms_phrase_query": true,
          "fuzzy_prefix_length": 0,
          "fuzzy_max_expansions": 50,
          "fuzzy_transpositions": true,
          "boost": 1
        }
      }
    }
    

    此解决方案对您的集群来说非常繁重,因为它会使您的索引变得更大

    1. 您可以修改您的查询并分析您的查询两次,然后将其包装在一个 bool 查询中的 should 子句中。基本上你做的就是这个
    Match MY_QUERY(analyzed with my custom analyzer) 
    OR 
    Match MY_QUERY(by using the same analyzer my field used when it was saved)
    
    {
      "query": {
        "bool": {
          "minimum_should_match": 1, 
          "should": [
            {
              "simple_query_string": {
                "query": "loopbaan",
                "fields": [
                  "content^1.0"
                ],
                "analyzer": "test_analyzer",
                "flags": -1,
                "default_operator": "or",
                "analyze_wildcard": false,
                "auto_generate_synonyms_phrase_query": true,
                "fuzzy_prefix_length": 0,
                "fuzzy_max_expansions": 50,
                "fuzzy_transpositions": true,
                "boost": 1
              }
            },
            {
              "simple_query_string": {
                "query": "loopbaan",
                "fields": [
                  "content^1.0"
                ],
                "flags": -1,
                "default_operator": "or",
                "analyze_wildcard": false,
                "auto_generate_synonyms_phrase_query": true,
                "fuzzy_prefix_length": 0,
                "fuzzy_max_expansions": 50,
                "fuzzy_transpositions": true,
                "boost": 1
              }
            }
          ]
        }
      }
    }
    

    我会使用第二个选项

    总之,您可以选择分析两次文档或分析两次查询。这取决于你。

    更新

    PUT documents
    {
      "aliases": {},
      "mappings": {
        "properties": {
          "content": {
            "type": "text",
            "analyzer": "test_analyzer_without_stemmer"
          },
          "title": {
            "type": "text"
          }
        }
      },
      "settings": {
        "analysis": {
          "filter": {
            "test_synonyms": {
              "type": "synonym",
              "synonyms": [
                "loopbaan,carriere,carrieres",
                "schakelen,koppelen,toggelen"
              ],
              "lenient": "true"
            },
            "dutch_stemmer": {
              "type": "stemmer",
              "language": "dutch"
            },
            "dutch_stopwords": {
              "type": "stop",
              "stopwords": "_dutch_"
            },
            "test_ascii_folding": {
              "type": "asciifolding"
            }
          },
          "analyzer": {
            "test_analyzer": {
              "filter": [
                "lowercase",
                "test_ascii_folding",
                "dutch_stopwords",
                "dutch_stemmer",
                "test_synonyms"
              ],
              "tokenizer": "standard"
            },
            "test_analyzer_without_stemmer": {
              "filter": [
                "lowercase",
                "test_ascii_folding",
                "dutch_stopwords",
                "test_synonyms"
              ],
              "tokenizer": "standard"
            }
          }
        }
      }
    }
    
    PUT documents/_doc/1
    {
       "title": "Hoezo is dit goed gedaan in het onderwijs?",
       "content": "Werken is goed voor de mensen die in Nederlands wonen. Het verbetert de economie en de welzijn van de mensen. Carrière opbouwen is ook zeer belangrijk voor de specialisatie van de nederlandse mensen in onze samenleving."
    }
    
    PUT documents/_doc/2
    {
       "title": "Dit slaat toch nergens op dat mensen dit kunnen doen.",
       "content": "Mensen moeten koppelen. Wat nou \"dit\" is in deze context weet ik ook niet maar ja zo kan je zien dat elke woord zomaar iets kan betekenen toch? Zou zeggen van wel maar dit heeft niks te maken met iets dus de mazzel."
    }
    
    PUT documents/_doc/3
    {
       "title": "Werken moet door iedereen gedaan worden en niet alleen door paar mensen in nederland",
       "content": "Werken moet door iedereen gedaan worden en niet alleen door paar mensen in nederland. Het moet echt zo zijn dat mensen carrieres opbouwen en niet alleen thuis zitten, want dat is slecht voor gezondheid van de mensen en de economie over het algemeen."
    }
    
    GET documents/_search
    {
      "query": {
        "bool": {
          "minimum_should_match": 1, 
          "should": [
            {
              "simple_query_string": {
                "query": "loopbaan",
                "fields": [
                  "content"
                ],
                "analyzer": "test_analyzer",
                "flags": -1,
                "default_operator": "or",
                "analyze_wildcard": false,
                "auto_generate_synonyms_phrase_query": true,
                "fuzzy_prefix_length": 0,
                "fuzzy_max_expansions": 50,
                "fuzzy_transpositions": true,
                "boost": 1
              }
            },
            {
              "simple_query_string": {
                "query": "loopbaan",
                "fields": [
                  "content^1.0"
                ],
                "default_operator": "or",
                "flags": -1,
                "analyze_wildcard": false,
                "auto_generate_synonyms_phrase_query": true,
                "fuzzy_prefix_length": 0,
                "fuzzy_max_expansions": 50,
                "fuzzy_transpositions": true,
                "boost": 1
              }
            }
          ]
        }
      }
    }
    

    【讨论】:

    • 嗨,Alkis,非常感谢您的回复。我对 Elasticsearch 很陌生,所以你能澄清一下Match MY_QUERY(by using the same analyzer my field used when it was saved) 的意思吗?因为我不知道哪个分析器用于索引和搜索。
    • 查看elastic.co/guide/en/elasticsearch/reference/current/…,如果您需要更多信息,请在此处发表评论
    • 哦,好吧,现在我明白了区别,虽然我没有定义搜索分析器,只是我自己制作的自定义分析器。那么在这种情况下,什么被用作我的标准分析器?
    • 有两种方法可以定义不同于分析器(索引/文档分析器)的搜索分析器。 1. 如文档中所述,在映射中使用 search_analyzer。 2.你做了什么,在查询期间指定一个分析器。因此,在您的情况下,您正在使用标准分析器(文本类型的默认值)分析您的文档,并使用 test_analyzer 分析您的查询。这就是问题所在。
    • 第二个建议应该可以在不更改映射的情况下解决您的问题。试过了吗?
    猜你喜欢
    • 2010-09-05
    • 1970-01-01
    • 1970-01-01
    • 2011-10-08
    • 1970-01-01
    • 2020-09-13
    • 1970-01-01
    • 2018-03-11
    • 1970-01-01
    相关资源
    最近更新 更多