mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Implement enhancement #5481, relational searches for int and float custom columns
This commit is contained in:
parent
2a5cd2a1a4
commit
aab34ff197
@ -159,7 +159,20 @@ class ResultCache(SearchQueryParser):
|
|||||||
locations=SearchQueryParser.DEFAULT_LOCATIONS +
|
locations=SearchQueryParser.DEFAULT_LOCATIONS +
|
||||||
[c for c in cc_label_map])
|
[c for c in cc_label_map])
|
||||||
self.build_date_relop_dict()
|
self.build_date_relop_dict()
|
||||||
self.build_rating_relop_dict()
|
self.build_numeric_relop_dict()
|
||||||
|
|
||||||
|
def __getitem__(self, row):
|
||||||
|
return self._data[self._map_filtered[row]]
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self._map_filtered)
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
for id in self._map_filtered:
|
||||||
|
yield self._data[id]
|
||||||
|
|
||||||
|
def universal_set(self):
|
||||||
|
return set([i[0] for i in self._data if i is not None])
|
||||||
|
|
||||||
def build_date_relop_dict(self):
|
def build_date_relop_dict(self):
|
||||||
'''
|
'''
|
||||||
@ -205,30 +218,14 @@ class ResultCache(SearchQueryParser):
|
|||||||
def relop_le(db, query, field_count):
|
def relop_le(db, query, field_count):
|
||||||
return not relop_gt(db, query, field_count)
|
return not relop_gt(db, query, field_count)
|
||||||
|
|
||||||
self.date_search_relops = {'=':[1, relop_eq], '>':[1, relop_gt], '<':[1, relop_lt], \
|
self.date_search_relops = {
|
||||||
'!=':[2, relop_ne], '>=':[2, relop_ge], '<=':[2, relop_le]}
|
'=' :[1, relop_eq],
|
||||||
|
'>' :[1, relop_gt],
|
||||||
def build_rating_relop_dict(self):
|
'<' :[1, relop_lt],
|
||||||
self.rating_search_relops = {
|
'!=':[2, relop_ne],
|
||||||
'=':[1, lambda r, q: r == q],
|
'>=':[2, relop_ge],
|
||||||
'>':[1, lambda r, q: r > q],
|
'<=':[2, relop_le]
|
||||||
'<':[1, lambda r, q: r < q],
|
}
|
||||||
'!=':[2, lambda r, q: r != q],
|
|
||||||
'>=':[2, lambda r, q: r >= q],
|
|
||||||
'<=':[2, lambda r, q: r <= q]}
|
|
||||||
|
|
||||||
def __getitem__(self, row):
|
|
||||||
return self._data[self._map_filtered[row]]
|
|
||||||
|
|
||||||
def __len__(self):
|
|
||||||
return len(self._map_filtered)
|
|
||||||
|
|
||||||
def __iter__(self):
|
|
||||||
for id in self._map_filtered:
|
|
||||||
yield self._data[id]
|
|
||||||
|
|
||||||
def universal_set(self):
|
|
||||||
return set([i[0] for i in self._data if i is not None])
|
|
||||||
|
|
||||||
def get_dates_matches(self, location, query):
|
def get_dates_matches(self, location, query):
|
||||||
matches = set([])
|
matches = set([])
|
||||||
@ -277,7 +274,17 @@ class ResultCache(SearchQueryParser):
|
|||||||
matches.add(item[0])
|
matches.add(item[0])
|
||||||
return matches
|
return matches
|
||||||
|
|
||||||
def get_ratings_matches(self, location, query):
|
def build_numeric_relop_dict(self):
|
||||||
|
self.numeric_search_relops = {
|
||||||
|
'=':[1, lambda r, q: r == q],
|
||||||
|
'>':[1, lambda r, q: r > q],
|
||||||
|
'<':[1, lambda r, q: r < q],
|
||||||
|
'!=':[2, lambda r, q: r != q],
|
||||||
|
'>=':[2, lambda r, q: r >= q],
|
||||||
|
'<=':[2, lambda r, q: r <= q]
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_numeric_matches(self, location, query):
|
||||||
matches = set([])
|
matches = set([])
|
||||||
if len(query) == 0:
|
if len(query) == 0:
|
||||||
return matches
|
return matches
|
||||||
@ -286,20 +293,33 @@ class ResultCache(SearchQueryParser):
|
|||||||
elif query == 'true':
|
elif query == 'true':
|
||||||
query = '>0'
|
query = '>0'
|
||||||
relop = None
|
relop = None
|
||||||
for k in self.rating_search_relops.keys():
|
for k in self.numeric_search_relops.keys():
|
||||||
if query.startswith(k):
|
if query.startswith(k):
|
||||||
(p, relop) = self.rating_search_relops[k]
|
(p, relop) = self.numeric_search_relops[k]
|
||||||
query = query[p:]
|
query = query[p:]
|
||||||
if relop is None:
|
if relop is None:
|
||||||
(p, relop) = self.rating_search_relops['=']
|
(p, relop) = self.numeric_search_relops['=']
|
||||||
try:
|
|
||||||
r = int(query)
|
|
||||||
except:
|
|
||||||
return matches
|
|
||||||
if location in self.custom_column_label_map:
|
if location in self.custom_column_label_map:
|
||||||
loc = self.FIELD_MAP[self.custom_column_label_map[location]['num']]
|
loc = self.FIELD_MAP[self.custom_column_label_map[location]['num']]
|
||||||
|
dt = self.custom_column_label_map[location]['datatype']
|
||||||
|
if dt == 'int':
|
||||||
|
cast = (lambda x: int (x))
|
||||||
|
adjust = lambda x: x
|
||||||
|
elif dt == 'rating':
|
||||||
|
cast = (lambda x: int (x))
|
||||||
|
adjust = lambda x: x/2
|
||||||
|
elif dt == 'float':
|
||||||
|
cast = lambda x : float (x)
|
||||||
|
adjust = lambda x: x
|
||||||
else:
|
else:
|
||||||
loc = self.FIELD_MAP['rating']
|
loc = self.FIELD_MAP['rating']
|
||||||
|
cast = (lambda x: int (x))
|
||||||
|
adjust = lambda x: x/2
|
||||||
|
|
||||||
|
try:
|
||||||
|
q = cast(query)
|
||||||
|
except:
|
||||||
|
return matches
|
||||||
|
|
||||||
for item in self._data:
|
for item in self._data:
|
||||||
if item is None:
|
if item is None:
|
||||||
@ -307,8 +327,8 @@ class ResultCache(SearchQueryParser):
|
|||||||
if not item[loc]:
|
if not item[loc]:
|
||||||
i = 0
|
i = 0
|
||||||
else:
|
else:
|
||||||
i = item[loc]/2
|
i = adjust(item[loc])
|
||||||
if relop(i, r):
|
if relop(i, q):
|
||||||
matches.add(item[0])
|
matches.add(item[0])
|
||||||
return matches
|
return matches
|
||||||
|
|
||||||
@ -323,11 +343,12 @@ class ResultCache(SearchQueryParser):
|
|||||||
self.custom_column_label_map[location]['datatype'] == 'datetime'):
|
self.custom_column_label_map[location]['datatype'] == 'datetime'):
|
||||||
return self.get_dates_matches(location, query.lower())
|
return self.get_dates_matches(location, query.lower())
|
||||||
|
|
||||||
### take care of ratings special case
|
### take care of numerics special case
|
||||||
if location == 'rating' or \
|
if location == 'rating' or \
|
||||||
((location in self.custom_column_label_map) and \
|
(location in self.custom_column_label_map and
|
||||||
self.custom_column_label_map[location]['datatype'] == 'rating'):
|
self.custom_column_label_map[location]['datatype'] in
|
||||||
return self.get_ratings_matches(location, query.lower())
|
('rating', 'int', 'float')):
|
||||||
|
return self.get_numeric_matches(location, query.lower())
|
||||||
|
|
||||||
### everything else
|
### everything else
|
||||||
matchkind = CONTAINS_MATCH
|
matchkind = CONTAINS_MATCH
|
||||||
@ -426,14 +447,15 @@ class ResultCache(SearchQueryParser):
|
|||||||
matches.add(item[0])
|
matches.add(item[0])
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if IS_CUSTOM[loc] == 'rating':
|
if IS_CUSTOM[loc] == 'rating': # get here if 'all' query
|
||||||
if rating_query and rating_query == int(item[loc]):
|
if rating_query and rating_query == int(item[loc]):
|
||||||
matches.add(item[0])
|
matches.add(item[0])
|
||||||
continue
|
continue
|
||||||
|
|
||||||
try: # a conversion below might fail
|
try: # a conversion below might fail
|
||||||
|
# relationals not supported in 'all' queries
|
||||||
if IS_CUSTOM[loc] == 'float':
|
if IS_CUSTOM[loc] == 'float':
|
||||||
if float(query) == item[loc]: # relationals not supported
|
if float(query) == item[loc]:
|
||||||
matches.add(item[0])
|
matches.add(item[0])
|
||||||
continue
|
continue
|
||||||
if IS_CUSTOM[loc] == 'int':
|
if IS_CUSTOM[loc] == 'int':
|
||||||
@ -441,7 +463,8 @@ class ResultCache(SearchQueryParser):
|
|||||||
matches.add(item[0])
|
matches.add(item[0])
|
||||||
continue
|
continue
|
||||||
except:
|
except:
|
||||||
# A conversion threw an exception. Because of the type, no further match possible
|
# A conversion threw an exception. Because of the type,
|
||||||
|
# no further match is possible
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if loc not in EXCLUDE_FIELDS:
|
if loc not in EXCLUDE_FIELDS:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user