Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Indexing and searching related objects with haystack

I'm pretty new to search implementation, bear with me while I'm learning!

So my pet project is a recipe site and each recipe can have n steps. the model looks something like:

class Recipe(models.Model):
    title = models.CharField(max_length=255)
    description = models.TextField()
    hotness = models.ForeignKey(Hotness)
    recipe_diet = models.ManyToManyField(DietType)
    ingredients = models.ManyToManyField(Ingredient, through="RecipeIngredient")

class DietType(models.Model):
    diet_type = models.CharField(max_length=50)
    description = models.TextField(null=True, blank=True)

class RecipeIngredient(models.Model):
    recipe = models.ForeignKey(Recipe)
    ingredient = models.ForeignKey(Ingredient)
    quantifier = models.ForeignKey(Quantifier)
    quantity = models.FloatField()

class RecipeSteps(models.Model):
    step_number = models.IntegerField()
    description = models.TextField()
    recipe = models.ForeignKey(Recipe)

(shortened for brevity)

I want to index all of it: Recipe, RecipeIngredient, DietType and Steps... The DietType and RecipeIngredient seem to be working fine, but the Steps are not. I assume this has to do with the usage of 'RelatedSearchQuerySet' ?

Here is my search_indexes.py:

from haystack import indexes
from recipes.models import Recipe

class RecipeIndex(indexes.SearchIndex, indexes.Indexable):
    text = indexes.CharField(document=True, use_template=True)
    title = indexes.CharField(model_attr='title')
    ingredients = indexes.MultiValueField(indexed=True, stored=True)
    description = indexes.CharField(model_attr='description')
    hotness = indexes.CharField(model_attr='hotness')
    diet_type = indexes.MultiValueField(indexed=True, stored=True)
    recipesteps = indexes.MultiValueField(indexed=True, stored=True)

    def prepare_steps(self, object):
        return [step.description for step in object.recipesteps.all()]

    def get_model(self):
        return Recipe

    def load_all_queryset(self):
        # Pull all objects related to the Note in search results.
        return Recipe.objects.all().select_related()

Here is the template recipe_text.txt:

{{ object.title }}
{{ object.cuisine }}
{% for ingr in object.ingredients.all %}
  {{ ingr.title }}
{% endfor %}
{{ object.description }}
{% for dt in object.recipe_diet.all %}
  {{ dt.diet_type }}
{% endfor %}
{{ object.user }}
{{ object.hotness }}
{% for step in object.recipesteps.all %}
  {{ step.description }}
{% endfor %}
{{ object.body }}

I can search ingredients, title, description, diet type - everything works, except the RecipeSteps.

Finally, I'm making queries through the shell only at the moment:

#producing results:
sq = SearchQuerySet().filter(content='onion') #ingredient
sq = SearchQuerySet().filter(content='bolognese') #title
sq = SearchQuerySet().filter(content='bologna') #description
#not producing any results:
sq = SearchQuerySet().filter(content='chop') #step
sq = RelatedSearchQuerySet().filter(content='chop').load_all() #assuming this does the expanded search

Any idea?

like image 464
dengar81 Avatar asked Jun 26 '17 20:06

dengar81


People also ask

What is Haystack search?

Haystack is building a search engine for the inventory of local retailers, allowing you to search for a product and see the closest stores carrying it.

What is haystack Elasticsearch?

Haystack is a Python framework for natural language processing (NLP) that focuses on semantic search and question answering (QA). Haystack enables the users to ask a question in natural language, and receive an informative answer from a large collection of documents — all within seconds.

What is Haystack in Django?

Haystack provides modular search for Django. It features a unified, familiar API that allows you to plug in different search backends (such as Solr, Elasticsearch, Whoosh, Xapian, etc.) without having to modify your code.


1 Answers

I have identified two issues:

  1. The name prepare_steps in your RecipeIndex is wrong it should be prepare_{field_name} so change it to prepare_recipesteps

  2. You are trying to access related steps object.recipesteps.all objects recipe_text.txt in a wrong way, it should be object.recipesteps_set.all. Or keep using recipesteps but add this as a related_name in RecipeSteps model for ForeignKey Recipe e.g.

    class RecipeSteps(models.Model):
        # //
        recipe = models.ForeignKey(Recipe, related_name='recipesteps')
    
like image 66
Aamir Rind Avatar answered Sep 21 '22 03:09

Aamir Rind