索引定义

在数据库中对表的一列或者多列进行排序的一种数据结构,类似于书籍中的目录,可以帮助我们快速查询所需要的数据。在数据量很大时,合理使用索引的表相当于一辆法拉利,而没有使用索引的表就相当于一辆人力三轮车,查询效率相差甚远。
虽然索引可以提高数据检索的效率,但是会降低数据库更新的效率,因为在更新数据时,索引也要进行相应的更新,耗费一定的资源。所以索引经常用于数据量较大的表中,且经常被查询,很少更新的那些字段。

索引的数据结构及类别

经常使用的关系型数据库mysql,它的索引是基于B+树的数据结构;

B+树是B树的一种改进版,具有更高的查询效率。

如下为简单的2阶B树:

全文索引 低层实现 全文索引作用_搜索

主键索引, 主键自动建立索引,进行排序,加快检索速度
单列索引 ,单个列上的索引
复合索引 (建立在多列组合的索引)
唯一索引(字段的值必须是唯一的)
全文索引(类似模糊查询,但比模糊查询效率高,尤其数据量大时)

全文索引与模糊查询

全文索引,通过一种相似度方式,查询所需要的数据字段的类型必须是字符或者文本类型,类似于模糊查询,但比模糊查询效率高,尤其是数据量较大时。比如百度、谷歌搜索引擎就是使用全文索引进行文本检索。

模糊查询,即like ‘%xxxx%’, 这种模糊查询对于小数据量的查询效果还可以,对于大数据量,查询性能明显下降。

所以,这里使用全文索引方式,来实现商品的搜索。

haystack+whoosh

配置全文索引,为搜索栏提供搜索功能
  1. django-haystack 是专门提供搜索功能的 django 第三方应用,它还支持 Elasticsearch、Whoosh、Xapian 等多种搜索引擎,配合中文自然语言处理库 jieba 分词,就可以提供一个效果不错的文字搜索系统。
  2. whoosh,whoosh 是一个由 Python 实现的全文搜索引擎,没有二进制文件等,比较小巧,配置简单方便,但是whoosh 自带的是英文分词,对中文的分词支持不太好,所以使用 jieba 替换whoosh 的分词组件。
  3. 使用pip 安装这些包
#Ubuntu系统
sudo pip3 install django-haystack whoosh jieba
#windows
pip install django-haystack whoosh jieba -i https://pypi.tuna.tsinghua.edu.cn/simple/

    haystack版本     haystack文档

  1. 配置haystack
#settings.py
INSTALLED_APPS = [
	...
	'haystack',
]

# 配置全文搜索
# 指定搜索引擎
HAYSTACK_CONNECTIONS = { #指定搜索引擎
    'default': {
        'ENGINE': 'haystack.backends.whoosh_cn_backend.WhooshEngine',#需自定义
        'PATH': os.path.join(BASE_DIR, 'whoosh_index'), #索引文件存放的目录,建立索引时自动创建
    },
}

# 设置为每 10 项结果为一页,默认是 20 项为一页
HAYSTACK_SEARCH_RESULTS_PER_PAGE = 10

# 当数据库改变时,自动更新索引
HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'
  1. 对某app中的模型类建立全文索引(索引类)
#app>search_indexes.py   创建search_indexes.py文件
from .models import Book
from haystack import indexes

#创建一个索引类
class BookIndex(indexes.SearchIndex,indexes.Indexable):
	text = indexes.CharField(document=True,use_template=True)#固定语句,惯例使用’text‘命名字段 
	#document=True,说明搜索引擎使用该字段内容作为索引来检索,只能有一个字段具有该属性True
	#use_template=True 使用模板建立索引
	#换名字的时候,注意配置HAYSTACK_DOCUMENT_FIELD = "XXX"
	#templates/search/indexes/yourapp/<model_name>_text.txt
	#如templates/search/indexes/app/book_text.txt
	#{{ object.title }}  必须object
	#{{ object.content }}
	#对Book.title/Book.content两个字段建立索引

    def get_model(self):  #必须
        return Book

    def index_queryset(self, using=None):
        """Used when the entire index for model is updated."""
        return self.get_model().objects.all()
  1. 配置url
