Skip to content

Commit 8cef7e7

Browse files
committed
Haystack's reserved field names are now configurable.
1 parent bb04d0c commit 8cef7e7

File tree

8 files changed

+94
-29
lines changed

8 files changed

+94
-29
lines changed

docs/searchindex_api.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ search, et cetera).
7575
Haystack reserves the following field names for internal use: ``id``,
7676
``django_ct``, ``django_id`` & ``content``. The ``name`` & ``type`` names
7777
used to be reserved but no longer are.
78+
79+
You can override these field names using the ``HAYSTACK_ID_FIELD``,
80+
``HAYSTACK_DJANGO_CT_FIELD`` & ``HAYSTACK_DJANGO_ID_FIELD`` if needed.
7881

7982

8083
Significance Of ``document=True``

docs/settings.rst

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,3 +299,51 @@ An example::
299299
HAYSTACK_LIMIT_TO_REGISTERED_MODELS = False
300300

301301
Default is ``True``.
302+
303+
304+
``HAYSTACK_ID_FIELD``
305+
=====================
306+
307+
**Optional**
308+
309+
This setting allows you to control what the unique field name used internally
310+
by Haystack is called. Rarely needed unless your field names collide with
311+
Haystack's defaults.
312+
313+
An example::
314+
315+
HAYSTACK_ID_FIELD = 'my_id'
316+
317+
Default is ``id``.
318+
319+
320+
``HAYSTACK_DJANGO_CT_FIELD``
321+
============================
322+
323+
**Optional**
324+
325+
This setting allows you to control what the content type field name used
326+
internally by Haystack is called. Rarely needed unless your field names
327+
collide with Haystack's defaults.
328+
329+
An example::
330+
331+
HAYSTACK_DJANGO_CT_FIELD = 'my_django_ct'
332+
333+
Default is ``django_ct``.
334+
335+
336+
``HAYSTACK_DJANGO_ID_FIELD``
337+
============================
338+
339+
**Optional**
340+
341+
This setting allows you to control what the primary key field name used
342+
internally by Haystack is called. Rarely needed unless your field names
343+
collide with Haystack's defaults.
344+
345+
An example::
346+
347+
HAYSTACK_DJANGO_ID_FIELD = 'my_django_id'
348+
349+
Default is ``django_id``.

haystack/backends/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from django.db.models.base import ModelBase
88
from django.utils import tree
99
from django.utils.encoding import force_unicode
10-
from haystack.constants import VALID_FILTERS, FILTER_SEPARATOR
10+
from haystack.constants import ID, DJANGO_CT, DJANGO_ID, VALID_FILTERS, FILTER_SEPARATOR
1111
from haystack.exceptions import SearchBackendError, MoreLikeThisError, FacetingError
1212
try:
1313
set
@@ -476,7 +476,7 @@ def build_query(self):
476476
query = self.matching_all_fragment()
477477

478478
if len(self.models):
479-
models = sorted(['django_ct:%s.%s' % (model._meta.app_label, model._meta.module_name) for model in self.models])
479+
models = sorted(['%s:%s.%s' % (DJANGO_CT, model._meta.app_label, model._meta.module_name) for model in self.models])
480480
models_clause = ' OR '.join(models)
481481

482482
if query != self.matching_all_fragment():

haystack/backends/solr_backend.py

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from django.core.exceptions import ImproperlyConfigured
55
from django.db.models.loading import get_model
66
from haystack.backends import BaseSearchBackend, BaseSearchQuery, log_query
7+
from haystack.constants import ID, DJANGO_CT, DJANGO_ID
78
from haystack.exceptions import MissingDependency, MoreLikeThisError
89
from haystack.models import SearchResult
910
from haystack.utils import get_identifier
@@ -75,7 +76,11 @@ def remove(self, obj_or_string, commit=True):
7576
solr_id = get_identifier(obj_or_string)
7677

7778
try:
78-
self.conn.delete(id=solr_id, commit=commit)
79+
kwargs = {
80+
'commit': commit,
81+
ID: solr_id
82+
}
83+
self.conn.delete(**kwargs)
7984
except (IOError, SolrError), e:
8085
self.log.error("Failed to remove document '%s' from Solr: %s", solr_id, e)
8186

@@ -88,7 +93,7 @@ def clear(self, models=[], commit=True):
8893
models_to_delete = []
8994

9095
for model in models:
91-
models_to_delete.append("django_ct:%s.%s" % (model._meta.app_label, model._meta.module_name))
96+
models_to_delete.append("%s:%s.%s" % (DJANGO_CT, model._meta.app_label, model._meta.module_name))
9297

9398
self.conn.delete(q=" OR ".join(models_to_delete), commit=commit)
9499

