当前位置: 首页 > 面试题库 >

搜索多个单词elasticsearch haystack

常睿范
2023-03-14
问题内容

我曾经使用过django,haystack和elasticsearch。

我的search_index.py:

from haystack import indexes
from models import Advertisement



class AdvertisementIndex(indexes.SearchIndex, indexes.Indexable):
    text = indexes.CharField(document=True, use_template=True)
    make = indexes.CharField()
    section = indexes.CharField()
    subcategory = indexes.CharField()
    content = indexes.CharField(model_attr='content')
    images = indexes.CharField(model_attr='images')

    def get_model(self):
        return Advertisement

    def index_queryset(self, using=None):
        return self.get_model().objects.filter(is_published=True).select_related('make').select_related('section').select_related('subcategory')

搜索表格:

    <form action="/search" method="get">
        <input type="text-search" name="q">
        <input type="submit" value="">
    </form>

模板:

{% block content %}

{% for result in page.object_list %}
   <p>{{ result.object.title }}</p>
   <p>{{ result.object.content }}</p>
   <p>{{ result.object.images }}</p>
   <p>{{ result.object.make }}</p>
   <p>{{ result.object.section }}</p>
   <p>{{ result.object.subcategory }}</p>
{% empty %}
   <p>No result.</p>
{% endfor %}

{% endblock %}

`curl -XGET "http://localhost:9200/_search?q=fender+boss" `
我得到所有的价值,那里有“老板”和“挡泥板”

当您在搜索框中输入“ boss fender”时,我没有结果。从搜索表单中,我只能得到一个单词的结果,例如“老板”。如何使搜索多个单词的能力?


问题答案:

这个月我陷入了这个问题。

为了执行正确的查询,您将需要覆盖一些干草堆对象。我发现这篇文章对扩展Haystack的Elasticsearch后端很有帮助。刚开始时非常复杂,但是一旦了解了它的工作原理… :-)

博客文章介绍了如何实现elasticsearch的嵌套查询…好吧…我已经实现了基本的multi_match查询。

# -*- coding: utf-8 -*-
from __future__ import absolute_import

from django.conf import settings

from haystack.backends.elasticsearch_backend import (
    ElasticsearchSearchBackend, ElasticsearchSearchEngine, ElasticsearchSearchQuery)
from haystack.query import SearchQuerySet


class ElasticsearchEngineBackendCustom(ElasticsearchSearchBackend):
    DEFAULT_ANALYZER = "snowball"

    def __init__(self, connection_alias, **connection_options):
        super(ElasticsearchEngineBackendCustom, self).__init__(connection_alias, **connection_options)

        user_settings = getattr(settings, 'ELASTICSEARCH_INDEX_SETTINGS', {})
        if user_settings:
            setattr(self, 'DEFAULT_SETTINGS', user_settings)

        user_analyzer = getattr(settings, 'ELASTICSEARCH_DEFAULT_ANALYZER', '')
        if user_analyzer:
            setattr(self, 'DEFAULT_ANALYZER', user_analyzer)

    def build_search_kwargs(self, query_string, sort_by=None, start_offset=0, end_offset=None,
                            fields='', highlight=False, facets=None,
                            date_facets=None, query_facets=None,
                            narrow_queries=None, spelling_query=None,
                            within=None, dwithin=None, distance_point=None,
                            models=None, limit_to_registered_models=None,
                            result_class=None, multi_match=None):

        out = super(ElasticsearchEngineBackendCustom, self).build_search_kwargs(query_string, sort_by, start_offset,
                                                                                end_offset,
                                                                                fields, highlight, facets,
                                                                                date_facets, query_facets,
                                                                                narrow_queries, spelling_query,
                                                                                within, dwithin, distance_point,
                                                                                models, limit_to_registered_models,
                                                                                result_class)

        if multi_match:
             out['query'] = {
                'multi_match': {
                    'query': multi_match['query'],
                    'fields': multi_match['fields'],
                    'tie_breaker': multi_match['tie_breaker'],
                    'minimum_should_match': multi_match['minimum_should_match'],
                }
            }

        return out

    def build_schema(self, fields):
        content_field_name, mapping = super(ElasticsearchEngineBackendCustom, self).build_schema(fields)

        for field_name, field_class in fields.items():
            field_mapping = mapping[field_class.index_fieldname]

            if field_mapping['type'] == 'string' and field_class.indexed:
                if not hasattr(field_class, 'facet_for') or field_class.field_type in ('ngram', 'edge_ngram'):
                    field_mapping['analyzer'] = getattr(field_class, 'analyzer', self.DEFAULT_ANALYZER)
            mapping.update({field_class.index_fieldname: field_mapping})

        return content_field_name, mapping

    def multi_match_run(self, query, fields, minimum_should_match, tie_breaker):
        from elasticsearch_dsl import Search
        from elasticsearch_dsl.query import MultiMatch

        raw = Search().using(self.conn).query(
            MultiMatch(query=u'{}'.format(query), fields=fields, minimum_should_match=minimum_should_match, tie_breaker=tie_breaker)
        ).execute()

        return self._process_results(raw)


