首页 > 其他分享 >django-haystack使用whoosh创建索引

django-haystack使用whoosh创建索引

时间:2022-08-18 00:11:16浏览次数:95  
标签: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
From: https://www.cnblogs.com/trty/p/16597308.html

相关文章