ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

django-haystack使用whoosh创建索引

2022-08-18 00:33:53  阅读:136  来源: 互联网

标签:False self whoosh page haystack django class


快速入门

环境安装

首先需要清楚以下各个库的作用

  1. django是基于python开发的web框架,阅读本文需要了解相关的基础知识
  2. django-haystack为 Django 提供模块化搜索。它具有统一、熟悉的 API,允许您插入不同的搜索后端(例如SolrElasticsearchWhooshXapian等),而无需修改代码
  3. Whoosh 是一个用纯 Python 实现的快速、功能强大的全文索引和搜索库
# 安装相关库
pip install Django==1.11.12
pip install django-haystack==2.7.0
pip install Whoosh==2.7.4

以下几点需要注意

  1. 这里使用python版本为python3.5.4,如果其他版本可能存在不兼容

  2. Whoosh最后一次更新是2016年,可能存在效率不高、兼容不够的问题

  3. django-haystackhaystack在当前版本不兼容,同时安装后运行项目会报错

django配置文件

进入你的django的配置文件settings.py,添加或修改以下配置

# HAYSTACK_CONNECTIONS配置搜索引擎,可设置多个引擎
# ENGINE为使用的引擎,设置使用whoosh
# PATH为索引存放路径
# BATCH_SIZE为索引每次更新数量
# STORAGE为索引存储形式,可选file或者ram
# POST_LIMIT为索引文件占用空间最大值,默认为128 * 1024 * 1024
HAYSTACK_CONNECTIONS = {
    'default': {
        'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine',
        'PATH': os.path.join(os.path.dirname(__file__), 'whoosh_index'),
        'BATCH_SIZE': 1000,
        'STORAGE': 'file',
        'POST_LIMIT': 128 * 1024 * 1024,
        },
    }
# 搜索结果每页结果数量
HAYSTACK_SEARCH_RESULTS_PER_PAGE = 10
# Haystack 是否将搜索结果限制为仅注册的模型,
HAYSTACK_LIMIT_TO_REGISTERED_MODELS = False
# 控制信号,默认为'haystack.signals.BaseSignalProcessor'
# 为haystack.signals.RealtimeSignalProcessor表示实时更新
HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'

注意,将HAYSTACK_SIGNAL_PROCESSOR设置为haystack.signals.RealtimeSignalProcessor

经过测试,只能保证通过django更新数据的索引实时性,如果直接在数据库修改数据需要更新索引保证索引实时性

建立模型索引

建立seach_indexes.py文件

需要建立在需要检索的应用目录下,如果要检索多个可以建立多个文件

# models.py
from django.db import models


class Student(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=16)

# seach_indexes.py
from haystack import indexes
from .models import *

# 类的命名为被检索的模型名+`Index`
class StudentIndex(indexes.SearchIndex, indexes.Indexable):
    # text为必须字段,表示保存索引数据
    # document=True, use_template=True为必需配置
    text = indexes.CharField(document=True, use_template=True)
    # 可添加自定义字段,添加后表示字段可以被索引
    name = indexes.CharField(model_attr="name")

    def get_model(self):
        # 重写获取模型方法,修改为你的模型
        return Student

    def index_queryset(self, using=None):
        # 可加上你的过滤条件
        return self.get_model().objects.all()

注意

text为必须字段,表示索引存储的text内容,但其字段类型可以为其它
例如NgramField等等
其存储的内容为下列templates文本的内容

创建templates

第一步,检查你的setting.py文件是否配置了templates路径,如果没有配置,可以参考下列

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates'),],   # 在这里添加你的templates路径
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

找到/templates/search/indexes/目录,如果没有自行创建

创建目录,名为你的应用目录,如果存在多个seach_indexes.py创建多个目录

在目录下创建txt文件,命名规则为模型名+_text.txt,存在多个模型需要分别创建多个txt文件

这是student_text.txt的内容,在后续使用模板时使用,代表StudentIndextext内容

如果没有此文件,当模型数据更新时会出错
字段为模型字段,非模型索引的字段

{{ object.name }}

初始化序列

第一次使用或者修改过序列模型后,需要重建索引

python manage.py rebuild_index

如果数据库批量更新数据,需要手动更新索引

python manage.py update_index

配置接口

如果使用haystack提供的接口,请在对应urls文件添加

urlpatterns = [
    # 默认接口
    url('search/', include('haystack.urls'), name='haystack'),
    # 自定义接口
    url('mysearch/', views.MySearchView.as_view(), name="search"),
]

注意,默认接口返回的是模版文件,如果需要修改为json等类型,需要自定义接口

请查看这里的代码

配置搜索界面

这里是展示官方提供的搜索模板,也可以直接调用接口

创建/templates/search/search.html文件

