kieran
kieran

Reputation: 2312

Django Haystack - Indexing single field

I am using Django Haystack for search.

I only want to target the title field of my model when searching for results.

At present however, it returns results if the search term is in any of the fields in my model.

For example: searching xyz gives results where xyz is in the bio field.

This should not happen, I only want to return results where xyz is in the title field. Totally ignoring all other fields other than Artist.title for searching on.

artists/models.py :

class Artist(models.Model):
    title = models.CharField(max_length=255)
    slug = models.SlugField(max_length=100)
    strapline = models.CharField(max_length=255)
    image = models.ImageField(upload_to=get_file_path, storage=s3, max_length=500)
    bio = models.TextField()

artists/search_indexes.py

from haystack import indexes
from app.artists.models import Artist

class ArtistIndex(indexes.SearchIndex, indexes.Indexable):
    text = indexes.CharField(document=True, use_template=True, model_attr='title')

    def get_model(self):
        return Artist

I guess thinking of it like a SQL query:

SELECT * FROM artists WHERE title LIKE '%{search_term}%'

UPDATE

Following suggestion to remove use_template=True, my search_indexes.py now looks like:

from haystack import indexes
from app.artists.models import Artist


class ArtistIndex(indexes.SearchIndex, indexes.Indexable):
    text = indexes.CharField(document=True, model_attr='title')
    title = indexes.CharField(model_attr='title')

    def get_model(self):
        return Artist

But I am having the same problem. (Have tried python manage.py rebuild_index)

This is my Haystack settings if that makes any difference:

HAYSTACK_CONNECTIONS = {
    'default': {
        'ENGINE': 'haystack.backends.simple_backend.SimpleEngine',
    },
}

Upvotes: 2

Views: 1803

Answers (4)

Ashok Joshi
Ashok Joshi

Reputation: 448

Basically your search_indexes.py file is written wrong. It should be like:-

from haystack import indexes
from app.artists.models import Artist

class ArtistIndex(indexes.SearchIndex, indexes.Indexable):
    text = indexes.CharField(document=True, use_template=True)
    title= indexes.CharField(model_attr='title',null=True)

def get_model(self):
    return Artist

def index_queryset(self, using=None):
    return self.get_model().objects.all()

Then you have to create a template in your app. The directory structure would be like:

templates/search/indexes/artists/artist_text.txt

and add the following code to the artist_text.txt file:

{{ object.title }}

Now do python manage.py rebuild_index.

Now It will return result only for title.

Upvotes: 0

user710907
user710907

Reputation: 772

From the Docs

Additionally, we’re providing use_template=True on the text field. This allows us to use a data template (rather than error prone concatenation) to build the document the search engine will use in searching. You’ll need to create a new template inside your template directory called search/indexes/myapp/note_text.txt and place the following inside:

{{ object.title }}
{{ object.user.get_full_name }}
{{ object.body }}

So I guess in this template you can declare which fields should be indexed/ searched upon

Other way is to override the def prepare(self, object) of Index class and explicitly define fields that need to be indexed/ searched upon. OR just use model_attr

Upvotes: 0

bruno desthuilliers
bruno desthuilliers

Reputation: 77942

If you don't have any other use case for your index (ie searches that should match terms elsewhere) you just have to not use_template at all (set the use_template param to False and just ditch your search template) and you'll be done. FWIW note that when passing True for use_template the model_attr param is ignored. Also, you may not have a use for a full text search engine then, you could possibly just use Django's standard QuerySet lookup API, ie Artist.objects.filter(title__icontains=searchterm).

Else - if you still need a 'full' document index for other searches and only want to restrict this one search to the title you can as well add another index.CharField (with document=False, model_attr='title') for the title and only search on this field. How to do so is fully documented in Haystack's SearchQuerySet API doc.

Upvotes: 1

knbk
knbk

Reputation: 53719

model_attr and use_template don't work together. In this case, as you're querying for a single model attribute there's no need to use a template. Templates in search indexes are purely meant to group data.

Thus, you end up with:

class ArtistIndex(indexes.SearchIndex, indexes.Indexable):
    text = indexes.CharField(document=True, model_attr='title')

    def get_model(self):
        return Artist

Upvotes: 5

Related Questions