Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django-haystack: how do I select which index to use in a SearchQuerySet?

I've been looking through the Haystack documentation on multiple indexes, but I can't figure out exactly how to use them.

The main model in this example is Proposal. I want to have two search indexes that return a list of proposals: one that only searches in the proposals themselves, and one that searches in proposals along with their comments. I've set up search_indexes.py like this:

class ProposalIndexBase(indexes.SearchIndex, indexes.Indexable)
    title = indexes.CharField(model_attr="title", boost=1.1)
    text = indexes.NgramField(document=True, use_template=True)
    date = indexes.DateTimeField(model_attr='createdAt')

    def get_model(self):
        return Proposal


class ProposalIndex(ProposalIndexBase):
    comments = indexes.MultiValueField()

    def prepare_comments(self, object):
        return [comment.text for comment in object.comments.all()]


class SimilarProposalIndex(ProposalIndexBase):
    pass

Here's my search in views.py:

def search(request):
    if request.method == "GET":
        if "q" in request.GET:
            query = str(request.GET.get("q"))
            results = SearchQuerySet().all().filter(content=query)
    return render(request, "search/search.html", {"results": results})

How do I set up a separate view that gets a SearchQuerySet from a specific index?

like image 278
tao_oat Avatar asked Mar 18 '23 22:03

tao_oat


1 Answers

The Haystack (and other auto-generated) documentation is not a good example of clarity and it's about as exciting as reading a phone book. I think the section you referred to on "Multiple Indexes" is actually about accessing different backend search engines (like whoosh, solr, etc.) for queries.

But your question seems to be about how to query the "SearchIndexes" for different models. In your example, you want to have one search query for "proposals" and another one for "proposals" + "comments," if I understand your question correctly.

I think you want to look at the SearchQuerySet API, which describes how to filter the queryset returned by the search. There is a method called models that allows you to supply a model class or a list of model classes to limit the queryset results.

For example, in your search view, you may want to have a query string parameter for, say, "content" that specifies whether the search is for "proposals" or for "everything" (proposals and comments). So your frontend needs to supply the extra content parameter when calling the view (or you can use separate views for the different searches).

Your query strings need to look something like:

/search/?q=python&content=proposal
/search/?q=python&content=everything

And your view should parse the content query string parameter to get the model(s) for filtering the search query results:

# import your model classes so you can use them in your search view
# (I'm just guessing these are what they are called in your project)
from proposals.models import Proposal
from comments.models import Comment

def search(request):
    results = None
    if request.method == "GET":
        if "q" in request.GET:
            query = str(request.GET.get("q"))
            # Add extra code here to parse the "content" query string parameter...
            # * Get content type to search for
            content_type = request.GET.get("content")
            # * Assign the model or models to a list for the "models" call
            search_models = []
            if content_type is "proposal":
                search_models = [Proposal]
            elif content_type is "everything":
                search_models = [Proposal, Comment]
            # * Add a "models" call to limit the search results to the particular models
            results = SearchQuerySet().all().filter(content=query).models(*search_models)
    return render(request, "search/search.html", {"results": results})

If you have a lot of search indexes (i.e. a lot of content from many models), you might not want to hardcode the models in your view but use the get_model function from django.db to fetch the model class dynamically.

like image 114
kchan Avatar answered Apr 08 '23 09:04

kchan