Skip to content

Commit

Permalink
Added .values() / .values_list() methods, for fetching less data. Tha…
Browse files Browse the repository at this point in the history
…nks to acdha for the original implementation!
  • Loading branch information
toastdriven committed Dec 15, 2011
1 parent d0ab16f commit 419550a
Show file tree
Hide file tree
Showing 7 changed files with 273 additions and 68 deletions.
14 changes: 8 additions & 6 deletions docs/searchquery_api.rst
Expand Up @@ -43,10 +43,10 @@ different combinations, you should use ``SQ`` objects. Like
Example::

from haystack.query import SQ

# We want "title: Foo AND (tags:bar OR tags:moof)"
sqs = SearchQuerySet().filter(title='Foo').filter(SQ(tags='bar') | SQ(tags='moof'))

# To clean user-provided data:
sqs = SearchQuerySet()
clean_query = sqs.query.clean(user_query)
Expand Down Expand Up @@ -109,24 +109,26 @@ A basic (override-able) implementation is provided.
``run``
~~~~~~~

.. method:: SearchQuery.run(self, spelling_query=None)
.. method:: SearchQuery.run(self, spelling_query=None, **kwargs)

Builds and executes the query. Returns a list of search results.

Optionally passes along an alternate query for spelling suggestions.

Optionally passes along more kwargs for controlling the search query.

``run_mlt``
~~~~~~~~~~~

.. method:: SearchQuery.run_mlt(self)
.. method:: SearchQuery.run_mlt(self, **kwargs)

Executes the More Like This. Returns a list of search results similar
to the provided document (and optionally query).

``run_raw``
~~~~~~~~~~~

.. method:: SearchQuery.run_raw(self)
.. method:: SearchQuery.run_raw(self, **kwargs)

Executes a raw query. Returns a list of search results.

Expand All @@ -143,7 +145,7 @@ the results.
``get_results``
~~~~~~~~~~~~~~~

.. method:: SearchQuery.get_results(self)
.. method:: SearchQuery.get_results(self, **kwargs)

Returns the results received from the backend.

Expand Down
44 changes: 44 additions & 0 deletions docs/searchqueryset_api.rst
Expand Up @@ -611,6 +611,50 @@ Example::
suggestion = SearchQuerySet().spelling_suggestion('moar exmples')
suggestion # u'more examples'

``values``
~~~~~~~~~~

.. method:: SearchQuerySet.values(self, *fields)

Returns a list of dictionaries, each containing the key/value pairs for the
result, exactly like Django's ``ValuesQuerySet``.

This method causes the query to evaluate and run the search if it hasn't already
run.

You must provide a list of one or more fields as arguments. These fields will
be the ones included in the individual results.

Example::

sqs = SearchQuerySet().auto_query('banana').values('title', 'description')


``values_list``
~~~~~~~~~~~~~~~

.. method:: SearchQuerySet.values_list(self, *fields, **kwargs)

Returns a list of field values as tuples, exactly like Django's
``ValuesListQuerySet``.

This method causes the query to evaluate and run the search if it hasn't already
run.

You must provide a list of one or more fields as arguments. These fields will
be the ones included in the individual results.

You may optionally also provide a ``flat=True`` kwarg, which in the case of a
single field being provided, will return a flat list of that field rather than
a list of tuples.

Example::

sqs = SearchQuerySet().auto_query('banana').values_list('title', 'description')

# ...or just the titles as a flat list...
sqs = SearchQuerySet().auto_query('banana').values_list('title', flat=True)


.. _field-lookups:

Expand Down
45 changes: 31 additions & 14 deletions haystack/backends/__init__.py
Expand Up @@ -292,6 +292,11 @@ def __init__(self, using=DEFAULT_ALIAS):
self.date_facets = {}
self.query_facets = []
self.narrow_queries = set()
#: If defined, fields should be a list of field names - no other values
#: will be retrieved so the caller must be careful to include django_ct
#: and django_id when using code which expects those to be included in
#: the results
self.fields = []
self._raw_query = None
self._raw_query_params = {}
self._more_like_this = False
Expand Down Expand Up @@ -361,42 +366,54 @@ def build_params(self, spelling_query=None):
if self.result_class:
kwargs['result_class'] = self.result_class

if self.fields:
kwargs['fields'] = self.fields

return kwargs

def run(self, spelling_query=None):
def run(self, spelling_query=None, **kwargs):
"""Builds and executes the query. Returns a list of search results."""
final_query = self.build_query()
kwargs = self.build_params(spelling_query=spelling_query)
search_kwargs = self.build_params(spelling_query=spelling_query)

if kwargs:
search_kwargs.update(kwargs)

results = self.backend.search(final_query, **kwargs)
results = self.backend.search(final_query, **search_kwargs)
self._results = results.get('results', [])
self._hit_count = results.get('hits', 0)
self._facet_counts = self.post_process_facets(results)
self._spelling_suggestion = results.get('spelling_suggestion', None)

