Django全文搜索引擎

背景

  • 解决问题

    网站首页提供了搜索框,这样的搜索框是为了在数据库进行数据检索的,但是如果直接在数据库中使用like进行查询数据会非常的慢,因此考虑到引入了全文检索的概念,借助搜索引擎能够对文字进行分词,并构建起文字对应的数据库中信息的关系,所以要比模糊查询的效率高很多。

    如果我们自己使用搜索引擎,也是可以实现全文检索,但是其中可能要繁琐一些,所以我们可以借用已经写好的全文搜索框架,然后搭配对应的搜索引擎即可

  • 使用框架

    • django-haystack:全文搜索框架,支持whoosh、solr、Xapian、Elasticsearc四种全文检索引擎,参考:http://haystacksearch.org/

    • whoosh:纯python编写的搜索引擎,性能偏低,但是很稳定,参考:https://whoosh.readthedocs.io/en/latest/

    • jieba:免费的中文分词包,中文分词包也有收费的,可以自取哟。

使用

  1. 安装环境包

    1
    2
    3
    4
    5
    6
    7
    # django-haystack依赖环境
    pip install setuptools_scm wheel

    # 按照包
    pip install django-haystack
    pip install whoosh
    pip install jieba
  2. 修改django中settings.py配置文件,配置搜索引擎

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    # 将django-haystack包注册到apps中
    INSTALLED_APPS = (
    ...
    'haystack',
    )

    # 配置中文搜索引擎
    HAYSTACK_CONNECTIONS = {
    'default': {
    # 使用whoosh引擎,这个whoosh_cn_backend文件对应的是django虚拟环境中文件的位置
    'ENGINE': 'haystack.backends.whoosh_cn_backend.WhooshEngine',
    # 配置索引文件生成的路径,该文件夹不用我们创建,运行的时候会自动创建
    'PATH': os.path.join(BASE_DIR, 'whoosh_index'),
    }
    }

    # 配置当添加、修改、删除数据时,自动生成索引
    HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'

    # 配置全文搜索每次返回的搜索结果条数,默认是20条,如果配置了,就按照配置的显示
    HAYSTACK_SEARCH_RESULTS_PER_PAGE=1
  3. 在对应的app下创建生成索引的py文件,文件名就叫【search_indexes.py】,这个名称是固定的

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    # 定义索引类
    from haystack import indexes
    # 导入需要生成索引的模型类
    from goods.models import GoodsSKU


    # 指定对于某个类的某些数据建立索引,这一块的内容都是固定的额,所以直接用即可
    # 索引类名格式: 模型类名+Index
    class GoodsSKUIndex(indexes.SearchIndex, indexes.Indexable):
    # text:索引字段,use_template=True表示根据表中的哪些字段建立索引文件的说明,将其放在一个文件中
    # 该文件就是我们一会要编辑的文件
    text = indexes.CharField(document=True, use_template=True)

    def get_model(self):
    # 返回原本的模型类
    return GoodsSKU

    # 建立索引的数据
    def index_queryset(self, using=None):
    return self.get_model().objects.all()

    # 这主要是一个关联操作,定义我们的数据表的model的索引类
    # text:该字段是必须的,
    # document=True:只能有一个,
    # use_template =true:表示使用模板
  4. 在静态文件的文件夹【templates】下,依次创建文件,路径格式必须按照:templates/search/indexes/【App名称】/【模型类名小写】_text.txt。如果不通过默认的路径定义,则创建索引类的时候使用template_name指定使用的模版文件

    1
    2
    mkdir -p templates/search/indexes/goods/
    touch templates/search/indexes/goods/goodssku_text.txt
  5. 修改索引文件生成模版的内容,用于生成指定的模版文件

    1
    2
    3
    4
    5
    6
    7
    # 根据表中的指定子段生成索引数据
    # 格式:object.子段名
    {{ object.name }}
    {{ object.desc }}

    # 同时它也支持通过外键建立索引之间的联系,type是外键名
    {{ object.type.detail }}
  6. 修改网站模板中搜索输入框

    1
    2
    3
    4
    5
    6
    7
    {# 搜索的输入框必须包裹在form表单中,method必须是get,action可以自己定 #}
    {# q其实就是提交的关键字,还有一个page就是传递的页码 #}
    <form method="get" action="/search">
    {# 搜索的输入框的name值必须为q #}
    <input type="text" class="input_text fl" name="q" placeholder="搜索商品">
    <input type="submit" class="input_btn fr" name="" value="搜索">
    </form>
  7. 在项目的urls.py文件中配置全文搜索的路由

    1
    2
    # 该配置用于haystack搜索,它的请求和模板中的action保持一直,它引入的路由一直是include('haystack.urls')
    url(r'^search/', include('haystack.urls')),
  8. 在template下的search目录中创建全文搜索结果的模板文件(文件名固定):search.html

    全文搜索出现结果后,则会给search.html模板文件传递如下变量信息:

    • query:搜索的关键字
    • page:当前页的page对象,遍历该对象,获取到的是SearchResult类的实例对象,内部对象的属性object才是查询数据的模型类的对象,跟直接使用django中的分页元素不大一样
    • paginator:分页paginator对象
  9. 将haystack框架中使用jieba分词

    1. 在haystack的环境包文件夹【haystack/backends/whoosh_cn_backend】中新建文件ChineseAnalyzer.py文件

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      import jieba
      from whoosh.analysis import Tokenizer, Token

      class ChineseTokenizer(Tokenizer):
      def __call__(self, value, positions=False, chars=False,
      keeporiginal=False, removestops=True,
      start_pos=0, start_char=0, mode='', **kwargs):
      t = Token(positions, chars, removestops=removestops, mode=mode,
      **kwargs)
      seglist = jieba.cut(value, cut_all=True)
      for w in seglist:
      t.original = t.text = w
      t.boost = 1.0
      if positions:
      t.pos = start_pos + value.find(w)
      if chars:
      t.startchar = start_char + value.find(w)
      t.endchar = start_char + value.find(w) + len(w)
      yield t

      def ChineseAnalyzer():
      return ChineseTokenizer()
    2. 修改haystack的环境包中的文件whoosh_backend.py文件的内容,修改前记得备份

      1
      2
      3
      4
      5
      6
      7
      # 在所有导入的最底部追加刚刚创建的ChieseAnalyzer方法,并注视掉原来的分词方法
      # from whoosh.analysis import StemmingAnalyzer
      from .ChineseAnalyzer import ChineseAnalyzer

      # 导入ChineseAnalyzer,并将原有analyzer的StemmingAnalyser替换为ChineseAnalyzer
      # 原来的是:analyzer=StemmingAnalyzer()
      analyzer=ChineseAnalyzer()
  10. 执行命令生成对应的索引文件

    1
    python manage.py rebuild_index
  11. 然后进入前台进行搜索就可以了

拓展

使用haystack过程中一开始遇到查不出结果,但是索引是正常的生成了,就是查不出结果,因此在官网中找了调试类的方式,记录如下:

1
2
3
4
5
6
7
# 先执行python manage.py shell进入调试页面,然后依次执行如下:
from haystack.query import SearchQuerySet
sqs = SearchQuerySet().all()
sqs.count()

# 如果获得的结果为0,则说明之前生成索引的步骤可能存在问题,需要细细检查
# 我的问题出在ChineseAnalyzer.py文件内容存在问题导致

django删除cookie

1
response.delete_cookie('username')