mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Boolean searches
This commit is contained in:
parent
e23df40adb
commit
e0df1634ab
@ -17,6 +17,22 @@ from calibre.utils.search_query_parser import SearchQueryParser, ParseException
|
|||||||
|
|
||||||
# TODO: Thread safety of saved searches
|
# TODO: Thread safety of saved searches
|
||||||
|
|
||||||
|
def force_to_bool(val):
|
||||||
|
if isinstance(val, (str, unicode)):
|
||||||
|
try:
|
||||||
|
val = icu_lower(val)
|
||||||
|
if not val:
|
||||||
|
val = None
|
||||||
|
elif val in [_('yes'), _('checked'), 'true', 'yes']:
|
||||||
|
val = True
|
||||||
|
elif val in [_('no'), _('unchecked'), 'false', 'no']:
|
||||||
|
val = False
|
||||||
|
else:
|
||||||
|
val = bool(int(val))
|
||||||
|
except:
|
||||||
|
val = None
|
||||||
|
return val
|
||||||
|
|
||||||
class DateSearch(object): # {{{
|
class DateSearch(object): # {{{
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@ -225,14 +241,57 @@ class NumericSearch(object): # {{{
|
|||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
class BoolenSearch(object): # {{{
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.local_no = icu_lower(_('no'))
|
||||||
|
self.local_yes = icu_lower(_('yes'))
|
||||||
|
self.local_unchecked = icu_lower(_('unchecked'))
|
||||||
|
self.local_checked = icu_lower(_('checked'))
|
||||||
|
self.local_empty = icu_lower(_('empty'))
|
||||||
|
self.local_blank = icu_lower(_('blank'))
|
||||||
|
self.local_bool_values = {
|
||||||
|
self.local_no, self.local_unchecked, '_no', 'false', 'no',
|
||||||
|
self.local_yes, self.local_checked, '_yes', 'true', 'yes',
|
||||||
|
self.local_empty, self.local_blank, '_empty', 'empty'}
|
||||||
|
|
||||||
|
def __call__(self, query, field_iter, bools_are_tristate):
|
||||||
|
matches = set()
|
||||||
|
if query not in self.local_bool_values:
|
||||||
|
raise ParseException(_('Invalid boolean query "{0}"').format(query))
|
||||||
|
for val, book_ids in field_iter():
|
||||||
|
val = force_to_bool(val)
|
||||||
|
if not bools_are_tristate:
|
||||||
|
if val is None or not val: # item is None or set to false
|
||||||
|
if query in { self.local_no, self.local_unchecked, 'no', '_no', 'false' }:
|
||||||
|
matches |= book_ids
|
||||||
|
else: # item is explicitly set to true
|
||||||
|
if query in { self.local_yes, self.local_checked, 'yes', '_yes', 'true' }:
|
||||||
|
matches |= book_ids
|
||||||
|
else:
|
||||||
|
if val is None:
|
||||||
|
if query in { self.local_empty, self.local_blank, 'empty', '_empty', 'false' }:
|
||||||
|
matches |= book_ids
|
||||||
|
elif not val: # is not None and false
|
||||||
|
if query in { self.local_no, self.local_unchecked, 'no', '_no', 'true' }:
|
||||||
|
matches |= book_ids
|
||||||
|
else: # item is not None and true
|
||||||
|
if query in { self.local_yes, self.local_checked, 'yes', '_yes', 'true' }:
|
||||||
|
matches |= book_ids
|
||||||
|
return matches
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
class Parser(SearchQueryParser):
|
class Parser(SearchQueryParser):
|
||||||
|
|
||||||
def __init__(self, dbcache, all_book_ids, gst, date_search, num_search,
|
def __init__(self, dbcache, all_book_ids, gst, date_search, num_search,
|
||||||
limit_search_columns, limit_search_columns_to, locations):
|
bool_search, limit_search_columns, limit_search_columns_to,
|
||||||
|
locations):
|
||||||
self.dbcache, self.all_book_ids = dbcache, all_book_ids
|
self.dbcache, self.all_book_ids = dbcache, all_book_ids
|
||||||
self.all_search_locations = frozenset(locations)
|
self.all_search_locations = frozenset(locations)
|
||||||
self.grouped_search_terms = gst
|
self.grouped_search_terms = gst
|
||||||
self.date_search, self.num_search = date_search, num_search
|
self.date_search, self.num_search = date_search, num_search
|
||||||
|
self.bool_search = bool_search
|
||||||
self.limit_search_columns, self.limit_search_columns_to = (
|
self.limit_search_columns, self.limit_search_columns_to = (
|
||||||
limit_search_columns, limit_search_columns_to)
|
limit_search_columns, limit_search_columns_to)
|
||||||
super(Parser, self).__init__(locations, optimize=True)
|
super(Parser, self).__init__(locations, optimize=True)
|
||||||
@ -344,6 +403,12 @@ class Parser(SearchQueryParser):
|
|||||||
self.dbcache.fields[location].iter_counts, candidates),
|
self.dbcache.fields[location].iter_counts, candidates),
|
||||||
location, dt, candidates)
|
location, dt, candidates)
|
||||||
|
|
||||||
|
# take care of boolean special case
|
||||||
|
if dt == 'bool':
|
||||||
|
return self.bool_search(icu_lower(query),
|
||||||
|
partial(self.field_iter, location, candidates),
|
||||||
|
self.dbcache.pref('bools_are_tristate'))
|
||||||
|
|
||||||
return matches
|
return matches
|
||||||
|
|
||||||
|
|
||||||
@ -353,6 +418,7 @@ class Search(object):
|
|||||||
self.all_search_locations = all_search_locations
|
self.all_search_locations = all_search_locations
|
||||||
self.date_search = DateSearch()
|
self.date_search = DateSearch()
|
||||||
self.num_search = NumericSearch()
|
self.num_search = NumericSearch()
|
||||||
|
self.bool_search = BoolenSearch()
|
||||||
|
|
||||||
def change_locations(self, newlocs):
|
def change_locations(self, newlocs):
|
||||||
self.all_search_locations = newlocs
|
self.all_search_locations = newlocs
|
||||||
@ -380,7 +446,8 @@ class Search(object):
|
|||||||
# 0.000974 seconds.
|
# 0.000974 seconds.
|
||||||
sqp = Parser(
|
sqp = Parser(
|
||||||
dbcache, all_book_ids, dbcache.pref('grouped_search_terms'),
|
dbcache, all_book_ids, dbcache.pref('grouped_search_terms'),
|
||||||
self.date_search, self.num_search, prefs[ 'limit_search_columns' ],
|
self.date_search, self.num_search, self.bool_search,
|
||||||
|
prefs[ 'limit_search_columns' ],
|
||||||
prefs[ 'limit_search_columns_to' ], self.all_search_locations)
|
prefs[ 'limit_search_columns_to' ], self.all_search_locations)
|
||||||
try:
|
try:
|
||||||
ret = sqp.parse(query)
|
ret = sqp.parse(query)
|
||||||
|
@ -208,6 +208,10 @@ class ReadingTest(BaseTest):
|
|||||||
'#float:10.01', 'series_index:1', 'series_index:<3', 'id:1',
|
'#float:10.01', 'series_index:1', 'series_index:<3', 'id:1',
|
||||||
'id:>2',
|
'id:>2',
|
||||||
|
|
||||||
|
# Bool tests
|
||||||
|
'#yesno:true', '#yesno:false', '#yesno:yes', '#yesno:no',
|
||||||
|
'#yesno:empty',
|
||||||
|
|
||||||
# TODO: Tests for searching the size column and
|
# TODO: Tests for searching the size column and
|
||||||
# cover:true|false
|
# cover:true|false
|
||||||
)}
|
)}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user