Workout With Friends
Stay fit with a little motivation
 All Classes Namespaces Files Functions Variables Properties
fulltext.py
Go to the documentation of this file.
1 ##
2 #
3 # Helpers to assist in Whoosh fulltext indexing and searching.
4 #
5 
6 from __future__ import unicode_literals
7 import os
8 from whoosh.analysis import NgramFilter, StandardAnalyzer
9 from whoosh.fields import ID, SchemaClass, TEXT
10 from whoosh.filedb.filestore import FileStorage, RamStorage
11 from whoosh.index import exists_in
12 from whoosh.qparser import QueryParser
13 
14 
15 index = None
16 parser = QueryParser
17 analyzer = StandardAnalyzer() | NgramFilter(minsize=2, maxsize=2)
18 
19 
20 def initialize_fulltext(settings):
21  global index
22  index_path = settings.get('fulltext_dir')
23  if not index_path:
24  storage = RamStorage()
25  index = storage.create_index(schema=WhooshSchema)
26  return
27  if not os.path.exists(index_path):
28  os.makedirs(index_path)
29  storage = FileStorage(index_path)
30  if not exists_in(index_path):
31  index = storage.create_index(schema=WhooshSchema)
32  else:
33  index = storage.open_index(schema=WhooshSchema)
34 
35 
36 ##
37 #
38 # Very generic indexing schema.
39 #
40 class WhooshSchema(SchemaClass):
41 
42  index_id = ID(stored=True, unique=True)
43  data = TEXT(stored=False, analyzer=analyzer)
44 
45 
46 ##
47 #
48 # Adds support for fulltext searching to SQLAlchemy's Base object.
49 #
50 class FulltextBase(object):
51 
52  index_fields = []
53 
54  @classmethod
55  def index_type(cls):
56  return cls.__name__
57 
58  def index_id(self):
59  return '%s:%s' % (self.index_type(), self.id)
60 
61  def index_data(self):
62  return ' '.join([unicode(getattr(self, attr)) for attr in self.index_fields])
63 
64  ##
65  #
66  # Add a fulltext index for this instance.
67  #
68  def add_index(self, writer):
69  if self.index_fields:
70  writer.add_document(index_id=self.index_id(), data=self.index_data())
71 
72  ##
73  #
74  # Delete the fulltext index for this instance.
75  #
76  def delete_index(self, writer):
77  if self.index_fields:
78  writer.delete_by_term('index_id', self.index_id())
79 
80  ##
81  #
82  # Update the fulltext index for this instance.
83  #
84  def update_index(self, writer):
85  if self.index_fields:
86  writer.update_document(index_id=self.index_id(), data=self.index_data())
87 
88  @classmethod
89  ##
90  #
91  # Return a query for items after performing a fulltext search.
92  #
93  def _get_fulltext_query(cls, terms):
94  ids = _search(terms, cls.index_type())
95  return cls.query.filter(cls.id.in_(ids))
96 
97 
98 ##
99 #
100 # Perform a search of the given type using the terms. Return all ids which
101 # match.
102 #
103 def _search(terms, index_type):
104  query = parser('data', index.schema).parse(terms)
105  with index.searcher() as searcher:
106  results = searcher.search(query)
107  ids = _results_to_ids(results, index_type)
108  return ids
109 
110 
111 ##
112 #
113 # Convert search results to ids to be used in a SQLAlchemy query.
114 #
115 def _results_to_ids(results, index_type):
116  ids = set()
117  for result in results:
118  type, id = result.get('index_id').split(':')
119  if type == index_type:
120  ids.add(id)
121  return ids
122