@@ -175,7 +180,7 @@ def search(self, query_string, sort_by=None, start_offset=0, end_offset=None,
175180
registered_models = self.build_registered_models_list()
176181

177182
if len(registered_models) > 0:
178-
narrow_queries.add('django_ct:(%s)' % ' OR '.join(registered_models))
183+
narrow_queries.add('%s:(%s)' % (DJANGO_CT, ' OR '.join(registered_models)))
179184

180185
if narrow_queries is not None:
181186
kwargs['fq'] = list(narrow_queries)
@@ -223,15 +228,15 @@ def more_like_this(self, model_instance, additional_query_string=None,
223228
registered_models = self.build_registered_models_list()
224229

225230
if len(registered_models) > 0:
226-
narrow_queries.add('django_ct:(%s)' % ' OR '.join(registered_models))
231+
narrow_queries.add('%s:(%s)' % (DJANGO_CT, ' OR '.join(registered_models)))
227232

228233
if additional_query_string:
229234
narrow_queries.add(additional_query_string)
230235

231236
if narrow_queries:
232237
params['fq'] = list(narrow_queries)
233238

234-
query = "id:%s" % get_identifier(model_instance)
239+
query = "%s:%s" % (ID, get_identifier(model_instance))
235240

236241
try:
237242
raw_results = self.conn.more_like_this(query, field_name, **params)
@@ -271,7 +276,7 @@ def _process_results(self, raw_results, highlight=False):
271276
indexed_models = site.get_indexed_models()
272277

273278
for raw_result in raw_results.docs:
274-
app_label, model_name = raw_result['django_ct'].split('.')
279+
app_label, model_name = raw_result[DJANGO_CT].split('.')
275280
additional_fields = {}
276281
model = get_model(app_label, model_name)
277282

@@ -285,14 +290,14 @@ def _process_results(self, raw_results, highlight=False):
285290
else:
286291
additional_fields[string_key] = self.conn._to_python(value)
287292

288-
del(additional_fields['django_ct'])
289-
del(additional_fields['django_id'])
293+
del(additional_fields[DJANGO_CT])
294+
del(additional_fields[DJANGO_ID])
290295
del(additional_fields['score'])
291296

292-
if raw_result['id'] in getattr(raw_results, 'highlighting', {}):
293-
additional_fields['highlighted'] = raw_results.highlighting[raw_result['id']]
297+
if raw_result[ID] in getattr(raw_results, 'highlighting', {}):
298+
additional_fields['highlighted'] = raw_results.highlighting[raw_result[ID]]
294299

295-
result = SearchResult(app_label, model_name, raw_result['django_id'], raw_result['score'], **additional_fields)
300+
result = SearchResult(app_label, model_name, raw_result[DJANGO_ID], raw_result['score'], **additional_fields)
296301
results.append(result)
297302
else:
298303
hits -= 1

haystack/backends/whoosh_backend.py

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from django.utils.datetime_safe import datetime
1010
from django.utils.encoding import force_unicode
1111
from haystack.backends import BaseSearchBackend, BaseSearchQuery, log_query
12+
from haystack.constants import ID, DJANGO_CT, DJANGO_ID
1213
from haystack.fields import DateField, DateTimeField, IntegerField, FloatField, BooleanField, MultiValueField
1314
from haystack.exceptions import MissingDependency, SearchBackendError
1415
from haystack.models import SearchResult
@@ -32,7 +33,8 @@
3233

3334
# Bubble up the correct error.
3435
from whoosh.analysis import StemmingAnalyzer
35-
from whoosh.fields import Schema, ID, IDLIST, STORED, TEXT, KEYWORD, NUMERIC, BOOLEAN, DATETIME
36+
from whoosh.fields import Schema, IDLIST, STORED, TEXT, KEYWORD, NUMERIC, BOOLEAN, DATETIME
37+
from whoosh.fields import ID as WHOOSH_ID
3638
from whoosh import index
3739
from whoosh.qparser import QueryParser
3840
from whoosh.filedb.filestore import FileStorage, RamStorage
@@ -118,9 +120,9 @@ def setup(self):
118120

119121
def build_schema(self, fields):
120122
schema_fields = {
121-
'id': ID(stored=True, unique=True),
122-
'django_ct': ID(stored=True),
123-
'django_id': ID(stored=True),
123+
ID: WHOOSH_ID(stored=True, unique=True),
124+
DJANGO_CT: WHOOSH_ID(stored=True),
125+
DJANGO_ID: WHOOSH_ID(stored=True),
124126
}
125127
# Grab the number of keys that are hard-coded into Haystack.
126128
# We'll use this to (possibly) fail slightly more gracefully later.
@@ -186,7 +188,7 @@ def remove(self, obj_or_string, commit=True):
186188

187189
self.index = self.index.refresh()
188190
whoosh_id = get_identifier(obj_or_string)
189-
self.index.delete_by_query(q=self.parser.parse(u'id:"%s"' % whoosh_id))
191+
self.index.delete_by_query(q=self.parser.parse(u'%s:"%s"' % (ID, whoosh_id)))
190192

191193
def clear(self, models=[], commit=True):
192194
if not self.setup_complete:
@@ -200,7 +202,7 @@ def clear(self, models=[], commit=True):
200202
models_to_delete = []
201203

202204
for model in models:
203-
models_to_delete.append(u"django_ct:%s.%s" % (model._meta.app_label, model._meta.module_name))
205+
models_to_delete.append(u"%s:%s.%s" % (DJANGO_CT, model._meta.app_label, model._meta.module_name))
204206

205207
self.index.delete_by_query(q=self.parser.parse(u" OR ".join(models_to_delete)))
206208

@@ -301,7 +303,7 @@ def search(self, query_string, sort_by=None, start_offset=0, end_offset=None,
301303
registered_models = self.build_registered_models_list()
302304

303305
if len(registered_models) > 0:
304-
narrow_queries.add('django_ct:(%s)' % ' OR '.join(registered_models))
306+
narrow_queries.add('%s:(%s)' % (DJANGO_CT, ' OR '.join(registered_models)))
305307

306308
if narrow_queries is not None:
307309
# Potentially expensive? I don't see another way to do it in Whoosh...
@@ -404,7 +406,7 @@ def _process_results(self, raw_page, highlight=False, query_string='', spelling_
404406

405407
for doc_offset, raw_result in enumerate(raw_page):
406408
score = raw_page.score(doc_offset) or 0
407-
app_label, model_name = raw_result['django_ct'].split('.')
409+
app_label, model_name = raw_result[DJANGO_CT].split('.')
408410
additional_fields = {}
409411
model = get_model(app_label, model_name)
410412

@@ -425,8 +427,8 @@ def _process_results(self, raw_page, highlight=False, query_string='', spelling_
425427
else:
426428
additional_fields[string_key] = self._to_python(value)
427429

428-
del(additional_fields['django_ct'])
429-
del(additional_fields['django_id'])
430+
del(additional_fields[DJANGO_CT])
431+
del(additional_fields[DJANGO_ID])
430432

431433
if highlight:
432434
from whoosh import analysis
@@ -438,7 +440,7 @@ def _process_results(self, raw_page, highlight=False, query_string='', spelling_
438440
self.content_field_name: [highlight(additional_fields.get(self.content_field_name), terms, sa, ContextFragmenter(terms), UppercaseFormatter())],
439441
}
440442

441-
result = SearchResult(app_label, model_name, raw_result['django_id'], score, **additional_fields)
443+
result = SearchResult(app_label, model_name, raw_result[DJANGO_ID], score, **additional_fields)
442444
results.append(result)
443445
else:
444446
hits -= 1

haystack/constants.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
from django.conf import settings
22

3+
# Reserved field names
4+
ID = getattr(settings, 'HAYSTACK_ID_FIELD', 'id')
5+
DJANGO_CT = getattr(settings, 'HAYSTACK_DJANGO_CT_FIELD', 'django_ct')
6+
DJANGO_ID = getattr(settings, 'HAYSTACK_DJANGO_ID_FIELD', 'django_id')
7+
38
# Default operator. Valid options are AND/OR.
49
DEFAULT_OPERATOR = getattr(settings, 'HAYSTACK_DEFAULT_OPERATOR', 'AND')
510

haystack/indexes.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import sys
33
from django.db.models import signals
44
from django.utils.encoding import force_unicode
5+
from haystack.constants import ID, DJANGO_CT, DJANGO_ID
56
from haystack.fields import *
67
from haystack.utils import get_identifier, get_facet_field_name
78

@@ -122,9 +123,9 @@ def prepare(self, obj):
122123
Fetches and adds/alters data before indexing.
123124
"""
124125
self.prepared_data = {
125-
'id': get_identifier(obj),
126-
'django_ct': "%s.%s" % (obj._meta.app_label, obj._meta.module_name),
127-
'django_id': force_unicode(obj.pk),
126+
ID: get_identifier(obj),
127+
DJANGO_CT: "%s.%s" % (obj._meta.app_label, obj._meta.module_name),
128+
DJANGO_ID: force_unicode(obj.pk),
128129
}
129130

130131
for field_name, field in self.fields.items():
@@ -302,7 +303,7 @@ class ModelSearchIndex(SearchIndex):
302303
"""
303304
text = CharField(document=True, use_template=True)
304305
# list of reserved field names
305-
fields_to_skip = ('id', 'django_ct', 'django_id', 'content', 'text')
306+
fields_to_skip = (ID, DJANGO_CT, DJANGO_ID, 'content', 'text')
306307

307308
def __init__(self, model, backend=None, extra_field_kwargs=None):
308309
self.model = model

haystack/utils/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import re
22
from django.utils.html import strip_tags
3+
from haystack.constants import ID, DJANGO_CT, DJANGO_ID
34
try:
45
set
56
except NameError:
@@ -26,7 +27,7 @@ def get_identifier(obj_or_string):
2627

2728

2829
def get_facet_field_name(fieldname):
29-
if fieldname in ['id', 'django_id', 'django_ct']:
30+
if fieldname in [ID, DJANGO_ID, DJANGO_CT]:
3031
return fieldname
3132

3233
return "%s_exact" % fieldname

0 commit comments

Comments
 (0)