def run_mlt(self):
def run_mlt(self, **kwargs):
"""
Executes the More Like This. Returns a list of search results similar
to the provided document (and optionally query).
"""
if self._more_like_this is False or self._mlt_instance is None:
raise MoreLikeThisError("No instance was provided to determine 'More Like This' results.")

kwargs = {
search_kwargs = {
'result_class': self.result_class,
}

if kwargs:
search_kwargs.update(kwargs)

additional_query_string = self.build_query()
results = self.backend.more_like_this(self._mlt_instance, additional_query_string, **kwargs)
results = self.backend.more_like_this(self._mlt_instance, additional_query_string, **search_kwargs)
self._results = results.get('results', [])
self._hit_count = results.get('hits', 0)

def run_raw(self):
def run_raw(self, **kwargs):
"""Executes a raw query. Returns a list of search results."""
kwargs = self.build_params()
kwargs.update(self._raw_query_params)
search_kwargs = self.build_params()
search_kwargs.update(self._raw_query_params)

results = self.backend.search(self._raw_query, **kwargs)
if kwargs:
search_kwargs.update(kwargs)

results = self.backend.search(self._raw_query, **search_kwargs)
self._results = results.get('results', [])
self._hit_count = results.get('hits', 0)
self._facet_counts = results.get('facets', {})
Expand Down Expand Up @@ -426,7 +443,7 @@ def get_count(self):

return self._hit_count

def get_results(self):
def get_results(self, **kwargs):
"""
Returns the results received from the backend.
Expand All @@ -436,12 +453,12 @@ def get_results(self):
if self._results is None:
if self._more_like_this:
# Special case for MLT.
self.run_mlt()
self.run_mlt(**kwargs)
elif self._raw_query:
# Special case for raw queries.
self.run_raw()
self.run_raw(**kwargs)
else:
self.run()
self.run(**kwargs)

return self._results

Expand Down
37 changes: 21 additions & 16 deletions haystack/backends/solr_backend.py
@@ -1,5 +1,4 @@
import logging
import sys
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.db.models.loading import get_model
Expand Down Expand Up @@ -120,6 +119,9 @@ def search(self, query_string, sort_by=None, start_offset=0, end_offset=None,
}

if fields:
if isinstance(fields, (list, set)):
fields = " ".join(fields)

kwargs['fl'] = fields

if sort_by is not None:
Expand Down Expand Up @@ -459,10 +461,10 @@ def build_query_fragment(self, field, filter_type, value):

return result

def run(self, spelling_query=None):
def run(self, spelling_query=None, **kwargs):
"""Builds and executes the query. Returns a list of search results."""
final_query = self.build_query()
kwargs = {
search_kwargs = {
'start_offset': self.start_offset,
'result_class': self.result_class,
}
Expand All @@ -476,50 +478,53 @@ def run(self, spelling_query=None):
else:
order_by_list.append('%s asc' % order_by)

kwargs['sort_by'] = ", ".join(order_by_list)
search_kwargs['sort_by'] = ", ".join(order_by_list)

if self.end_offset is not None:
kwargs['end_offset'] = self.end_offset
search_kwargs['end_offset'] = self.end_offset

if self.highlight:
kwargs['highlight'] = self.highlight
search_kwargs['highlight'] = self.highlight

if self.facets:
kwargs['facets'] = list(self.facets)
search_kwargs['facets'] = list(self.facets)

if self.date_facets:
kwargs['date_facets'] = self.date_facets
search_kwargs['date_facets'] = self.date_facets

if self.query_facets:
kwargs['query_facets'] = self.query_facets
search_kwargs['query_facets'] = self.query_facets

if self.narrow_queries:
kwargs['narrow_queries'] = self.narrow_queries
search_kwargs['narrow_queries'] = self.narrow_queries

if self.fields:
search_kwargs['fields'] = self.fields

if spelling_query:
kwargs['spelling_query'] = spelling_query
search_kwargs['spelling_query'] = spelling_query

results = self.backend.search(final_query, **kwargs)
results = self.backend.search(final_query, **search_kwargs)
self._results = results.get('results', [])
self._hit_count = results.get('hits', 0)
self._facet_counts = self.post_process_facets(results)
self._spelling_suggestion = results.get('spelling_suggestion', None)

def run_mlt(self):
def run_mlt(self, **kwargs):
"""Builds and executes the query. Returns a list of search results."""
if self._more_like_this is False or self._mlt_instance is None:
raise MoreLikeThisError("No instance was provided to determine 'More Like This' results.")

additional_query_string = self.build_query()
kwargs = {
search_kwargs = {
'start_offset': self.start_offset,
'result_class': self.result_class,
}

if self.end_offset is not None:
kwargs['end_offset'] = self.end_offset - self.start_offset
search_kwargs['end_offset'] = self.end_offset - self.start_offset

results = self.backend.more_like_this(self._mlt_instance, additional_query_string, **kwargs)
results = self.backend.more_like_this(self._mlt_instance, additional_query_string, **search_kwargs)
self._results = results.get('results', [])
self._hit_count = results.get('hits', 0)

Expand Down

0 comments on commit 419550a

Please sign in to comment.