为什么 elasticsearch 不包含 return 个包含几乎相同单词的文档?

Why does elasticsearch not return documents which contain almost equivalent word?

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

我还使用 return 相同文档的同义词来搜索拼写不同但在荷兰语中意思相同的搜索词。我将这些同义词存储在 elasticsearch 的配置文件夹中的 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 确实 NOT 与荷兰语中的 loopbaan 意思相同,并且 NOT 出现在我在 documents[=77 中索引的任何文本中=]索引.

因此,当我使用以下查询搜索 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": []
    }
}

问题: 我怎样才能通过搜索 [=94= 这个词来获得预期的结果(我知道至少有 5 个文档包含 "loopbaan" 这个词) ]?.

注意: 我知道 elasticsearch 中存在 stemmer-override,但我希望搜索尽可能通用,而不是每次都在荷兰语词干分析器做得不好。我还希望 loopbaan 的复数形式(即 loopbanen)到 return 与我搜索 loopbaan[=77 时完全相同的结果=].这就是我使用词干分析器的原因。

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

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."
}

您正在使用一个分析器进行索引,另一个进行搜索。可以找到做你想做的推荐方法 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. 您可以修改您的查询并分析您的查询两次,然后将其包装在 should 子句中的 bool 查询中。基本上你做的就是这个
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
          }
        }
      ]
    }
  }
}