ElasticSearch和whoosh实现项目中搜索功能

ElasticSearch和whoosh实现项目中搜索功能单表搜索单表搜索:实现对于特定表中的某些字段的模糊搜索匹配,这样的搜索一般是比较容易实现的。比如需要根据关键字搜索教师表中的授课教师的信息,django项目可以通过Q方法和contains来实现。#根据搜索词search_data查询结果Teacher.objects.filter(Q(name__contains=search_data)|Q(course__contains=search_data))全局搜索全局搜索的实现主要提供如下两种方式…

搜索

自己最近总结了一下项目中实现过的搜索功能,接下来就通过具体的python项目来进行说明。

一.单表搜索

实现对于特定表中的某些字段的模糊搜索匹配,比如需要根据关键字搜索教师表中的授课教师或者所教课程的信息:

在这里插入图片描述
可以通过数据库查询语句直接实现:

# 根据搜索词search_data查询结果
Teacher.objects.filter(Q(teacher__contains=search_data) | Q(course__contains=search_data))

优点: 使用简单方便
缺点: 模糊查询效率低,数据量较大时会变得异常吃力


二.全文搜索

既然数据库模糊匹配在数据量庞大时不能高效实现全文搜索,那我们就要需要选择合适的搜索引擎来实现,现在主流的搜索引擎大概就是:Lucene,Solr,ElasticSearch。python项目中还会经常用到一个纯python实现的全文搜索引擎whoosh,更加小巧简单。

在django项目中实现全文搜索,可以使用搜索框架haystack来实现,
haystack可以方便地在django中直接添加搜索功能,无需关注索引建立、搜索解析等细节问题。

下面就分别使用whoosh和ElasticSearch来实现项目中的全文搜索。


1.haystack+whoosh+Jieba

1.1 配置

  1. 安装依赖包
pip install whoosh/django-haystack/jieba

由于Whoosh自带的是英文分词,对中文的分词支持不是太好,故用Jieba替换whoosh的分词组件

  1. 注册haystack 到Django的 INSTALLED_APPS
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # haystack注册
    'haystack',
]
  1. settings中增加搜索引擎配置
import os
HAYSTACK_CONNECTIONS = { 
   
    'default': { 
   
        'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine',
        # PATH为Whoosh 索引文件的存放文件夹
        'PATH': os.path.join(os.path.dirname(__file__), 'whoosh_index'),
    },
}

1.2 创建索引

  1. 如果你想针对某个app例如courses做全文检索,则必须在blog的目录下面建立search_indexes.py文件,文件名不能修改
    在这里插入图片描述
from haystack import indexes
from .models import Course

class CourseIndex(indexes.SearchIndex, indexes.Indexable):
       # 这字段必须这么写,用来告诉haystack和搜索引擎要索引哪些字段
       text = indexes.CharField(document=True, use_template=True)
       # 模型字段,打包数据
       id = indexes.CharField(model_attr='id')
       course = indexes.CharField(model_attr='course ')
       teacher = indexes.CharField(model_attr='teacher')
       content = indexes.CharField(model_attr='content')
       image_url = indexes.CharField(model_attr='image_url')

    def get_model(self):
        # 重载get_model方法,必须要有!
        return Course

    def index_queryset(self, using=None):
        return self.get_model().objects.all()

创建索引会提供更快速的导航与查找。索引的实现细节是我们不需要关心的,每个索引里面必须有且只能有一个字段为 document=True,这代表haystack 和搜索引擎将使用此字段的内容作为索引进行检索(primary field)。其他的字段只是附属的属性,方便调用,并不作为检索数据

  1. 然后在template下面建立 search/indexes/course
    前两个目录都是固定的,第三层是应用的名称,一一对应上,然后建立course_text.txt,名字是刚刚指定的类名的小写加_text.txt ,这里面就是对应哪个字段建立索引。例如:
{ 
   { 
   object.course}}
{ 
   { 
   object.teacher}}

1.3 搜索模板

在templates/search/下面,建立一个search.html页面

<!DOCTYPE html>
<html>
<head>
    <title></title>
    <style> span.highlighted { 
     color: red; } </style>
