diff --git a/src/calibre/db/cache.py b/src/calibre/db/cache.py index 4faa819b42..c2d094eef0 100644 --- a/src/calibre/db/cache.py +++ b/src/calibre/db/cache.py @@ -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 = {} diff --git a/src/calibre/db/legacy.py b/src/calibre/db/legacy.py index 97babb3b60..b938e7dddf 100644 --- a/src/calibre/db/legacy.py +++ b/src/calibre/db/legacy.py @@ -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() diff --git a/src/calibre/db/search.py b/src/calibre/db/search.py index fbe1515920..013678e3b3 100644 --- a/src/calibre/db/search.py +++ b/src/calibre/db/search.py @@ -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 diff --git a/src/calibre/db/tests/reading.py b/src/calibre/db/tests/reading.py index 24d80d33c7..fcf309ea66 100644 --- a/src/calibre/db/tests/reading.py +++ b/src/calibre/db/tests/reading.py @@ -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)], diff --git a/src/calibre/utils/search_query_parser.py b/src/calibre/utils/search_query_parser.py index 2682088681..dc2a7b51b8 100644 --- a/src/calibre/utils/search_query_parser.py +++ b/src/calibre/utils/search_query_parser.py @@ -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