Haystack+Solr: Как настроить фасетный поиск, чтобы он работал методом «OR»?

Всем доброго времени суток:
Использую последнюю версию haystack (2.4.1) и solr 4ой версии (если необходимо, могу узнать точную версию, но мне кажется, что это не критично).
Настроил фасетный поиск, так выглядит индекс
class SouvenirProductIndex(indexes.SearchIndex, indexes.Indexable):
    text = indexes.CharField(document=True, use_template=True)
    category_title = indexes.CharField(model_attr='base_product__subcategory__category__title', faceted=True)
    baseproduct_title = indexes.CharField(model_attr='base_product__title', faceted=True)
    subcategory_title = indexes.CharField(model_attr='base_product__subcategory__title', faceted=True)
    people_categories = indexes.MultiValueField(faceted=True)
    holidays = indexes.MultiValueField(faceted=True)
    colors = indexes.MultiValueField(faceted=True)
    suggestions = indexes.FacetCharField()

    def get_model(self):
        return SouvenirProduct

    def prepare_people_categories(self, obj):
        return [people.style for people in obj.people_categories.all()]

    def prepare_holidays(self, obj):
        return [holiday.slug for holiday in obj.holidays.all()]

    def prepare_colors(self, obj):
        colors = []
        for product_color in obj.productcolor_set.all():
            color = product_color.color
            if color.image:
                colors.append(color.image.url)
            else:
                colors.append(color.rgb)
        return colors


Вьюха:
class ProductSearchView(FacetedSearchView):
    template_name = 'search/search.html'
    form_class = FacetedNotEmptySearchForm
    facet_fields = ['people_categories', 'holidays', 'category_title', 'colors']

Форма унаследована от FacetedSearchForm, изменения не влияют на мой вопрос.
В итоге у меня товары находятся по запросам, фасеты также выводятся (категории, например), но, если я выберу несколько категорий, то поиск пытается найти товар, который принадлежал бы все этим выбранным категориям (метод AND), а мне надо, чтобы он отдал те товары, которые принадлежат хотя бы одной из выбранных категорий(метод OR). Как это можно сделать?
файл schema.xml
<?xml version="1.0" ?>

