如何基于ElasticSearch实现用户检索系统

1. 序言

在大数据技术栈里,ElasticSearch常被作为NoSQL数据库用于OLAP查询场景。ElasticSearch底层基于Lucene索引实现,设计初衷是降低Lucene的使用门槛,并扩充其功能,用于全文检索领域。本文将介绍如何利用ElasticSearch提供的全文检索API实现用户检索系统。

2. 需求分析

2.1 用户数据

用户数据使用JSON格式,示例如下:

{
    “id": 7788,
    "nick_name": "天生妩媚",
    "followers": 259
}

id是用户唯一标识,nick_name代表用户昵称,followers代表粉丝量。

2.2 用户检索需求

用户检索需求非常简单,输入关键词,全文检索用户昵称,返回结果按相关性评分和用户粉丝量排序,相关性评分相当的情况下, 粉丝量越大的用户排名越靠前。

3. 系统实现

3.1 技术选型

ElasticSearch支持全文索引和检索,同时提供功能丰富的API方便自定义全文检索功能。 由于原生ElasticSearch只支持分隔符的分词,不支持中文分词,需要选择中文分词插件。 本文选用IK分词作为中文分词工具,IK可以很方便的与ElasticSearch进行集成。限于篇幅有限,本文不再介始IK插件的安装。 感兴趣的同学可以查看官方文档:https://github.com/medcl/elasticsearch-analysis-ik

3.2 创建用户信息索引

在ElasticSearch中创建用户信息索引,索引的配置信息如下:

PUT user_info
{
  "settings": {
    "number_of_shards": 1,
    "number_of_replicas" : 1,
    "analysis" : {
      "analyzer" : {
        "character" : {
          "filter" : [
            "lowercase"
          ],
          "tokenizer" : "character"
        },
        "pinyin_analyzer" : {
          "tokenizer" : "pinyin_tokenizer"
        }
      },
      "tokenizer" : {
        "pinyin_tokenizer" : {
          "keep_joined_full_pinyin" : "true",
          "lowercase" : "true",
          "keep_original" : "true",
          "remove_duplicated_term" : "true",
          "keep_separate_first_letter" : "false",
          "type" : "pinyin",
          "limit_first_letter_length" : "16",
          "keep_full_pinyin" : "true"
        },
        "character" : {
          "type" : "standard",
          "max_token_length" : "1"
        }
      }
    }
  },
  "mappings": {
    "numeric_detection" : false,
    "_source" : {
      "enabled" : true
    },
    "properties": {
      "id": {
        "type": "long"
      },
      "nick_name" : {
        "search_analyzer" : "ik_smart",
        "similarity" : "BM25",
        "analyzer" : "ik_max_word",
        "type" : "text",
        "fields" : {
          "pinyin" : {
            "search_analyzer" : "ik_smart",
            "similarity" : "BM25",
            "analyzer" : "pinyin_analyzer",
            "type" : "text"
          },
          "chars" : {
            "search_analyzer" : "ik_smart",
            "similarity" : "BM25",
            "analyzer" : "character",
            "type" : "text"
          }
        }
      },
      "followers" : {
        "type" : "long"
      }
    }
  }
}

索引配置说明:

  • 自定义分词器character,单个字符分词,解决检索关键词是单个字符的检索问题;
  • 自定义分词器pinyin_analyzer,将中文转化为拼音,实现拼音关键词检索;
  • nick_name多字段索引,用于单字符和拼音检索;
  • 指定nick_name字段全文检索相关性评分算法为BM25。

3.3 索引用户数据

向user_info索引写入三条用户数据。

POST user_info/_doc
{
  "id": 7788,
  "nick_name": "天生妩媚",
  "followers": 8
}

POST user_info/_doc
{
  "id": 7789,
  "nick_name": "天生丽质",
  "followers": 800
}

POST user_info/_doc
{
  "id": 7790,
  "nick_name": "小白",
  "followers": 200
}

3.4 用户数据检索

输入关键词,根据用户昵称全文检索用户数据。ElasticSearch检索请求参数如下:

POST user_info/_search
{
  "query": {
    "multi_match": {
      "query": "天生", 
      "operator": "and", 
      "type": "most_fields", 
      "fields": ["nick_name", "nick_name.chars", "name.pinyin"]
    }
  }
}   

响应结果:

{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : 0.47000363,
    "hits" : [
      {
        "_index" : "user_info",
        "_type" : "_doc",
        "_id" : "BOVC-XQBGXOapfjvCQyw",
        "_score" : 0.47000363,
        "_source" : {
          "id" : 7788,
          "nick_name" : "天生妩媚",
          "followers" : 8
        }
      },
      {
        "_index" : "user_info",
        "_type" : "_doc",
        "_id" : "BuVC-XQBGXOapfjvFAz-",
        "_score" : 0.39019167,
        "_source" : {
          "id" : 7789,
          "nick_name" : "天生丽质",
          "followers" : 800
        }
      }
    ]
  }
}

从响应结果中可以看到用户“天生妩媚”的相关性评分是0.47,用户“天生丽质”的相关性评分是0.39。该相关性评分是通过BM25算法计算而来。

用户检索需求,需要检索结果的排序同时考虑粉丝量因素,粉丝量越大相关性评分越高。

ElasticSearch提供丰富的API,可以很方便的自定义相关性评分算法。要考虑粉丝量因素,将全文检索请求参数调整为如下形式即可实现。

POST user_info/_search
{
  "query": {
    "bool": {
      "must": [
          {
            "function_score": {
              "query": {
                "multi_match": {
                  "query": "天生", 
                  "operator": "and", 
                  "type": "most_fields", 
                  "fields": ["nick_name", "nick_name.chars", "name.pinyin"]
                }
              },
              "field_value_factor": {
                "field": "followers",
                "factor": 1.0,
                "modifier": "ln1p",
                "missing": 1
              }, 
              "boost_mode": "multiply"
            }
          }
        ]
    }
  }
}

上面请求参数的响应结果如下:

{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : 2.6087673,
    "hits" : [
      {
        "_index" : "user_info",
        "_type" : "_doc",
        "_id" : "BuVC-XQBGXOapfjvFAz-",
        "_score" : 2.6087673,
        "_source" : {
          "id" : 7789,
          "nick_name" : "天生丽质",
          "followers" : 800
        }
      },
      {
        "_index" : "user_info",
        "_type" : "_doc",
        "_id" : "BOVC-XQBGXOapfjvCQyw",
        "_score" : 1.0327035,
        "_source" : {
          "id" : 7788,
          "nick_name" : "天生妩媚",
          "followers" : 8
        }
      }
    ]
  }
}

此时全文检索的相关性评分算法是,相关性评分(BM25) * ln(1 + followers)。 “天生丽质”的用户粉丝量远大于“天生妩媚”用户,相关性评分也就高了。

4. 总结与思考

用户检索系统实现的难点是优化检索结果,包括检索结果的准确性、相关性评分等。ElasticSearch提供丰富灵活的API,可以快速实现对全文检索结果的调整优化,极大的提高研发效率,是一款好用稳定的开源中间件。

posted @ 2020-10-05 23:46  孤独剑_001  阅读(675)  评论(0)    收藏  举报