ElasticSearch - 输入即搜索 edge n-gram

  • 在此之前,ES所有的查询都是针对整个词进行操作,也就是说倒排索引存了hello这个词,一定得输入hello才能找到这个词,输入 h 或是 he 都找不到倒排索引中的hello

    • 然而在现实情况下,用户已经渐渐习惯在输入完查询内容之前,就能为他们展现搜索结果,这就是所谓的即时搜索(instant search),或是可以称为 输入即搜索(search-as-you-type)

    • 虽然ES提供了一系列的前缀搜索match_phrase、prefix、wildcard、regexp,然而这样的查询的性能非常差,要知道用户每多输入一个新的字母,就意味著要重新进行一次搜索,在实时的web系统中,100毫秒可能就会是一个难以忍受的延迟

    • 因此为了加快 输入即搜索 的查询效率,可以改使用edge n-gram建立索引,如此可以避免使用前缀查询,在建立索引时就进行优化,使用空间换取时间,让查询的速率增快

  • 使用edge n-gram建立索引

    • 假设有一个词hello,普通建索引时,就是把这个词hello放入倒排索引

      • 用户输入h、he时会找不到索引(倒排索引中只有hello),因此匹配失败

    • 而对于输入即搜索这种应用场景,可以使用一种特殊的n-gram,称爲 边界n-grams (edge n-grams)

      • 所谓的edge n-gram,就是指它会固定词语开始的一边滑动窗口,他的结果取决于 n 的选择长度

      • 以单词hello爲例,它的edge n-gram的结果如下

        h
        he
        hel
        hell
        hello
      • 因此可以发现到,在使用edge n-gram建索引时,一个单词会生成好几个索引,而这些索引一定是重头开始

      • 这符合了输入即搜索的特性,即是用户打h、he能找到倒排中的索引hhe,而这些索引对应著的数据就是hello

  • 具体实例

    • 建立索引时使用edge n-gram的token过滤器,为每个经过这个token过滤器的词条们,都生成从头开始的字符组合

      • 假设有一个输入QUICK! RUN!,分词器会先将它分词成两个词quickrun,此时这些词再一一的通过edge n-gram token过滤器,产生了8个索引q、qu、qui、quic、quick、r、ru、run,接著存入倒排索引中

      • 如此,任何词条像是quick、run,都能生成他们自己的n-gram

    • 另外要注意,要额外定义一个search_analyzer分析器,供查询使用

      • 原因是因为我们为了要保证倒排索引中包含各种组合的词,所以在建索引时才加入了edge n-gram过滤器,然而在查询时,我们只想匹配用户输入的完整词组,像是用户的输入runqu

      • 因此需要定义两套分析器,一套是建索引的分析器(包含edge n-gram过滤器),另一套是查询使用的正常的分析器

      PUT 127.0.0.1:9200/my_index
      {
          "settings": {
              "number_of_shards": 1,
              "analysis": {
                  "filter": {
                      //定义一个edge n-gram的token过滤器,并设置任何通过这个过滤器的词条,都会生成一个最小固定值为1,最大固定值为20的n-gram
                      "my_autocomplete_filter": {
                          "type": "edge_ngram",
                          "min_gram": 1,
                          "max_gram": 20
                      }
                  },
                  "analyzer": {
                      //自定义一个分析器,并使用自定义的edge n-gram过滤器
                      "my_autocomplete_analyzer": {
                          "type": "custom",
                          "tokenizer": "standard",
                          "filter": [
                              "lowercase",
                              "my_autocomplete_filter"
                          ]
                      }
                  }
              }
          },
          "mapping": {
              "my_type": {
                  "properties": {
                      "name": {
                          "type": "text",
                          "analyzer": "my_autocomplete_analyzer", //在索引时用,生成edge n-gram的每个词
                          "search_analyzer": "standard"  //查询用,只搜索用户输入的词
                      }
                  }
              }
          }
      }
    • 让非text字段也能使用edge n-gram

      • 由于edge n-gram是一个token过滤器,他包含在analyzer分析器裡面,因此只有text类型的字段才能使用 (其他类型的字段不会被分词,所以不会使用到analyzer,因此不能用edge n-gram)

      • 但是可能会有一种情况是,有些精确值也希望能通过edge n-gram生成组合,这时就要搭配使用一个叫做keyword的分词器

        • 注意,此keyword分词器和keyword字段类型是不同的东西

        • keyword分词器主要的功用是,将输入的词条,原封不动的output出来,不对其内容做任何改变

        • 因此可以利用这个特性,将精确值的字段类型改成text,但是分词器使用keyword,如此就可以避免分词的效果,又能使用edge n-gram

      • 具体实例

        • 将postcode这个本来是keyword类型的精确值,改成使用text类型并搭配keyword分词器

        • 因此假设有一个输入ABC EF,先经过keyword分词器分词成ABC EF(和输入一模一样),接著再经过edge n-gram生成A、AB、ABC、ABC (有一个空格) 、ABC E、ABC EF

          • 如果是使用正常的分词器,生成的edge n-gram会是A、AB、ABC、E、EF,是有差别的

          PUT 127.0.0.1:9200/my_index
          {
              "settings": {
                  "analysis": {
                      "filter": {
                          "postcode_filter": {
                              "type": "edge_ngram",
                              "min_gram": 1,
                              "max_gram": 8
                          }
                      },
                      "analyzer": {
                          "postcode_index": {
                              "tokenizer": "keyword",
                              "filter": [
                                  "postcode_filter"
                              ]
                          },
                          "postcode_search": {
                              "tokenizer": "keyword"
                          }
                      }
                  }
              },
              "mapping": {
                  "my_type": {
                      "properties": {
                          "postcode": {
                              "type": "text",
                              "analyzer": "postcode_index",
                              "search_analyzer": "postcode_search"
                          }
                      }
                  }
              }
          }

猜你喜欢

转载自blog.csdn.net/weixin_40341116/article/details/81842989
今日推荐