class ElasticsearchSearchQueryCustom(ElasticsearchSearchQuery):
    def multi_match(self, query, fields, minimum_should_match, tie_breaker):
        results = self.backend.multi_match_run(query, fields, minimum_should_match, tie_breaker)
        self._results = results.get('results', [])
        self._hit_count = results.get('hits', 0)

    def add_multi_match_query(self, query, fields, minimum_should_match, tie_breaker):
        self.multi_match_query = {
            'query': query,
            'fields': fields,
            'minimum_should_match': minimum_should_match,
            'tie_breaker': tie_breaker
        }

    def build_params(self, spelling_query=None, **kwargs):
        search_kwargs = super(ElasticsearchSearchQueryCustom, self).build_params(spelling_query, **kwargs)
        if self.multi_match_query:
            search_kwargs['multi_match'] = self.multi_match_query

        return search_kwargs


class ElasticsearchSearchQuerySetCustom(SearchQuerySet):
    def multi_match(self, query, fields, minimum_should_match="35%", tie_breaker=0.3):
        clone = self._clone()
        clone.query.add_multi_match_query(query, fields, minimum_should_match, tie_breaker)
        clone.query.multi_match(query, fields, minimum_should_match, tie_breaker)
        return clone


class ElasticsearchEngineCustom(ElasticsearchSearchEngine):
    backend = ElasticsearchEngineBackendCustom
    query = ElasticsearchSearchQueryCustom

如您所见,我曾经elasticsearc- dsl执行查询(MultiMatch),这句话概括了博客文章:ElasticsearchSearchQuerySetCustom().multi_match(...)调用取决于ElasticsearchSearchQueryCustom,取决于ElasticsearchEngineBackendCustom

然后在您的设置中放入elasticsearch配置,例如:

ELASTICSEARCH_DEFAULT_ANALYZER = 'italian'
ELASTICSEARCH_INDEX_SETTINGS = {
    "settings": {[...]}
}

您可以ELASTICSEARCH_INDEX_SETTINGS从语言分析器中获取您的语言

您还需要覆盖SearchForm

# -*- coding: utf-8 -*-
from __future__ import absolute_import

from haystack.forms import SearchForm

from .backend import ElasticsearchSearchQuerySetCustom


class SearchFormCustom(SearchForm):
    def search(self):
        query = self.searchqueryset.query.clean(self.cleaned_data.get('q'))
        if not self.is_valid() or not query:
            return self.no_query_found()

        sqs = ElasticsearchSearchQuerySetCustom().multi_match(query, ['title^8', 'text^0.5'])

        return sqs

字段titletext必须在索引中,并且脱字符号用于对字段进行增强。

您需要覆盖haystack url模式才能使用自定义格式:

urlpatterns = patterns(
    'search.views',
    url('^$', search_view_factory(form_class=SearchFormCustom), name='haystack-search'),
)

就是这样,HTH :-)

注意
不要使用,result.object.something而是使用索引上的字段,例如result.tilte,因为result.object.tilte打数据库!参见干草堆最佳实践



 类似资料:
  • 问题内容: 我从来没有真正听到过关于这个问题的直接答案,我只需要FULLTEXT搜索包含多个单词“ Firstname Lastname”的几列 但是,如果我在此处输入多个单词,它将无法运行查询。 问题答案: 如果要进行精确搜索:

  • 问题内容: 我正在尝试在站点中创建搜索功能,并且希望用户能够搜索多个单词,并根据各种模型中存在的条件执行子字符串匹配。 为了这个示例,假设我有以下模型: 员工 公司 市政府 县 一个县有多个直辖市,有多个公司,有多个员工。 我希望搜索能够针对Employee.firstname,Employee.lastname,Company.name,Municipality.name和County.name

  • 问题内容: 我第一次使用Postgresql,并且试图在我的网站中创建一个搜索引擎。我有这张桌子: 然后我为表的每个字段创建了一个索引(这是正确的方法吗?或者我可以为所有字段创建一个索引?): 现在,如果我想在每个索引中搜索一个单词,SQL查询是什么? 我尝试了这个,它的工作原理: 是否存在更好的方法来做到这一点?我可以搜索多个吗?我的一个朋友提出了一个解决方案,但这是针对MySQL数据库的: P

  • 我想从文件。 示例: 我想给我们一种动态命令,因为我不必每次为每个用户手动输入。 我试过了 但这并没有达到预期的效果。

  • 我有一个记录数据库,每个记录都有一个右和一个左字段,这两个字段都包含文本。数据库使用Elasticsearch建立索引。 我想搜索这些记录的两个字段,并找到在任何字段中包含两个或更多带有特定前缀的单词的记录。搜索应该足够具体,以便只查找包含查询中所有单词的记录,而不仅仅是其中的一些单词。 例如,qui bro查询应该返回包含“敏捷的棕色狐狸跳过了懒惰的狗”这句话的记录,而不是包含“敏捷的狐狸跳过了

  • 这很有魅力: 但如果我想按多个单词搜索,它将不返回任何内容,例如: 当我使用CURL时,我通过将添加到我的JSON属性来解决这个问题: 卷曲-XGET“http://localhost:9200/_search“-d”{“查询”:{“匹配”:{“字段”:{“查询”:“word\u 1 word\u 2”,““模糊性”:“自动”,““运算符”:“和”}}}}}” 我如何在Java中实现这一点?