qcolumns.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. # Copyright 2012 Matt Chaput. All rights reserved.
  2. #
  3. # Redistribution and use in source and binary forms, with or without
  4. # modification, are permitted provided that the following conditions are met:
  5. #
  6. # 1. Redistributions of source code must retain the above copyright notice,
  7. # this list of conditions and the following disclaimer.
  8. #
  9. # 2. Redistributions in binary form must reproduce the above copyright
  10. # notice, this list of conditions and the following disclaimer in the
  11. # documentation and/or other materials provided with the distribution.
  12. #
  13. # THIS SOFTWARE IS PROVIDED BY MATT CHAPUT ``AS IS'' AND ANY EXPRESS OR
  14. # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  15. # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
  16. # EVENT SHALL MATT CHAPUT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  17. # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  18. # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
  19. # OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  20. # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  21. # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
  22. # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  23. #
  24. # The views and conclusions contained in the software and documentation are
  25. # those of the authors and should not be interpreted as representing official
  26. # policies, either expressed or implied, of Matt Chaput.
  27. from whoosh.matching import ConstantScoreMatcher, NullMatcher, ReadTooFar
  28. from whoosh.query import Query
  29. class ColumnQuery(Query):
  30. """A query that matches per-document values stored in a column rather than
  31. terms in the inverted index.
  32. This may be useful in special circumstances, but note that this is MUCH
  33. SLOWER than searching an indexed field.
  34. """
  35. def __init__(self, fieldname, condition):
  36. """
  37. :param fieldname: the name of the field to look in. If the field does
  38. not have a column, this query will not match anything.
  39. :param condition: if this is a callable, it is called on each value
  40. in the column, and for documents where callable(docvalue) returns
  41. True are returned as matching documents. If this is not a callable,
  42. the document values are compared to it (using ``==``).
  43. """
  44. self.fieldname = fieldname
  45. self.condition = condition
  46. def is_leaf(self):
  47. return True
  48. def matcher(self, searcher, context=None):
  49. fieldname = self.fieldname
  50. condition = self.condition
  51. if callable(condition):
  52. comp = condition
  53. else:
  54. def comp(v):
  55. # Made this a function instead of a lambda so I could put
  56. # debug prints here if necessary ;)
  57. return v == condition
  58. reader = searcher.reader()
  59. if not reader.has_column(fieldname):
  60. return NullMatcher()
  61. creader = reader.column_reader(fieldname)
  62. return ColumnMatcher(creader, comp)
  63. class ColumnMatcher(ConstantScoreMatcher):
  64. def __init__(self, creader, condition):
  65. self.creader = creader
  66. self.condition = condition
  67. self._i = 0
  68. self._find_next()
  69. def _find_next(self):
  70. condition = self.condition
  71. creader = self.creader
  72. while self._i < len(creader) and not condition(creader[self._i]):
  73. self._i += 1
  74. def is_active(self):
  75. return self._i < len(self.creader)
  76. def next(self):
  77. if not self.is_active():
  78. raise ReadTooFar
  79. self._i += 1
  80. self._find_next()
  81. def reset(self):
  82. self._i = 0
  83. self._find_next()
  84. def id(self):
  85. return self._i
  86. def all_ids(self):
  87. condition = self.condition
  88. for docnum, v in enumerate(self.creader):
  89. if condition(v):
  90. yield docnum
  91. def supports(self, astype):
  92. return False
  93. def skip_to_quality(self, minquality):
  94. if self._score <= minquality:
  95. self._i = len(self.creader)
  96. return True