Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
Szymon Teżewski committed Jul 19, 2013
2 parents 867f05b + 831d738 commit 15b3cdc
Show file tree
Hide file tree
Showing 74 changed files with 1,495 additions and 293 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.settings
*.pyc
.DS_Store
_build
Expand All @@ -7,6 +8,8 @@ dist
build
MANIFEST
.tox
env
env3

# Because I'm ghetto like that.
tests/pyelasticsearch.py
6 changes: 6 additions & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,9 @@ Thanks to
* Rodrigo Guzman (rz) for a fix to query handling in the ``simple`` backend.
* Martin J. Laubach (mjl) for fixing the logic used when combining querysets
* Eric Holscher (ericholscher) for a docs fix.
* Erik Rose (erikrose) for a quick pyelasticsearch-compatibility patch
* Stefan Wehrmeyer (stefanw) for a simple search filter fix
* Dan Watson (dcwatson) for various patches.
* Andrew Schoen (andrewschoen) for the addition of ``HAYSTACK_IDENTIFIER_METHOD``
* Pablo SEMINARIO (pabluk) for a docs fix.
* Eric Thurgood (ethurgood) for a import fix in the Elasticssearch backend.
21 changes: 3 additions & 18 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ Haystack
========

:author: Daniel Lindsley
:date: 2012/03/30
:date: 2013/04/28

Haystack provides modular search for Django. It features a unified, familiar
API that allows you to plug in different search backends (such as Solr_,
Expand Down Expand Up @@ -33,7 +33,8 @@ Documentation
=============

* Development version: http://docs.haystacksearch.org/
* v1.2.X: http://django-haystack.readthedocs.org/en/v1.2.6/
* v2.0.X: http://django-haystack.readthedocs.org/en/v2.0.0/
* v1.2.X: http://django-haystack.readthedocs.org/en/v1.2.7/
* v1.1.X: http://django-haystack.readthedocs.org/en/v1.1/


Expand All @@ -48,19 +49,3 @@ Haystack has a relatively easily-met set of requirements.
Additionally, each backend has its own requirements. You should refer to
http://docs.haystacksearch.org/dev/installing_search_engines.html for more
details.


Commercial Support
==================

If you're using Haystack in a commercial environment, paid support is available
from `Toast Driven`_. Services offered include:

* Advice/help with setup
* Implementation in your project
* Bugfixes in Haystack itself
* Features in Haystack itself

If you're interested, please contact Daniel Lindsley ([email protected]).

.. _`Toast Driven`: http://toastdriven.com/
157 changes: 150 additions & 7 deletions docs/autocomplete.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,19 @@ Example (continuing from the tutorial)::
import datetime
from haystack import indexes
from myapp.models import Note


class NoteIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True)
author = indexes.CharField(model_attr='user')
pub_date = indexes.DateTimeField(model_attr='pub_date')
# We add this for autocomplete.
content_auto = indexes.EdgeNgramField(model_attr='content')

def get_model(self):
return Note
def index_queryset(self):

def index_queryset(self, using=None):
"""Used when the entire index for model is updated."""
return Note.objects.filter(pub_date__lte=datetime.datetime.now())

Expand All @@ -59,7 +59,7 @@ You simply provide a field & the query you wish to search on to the
search would look like::

from haystack.query import SearchQuerySet

SearchQuerySet().autocomplete(content_auto='old')
# Result match things like 'goldfish', 'cuckold' & 'older'.

Expand All @@ -70,8 +70,151 @@ If you need more control over your results, you can use standard
``SearchQuerySet.filter`` calls. For instance::

from haystack.query import SearchQuerySet

sqs = SearchQuerySet().filter(content_auto=request.GET.get('q', ''))

This can also be extended to use ``SQ`` for more complex queries (and is what's
being done under the hood in the ``SearchQuerySet.autocomplete`` method).


Example Implementation
======================

The above is the low-level backend portion of how you implement autocomplete.
To make it work in browser, you need both a view to run the autocomplete
& some Javascript to fetch the results.

Since it comes up often, here is an example implementation of those things.

.. warning::

This code comes with no warranty. Don't ask for support on it. If you
copy-paste it & it burns down your server room, I'm not liable for any
of it.

