<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -28,17 +28,25 @@ in your class.
 ``SQ`` Objects
 ==============
 
-The ``SearchQuery`` object maintains a tree of ``SQ`` objects. Each ``SQ``
-object supports what field it looks up against, what kind of lookup (i.e.
-the __'s), what value it's looking for, if it's a AND/OR/NOT and tracks
-any children it may have. The ``SearchQuery.build_query`` method starts with
-the root of the tree, building part of the final query at each node until
-the full final query is ready for the ``SearchBackend``.
-
-Much like ``django.db.models.Q`` objects, ``SQ`` objects can be passed to
+For expressing more complex queries, especially involving AND/OR/NOT in
+different combinations, you should use ``SQ`` objects. Like
+``django.db.models.Q`` objects, ``SQ`` objects can be passed to
 ``SearchQuerySet.filter`` and use the familiar unary operators (``&amp;``, ``|`` and
 ``~``) to generate complex parts of the query.
 
+Example::
+
+    from haystack.query import SQ
+    # We want &quot;title: Foo AND (tags:bar OR tags:moof)&quot;
+    sqs = SearchQuerySet().filter(title='Foo').filter(SQ(tags='bar') | SQ(tags='moof'))
+
+Internally, the ``SearchQuery`` object maintains a tree of ``SQ`` objects. Each
+``SQ`` object supports what field it looks up against, what kind of lookup (i.e.
+the ``__`` filters), what value it's looking for, if it's a AND/OR/NOT and
+tracks any children it may have. The ``SearchQuery.build_query`` method starts
+with the root of the tree, building part of the final query at each node until
+the full final query is ready for the ``SearchBackend``.
+
 
 Backend-Specific Methods
 ========================</diff>
      <filename>docs/searchquery_api.rst</filename>
    </modified>
    <modified>
      <diff>@@ -4,6 +4,7 @@ from copy import deepcopy
 from time import time
 from django.conf import settings
 from django.core import signals
+from django.db.models import Q
 from django.db.models.base import ModelBase
 from django.utils import tree
 from django.utils.encoding import force_unicode
@@ -186,7 +187,7 @@ class BaseSearchBackend(object):
 SearchBackend = BaseSearchBackend
 
 
-class SQ(tree.Node):
+class SearchNode(tree.Node):
     &quot;&quot;&quot;
     Manages an individual condition within a query.
     
@@ -201,50 +202,6 @@ class SQ(tree.Node):
     OR = 'OR'
     default = AND
     
-    def __init__(self, children=None, connector=None, negated=False, *args, **kwargs):
-        &quot;&quot;&quot;
-        Constructs a new SQ. If no connector is given, the default will be used.
-        &quot;&quot;&quot;
-        children = children or []
-        
-        if hasattr(children, 'children'): # if we have a Node-like object turn it into a list
-            children = [children]
-        
-        try:
-            children.extend(list(args) + kwargs.items())
-        except AttributeError:
-            raise ValueError(&quot;'children' should be None or an list.&quot;)
-        
-        super(SQ, self).__init__(children, connector, negated)
-    
-    def add(self, data, connector=None):
-        &quot;&quot;&quot;
-        Adds a child node. If connector differs from the root then the tree is
-        pushed down a level.
-        
-        data is a tuple of (expression, value)
-        &quot;&quot;&quot;
-        super(SQ, self).add(data, connector or self.default)
-    
-    def _combine(self, other, conn):
-        if not isinstance(other, SQ):
-            raise TypeError(other)
-        
-        obj = deepcopy(self)
-        obj.add(other, conn)
-        return obj
-    
-    def __or__(self, other):
-        return self._combine(other, self.OR)
-    
-    def __and__(self, other):
-        return self._combine(other, self.AND)
-    
-    def __invert__(self):
-        obj = deepcopy(self)
-        obj.negate()
-        return obj
-    
     def __repr__(self):
         return '&lt;SQ: %s %s&gt;' % (self.connector, self.as_query_string(self._repr_query_fragment_callback))
     
@@ -290,6 +247,17 @@ class SQ(tree.Node):
         return (field, filter_type)
 
 
+class SQ(Q, SearchNode):
+    &quot;&quot;&quot;
+    Manages an individual condition within a query.
+    
+    Most often, this will be a lookup to ensure that a certain word or phrase
+    appears in the documents being indexed. However, it also supports filtering
+    types (such as 'lt', 'gt', 'in' and others) for more complex lookups.
+    &quot;&quot;&quot;
+    pass
+
+
 class BaseSearchQuery(object):
     &quot;&quot;&quot;
     A base class for handling the query itself.
@@ -310,7 +278,7 @@ class BaseSearchQuery(object):
     &quot;&quot;&quot;
     
     def __init__(self, backend=None):
-        self.query_filter = SQ()
+        self.query_filter = SearchNode()
         self.order_by = []
         self.models = set()
         self.boost = {}</diff>
      <filename>haystack/backends/__init__.py</filename>
    </modified>
    <modified>
      <diff>@@ -16,19 +16,19 @@ from core.tests.mocks import MockSearchQuery, MockSearchBackend, MixedMockSearch
 
 class SQTestCase(TestCase):
     def test_split_expression(self):
-        qf = SQ(foo='bar')
+        sq = SQ(foo='bar')
         
-        self.assertEqual(qf.split_expression('foo'), ('foo', 'exact'))
-        self.assertEqual(qf.split_expression('foo__exact'), ('foo', 'exact'))
-        self.assertEqual(qf.split_expression('foo__lt'), ('foo', 'lt'))
-        self.assertEqual(qf.split_expression('foo__lte'), ('foo', 'lte'))
-        self.assertEqual(qf.split_expression('foo__gt'), ('foo', 'gt'))
-        self.assertEqual(qf.split_expression('foo__gte'), ('foo', 'gte'))
-        self.assertEqual(qf.split_expression('foo__in'), ('foo', 'in'))
-        self.assertEqual(qf.split_expression('foo__startswith'), ('foo', 'startswith'))
+        self.assertEqual(sq.split_expression('foo'), ('foo', 'exact'))
+        self.assertEqual(sq.split_expression('foo__exact'), ('foo', 'exact'))
+        self.assertEqual(sq.split_expression('foo__lt'), ('foo', 'lt'))
+        self.assertEqual(sq.split_expression('foo__lte'), ('foo', 'lte'))
+        self.assertEqual(sq.split_expression('foo__gt'), ('foo', 'gt'))
+        self.assertEqual(sq.split_expression('foo__gte'), ('foo', 'gte'))
+        self.assertEqual(sq.split_expression('foo__in'), ('foo', 'in'))
+        self.assertEqual(sq.split_expression('foo__startswith'), ('foo', 'startswith'))
         
         # Unrecognized filter. Fall back to exact.
-        self.assertEqual(qf.split_expression('foo__moof'), ('foo', 'exact'))
+        self.assertEqual(sq.split_expression('foo__moof'), ('foo', 'exact'))
     
     def test_repr(self):
         self.assertEqual(repr(SQ(foo='bar')), '&lt;SQ: AND foo__exact=bar&gt;')</diff>
      <filename>tests/core/tests/query.py</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>2a9fb83db0764af5ee3e81fe34aec022f4b48a5c</id>
    </parent>
  </parents>
  <author>
    <name>Daniel Lindsley</name>
    <email>daniel@toastdriven.com</email>
  </author>
  <url>http://github.com/toastdriven/django-haystack/commit/24fcb86ecef6fb91efd366a60ea2740def0df09f</url>
  <id>24fcb86ecef6fb91efd366a60ea2740def0df09f</id>
  <committed-date>2009-11-02T23:44:38-08:00</committed-date>
  <authored-date>2009-11-02T23:34:52-08:00</authored-date>
  <message>Simplified the SQ object and removed a limitation on kwargs/field names that could be passed in. Thanks to traviscline for the patch.</message>
  <tree>9135cbea19ff1ad8a80dbe6e06637ccd81ba919d</tree>
  <committer>
    <name>Daniel Lindsley</name>
    <email>daniel@toastdriven.com</email>
  </committer>
</commit>
