Browse files

Suggestions support

Conflicts:
	docs/api.rst
	elasticutils/__init__.py
	elasticutils/tests/test_query.py
  • Loading branch information...
1 parent 37d9de0 commit 94f77c2734f1f83b9336e8c72f9a18c18ccea4fd @koterpillar koterpillar committed with willkg Nov 25, 2013
Showing with 99 additions and 0 deletions.
  1. +2 −0 docs/api.rst
  2. +18 −0 docs/searching.rst
  3. +42 −0 elasticutils/__init__.py
  4. +37 −0 elasticutils/tests/test_query.py
View
2 docs/api.rst
@@ -45,6 +45,8 @@ The S class
.. automethod:: elasticutils.S.search_type
+ .. automethod:: elasticutils.S.suggest
+
.. automethod:: elasticutils.S.values_list
.. automethod:: elasticutils.S.values_dict
View
18 docs/searching.rst
@@ -736,6 +736,24 @@ See :py:meth:`elasticutils.S.highlight` for more details.
Elasticsearch docs for highlight
+Suggestions: ``suggest``
+========================
+
+Spelling suggestions can be retrieved by using the
+:py:meth:`elasticutils.S.suggest` method:
+
+::
+
+ q = S().query(text='Aice').suggest('mysuggest', 'Alice', field='text')
+ print q.suggestions()['mysuggest'][0]['options']
+
+
+.. seealso::
+
+ http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-suggesters.html
+ Elasticsearch docs for suggesters
+
+
.. _queries-chapter-facets-section:
Facets
View
42 elasticutils/__init__.py
@@ -958,6 +958,27 @@ def search_type(self, search_type):
"""
return self._clone(next_step=('search_type', search_type))
+ def suggest(self, name, term, **kwargs):
+ """
+ Set suggestion options.
+
+ :arg name: The name to use for the suggestions.
+ :arg term: The term to suggest similar looking terms for.
+
+ Additional keyword options:
+
+ * ``field`` -- The field to base suggestions upon, defaults to _all
+
+ Results will have a ``_suggestions`` property containing the
+ suggestions for all terms.
+
+ .. Note::
+
+ Calling this multiple times will add multiple suggest clauses to
+ the query.
+ """
+ return self._clone(next_step=('suggest', (name, term, kwargs)))
+
def extra(self, **kw):
"""
Return a new S instance with extra args combined with existing
@@ -1001,6 +1022,7 @@ def _build_query(self):
demote = None
highlight_fields = set()
highlight_options = {}
+ suggestions = {}
explain = False
as_list = as_dict = False
search_type = None
@@ -1050,6 +1072,8 @@ def _build_query(self):
highlight_options.update(value[1])
elif action == 'search_type':
search_type = value
+ elif action == 'suggest':
+ suggestions[value[0]] = (value[1], value[2])
elif action in ('es', 'indexes', 'doctypes', 'boost'):
# Ignore these--we use these elsewhere, but want to
# make sure lack of handling it here doesn't throw an
@@ -1123,6 +1147,14 @@ def _build_query(self):
if explain:
qs['explain'] = True
+ for suggestion, (term, kwargs) in suggestions.iteritems():
+ qs.setdefault('suggest', {})[suggestion] = {
+ 'text': term,
+ 'term': {
+ 'field': kwargs.get('field', '_all'),
+ },
+ }
+
self.fields, self.as_list, self.as_dict = fields, as_list, as_dict
self.search_type = search_type
return qs
@@ -1514,6 +1546,16 @@ def facet_counts(self):
"""
return _facet_counts(self._raw_facets().items())
+ def suggestions(self):
+ """
+ Executes search and returns suggestions.
+
+ >>> s = S().query(name='Aice').suggest(name='Aice')
+ >>> suggestions = s.suggestions()['name']
+
+ """
+ return self._do_search().response.get('suggest', {})
+
class MLT(PythonMixin):
"""Represents a lazy Elasticsearch More Like This API request.
View
37 elasticutils/tests/test_query.py
@@ -1349,6 +1349,43 @@ def test_query_and_fetch(self):
eq_(len(s[:1]), 2)
+class SuggestionTest(ESTestCase):
+ @classmethod
+ def setup_class(cls):
+ super(SuggestionTest, cls).setup_class()
+ if cls.skip_tests:
+ return
+
+ cls.create_index()
+ cls.index_data([
+ {'id': 1, 'name': 'bar'},
+ {'id': 2, 'name': 'mark', 'location': 'mart'},
+ {'id': 3, 'name': 'car'},
+ {'id': 4, 'name': 'duck'},
+ {'id': 5, 'name': 'train car'}
+ ])
+ cls.refresh()
+
+ def test_suggestions(self):
+ """Make sure correct suggestions are being returned.
+
+ Test adding multiple ``suggest()`` clauses to the query, including
+ different fields.
+
+ """
+ s = (self.get_s().query(name__text='mary')
+ .suggest('mysuggest', 'mary'))
+ suggestions = s.suggestions()
+ options = [o['text'] for o in suggestions['mysuggest'][0]['options']]
+ eq_(options, ['mark', 'mart'])
+
+ s = (self.get_s().query(name__text='mary')
+ .suggest('mysuggest', 'mary', field='name'))
+ suggestions = s.suggestions()
+ options = [o['text'] for o in suggestions['mysuggest'][0]['options']]
+ eq_(options, ['mark'])
+
+
def test_to_python():
def check_to_python(obj, expected):
eq_(S().to_python(obj), expected)

0 comments on commit 94f77c2

Please sign in to comment.