<form method="get" action=".">
    <table>
        {{ form.as_table }}
        <tr>
            <td>&nbsp;</td>
            <td>
                <input type="submit" value="Search">
            </td>
        </tr>
    </table>

    {% if query %}
    <h3>Results</h3>

    {% for result in page.object_list %}
    <p>
        <a href="{{ result.object.get_absolute_url }}">{{ result.object.title }}</a>
    </p>
    {% empty %}
    <p>No results found.</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;
            Previous{% if page.has_previous %}</a>{% endif %}
        |
        {% if page.has_next %}<a href="?q={{ query }}&amp;page={{ page.next_page_number }}">{% endif %}Next &raquo;{% if
            page.has_next %}</a>{% endif %}
    </div>
    {% endif %}
    {% else %}
    {# Show some example queries to run, maybe query syntax, something else? #}
    {% endif %}
</form>

开始搜索

访问配置的接口,尝试搜索

参数 默认值 含义
q 传入搜索关键字,可加上~模糊搜索
page 1 分页,当前页数
models 无参数 无参数表示查询全部,如果需要多个models,传入多个即可

自定义入参和传出JSON

class MySearchView(SearchView):

    def get_form_kwargs(self):

        kwargs = {'initial': self.get_initial()}
        # 调整传入参数
        data = self.request.GET.copy()
        try:
            data["q"] = data["search"]
            del data["search"]
        except:
            pass
        if self.request.method == 'GET':
            kwargs.update({
                'data': data,
            })
        kwargs.update({
            'searchqueryset': self.get_queryset(),
            'load_all': self.load_all,
        })
        return kwargs

    def render_to_response(self, context, **response_kwargs):
        try:
            # 修改为传出json
            data = []
            object_list = context["object_list"]
            for obj in object_list:
                model_name = obj.model_name
                d = {"value": obj.text,"id": obj.pk}
                data.append(d)
            page_obj = context["page_obj"]
            paginator = page_obj.paginator
            res = json.dumps(
                {'success': True, "data": data, "page": {"count": paginator.count, "num_pages": paginator.num_pages,
                                                         "per_page": paginator.per_page, "index": page_obj.number}})
            return HttpResponse(res, content_type="application/json")
        except Exception as ex:
            return JsonResponse({'success': False, 'error_message': ex})

遇到的坑

无法搜索其它字段

我在创建模型序列类的地方有备注,可以添加其它字段来进行检索,但我在django-haystack里没有找到相关办法

默认只能检索字段text,虽然可以通过修改templates文件来修改text,但还是相当麻烦

我尝试过直接使用whoosh来搜索,是可以指定字段搜索的,代码如下

# 这里是选择序列文件路径
ix = open_dir("whoosh_index")
sc = ix.schema
from whoosh.qparser import QueryParser
with ix.searcher() as searcher:
    query = QueryParser("text", ix.schema).parse("检索内容")
    results = searcher.search(query, collapse_limit=0, terms=True)
    for i in results:
        print(i)

上述代码第六行的text便是检索的字段名,在whoosh使用是没有问题的

但是在django-haystack似乎默认只能检索text

这一点让我非常困惑,在相关文档里没有找到方法,本人能力有限,阅读源码没有找到解决办法

如果有大佬希望指出,非常感谢

NgramField字段无法修改最小最大值

在阅读whoosh文档的时候,我发现了以下两个类,可以将字段切割为N-gram再进行索引
如下所示,是可以配置最小最大值的
minsize – N-gram 的最小长度。
maxsize – N-gram 的最大长度。

class whoosh.fields.NGRAM(minsize=2, maxsize=4, stored=False, field_boost=1.0, queryor=False, phrase=False, sortable=False)

class whoosh.fields.NGRAMWORDS(minsize=2, maxsize=4, stored=False, field_boost=1.0, tokenizer=None, at=None, queryor=False, sortable=False)

django-haystack里找到对应的类

class NgramField(CharField):
    field_type = 'ngram'

    def __init__(self, **kwargs):
        if kwargs.get('faceted') is True:
            raise SearchFieldError("%s can not be faceted." % self.__class__.__name__)

        super(NgramField, self).__init__(**kwargs)


class EdgeNgramField(NgramField):
    field_type = 'edge_ngram'

两者皆为继承SearchField

class SearchField(object):
    """The base implementation of a search field."""
    field_type = None

    def __init__(self, model_attr=None, use_template=False, template_name=None,
                 document=False, indexed=True, stored=True, faceted=False,
                 default=NOT_PROVIDED, null=False, index_fieldname=None,
                 facet_class=None, boost=1.0, weight=None):
        # Track what the index thinks this field is called.
        self.instance_name = None
        self.model_attr = model_attr
        self.use_template = use_template
        self.template_name = template_name
        self.document = document
        self.indexed = indexed
        self.stored = stored
        self.faceted = faceted
        self._default = default
        self.null = null
        self.index_fieldname = index_fieldname
        self.boost = weight or boost
        self.is_multivalued = False

        # We supply the facet_class for making it easy to create a faceted
        # field based off of this field.
        self.facet_class = facet_class

        if self.facet_class is None:
            self.facet_class = FacetCharField

        self.set_instance_name(None)

可以看到并没有最小最大值的的设置

更多

django-haystack还有很多模块和功能,例如关键字高亮、搜索评分、Faceting(刻面|分面)、多个索引、日期或空间检索、复杂查询等等

本文基于自身经验提供基础的安装配置以及心得,如果存在差错麻烦指出,谢谢

标签:False,self,whoosh,page,haystack,django,class
来源: https://www.cnblogs.com/trty/p/16597308.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有