It worked this one time on my machine in a simulated environment.

And yeah, semicolon-less + 2 space + comma-first. Deal with it.

A stripped-down view might look like::

# views.py
import simplejson as json
from django.http import HttpResponse
from haystack.query import SearchQuerySet


def autocomplete(request):
sqs = SearchQuerySet().autocomplete(request.GET.get('q', ''))[:5]
suggestions = [result.title for result in sqs]
# Make sure you return a JSON object, not a bare list.
# Otherwise, you could be vulnerable to an XSS attack.
the_data = json.dumps({
'results': suggestions
})
return HttpResponse(the_data, content_type='application/json')

The template might look like::

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Autocomplete Example</title>
</head>
<body>
<h1>Autocomplete Example</h1>

<form method="post" action="/search/" class="autocomplete-me">
<input type="text" id="id_q" name="q">
<input type="submit" value="Search!">
</form>

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script type="text/javascript">
// In a perfect world, this would be its own library file that got included
// on the page & only the ``$(document).ready(...)`` below would be present.
// But this is an example.
var Autocomplete = function(options) {
this.form_selector = options.form_selector
this.url = options.url || '/search/autocomplete/'
this.delay = parseInt(options.delay || 300)
this.minimum_length = parseInt(options.minimum_length || 3)
this.form_elem = null
this.query_box = null
}

Autocomplete.prototype.setup = function() {
var self = this

this.form_elem = $(this.form_selector)
this.query_box = this.form_elem.find('input[name=q]')

// Watch the input box.
this.query_box.on('keyup', function() {
var query = self.query_box.val()

if(query.length < self.minimum_length) {
return false
}

self.fetch(query)
})

// On selecting a result, populate the search field.
this.form_elem.on('click', '.ac-result', function(ev) {
self.query_box.val($(this).text())
$('.ac-results').remove()
return false
})
}

Autocomplete.prototype.fetch = function(query) {
var self = this

$.ajax({
url: this.url
, data: {
'q': query
}
, success: function(data) {
self.show_results(data)
}
})
}

Autocomplete.prototype.show_results = function(data) {
// Remove any existing results.
$('.ac-results').remove()

var results = data.results || []
var results_wrapper = $('<div class="ac-results"></div>')
var base_elem = $('<div class="result-wrapper"><a href="#" class="ac-result"></a></div>')

if(results.length > 0) {
for(var res_offset in results) {
var elem = base_elem.clone()
// Don't use .html(...) here, as you open yourself to XSS.
// Really, you should use some form of templating.
elem.find('.ac-result').text(results[res_offset])
results_wrapper.append(elem)
}
}
else {
var elem = base_elem.clone()
elem.text("No results found.")
results_wrapper.append(elem)
}

this.query_box.after(results_wrapper)
}

$(document).ready(function() {
window.autocomplete = new Autocomplete({
form_selector: '.autocomplete-me'
})
window.autocomplete.setup()
})
</script>
</body>
</html>
7 changes: 4 additions & 3 deletions docs/backend_support.rst
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ Elasticsearch
* Stored (non-indexed) fields
* Highlighting
* Spatial search
* Requires: pyelasticsearch 0.2+ & Elasticsearch 0.17.7+
* Requires: pyelasticsearch 0.4+ & Elasticsearch 0.17.7+

Whoosh
------
Expand All @@ -59,10 +59,11 @@ Whoosh

* Full SearchQuerySet support
* Automatic query building
* "More Like This" functionality
* Term Boosting
* Stored (non-indexed) fields
* Highlighting
* Requires: whoosh (1.1.1+)
* Requires: whoosh (2.0.0+)

Xapian
------
Expand All @@ -89,7 +90,7 @@ Backend Support Matrix
+----------------+------------------------+---------------------+----------------+------------+----------+---------------+--------------+---------+
| Elasticsearch | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
+----------------+------------------------+---------------------+----------------+------------+----------+---------------+--------------+---------+
| Whoosh | Yes | Yes | No | Yes | No | Yes | Yes | No |
| Whoosh | Yes | Yes | Yes | Yes | No | Yes | Yes | No |
+----------------+------------------------+---------------------+----------------+------------+----------+---------------+--------------+---------+
| Xapian | Yes | Yes | Yes | Yes | Yes | Yes | Yes (plugin) | No |
+----------------+------------------------+---------------------+----------------+------------+----------+---------------+--------------+---------+
Expand Down
20 changes: 10 additions & 10 deletions docs/boost.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ Despite all being types of boost, they take place at different times and have
slightly different effects on scoring.

