Skip to content

Commit 453aa69

Browse files
notanumbertoastdriven
authored andcommitted
Added the ability to "weight" individual fields to adjust their relevance.
1 parent 5ba519f commit 453aa69

File tree

6 files changed

+85
-4
lines changed

6 files changed

+85
-4
lines changed

haystack/backends/solr_backend.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ def update(self, index, iterable, commit=True):
6767

6868
if len(docs) > 0:
6969
try:
70-
self.conn.add(docs, commit=commit)
70+
self.conn.add(docs, commit=commit, boost=index.get_field_weights())
7171
except (IOError, SolrError), e:
7272
self.log.error("Failed to add documents to Solr: %s", e)
7373

haystack/fields.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class SearchField(object):
2020
def __init__(self, model_attr=None, use_template=False, template_name=None,
2121
document=False, indexed=True, stored=True, faceted=False,
2222
default=NOT_PROVIDED, null=False, index_fieldname=None,
23-
facet_class=None):
23+
facet_class=None, weight=1.0):
2424
# Track what the index thinks this field is called.
2525
self.instance_name = None
2626
self.model_attr = model_attr
@@ -33,6 +33,7 @@ def __init__(self, model_attr=None, use_template=False, template_name=None,
3333
self._default = default
3434
self.null = null
3535
self.index_fieldname = index_fieldname
36+
self.weight = weight
3637
self.is_multivalued = False
3738

3839
# We supply the facet_class for making it easy to create a faceted

haystack/indexes.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,14 @@ def get_content_field(self):
166166
if field.document is True:
167167
return field.index_fieldname
168168

169+
def get_field_weights(self):
170+
"""Returns a dict of fields with weight values"""
171+
weights = {}
172+
for field_name, field in self.fields.items():
173+
if field.weight:
174+
weights[field_name] = field.weight
175+
return weights
176+
169177
def update(self):
170178
"""Update the entire index"""
171179
self.backend.update(self, self.get_queryset())

tests/core/models.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,12 @@ class AThirdMockModel(AnotherMockModel):
3535

3636
class CharPKMockModel(models.Model):
3737
key = models.CharField(primary_key=True, max_length=10)
38+
39+
40+
class AFourthMockModel(models.Model):
41+
author = models.CharField(max_length=255)
42+
editor = models.CharField(max_length=255)
43+
pub_date = models.DateTimeField(default=datetime.datetime.now)
44+
45+
def __unicode__(self):
46+
return self.author

tests/solr_test_schema.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@
6868
<field name="type" type="string" indexed="true" stored="true" multiValued="false" />
6969
<field name="name" type="string" indexed="true" stored="true" multiValued="false" />
7070
<field name="name_exact" type="string" indexed="true" stored="true" multiValued="false" />
71-
<field name="author" type="string" indexed="true" stored="true" multiValued="false" />
71+
<field name="author" type="string" indexed="true" stored="true" multiValued="false" omitNorms="false" />
72+
<field name="editor" type="string" indexed="true" stored="true" multiValued="false" omitNorms="false" />
7273
<field name="text" type="text" indexed="true" stored="true" multiValued="false" />
7374
<field name="pub_date" type="date" indexed="true" stored="true" multiValued="false" />
7475
<field name="month" type="string" indexed="false" stored="true" multiValued="false" />

tests/solr_tests/tests/solr_backend.py

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from haystack.exceptions import HaystackError
1010
from haystack.query import SearchQuerySet, RelatedSearchQuerySet, SQ
1111
from haystack.sites import SearchSite
12-
from core.models import MockModel, AnotherMockModel
12+
from core.models import MockModel, AnotherMockModel, AFourthMockModel
1313
try:
1414
set
1515
except NameError:
@@ -63,6 +63,16 @@ def prepare_text(self, obj):
6363
return u"You might be searching for the user %s" % obj.author
6464

6565

66+
class SolrBoostMockSearchIndex(SearchIndex):
67+
text = CharField(
68+
document=True, use_template=True,
69+
template_name='search/indexes/core/mockmodel_template.txt'
70+
)
71+
author = CharField(model_attr='author', weight=2.0)
72+
editor = CharField(model_attr='editor')
73+
pub_date = DateField(model_attr='pub_date')
74+
75+
6676
class SolrRoundTripSearchIndex(SearchIndex):
6777
text = CharField(document=True, default='')
6878
name = CharField()
@@ -974,3 +984,55 @@ def test_pickling(self):
974984
like_a_cuke = pickle.loads(in_a_pickle)
975985
self.assertEqual(len(like_a_cuke), len(results))
976986
self.assertEqual(like_a_cuke[0].id, results[0].id)
987+
988+
989+
class SolrBoostBackendTestCase(TestCase):
990+
def setUp(self):
991+
super(SolrBoostBackendTestCase, self).setUp()
992+
993+
# Wipe it clean.
994+
self.raw_solr = pysolr.Solr(settings.HAYSTACK_SOLR_URL)
995+
clear_solr_index()
996+
997+
self.site = SearchSite()
998+
self.sb = SearchBackend(site=self.site)
999+
self.smmi = SolrBoostMockSearchIndex(AFourthMockModel, backend=self.sb)
1000+
self.site.register(AFourthMockModel, SolrBoostMockSearchIndex)
1001+
1002+
# Stow.
1003+
import haystack
1004+
self.old_site = haystack.site
1005+
haystack.site = self.site
1006+
self.sample_objs = []
1007+
1008+
for i in xrange(1, 5):
1009+
mock = AFourthMockModel()
1010+
mock.id = i
1011+
1012+
if i % 2:
1013+
mock.author = 'daniel'
1014+
mock.editor = 'david'
1015+
else:
1016+
mock.author = 'david'
1017+
mock.editor = 'daniel'
1018+
1019+
mock.pub_date = datetime.date(2009, 2, 25) - datetime.timedelta(days=i)
1020+
self.sample_objs.append(mock)
1021+
1022+
def tearDown(self):
1023+
import haystack
1024+
haystack.site = self.old_site
1025+
super(SolrBoostBackendTestCase, self).tearDown()
1026+
1027+
def test_boost(self):
1028+
self.sb.update(self.smmi, self.sample_objs)
1029+
self.assertEqual(self.raw_solr.search('*:*').hits, 4)
1030+
1031+
results = SearchQuerySet().filter(SQ(author='daniel') | SQ(editor='daniel'))
1032+
1033+
self.assertEqual([result.id for result in results], [
1034+
'core.afourthmockmodel.1',
1035+
'core.afourthmockmodel.3',
1036+
'core.afourthmockmodel.2',
1037+
'core.afourthmockmodel.4'
1038+
])

0 commit comments

Comments
 (0)