LINUX.ORG.RU

[haystack] фасетный поиск


0

1

Как сделан текущий/требуемый функционал сейчас (с помощью Django ORM).
Есть три модели, нужно посчитать количество используемых ингридиентов в каждом ресторане,
делаем это с помощью «backward relationships».

Данные модели имеют ту же структуру, что и оригинальные, но здесь просто
отличаются названия - так что не надо кричать нафига оно нужно так делать.

# models.py
class Restaurant(models.Model):
    name = models.CharField(max_length=128)
    dishes = models.ManyToManyField('Dish', related_name="restaurant_dish")

class Dish(models.Model):	
    ingredient = models.ForeignKey('Ingredient', related_name="dish")
    description = models.CharField(max_length=512)

class Ingredient(models.Model):
    name = models.CharField(max_length=256)

# views.py
# select ingredients, related to Restaurants, with current query in name
ingredients = Ingredient.objects.filter(dish__restaurant_dish__name__icontains=query).distinct()
# count all Restaurants, where ingredients are used. order by number of dishes (descending), then by id.
ingredients = Ingredient.objects.filter(id__in=[a.id for a in ingredients])
ingredients = ingredients.annotate(restaurant_count=Count('dish')).order_by('-restaurant_count', 'id')

получаем такое.

for entry in ingredients:
    print "%s (used in %s Restaurants)" % (entry.name, entry.restaurant_count)
    
"ingredient1 second third word (used in 24 Restaurants)"[br]
"ingredient2 second third word (used in 10 Restaurants)"[br]
то что нужно. ок.

делаем используя Haystack:
объявлем индекс

class RestaurantIndex(indexes.SearchIndex):
    name = indexes.CharField(document=True, model_attr='name')
    ingredients = indexes.MultiValueField(faceted=True)

    def prepare(self, obj):
        self.prepared_data = super(RestaurantIndex, self).prepare(obj)
        # ["ingredient1 second third word", "ingredient2 second third word",]
        self.prepared_data['ingredients'] = [c.ingredient.name for c in obj.dish.all()] 
        return self.prepared_data

обновляем индекс и тестим:

>>> sqs = SearchQuerySet().all().facet('ingredients')
>>> sqs.facet_counts()
{'dates': {},
 'fields': {u'ingredients': [(u'ingredient1', 24),
                             (u'ingredient2', 10),}
 'queries': {}}
 

Получаем только одно (первое слово) в фасете: «ingredient1» вместо «ingredient1 second third word».
Нужно как-то сделать так, чтобы использовать фасетный поиск, учитывающий полное словосочетание.
В общем, в доках указано что можно искать несколько слов, только вот не могу понять как.
В примерах, у них также ищется по одному слову в фасете.
Думаю, если фасеты могут строиться в цепочки, то надо плясать от этого.

it gives document counts based on words in the corpus, date ranges, numeric ranges or even advanced queries



Последнее исправление: iSlava (всего исправлений: 3)

Мда, да я и не надеялся здесь ответ получить х) После вкуривания манов оказалось что собака таки в бэкенде была зарыта.
Сейчас использую Solr, для него генерируется схема c помощью Haystack на основе некоторого конфига с предопределёнными значениями.
Что нам говорит вики Solr'a, раздел Facet Indexing:

  • For searching: Tokenized, case-folded, punctuation-stripped:
    • schildt / herbert / wolpert / lewis / davies / p
  • For sorting: Untokenized, case-folded, punctuation-stripped:
    • schildt herbert wolpert lewis davies p
  • For faceting: Primary author only, using a solr.StringField:
    • Schildt, Herbert

Хлопаем себя по лбу, находим страницу Analyzers, Tokenizers, and Token Filters.
В моем случае достаточно убрать фильтр solr.WordDelimiterFilterFactory в шаблоне, поставляемом с Haystack:

lib/python2.7/site-packages/haystack/templates/search_configuration/solr.xml

А вообще, схему, конечно же, нужно подпиливать в каждом конкретном случае. Всем спасибо, все свободны.

iSlava
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.