<schema name="default" version="1.5">
  <types>
    <fieldtype name="string"  class="solr.StrField" sortMissingLast="true" omitNorms="true"/>
    <fieldType name="boolean" class="solr.BoolField" sortMissingLast="true" omitNorms="true"/>
    <fieldtype name="binary" class="solr.BinaryField"/>

    <fieldType name="int" class="solr.TrieIntField" precisionStep="0" omitNorms="true" sortMissingLast="true" positionIncrementGap="0"/>
    <fieldType name="float" class="solr.TrieFloatField" precisionStep="0" omitNorms="true" sortMissingLast="true" positionIncrementGap="0"/>
    <fieldType name="long" class="solr.TrieLongField" precisionStep="0" omitNorms="true" sortMissingLast="true" positionIncrementGap="0"/>
    <fieldType name="double" class="solr.TrieDoubleField" precisionStep="0" omitNorms="true" sortMissingLast="true" positionIncrementGap="0"/>
    <fieldType name="sint" class="solr.SortableIntField" sortMissingLast="true" omitNorms="true"/>
    <fieldType name="slong" class="solr.SortableLongField" sortMissingLast="true" omitNorms="true"/>
    <fieldType name="sfloat" class="solr.SortableFloatField" sortMissingLast="true" omitNorms="true"/>
    <fieldType name="sdouble" class="solr.SortableDoubleField" sortMissingLast="true" omitNorms="true"/>

    <fieldType name="tint" class="solr.TrieIntField" precisionStep="8" omitNorms="true" positionIncrementGap="0"/>
    <fieldType name="tfloat" class="solr.TrieFloatField" precisionStep="8" omitNorms="true" positionIncrementGap="0"/>
    <fieldType name="tlong" class="solr.TrieLongField" precisionStep="8" omitNorms="true" positionIncrementGap="0"/>
    <fieldType name="tdouble" class="solr.TrieDoubleField" precisionStep="8" omitNorms="true" positionIncrementGap="0"/>

    <fieldType name="date" class="solr.TrieDateField" omitNorms="true" precisionStep="0" positionIncrementGap="0"/>
    <fieldType name="tdate" class="solr.TrieDateField" omitNorms="true" precisionStep="6" positionIncrementGap="0"/>

    <fieldType name="point" class="solr.PointType" dimension="2" subFieldSuffix="_d"/>
    <fieldType name="location" class="solr.LatLonType" subFieldSuffix="_coordinate"/>
    <fieldtype name="geohash" class="solr.GeoHashField"/>

    <fieldType name="text_general" class="solr.TextField" positionIncrementGap="100">
      <analyzer type="index">
        <tokenizer class="solr.StandardTokenizerFactory"/>
        <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" enablePositionIncrements="true" />
        <filter class="solr.LowerCaseFilterFactory"/>
      </analyzer>
      <analyzer type="query">
        <tokenizer class="solr.StandardTokenizerFactory"/>
        <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" enablePositionIncrements="true" />
        <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/>
        <filter class="solr.LowerCaseFilterFactory"/>
      </analyzer>
    </fieldType>

    <fieldType name="spell_ru" class="solr.TextField" positionIncrementGap="100" omitNorms="true">
      <analyzer>
        <tokenizer class="solr.StandardTokenizerFactory"/>
        <filter class="solr.LowerCaseFilterFactory"/>
        <filter class="solr.HunspellStemFilterFactory" dictionary="ru_RU.dic" affix="ru_RU.aff" ignoreCase="true" />
      </analyzer>
    </fieldType>

    <fieldType name="text_ws" class="solr.TextField" positionIncrementGap="100">
      <analyzer>
        <tokenizer class="solr.WhitespaceTokenizerFactory"/>
      </analyzer>
    </fieldType>

    <fieldType name="ngram" class="solr.TextField" >
      <analyzer type="index">
        <tokenizer class="solr.KeywordTokenizerFactory"/>
        <filter class="solr.LowerCaseFilterFactory"/>
        <filter class="solr.NGramFilterFactory" minGramSize="3" maxGramSize="15" />
      </analyzer>
      <analyzer type="query">
        <tokenizer class="solr.KeywordTokenizerFactory"/>
        <filter class="solr.LowerCaseFilterFactory"/>
      </analyzer>
    </fieldType>

    <fieldType name="edge_ngram" class="solr.TextField" positionIncrementGap="1">
      <analyzer type="index">
        <tokenizer class="solr.WhitespaceTokenizerFactory" />
        <filter class="solr.LowerCaseFilterFactory" />
        <filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" catenateWords="0" catenateNumbers="0" catenateAll="0" splitOnCaseChange="1"/>
        <filter class="solr.EdgeNGramFilterFactory" minGramSize="2" maxGramSize="15" side="front" />
      </analyzer>
      <analyzer type="query">
        <tokenizer class="solr.WhitespaceTokenizerFactory" />
        <filter class="solr.LowerCaseFilterFactory" />
        <filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" catenateWords="0" catenateNumbers="0" catenateAll="0" splitOnCaseChange="1"/>
      </analyzer>
    </fieldType>
  </types>

  <fields>
    <field name="id" type="string" indexed="true" stored="true" multiValued="false" required="true"/>
    <field name="django_ct" type="string" indexed="true" stored="true" multiValued="false"/>
    <field name="django_id" type="string" indexed="true" stored="true" multiValued="false"/>
    <field name="_version_" type="long" indexed="true" stored ="true"/>

    <dynamicField name="*_i"  type="int"    indexed="true"  stored="true"/>
    <dynamicField name="*_s"  type="string"  indexed="true"  stored="true"/>
    <dynamicField name="*_l"  type="long"   indexed="true"  stored="true"/>
    <dynamicField name="*_t"  type="spell_ru"    indexed="true"  stored="true"/>
    <dynamicField name="*_b"  type="boolean" indexed="true"  stored="true"/>
    <dynamicField name="*_f"  type="float"  indexed="true"  stored="true"/>
    <dynamicField name="*_d"  type="double" indexed="true"  stored="true"/>
    <dynamicField name="*_dt" type="date" indexed="true" stored="true"/>
    <dynamicField name="*_p" type="location" indexed="true" stored="true"/>
    <dynamicField name="*_coordinate"  type="tdouble" indexed="true"  stored="false"/>

    <field name="text" type="spell_ru" indexed="true" stored="true" multiValued="false" />
    <field name="holidays" type="spell_ru" indexed="true" stored="true" multiValued="true" />
    <field name="category_title" type="spell_ru" indexed="true" stored="true" multiValued="false" />
    <field name="category_title_exact" type="string" indexed="true" stored="true" multiValued="false" />
    <field name="subcategory_title" type="spell_ru" indexed="true" stored="true" multiValued="false" />
    <field name="subcategory_title_exact" type="string" indexed="true" stored="true" multiValued="false" />
    <field name="baseproduct_title" type="spell_ru" indexed="true" stored="true" multiValued="false" />
    <field name="baseproduct_title_exact" type="string" indexed="true" stored="true" multiValued="false" />
    <field name="people_categories" type="spell_ru" indexed="true" stored="true" multiValued="true" />
    <field name="suggestions" type="spell_ru" indexed="true" stored="true" multiValued="false" />
    <field name="colors" type="spell_ru" indexed="true" stored="true" multiValued="true" />
    <field name="colors_exact" type="string" indexed="true" stored="true" multiValued="true" />
    <field name="people_categories_exact" type="string" indexed="true" stored="true" multiValued="true" />
    <field name="holidays_exact" type="string" indexed="true" stored="true" multiValued="true" />
  </fields>
  <uniqueKey>id</uniqueKey>
  <defaultSearchField>text</defaultSearchField>
  <solrQueryParser defaultOperator="OR"/>
</schema>
  • Вопрос задан
  • 1129 просмотров
Решения вопроса 2
@skorpix Автор вопроса
Решение (вдруг кому пригодится) (у меня здесь еще сделано, что если поисковая строка не задана, то выдаются все результаты):
from collections import defaultdict

from haystack.forms import FacetedSearchForm


class FacetedNotEmptySearchForm(FacetedSearchForm):

    def no_query_found(self):
        return self.searchqueryset.all()

    @property
    def selected_multi_facets(self):
        selected_multi_facets = defaultdict(list)
        for facet_kv in self.selected_facets:
            if ":" not in facet_kv:
                continue
            field_name, value = facet_kv.split(':', 1)
            selected_multi_facets[field_name].append(value)
        return selected_multi_facets

    def search(self):
        sqs = super(FacetedSearchForm, self).search()
        for field, values in self.selected_multi_facets.items():
            if not values:
                continue
            clean_values = ['"%s"' % sqs.query.clean(val) for val in values]
            sqs = sqs.narrow(u'%s:(%s)' % (field, " OR ".join(clean_values)))
        return sqs
Ответ написан
Комментировать
winordie
@winordie
Лучшая документация -- исходники
https://github.com/django-oscar/django-oscar/blob/...
Посмотрите как тут организовано. Думаю это то что вам надо.
Если что то не понятно -- спрашивайте.
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы