Commits

Matt Chaput committed 321dd76

Fixed inheritance problems with InverseMatcher. Fixes issue #263.
Replaced too-cute InverseMatcher.all_ids() with base imp. because it was wasteful
and masked issues with normal iteration.

  • Participants
  • Parent commits 339530c
  • Branches 2.4x

Comments (0)

Files changed (2)

File src/whoosh/matching/wrappers.py

     def replace(self, minquality=0):
         # Replace the child matcher
         r = self.child.replace(minquality)
-        if not r.is_active():
-            # If the replaced child is inactive, return an inactive matcher
-            return mcore.NullMatcher()
-        elif r is not self.child:
+        if r is not self.child:
             # If the child changed, return a new wrapper on the new child
             return self._replacement(r)
         else:
     wrapped matcher.
     """
 
-    def __init__(self, child, limit, missing=None, weight=1.0):
+    def __init__(self, child, limit, missing=None, weight=1.0, id=0):
         super(InverseMatcher, self).__init__(child)
         self.limit = limit
         self._weight = weight
         self.missing = missing or (lambda id: False)
-        self._id = 0
+        self._id = id
         self._find_next()
 
     def copy(self):
         return self.__class__(self.child.copy(), self.limit,
-                              weight=self._weight, missing=self.missing)
+                              weight=self._weight, missing=self.missing,
+                              id=self._id)
 
     def _replacement(self, newchild):
         return self.__class__(newchild, self.limit, missing=self.missing,
-                              weight=self.weight)
+                              weight=self._weight, id=self._id)
 
     def is_active(self):
         return self._id < self.limit
         child = self.child
         missing = self.missing
 
+        # If the current docnum isn't missing and the child matcher is
+        # exhausted (so we don't have to worry about skipping its matches), we
+        # don't have to do anything
         if not child.is_active() and not missing(self._id):
             return
 
+        # Catch the child matcher up to where this matcher is
         if child.is_active() and child.id() < self._id:
             child.skip_to(self._id)
 
         return self._id
 
     def all_ids(self):
-        missing = self.missing
-        negs = set(self.child.all_ids())
-        return (id for id in xrange(self.limit)
-                if id not in negs and not missing(id))
+        return mcore.Matcher.all_ids(self)
 
     def next(self):
         if self._id >= self.limit:

File tests/test_matching.py

 
 from nose.tools import assert_equal, assert_not_equal  # @UnresolvedImport
 
-from whoosh import fields, matching, query
+from whoosh import fields, matching, qparser, query
 from whoosh.compat import u, xrange, permutations
 from whoosh.filedb.filestore import RamStorage
 from whoosh.query import And, Term
 
 
 def test_exclude():
-    em = matching.FilterMatcher(matching.ListMatcher([1, 2, 5, 9, 10]), frozenset([2, 9]), exclude=True)
+    em = matching.FilterMatcher(matching.ListMatcher([1, 2, 5, 9, 10]),
+                                frozenset([2, 9]), exclude=True)
     assert_equal(list(em.all_ids()), [1, 5, 10])
 
-    em = matching.FilterMatcher(matching.ListMatcher([1, 2, 5, 9, 10]), frozenset([2, 9]), exclude=True)
+    em = matching.FilterMatcher(matching.ListMatcher([1, 2, 5, 9, 10]),
+                                frozenset([2, 9]), exclude=True)
     assert_equal(list(em.all_ids()), [1, 5, 10])
 
-    em = matching.FilterMatcher(matching.ListMatcher([1, 2, 5, 9, 10]), frozenset([2, 9]), exclude=True)
+    em = matching.FilterMatcher(matching.ListMatcher([1, 2, 5, 9, 10]),
+                                frozenset([2, 9]), exclude=True)
     em.next()
     em.next()
     em = em.copy()
     while um.is_active():
         ls.append((um.id(), um.score()))
         um.next()
-    assert_equal(ls, [(0, 1.0), (1, 1.0), (4, 2.0), (10, 1.0), (20, 2.0), (90, 1.0)])
+    assert_equal(ls, [(0, 1.0), (1, 1.0), (4, 2.0), (10, 1.0), (20, 2.0),
+                      (90, 1.0)])
 
     lm1 = matching.ListMatcher([1, 4, 10, 20, 90])
     lm2 = matching.ListMatcher([0, 4, 20])
 
 
 def test_intersection():
-    schema = fields.Schema(key=fields.ID(stored=True), value=fields.TEXT(stored=True))
+    schema = fields.Schema(key=fields.ID(stored=True),
+                           value=fields.TEXT(stored=True))
     st = RamStorage()
     ix = st.create_index(schema)
 
 
 def test_random_intersections():
     domain = [u("alpha"), u("bravo"), u("charlie"), u("delta"), u("echo"),
-              u("foxtrot"), u("golf"), u("hotel"), u("india"), u("juliet"), u("kilo"),
-              u("lima"), u("mike")]
+              u("foxtrot"), u("golf"), u("hotel"), u("india"), u("juliet"),
+              u("kilo"), u("lima"), u("mike")]
     segments = 5
     docsperseg = 50
     fieldlimits = (3, 10)
     w.commit()
 
     with ix.searcher() as s:
-        q = query.And([query.Term("text", "alfa"), query.Term("text", "charlie")])
+        q = query.And([query.Term("text", "alfa"),
+                       query.Term("text", "charlie")])
         m = q.matcher(s)
 
         while m.is_active():
-            assert_equal(sorted(m.matching_terms()), [("text", "alfa"), ("text", "charlie")])
+            assert_equal(sorted(m.matching_terms()),
+                         [("text", "alfa"), ("text", "charlie")])
             m.next()
+
+
+def test_exclusion():
+    from datetime import datetime
+
+    schema = fields.Schema(id=fields.ID(stored=True), date=fields.DATETIME)
+    ix = RamStorage().create_index(schema)
+    dt1 = datetime(1950, 1, 1)
+    dt2 = datetime(1960, 1, 1)
+    with ix.writer() as w:
+        # Make 39 documents with dates != dt1 and then make a last document
+        # with feed == dt1.
+        for i in xrange(40):
+            w.add_document(id=u(str(i)), date=(dt2 if i >= 1 else dt1))
+
+    with ix.searcher() as s:
+        qp = qparser.QueryParser("id", schema)
+        # Find documents where date != dt1
+        q = qp.parse("NOT (date:(19500101000000))")
+
+        r = s.search(q, limit=None)
+        assert_equal(len(r), 39)  # Total number of matched documents
+        assert_equal(r.scored_length(), 39)  # Number of docs in the results
+
+
+