Implement saved searches API

This commit is contained in:
Kovid Goyal 2013-07-19 13:28:27 +05:30
parent 72849dfee6
commit b60530fafe
5 changed files with 26 additions and 15 deletions

View File

@ -89,7 +89,7 @@ class Cache(object):
self.formatter_template_cache = {}
self.dirtied_cache = {}
self.dirtied_sequence = 0
self._search_api = Search(self.field_metadata.get_search_terms())
self._search_api = Search(self, 'saved_searches', self.field_metadata.get_search_terms())
# Implement locking for all simple read/write API methods
# An unlocked version of the method is stored with the name starting
@ -127,9 +127,8 @@ class Cache(object):
except:
traceback.print_exc()
# TODO: Saved searches
# if len(saved_searches().names()):
# self.field_metadata.add_search_category(label='search', name=_('Searches'))
if len(self._search_api.get_saved_searches().names()):
self.field_metadata.add_search_category(label='search', name=_('Searches'))
self.field_metadata.add_grouped_search_terms(
self._pref('grouped_search_terms', {}))
@ -141,6 +140,11 @@ class Cache(object):
if self.dirtied_cache:
self.dirtied_sequence = max(self.dirtied_cache.itervalues())+1
@property
def prefs(self):
'For internal use only (used by SavedSearchQueries). For thread-safe access to the preferences, use the pref() and set_pref() methods.'
return self.backend.prefs
@write_api
def initialize_template_cache(self):
self.formatter_template_cache = {}

View File

@ -22,6 +22,7 @@ from calibre.db.categories import CATEGORY_SORTS
from calibre.db.view import View
from calibre.db.write import clean_identifier
from calibre.utils.date import utcnow
from calibre.utils.search_query_parser import set_saved_searches
def cleanup_tags(tags):
tags = [x.strip().replace(',', ';') for x in tags if x.strip()]
@ -73,6 +74,9 @@ class LibraryDatabase(object):
self.last_update_check = self.last_modified()
if not self.is_second_db:
set_saved_searches(self, 'saved_searches')
def close(self):
self.backend.close()

View File

@ -15,7 +15,7 @@ from calibre.utils.config_base import prefs
from calibre.utils.date import parse_date, UNDEFINED_DATE, now
from calibre.utils.icu import primary_find
from calibre.utils.localization import lang_map, canonicalize_lang
from calibre.utils.search_query_parser import SearchQueryParser, ParseException
from calibre.utils.search_query_parser import SearchQueryParser, ParseException, SavedSearchQueries
CONTAINS_MATCH = 0
EQUALS_MATCH = 1
@ -392,7 +392,7 @@ class Parser(SearchQueryParser):
def __init__(self, dbcache, all_book_ids, gst, date_search, num_search,
bool_search, keypair_search, limit_search_columns, limit_search_columns_to,
locations, virtual_fields):
locations, virtual_fields, get_saved_searches):
self.dbcache, self.all_book_ids = dbcache, all_book_ids
self.all_search_locations = frozenset(locations)
self.grouped_search_terms = gst
@ -403,7 +403,7 @@ class Parser(SearchQueryParser):
self.virtual_fields = virtual_fields or {}
if 'marked' not in self.virtual_fields:
self.virtual_fields['marked'] = self
super(Parser, self).__init__(locations, optimize=True)
super(Parser, self).__init__(locations, optimize=True, get_saved_searches=get_saved_searches)
@property
def field_metadata(self):
@ -651,12 +651,16 @@ class Parser(SearchQueryParser):
class Search(object):
def __init__(self, all_search_locations=()):
def __init__(self, db, opt_name, all_search_locations=()):
self.all_search_locations = all_search_locations
self.date_search = DateSearch()
self.num_search = NumericSearch()
self.bool_search = BooleanSearch()
self.keypair_search = KeyPairSearch()
self.saved_searches = SavedSearchQueries(db, opt_name)
def get_saved_searches(self):
return self.saved_searches
def change_locations(self, newlocs):
self.all_search_locations = newlocs
@ -689,11 +693,11 @@ class Search(object):
self.keypair_search,
prefs['limit_search_columns'],
prefs['limit_search_columns_to'], self.all_search_locations,
virtual_fields)
virtual_fields, self.get_saved_searches)
try:
ret = sqp.parse(q)
finally:
sqp.dbcache = None
sqp.dbcache = sqp.get_saved_searches = None
return ret

View File

@ -149,8 +149,6 @@ class ReadingTest(BaseTest):
'#tags':[3, 2, 1],
'#yesno':[3, 1, 2],
'#comments':[3, 2, 1],
# TODO: Add an empty book to the db and ensure that empty
# fields sort the same as they do in db2
}.iteritems():
x = list(reversed(order))
self.assertEqual(order, cache.multisort([(field, True)],

View File

@ -40,7 +40,7 @@ class SavedSearchQueries(object):
self.queries = {}
try:
self._db = weakref.ref(db)
except:
except TypeError:
# db could be None
self._db = lambda : None
@ -292,9 +292,10 @@ class SearchQueryParser(object):
failed.append(test[0])
return failed
def __init__(self, locations, test=False, optimize=False):
def __init__(self, locations, test=False, optimize=False, get_saved_searches=None):
self.sqp_initialize(locations, test=test, optimize=optimize)
self.parser = Parser()
self.get_saved_searches = saved_searches if get_saved_searches is None else get_saved_searches
def sqp_change_locations(self, locations):
self.sqp_initialize(locations, optimize=self.optimize)
@ -367,7 +368,7 @@ class SearchQueryParser(object):
raise ParseException(_('Recursive saved search: {0}').format(query))
if self.recurse_level > 5:
self.searches_seen.add(query)
return self._parse(saved_searches().lookup(query), candidates)
return self._parse(self.get_saved_searches().lookup(query), candidates)
except ParseException as e:
raise e
except: # convert all exceptions (e.g., missing key) to a parse error