Commits

Matt Chaput  committed a1e09af

Added Searcher.context() and boolean_context() methods.
Changed AndNot and Required to pass boolean context to second matcher.

  • Participants
  • Parent commits c7ff919

Comments (0)

Files changed (6)

File src/whoosh/qparser/default.py

             # example, on a stop word)
             if not texts:
                 return None
-
             text = texts[0]
 
         return termclass(fieldname, text, boost=boost)

File src/whoosh/query/nary.py

             return qcore.NullQuery
 
     def matcher(self, searcher, context=None):
-        from whoosh.searching import boolean_context
-
         # Pull any queries inside a Not() out into their own list
         subs, nots = self._split_queries()
         if not subs:
                 notq = nots[0]
             else:
                 notq = Or(nots)
-            notm = notq.matcher(searcher, boolean_context)
+            notm = notq.matcher(searcher, searcher.boolean_context())
             if notm.is_active():
                 m = matching.AndNotMatcher(m, notm)
 
 
     def _matcher(self, subs, searcher, context):
         needs_current = context.needs_current if context else True
+        scored = context.weighting is not None if context else True
 
         # A binary tree of UnionMatchers is usually slower than
         # ArrayUnionMatcher, but in certain circumstances the binary tree is
             m = self._tree_matcher(subs, matching.UnionMatcher, searcher,
                                    context, q_weight_fn)
         else:
+            # Get submatchers
+            ms = [subq.matcher(searcher, context) for subq in subs]
+            # Remove null matchers
+            ms = [m for m in ms if m.is_active()]
+            if not ms:
+                return matching.NullMatcher()
             # Make an ArrayUnionMatcher object
-            ms = [subq.matcher(searcher, context) for subq in subs]
             m = matching.ArrayUnionMatcher(ms, searcher.doc_count_all(),
-                                           boost=self.boost)
+                                           boost=self.boost, scored=scored)
 
         # If a scaling factor was given, wrap the matcher in a CoordMatcher
         # to alter scores based on term coordination
     """
 
     JOINT = " ANDNOT "
-    matcherclass = matching.AndNotMatcher
 
     def with_boost(self, boost):
         return self.__class__(self.a.with_boost(boost), self.b)
     def requires(self):
         return self.a.requires()
 
+    def matcher(self, searcher, context=None):
+        scoredm = self.a.matcher(searcher, context)
+        notm = self.b.matcher(searcher, searcher.boolean_context())
+        return matching.AndNotMatcher(scoredm, notm)
+
 
 class Otherwise(BinaryQuery):
     """A binary query that only matches the second clause if the first clause
     def docs(self, searcher):
         return And(self.subqueries).docs(searcher)
 
+    def matcher(self, searcher, context=None):
+        scoredm = self.a.matcher(searcher, context)
+        requiredm = self.b.matcher(searcher, searcher.boolean_context())
+        return matching.AndNotMatcher(scoredm, requiredm)
+
 
 class AndMaybe(BinaryQuery):
     """Binary query takes results from the first query. If and only if the

File src/whoosh/query/nested.py

                                         searcher.doc_count_all())
 
     def deletion_docs(self, searcher):
-        from whoosh.searching import boolean_context
-
         bits = searcher._filter_to_comb(self.parents)
         if not bits:
             return
 
-        m = self.child.matcher(searcher, boolean_context)
+        m = self.child.matcher(searcher, searcher.boolean_context())
         maxdoc = searcher.doc_count_all()
         while m.is_active():
             docnum = m.id()

File src/whoosh/query/qcore.py

         :param searcher: A :class:`whoosh.searching.Searcher` object.
         """
 
-        from whoosh.searching import boolean_context
-
         try:
-            return self.matcher(searcher, boolean_context).all_ids()
+            context = searcher.boolean_context()
+            return self.matcher(searcher, context).all_ids()
         except TermNotFound:
             return iter([])
 

File src/whoosh/query/wrappers.py

         return 1 if ixreader.doc_count() else 0
 
     def matcher(self, searcher, context=None):
-        from whoosh.searching import boolean_context
-
         # Usually only called if Not is the root query. Otherwise, queries such
         # as And and Or do special handling of Not subqueries.
         reader = searcher.reader()
-        child = self.query.matcher(searcher, boolean_context)
+        child = self.query.matcher(searcher, searcher.boolean_context())
         return matching.InverseMatcher(child, reader.doc_count_all(),
                                        missing=reader.is_deleted)
 

File src/whoosh/searching.py

         return ctx
 
 
-boolean_context = SearchContext(needs_current=False, weighting=None)
-
-
 # Searcher class
 
 class Searcher(object):
         """
         return self.ixreader
 
-    def set_caching_policy(self, *args, **kwargs):
-        self.ixreader.set_caching_policy(*args, **kwargs)
+    def context(self, **kwargs):
+        """Generates a :class:`SearchContext` for this searcher.
+        """
+
+        if "weighting" not in kwargs:
+            kwargs["weighting"] = self.weighting
+
+        return SearchContext(**kwargs)
+
+    def boolean_context(self):
+        """Shortcut returns a SearchContext set for unscored (boolean)
+        searching.
+        """
+
+        return self.context(needs_current=False, weighting=None)
 
     def postings(self, fieldname, text, weighting=None, qf=1):
         """Returns a :class:`whoosh.matching.Matcher` for the postings of the
             the results into.
         """
 
-        context = SearchContext(weighting=self.weighting)
+        context = self.context()
         collector.prepare(self, q, context)
 
         # Make a list of subsearchers (if the searcher is atomic, it's a list
 
         self.results = results
         self.searcher = results.searcher
+        self.reader = self.searcher.reader()
         self.pos = self.rank = pos
         self.docnum = docnum
         self.score = score
         if fieldname in self.fields():
             return self._fields[fieldname]
 
-        reader = self.searcher.reader()
+        reader = self.reader
         if reader.has_column(fieldname):
             cr = reader.column_reader(fieldname)
             return cr[self.docnum]
         raise KeyError(fieldname)
 
     def __contains__(self, key):
-        return key in self.fields()
+        return (key in self.fields()
+                or self.reader.has_column(key))
 
     def items(self):
         return list(self.fields().items())