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

1

5

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?

Burtburta answered 24/7, 2014 at 22:37 Comment(0)
R
8

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.

Rex answered 25/7, 2014 at 7:8 Comment(2)
Thank you! If you add Comment to your search_models list, will that return both proposals and comments in the search results? I think I was unclear in my question: I meant that the search results should always return only proposals, but in one case it should index just the proposal, in the other case it should index both the proposal and its comments.Burtburta
Yes, the SearchQuerySet models method will search in the appropriate index depending on what models you supply in the argument list. If you supply only Proposal it will return results only for that content type. Supplying Proposal and Comment will return results for both content types. Your search view/function should handle the queries for the content types depending on what search you (or your user) want to perform.Rex

© 2022 - 2024 — McMap. All rights reserved.