</head>
<body>
{% load highlight %}
{% if query %}
    <h3>搜索结果如下:</h3>
    {% for result in page.object_list %}
{#        <a href="/{ 
    { result.object.id }}/">{
  
  { result.object.teacher }}</a><br/>#}
        <a href="/{ 
    { result.object.id }}/">{% highlight result.object.teacher with query max_length 2%}</a><br/>
        <p>{
  
  { result.object.course|safe }}</p>
        <p>{% highlight result.course with query %}</p>
    {% empty %}
        <p>啥也没找到</p>
    {% endfor %}

    {% if page.has_previous or page.has_next %}
        <div>
            {% if page.has_previous %}
                <a href="?q={ 
    { query }}&amp;page={ 
    { page.previous_page_number }}">{% endif %}&laquo; 上一页
            {% if page.has_previous %}</a>{% endif %}
            |
            {% if page.has_next %}<a href="?q={ 
    { query }}&amp;page={ 
    { page.next_page_number }}">{% endif %}下一页 &raquo;
            {% if page.has_next %}</a>{% endif %}
        </div>
    {% endif %}
{% endif %}
</body>
</html>

需要注意的是page.object_list实际上是SearchResult对象的列表。这些对象返回索引的所有数据。它们可以通过{
{result.object}}来访问。所以{
{ result.object.teacher}}实际使用的是数据库中course对象来访问teacher字段的

1.4 配置URL

添加SearchView到URLconf,在URLconf中添加下面一行:

# 前后端不分离url
url(r'^search/', include('haystack.urls')),

# 前后端分离url
path('search/', MySearchView(), name='haystack_search'),

1.5 视图函数

前后端分离后台需要重写SearchView中的create_response方法,用到的时候可以根据需要重写即可。

1.6 使用jieba分词

建立ChineseAnalyzer.py文件
保存在haystack的安装文件夹下,路径如“D:\python3\Lib\site-packages\haystack\backends”

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()

复制whoosh_backend.py文件,改名为whoosh_cn_backend.py

from .ChineseAnalyzer import ChineseAnalyzer 
# 查找
analyzer=StemmingAnalyzer()
# 改为
analyzer=ChineseAnalyzer()

2.haystack+ES

2.1 环境配置

下载elasticsearch和其依赖包

pip install django-haystack/elasticsearch

然后在settings中增加搜索引擎配置

HAYSTACK_CONNECTIONS = { 
   
    'default': { 
   
        'ENGINE': 'haystack.backends.elasticsearch_backend.ElasticsearchSearchEngine',
        'URL': 'http://127.0.0.1:9200/',    # 此处为elasticsearch运行的服务器ip地址和端口
        'INDEX_NAME': 'course',           # 指定elasticserach建立的索引库名称
    },
}

# 搜索结果每页显示数量
HAYSTACK_SEARCH_RESULTS_PER_PAGE = 5
# 实时更新index
HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'

2.2 创建索引

和whoosh的步骤一样

2.3 创建索引数据模板

和whoosh的步骤一样

3.4 配置URL

在course/urls.py中添加如下路由

path('course/search/', views.CourseSearchView.as_view(), name='course_search')

3.5 视图函数

from haystack.generic_views import SearchView
from .models import Course

class CourseSearchView(SearchView):
    """ 新闻搜索视图 """
    # 设置搜索模板文件
    template_name = 'course/search.html'

    # 否则根据参数q搜索相关数据
    def get(self, request, *args, **kwargs):
        # 1. 获取查询参数
        query = request.GET.get('q')
        # 2. 如果没有查询参数
        if not query:
            # 获取课程对象
            course_datas = Course.objects.all()
            # 分页, 从配置文件中拿到haystack参数
            paginator = Paginator(course_datas, settings.HAYSTACK_SEARCH_RESULTS_PER_PAGE)
            try:
                # 拿到前端传递的page,
                page = paginator.get_page(int(request.GET.get('page')))
            except Exception as e:
                # 如果出错则返回第一页,保证容错性
                page = paginator.get_page(1)

            return render(request, self.template_name, context={ 
   
                'page': page,
                # 'paginator': paginator,
                'query': query
            })
        # 3. 如果有查询参数
        else:
            # 则执行搜索
            return super().get(request, *args, **kwargs)

    def get_context_data(self, *args, **kwargs):
        """ 在context中添加page变量 """
        context = super().get_context_data(*args, **kwargs)
        if context['page_obj']:
            # 捕获page_obj,将其赋值到page
            context['page'] = context['page_obj']
        return context

总结

使用sql语句的模糊搜索效率较低,但是使用非常简单,在数据量较小、搜索精准度要求不高的情况下可以使用。

ES和whoosh搜索引擎在项目的配置步骤是相差不多的,ES的性能还是会比whoosh要高一些的,同时也因为whoosh是纯python实现的搜索引擎,在功能和性能上远不如ES。个人更推荐在项目中使用ES+haystack的方式去实现搜索功能。

今天的文章ElasticSearch和whoosh实现项目中搜索功能分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/32300.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注