将国际字符映射到多个选项

时间:2017-02-17 15:15:58

标签: elasticsearch

我想要实现的是人们在没有语言意识的情况下搜索个人的能力,但不会惩罚那些人。我的意思是:

鉴于我建立索引:

  1. 乔根森
  2. 乔根森
  3. 约根森
  4. 我希望能够允许这样的转换:

    1. öto o
    2. öto oe
    3. ø到oe
    4. ø到oe
    5. 所以如果有人搜索: QUERY |结果(我只包括ID,但它实际上是完整的记录)

      • 约根森回归 - 1,2,3
      • Jörgensen返回 - 1,2
      • Jørgensen返回 - 1,3
      • Joergensen回归 - 2,3

      从那开始我尝试创建索引分析器并过滤:

      {
      "settings": {
          "analysis": {
            "analyzer": {
              "my_analyzer": {
                "tokenizer": "keyword",
                "char_filter": [
                  "my_char_filter"
                ]
              }
            },
            "char_filter": {
              "my_char_filter": {
                "type": "mapping",
                "mappings": [
                  "ö => o",
                  "ö => oe"
                ]
              }
            }
          }
        }
      }
      

      但这是无效的,因为它试图映射到同一个角色。

      我错过了什么?我需要多个分析仪吗?任何方向都会受到赞赏。

2 个答案:

答案 0 :(得分:1)

由于自定义映射在您的情况下是不够的,如上面的显示注释,让我们使用您的数据和char规范化。
在您的情况下,由于ø和oe转换,使用unidecode进行规范化是不够的。例如:

import unicodedata
def strip_accents(s):
    return ''.join(
        c for c in unicodedata.normalize('NFD', s)
        if unicodedata.category(c) != 'Mn'
    )

body_matches = [
    u'Jorgensen',
    u'Jörgensen',
    u'Jørgensen',
    u'Joergensen',
]
for b in body_matches:
    print b,strip_accents(b)

>>>> Jorgensen Jorgensen
>>>> Jörgensen Jorgensen
>>>> Jørgensen Jørgensen
>>>> Joergensen Joergensen

所以,我们需要一个自定义翻译。到目前为止,我只设置了你展示的那些字符,但随时可以填写清单。

accented_letters = {
    u'ö' : [u'o',u'oe'],
    u'ø' : [u'o',u'oe'],
}

然后,我们可以对单词进行规范化并将它们存储在特殊属性body_normalized中,并将它们作为Elasticsearch记录的字段编入索引。 插入后,您可以执行两种类型的搜索:

  1. 完全搜索:用户输入未规范化,Elasticsearch对body字段进行查询搜索,也未进行规范化。
  2. simliar搜索。用户输入已标准化,我们将搜索 body_normalized字段
  3. 让我们看一个例子

    body_matches = [
        u'Jorgensen',
        u'Jörgensen',
        u'Jørgensen',
        u'Joergensen',
    ]
    print "------EXACT MATCH------"
    for body_match in body_matches:
        elasticsearch_query = {
            "query": {
                "match" : {
                    "body" : body_match
                }
            }
        }
        es_kwargs = { 
            "doc_type"  : "your_type", 
            "index" : 'your_index', 
            "body" : elasticsearch_query
        }
    
        res = es.search(**es_kwargs)
        print body_match," MATCHING BODIES=",res['hits']['total']
    
        for r in res['hits']['hits']:
            print "-",r['_source'].get('body','')
    
    print "\n------SIMILAR MATCHES------"
    for body_match in body_matches:
        body_match = normalize_word(body_match)
        elasticsearch_query = {
            "query": {
                "match" : {
                    "body_normalized" : body_match
                }
            }
        }
        es_kwargs = { 
            "doc_type"  : "your_type", 
            "index" : 'your_index', 
            "body" : elasticsearch_query
        }
    
        res = es.search(**es_kwargs)
        print body_match," MATCHING NORMALIZED BODIES=",res['hits']['total']
    
        for r in res['hits']['hits']:
            print "-",r['_source'].get('body','')
    

    您可以在this notebook

    中看到正在运行的示例

答案 1 :(得分:0)

在玩了很多之后,到目前为止我想出了以下方法:

我们无法在一个字段中存储多个数据表示。这确实有意义,所以相反,就像它被建议一样,我们将它存储在像子字段这样的相同字段的多个表示中。我使用Kibana和/或Postman完成了所有操作。

使用以下设置创建索引:

PUT surname
{
  "mappings": {
    "individual": { 
      "_all": { "enabled": false  }, 
      "properties": { 
        "id":    { "type": "integer"  }, 
        "name" : {
            "type": "string",
            "analyzer": "not_folded",
            "fields": {
                "double": { 
                  "type":     "string",
                  "analyzer": "double_folder"
                },
                "single": { 
                  "type":     "string",
                  "analyzer": "folded"
                }
            }
        }
      }
    }
  },
  "settings": {
    "number_of_shards": 1,
    "analysis": {
      "analyzer": {
        "double_folder": {
          "tokenizer": "icu_tokenizer",
          "filter" : [
            "icu_folding"
          ],
          "char_filter": [
            "my_char_filter"
          ]
        },
        "folded": {
            "tokenizer": "icu_tokenizer",
            "filter": [
              "icu_folding"
            ]
          },
        "not_folded": {
            "tokenizer": "icu_tokenizer",
            "filter": [
              "lowercase"
            ]
          }
      },
      "char_filter": {
        "my_char_filter": {
          "type": "mapping",
          "mappings": [
            "ö => oe"
          ]
        }
      }
    }
  }
}

在这种情况下,它以3种不同的格式存储所有名称:

  1. 输入的方式
  2. 折叠到我想要的多个符号
  3. 折叠为单个符号
  4. 分片数量一个是测试的重要位,因为在没有足够数据的情况下,多个分片不能很好地工作。更多阅读Relevance is broken

    然后我们可以将测试数据添加到我们的索引中:

    POST surname/individual/_bulk
    { "index": { "_id": 1}}
    { "id": "1", "name": "Matt Jorgensen"}
    { "index": { "_id": 2}}
    { "id": "2", "name": "Matt Jörgensen"}
    { "index": { "_id": 3}}
    { "id": "3", "name": "Matt Jørgensen"}
    { "index": { "_id": 4}}
    { "id": "4", "name": "Matt Joergensen"}
    

    剩下的就是测试我们是否得到了适当的回应:

    GET surname/_search
    {
      "query": {
        "multi_match": {
          "type":     "most_fields", 
          "query":    "Jorgensen",
          "fields": [ "name","name.double",   "name.single" ]
        }
      }
    }