#主路由中
urlpatterns = [
	...,
	url(r"^search/",include("haystack.urls")),
]
  1. 前端搜索表单
    haystack源码中响应就是渲染templates/search/search.html
    前后端不分离时,自定义search.html结构
#haystack_search 视图命名空间固定
#GET 请求action时 haystack应用自动处理,并返回templates/search/search.html
#该模板需要创建
<form method="get" action="{% url 'haystack_search' %}">
  <input type="search" name="q" placeholder="搜索" required>  
  <button type="submit">搜索提交</button>
  #<input type="submit" value="搜索">
</form>

name 必须为’q‘

全文索引 低层实现 全文索引作用_haystack_02

  1. 响应的search.html页面
    定义templates>search/search.html
    haystack源码是自动渲染该模板页面,也可以改源码,实现返回json响应。
    /search/?q=三国演义
    /search/?q=三国演义&page=2
{% load highlight %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .keyword{
            color: red;
        }
    </style>
</head>
<body>
	<form role="search" method="get" id="searchform" action="{% url 'haystack_search' %}">
  		<input type="search" name="q" placeholder="搜索" required>
  		<button type="submit">
  			点击搜索
  		</button>
		<br>

	<!--搜索结果为context字典
		{
            "query": self.query,
            "form": self.form,
            "page": page,
            "paginator": paginator,
            "suggestion": None,
        }
	-->
    {% if query %}
        <h3>有查询结果</h3>
		#自动分页
        {% for result in page.object_list %}
        #result.object  获取对象
            <p>
                <a href="{{ result.object.imgurl }}">{{ result.object.title }}</a><br/>
            </p>
        {% empty %}
            <p>没有结果发现.</p>
        {% endfor %}

        {% if page.has_previous or page.has_next %}
            <div>
                {% if page.has_previous %}
                <a href="?q={{ query }}&page={{ page.previous_page_number }}">
                « Previous</a>
                {% endif %}
                {% if page.has_next %}
                <a href="?q={{ query }}&page={{ page.next_page_number }}">
                Next »</a>
                {% endif %}
            </div>
        {% endif %}
    {% else %}
        {# Show some example queries to run, maybe query syntax, something else? #}
    {% endif %}
</form>
</body>
</html>
  1. 替换whoosh的分词组件
    查看haystack 应用的目录(当前使用的环境中的包)
    haystack.backends目录下均为支持的搜索引擎
    默认whoosh_backend.py 支持英文分词
    创建一个中文分词组件
    ChineseTokenizer.py
#ChineseTokenizer.py
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):
                 """
					value:需分词的内容
					positions:
					chars:
					keeporiginal:
					removestops:
					start_pos:字符索引起始位置
					start_char:起始字符
					
				 """
        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 chinese_tokenizer():
    return ChineseTokenizer()

然后,复制whoosh_backend.py---->whoosh_cn_backend.py

全文索引 低层实现 全文索引作用_django-haystack_03

#whoosh_cn_backend.py
from .ChineseTokenizer import ChineseTokenizer

#搜索
schema_fields[field_class.index_fieldname] = \
TEXT(stored=True, analyzer=StemmingAnalyzer(), \
field_boost=field_class.boost, sortable=True)
#将其中的analyzer替换为ChineseTokenizer()

10.建立索引文件

#模型类已存储数据的情况下
#每次更新索引相关内容都要重建索引,将数据同步到搜索引擎
python3 manage.py rebuild_index

#将数据库的数据---->同步到搜索引擎
#查询时,从搜索引擎 查询数据,比模糊查询更高效

全文索引 低层实现 全文索引作用_搜索_04

实战案例

单表搜索

/book/search_title/
实战1 提取码:ha8k

多表搜索

haystack会逐一搜索每个索引类
实战2 提取码:2k8q