Flask项目中实现搜索

Flask项目中实现搜索

  1. python中使用elasticsearch
    1. 安装

      pip install elasticsearch
      
    2. 对于elasticsearch 5.x 版本 需要按以下方式导入

      from elasticsearch5 import Elasticsearch
      
      # elasticsearch集群服务器的地址
      ES = [
          '127.0.0.1:9200'
      ]
      
      # 创建elasticsearch客户端
      es = Elasticsearch(
          ES,
          # 启动前嗅探es集群服务器
          sniff_on_start=True,
          # es集群服务器结点连接异常时是否刷新es结点信息
          sniff_on_connection_fail=True,
          # 每60秒刷新结点信息
          sniffer_timeout=60
      )
      
    3. 使用

      query = {
          'query': {
              'bool': {
                  'must': [
                      {'match': {'_all': 'python web'}}
                  ],
                  'filter': [
                      {'term': {'status': 2}}
                  ]
              }
          }
      }
      # index:库名,doc_typ:表名 类型, body:查询条件 字典
      ret = es.search(index='articles', doc_type='article', body=query)
      print(ret['hits']['hits'])
      
  2. suggest建议查询(实现户业务:没完全输入完成的情况,可以做自动补全提升)
    1. 拼写纠错

      # 当我们输入错误的关键词phtyon web时,es可以提供根据索引库数据得出的正确拼写python web
      curl 127.0.0.1:9200/articles/article/_search?pretty -d '
      {
          "from": 0,
          "size": 10,
          "_source": false,
          "suggest": {
              "text": "phtyon web",
              "word-phrase": {
                  "phrase": {
                      "field": "_all",
                      "size": 1
                  }
              }
          }
      }'
      # word-phrase表示返回来的数据放在word-phrase中。
      # phrase表示以短语的方式返回,field指定从哪里获取数据,size指定返回一条数据。
      
    2. 自动补全

      自动补全需要用到suggest查询建议中的type=completion,所以原先建立的文章索引库不能用于自动补全,需要再建立一个自动补全的索引库

      1. 创建自动补全的索引库

        curl -X PUT 127.0.0.1:9200/completions -H 'Content-Type: application/json' -d'
        {
           "settings" : {
               "index": {
                   "number_of_shards" : 3,
                   "number_of_replicas" : 1
               }
           }
        }'
        
      2. 创建自动补全的映射类型

        curl -X PUT 127.0.0.1:9200/completions/_mapping/words -H 'Content-Type: application/json' -d'
        {
             "words": {
                  "properties": {
                      "suggest": {
                          "type": "completion",
                          "analyzer": "ik_max_word"
                      }
                  }
             }
        }'
        # 自动补全建议,必须是completion类型。
        
      3. 使用logstash导入自动补全初始数据

        # 创建脚本:
        	vim /home/python/logstash_mysql_completion.conf
        # 脚本内容:
        input{
             jdbc {
                 jdbc_driver_library => "/home/python/mysql-connector-java-8.0.13/mysql-connector-java-8.0.13.jar"
                 jdbc_driver_class => "com.mysql.jdbc.Driver"
                 jdbc_connection_string => "jdbc:mysql://127.0.0.1:3306/toutiao?tinyInt1isBit=false"
                 jdbc_user => "root"
                 jdbc_password => "mysql"
                 jdbc_paging_enabled => "true"
                 jdbc_page_size => "1000"
                 jdbc_default_timezone =>"Asia/Shanghai"
                 statement => "select title as suggest from news_article_basic"
                 clean_run => true
             }
        }
        output{
              elasticsearch {
                 hosts => "127.0.0.1:9200"
                 index => "completions"
                 document_type => "words"
              }
        }
        # 执行脚本:
        	sudo /usr/share/logstash/bin/logstash -f /home/python/logstash_mysql_completion.conf
        
      4. 自动补全建议查询

        curl 127.0.0.1:9200/completions/words/_search?pretty -d '
        {
            "suggest": {
                "title-suggest" : {
                    "prefix" : "pyth", 
                    "completion" : { 
                        "field" : "suggest" 
                    }
                }
            }
        }'
        
  3. 头条项目中文章搜索业务实现代码

    业务需求

    • 用户没完全输入完成的情况,可以做自动补全提升

      • 首先查询es进行提示补全

      • 如果查询到结果直接返回

      • 如果没有查询到结果,可能用户拼写错误

      • 查询es进行拼写纠错

      • 返回结果

    • 当用户在输入库输入关键词点击确定进行搜索时,在es中检索匹配关键词的结果

    配置elasticsearch集群服务器的地址,放在配置文件中

    # elasticsearch 集群配置
    ES = [
        '127.0.0.1:9200'
    ]
    

    创建elasticsearch客户端,放在工厂函数里

     # 创建elasticsearch对象
        app.es = Elasticsearch(
         app.config['ES'],
            # 启动前嗅探es集群服务器
            sniff_on_start=True,
            # es集群服务器结点连接异常时是否刷新es结点信息
            sniff_on_connection_fail=True,
            # 每60秒刷新结点信息
            sniffer_timeout=60
        )
    

    编写视图

    用户搜索输入提示

    class SuggestionResource(Resource):
        """
        联想建议
        """
        def get(self):
            """
            获取联想建议
            """
            qs_parser = RequestParser()
            qs_parser.add_argument('q', type=inputs.regex(r'^.{1,50}$'), required=True, location='args')
            args = qs_parser.parse_args()
            q = args.q
            query = {
                'from': 0, # 从第0个文档开始
                'size': 10, # 返回10条数据
                '_source': False, # 不用返回文档中的字段
                'suggest': { # 建议查询
                    'word-completion': { # 指定建议查询返回结果的字段名称
                        'prefix': q, # 需要补全的前缀
                        'completion': { # 从哪个字段获取补全的数据
                            'field': 'suggest' # 指定从suggest字段中获取补全的数据
                        }
                    }
                }
            }
            # 先从completions索引库,获取自动补全数据
            ret = current_app.es.search(index='completions', body=query)
            options = ret['suggest']['word-completion'][0]['options']
            # 自动补全没数据,再从自动纠错中获取数据
            if not options:
                query = {
                    'from': 0,
                    'size': 10,
                    '_source': False,
                    'suggest': { # 建议查询
                        'text': q, # 需要纠错的数据
                        'word-phrase': { # 指定建议查询返回结果的字段名称
                            'phrase': { # 短语
                                'field': '_all', # 从_all字段中获取纠错数据
                                'size': 1, # 返回一个纠错结果
                                'direct_generator': [{
                                    'field': '_all',
                                    'suggest_mode': 'always'
                             }]
                            }
                     }
                    }
                }
                ret = current_app.es.search(index='articles', doc_type='article', body=query)
                options = ret['suggest']['word-phrase'][0]['options']
            results = []
            for option in options:
                if option['text'] not in results:
                    results.append(option['text'])
            return {'options': results}
    

    搜索结果

    class SearchResource(Resource):
        """
        搜索结果
        """
        def get(self):
            """
            获取搜索结果
            """
            if g.use_token and not g.user_id:
                return {'message': 'Token has some errors.'}, 401
    
            qs_parser = RequestParser()
            qs_parser.add_argument('q', type=inputs.regex(r'^.{1,50}$'), required=True, location='args')
            qs_parser.add_argument('page', type=inputs.positive, required=False, location='args')
            qs_parser.add_argument('per_page', type=inputs.int_range(constants.DEFAULT_SEARCH_PER_PAGE_MIN,
                                                                     constants.DEFAULT_SEARCH_PER_PAGE_MAX,
                                                                     'per_page'),
                                   required=False, location='args')
            args = qs_parser.parse_args()
            q = args.q
            page = 1 if args.page is None else args.page
            per_page = args.per_page if args.per_page else constants.DEFAULT_SEARCH_PER_PAGE_MIN
            # Search from Elasticsearch
            query = {
                'from': (page-1)*per_page, # 偏移
                'size': per_page, # 返回数量
                '_source': ["article_id", "title"],
                'query': {
                    'bool': {
                        'must': [
                            {'match': {'_all': q}} # 对q进行全文检索
                        ],
                        'filter': [
                            {'term': {'status': 2}} # 必须是通过审核的文章
                        ]
                    }
                }
            }
            ret = current_app.es.search(index='articles', doc_type='article', body=query)
            total_count = ret['hits']['total']
            results = []
            hits = ret['hits']['hits']
            for result in hits:
                article_id = int(result['_id'])
                # 从缓存中获取文章数据
                article = cache_article.ArticleInfoCache(article_id).get()
                # 如果缓存中存在就添加到返回结果
                if article:
                    results.append(article)
            # 记录用户的搜索记录
            if g.user_id and page == 1:
                try:
                    cache_user.UserSearchingHistoryStorage(g.user_id).save(q)
                except RedisError as e:
                    current_app.logger.error(e)
            # 添加elasticsearch索引文档
            if total_count and page == 1:
                query = {
                    '_source': False,
                    'query': {
                        'match': {
                            'suggest': q
                        }
                    }
                }
                # 查询自动补全是否有该搜索词
                ret = current_app.es.search(index='completions', doc_type='words', body=query)
                if ret['hits']['total'] == 0:
                    doc = {
                        'suggest': {
                            'input': q,
                            'weight': constants.USER_KEYWORD_ES_SUGGEST_WEIGHT
                        }
                    }
                    try:
                        # 没有就添加一个文档记录
                        current_app.es.index(index='completions', doc_type='words', body=doc)
                    except Exception:
                        pass
            return {'total_count': total_count, 'page': page, 'per_page': per_page, 'results': results}
    

你可能感兴趣的:(Flask框架,suggest建议查询,文章搜索)