Term boost happens at query time (when the search query is run) and is based
around increasing the score is a certain word/phrase is seen.
around increasing the score if a certain word/phrase is seen.

On the other hand, document & field boosts take place at indexing time (when
the document is being added to the index). Document boost causes the relevance
of the entire result to go up, where field boost causes only searches within
that field to do better.

.. warning:
.. warning::

Be warned that boost is very, very sensitive & can hurt overall search
quality if over-zealously applied. Even very small adjustments can affect
Expand All @@ -47,7 +47,7 @@ Example::

# Slight increase in relevance for documents that include "banana".
sqs = SearchQuerySet().boost('banana', 1.1)

# Big decrease in relevance for documents that include "blueberry".
sqs = SearchQuerySet().boost('blueberry', 0.8)

Expand All @@ -63,16 +63,16 @@ Document boosting is done by adding a ``boost`` field to the prepared data

from haystack import indexes
from notes.models import Note


class NoteSearchIndex(indexes.SearchIndex, indexes.Indexable):
# Your regular fields here then...

def prepare(self, obj):
data = super(NoteSearchIndex, self).prepare(obj)
data['boost'] = 1.1
return data


Another approach might be to add a new field called ``boost``. However, this
can skew your schema and is not encouraged.
Expand All @@ -86,11 +86,11 @@ An example of this might be increasing the significance of a ``title``::

from haystack import indexes
from notes.models import Note


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

def get_model(self):
return Note
4 changes: 2 additions & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@
# built documents.
#
# The short X.Y version.
version = '2.0'
version = '2.0.1'
# The full version, including alpha/beta/rc tags.
release = '2.0.0-beta'
release = '2.0.1-dev'

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
Expand Down
6 changes: 3 additions & 3 deletions docs/debugging.rst
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@ Several issues can cause no results to be found. Most commonly it is either
not running a ``rebuild_index`` to populate your index or having a blank
``document=True`` field, resulting in no content for the engine to search on.

* Do you have a ``search_sites.py`` that runs ``haystack.autodiscover``?
* Have you registered your models with the main ``haystack.site`` (usually
within your ``search_indexes.py``)?
* Do you have a ``search_indexes.py`` located within an installed app?
* Do you have data in your database?
* Have you run a ``./manage.py rebuild_index`` to index all of your content?
* Try running ``./manage.py rebuild_index -v2`` for more verbose output to
ensure data is being processed/inserted.
* Start a Django shell (``./manage.py shell``) and try::

>>> from haystack.query import SearchQuerySet
Expand Down
2 changes: 1 addition & 1 deletion docs/faceting.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ What Is Faceting?
-----------------

Faceting is a way to provide users with feedback about the number of documents
which match terms they may be interested in. At it's simplest, it gives
which match terms they may be interested in. At its simplest, it gives
document counts based on words in the corpus, date ranges, numeric ranges or
even advanced queries.

Expand Down
2 changes: 1 addition & 1 deletion docs/glossary.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
Glossary
========

Search is a domain full of it's own jargon and definitions. As this may be an
Search is a domain full of its own jargon and definitions. As this may be an
unfamiliar territory to many developers, what follows are some commonly used
terms and what they mean.

Expand Down
4 changes: 2 additions & 2 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ Elasticsearch_, Whoosh_, Xapian_, etc.) without having to modify your code.

.. note::

This documentation represents the development version of Haystack. For
old versions of the documentation: `1.2`_, `1.1`_.
This documentation represents the development version of Haystack (2.0.x).
For old versions of the documentation: `1.2`_, `1.1`_.

.. _`1.2`: http://django-haystack.readthedocs.org/en/v1.2.6/index.html
.. _`1.1`: http://django-haystack.readthedocs.org/en/v1.1/index.html
Expand Down
Loading

0 comments on commit 15b3cdc

Please sign in to comment.