mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Move _match to db.search module
This commit is contained in:
parent
e0df1634ab
commit
d915e49815
@ -13,9 +13,15 @@ from datetime import timedelta
|
|||||||
|
|
||||||
from calibre.utils.config_base import prefs
|
from calibre.utils.config_base import prefs
|
||||||
from calibre.utils.date import parse_date, UNDEFINED_DATE, now
|
from calibre.utils.date import parse_date, UNDEFINED_DATE, now
|
||||||
|
from calibre.utils.icu import primary_find
|
||||||
from calibre.utils.search_query_parser import SearchQueryParser, ParseException
|
from calibre.utils.search_query_parser import SearchQueryParser, ParseException
|
||||||
|
|
||||||
# TODO: Thread safety of saved searches
|
# TODO: Thread safety of saved searches
|
||||||
|
CONTAINS_MATCH = 0
|
||||||
|
EQUALS_MATCH = 1
|
||||||
|
REGEXP_MATCH = 2
|
||||||
|
|
||||||
|
# Utils {{{
|
||||||
|
|
||||||
def force_to_bool(val):
|
def force_to_bool(val):
|
||||||
if isinstance(val, (str, unicode)):
|
if isinstance(val, (str, unicode)):
|
||||||
@ -33,6 +39,45 @@ def force_to_bool(val):
|
|||||||
val = None
|
val = None
|
||||||
return val
|
return val
|
||||||
|
|
||||||
|
def _match(query, value, matchkind, use_primary_find_in_search=True):
|
||||||
|
if query.startswith('..'):
|
||||||
|
query = query[1:]
|
||||||
|
sq = query[1:]
|
||||||
|
internal_match_ok = True
|
||||||
|
else:
|
||||||
|
internal_match_ok = False
|
||||||
|
for t in value:
|
||||||
|
try: ### ignore regexp exceptions, required because search-ahead tries before typing is finished
|
||||||
|
t = icu_lower(t)
|
||||||
|
if (matchkind == EQUALS_MATCH):
|
||||||
|
if internal_match_ok:
|
||||||
|
if query == t:
|
||||||
|
return True
|
||||||
|
comps = [c.strip() for c in t.split('.') if c.strip()]
|
||||||
|
for comp in comps:
|
||||||
|
if sq == comp:
|
||||||
|
return True
|
||||||
|
elif query[0] == '.':
|
||||||
|
if t.startswith(query[1:]):
|
||||||
|
ql = len(query) - 1
|
||||||
|
if (len(t) == ql) or (t[ql:ql+1] == '.'):
|
||||||
|
return True
|
||||||
|
elif query == t:
|
||||||
|
return True
|
||||||
|
elif matchkind == REGEXP_MATCH:
|
||||||
|
if re.search(query, t, re.I|re.UNICODE):
|
||||||
|
return True
|
||||||
|
elif matchkind == CONTAINS_MATCH:
|
||||||
|
if use_primary_find_in_search:
|
||||||
|
if primary_find(query, t)[0] != -1:
|
||||||
|
return True
|
||||||
|
elif query in t:
|
||||||
|
return True
|
||||||
|
except re.error:
|
||||||
|
pass
|
||||||
|
return False
|
||||||
|
# }}}
|
||||||
|
|
||||||
class DateSearch(object): # {{{
|
class DateSearch(object): # {{{
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -16,12 +16,12 @@ from calibre.utils.pyparsing import ParseException
|
|||||||
from calibre.ebooks.metadata import fmt_sidx, authors_to_string, string_to_authors
|
from calibre.ebooks.metadata import fmt_sidx, authors_to_string, string_to_authors
|
||||||
from calibre.ebooks.metadata.book.base import SafeFormat
|
from calibre.ebooks.metadata.book.base import SafeFormat
|
||||||
from calibre.ptempfile import PersistentTemporaryFile
|
from calibre.ptempfile import PersistentTemporaryFile
|
||||||
from calibre.utils.config import tweaks, device_prefs
|
from calibre.utils.config import tweaks, device_prefs, prefs
|
||||||
from calibre.utils.date import dt_factory, qt_to_dt, as_local_time
|
from calibre.utils.date import dt_factory, qt_to_dt, as_local_time
|
||||||
from calibre.utils.icu import sort_key
|
from calibre.utils.icu import sort_key
|
||||||
from calibre.utils.search_query_parser import SearchQueryParser
|
from calibre.utils.search_query_parser import SearchQueryParser
|
||||||
from calibre.library.caches import (_match, CONTAINS_MATCH, EQUALS_MATCH,
|
from calibre.db.search import _match, CONTAINS_MATCH, EQUALS_MATCH, REGEXP_MATCH
|
||||||
REGEXP_MATCH, MetadataBackup, force_to_bool)
|
from calibre.library.caches import (MetadataBackup, force_to_bool)
|
||||||
from calibre.library.save_to_disk import find_plugboard
|
from calibre.library.save_to_disk import find_plugboard
|
||||||
from calibre import strftime, isbytestring
|
from calibre import strftime, isbytestring
|
||||||
from calibre.constants import filesystem_encoding, DEBUG
|
from calibre.constants import filesystem_encoding, DEBUG
|
||||||
@ -1037,6 +1037,7 @@ class OnDeviceSearch(SearchQueryParser): # {{{
|
|||||||
}
|
}
|
||||||
for x in ('author', 'format'):
|
for x in ('author', 'format'):
|
||||||
q[x+'s'] = q[x]
|
q[x+'s'] = q[x]
|
||||||
|
upf = prefs['use_primary_find_in_search']
|
||||||
for index, row in enumerate(self.model.db):
|
for index, row in enumerate(self.model.db):
|
||||||
for locvalue in locations:
|
for locvalue in locations:
|
||||||
accessor = q[locvalue]
|
accessor = q[locvalue]
|
||||||
@ -1063,7 +1064,7 @@ class OnDeviceSearch(SearchQueryParser): # {{{
|
|||||||
vals = accessor(row).split(',')
|
vals = accessor(row).split(',')
|
||||||
else:
|
else:
|
||||||
vals = [accessor(row)]
|
vals = [accessor(row)]
|
||||||
if _match(query, vals, m):
|
if _match(query, vals, m, use_primary_find_in_search=upf):
|
||||||
matches.add(index)
|
matches.add(index)
|
||||||
break
|
break
|
||||||
except ValueError: # Unicode errors
|
except ValueError: # Unicode errors
|
||||||
|
@ -10,8 +10,8 @@ from PyQt4.Qt import (Qt, QAbstractItemModel, QIcon, QVariant, QModelIndex, QSiz
|
|||||||
|
|
||||||
from calibre.gui2 import NONE
|
from calibre.gui2 import NONE
|
||||||
from calibre.customize.ui import is_disabled, disable_plugin, enable_plugin
|
from calibre.customize.ui import is_disabled, disable_plugin, enable_plugin
|
||||||
from calibre.library.caches import _match, CONTAINS_MATCH, EQUALS_MATCH, \
|
from calibre.db.search import _match, CONTAINS_MATCH, EQUALS_MATCH, REGEXP_MATCH
|
||||||
REGEXP_MATCH
|
from calibre.utils.config_base import prefs
|
||||||
from calibre.utils.icu import sort_key
|
from calibre.utils.icu import sort_key
|
||||||
from calibre.utils.search_query_parser import SearchQueryParser
|
from calibre.utils.search_query_parser import SearchQueryParser
|
||||||
|
|
||||||
@ -60,13 +60,13 @@ class Matches(QAbstractItemModel):
|
|||||||
index = self.createIndex(i, 0)
|
index = self.createIndex(i, 0)
|
||||||
data = QVariant(True)
|
data = QVariant(True)
|
||||||
self.setData(index, data, Qt.CheckStateRole)
|
self.setData(index, data, Qt.CheckStateRole)
|
||||||
|
|
||||||
def enable_none(self):
|
def enable_none(self):
|
||||||
for i in xrange(len(self.matches)):
|
for i in xrange(len(self.matches)):
|
||||||
index = self.createIndex(i, 0)
|
index = self.createIndex(i, 0)
|
||||||
data = QVariant(False)
|
data = QVariant(False)
|
||||||
self.setData(index, data, Qt.CheckStateRole)
|
self.setData(index, data, Qt.CheckStateRole)
|
||||||
|
|
||||||
def enable_invert(self):
|
def enable_invert(self):
|
||||||
for i in xrange(len(self.matches)):
|
for i in xrange(len(self.matches)):
|
||||||
self.toggle_plugin(self.createIndex(i, 0))
|
self.toggle_plugin(self.createIndex(i, 0))
|
||||||
@ -243,6 +243,7 @@ class SearchFilter(SearchQueryParser):
|
|||||||
'name': lambda x : x.name.lower(),
|
'name': lambda x : x.name.lower(),
|
||||||
}
|
}
|
||||||
q['formats'] = q['format']
|
q['formats'] = q['format']
|
||||||
|
upf = prefs['use_primary_find_in_search']
|
||||||
for sr in self.srs:
|
for sr in self.srs:
|
||||||
for locvalue in locations:
|
for locvalue in locations:
|
||||||
accessor = q[locvalue]
|
accessor = q[locvalue]
|
||||||
@ -276,7 +277,7 @@ class SearchFilter(SearchQueryParser):
|
|||||||
vals = accessor(sr).split(',')
|
vals = accessor(sr).split(',')
|
||||||
else:
|
else:
|
||||||
vals = [accessor(sr)]
|
vals = [accessor(sr)]
|
||||||
if _match(query, vals, m):
|
if _match(query, vals, m, use_primary_find_in_search=upf):
|
||||||
matches.add(sr)
|
matches.add(sr)
|
||||||
break
|
break
|
||||||
except ValueError: # Unicode errors
|
except ValueError: # Unicode errors
|
||||||
|
@ -11,13 +11,13 @@ from operator import attrgetter
|
|||||||
from PyQt4.Qt import (Qt, QAbstractItemModel, QModelIndex, QVariant, pyqtSignal)
|
from PyQt4.Qt import (Qt, QAbstractItemModel, QModelIndex, QVariant, pyqtSignal)
|
||||||
|
|
||||||
from calibre.gui2 import NONE
|
from calibre.gui2 import NONE
|
||||||
from calibre.library.caches import _match, CONTAINS_MATCH, EQUALS_MATCH, \
|
from calibre.db.search import _match, CONTAINS_MATCH, EQUALS_MATCH, REGEXP_MATCH
|
||||||
REGEXP_MATCH
|
from calibre.utils.config_base import prefs
|
||||||
from calibre.utils.icu import sort_key
|
from calibre.utils.icu import sort_key
|
||||||
from calibre.utils.search_query_parser import SearchQueryParser
|
from calibre.utils.search_query_parser import SearchQueryParser
|
||||||
|
|
||||||
class BooksModel(QAbstractItemModel):
|
class BooksModel(QAbstractItemModel):
|
||||||
|
|
||||||
total_changed = pyqtSignal(int)
|
total_changed = pyqtSignal(int)
|
||||||
|
|
||||||
HEADERS = [_('Title'), _('Author(s)'), _('Format')]
|
HEADERS = [_('Title'), _('Author(s)'), _('Format')]
|
||||||
@ -37,8 +37,8 @@ class BooksModel(QAbstractItemModel):
|
|||||||
return self.books[row]
|
return self.books[row]
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def search(self, filter):
|
def search(self, filter):
|
||||||
self.filter = filter.strip()
|
self.filter = filter.strip()
|
||||||
if not self.filter:
|
if not self.filter:
|
||||||
self.books = self.all_books
|
self.books = self.all_books
|
||||||
@ -50,7 +50,7 @@ class BooksModel(QAbstractItemModel):
|
|||||||
self.layoutChanged.emit()
|
self.layoutChanged.emit()
|
||||||
self.sort(self.sort_col, self.sort_order)
|
self.sort(self.sort_col, self.sort_order)
|
||||||
self.total_changed.emit(self.rowCount())
|
self.total_changed.emit(self.rowCount())
|
||||||
|
|
||||||
def index(self, row, column, parent=QModelIndex()):
|
def index(self, row, column, parent=QModelIndex()):
|
||||||
return self.createIndex(row, column)
|
return self.createIndex(row, column)
|
||||||
|
|
||||||
@ -64,7 +64,7 @@ class BooksModel(QAbstractItemModel):
|
|||||||
|
|
||||||
def columnCount(self, *args):
|
def columnCount(self, *args):
|
||||||
return len(self.HEADERS)
|
return len(self.HEADERS)
|
||||||
|
|
||||||
def headerData(self, section, orientation, role):
|
def headerData(self, section, orientation, role):
|
||||||
if role != Qt.DisplayRole:
|
if role != Qt.DisplayRole:
|
||||||
return NONE
|
return NONE
|
||||||
@ -112,7 +112,7 @@ class BooksModel(QAbstractItemModel):
|
|||||||
|
|
||||||
|
|
||||||
class SearchFilter(SearchQueryParser):
|
class SearchFilter(SearchQueryParser):
|
||||||
|
|
||||||
USABLE_LOCATIONS = [
|
USABLE_LOCATIONS = [
|
||||||
'all',
|
'all',
|
||||||
'author',
|
'author',
|
||||||
@ -161,6 +161,7 @@ class SearchFilter(SearchQueryParser):
|
|||||||
}
|
}
|
||||||
for x in ('author', 'format'):
|
for x in ('author', 'format'):
|
||||||
q[x+'s'] = q[x]
|
q[x+'s'] = q[x]
|
||||||
|
upf = prefs['use_primary_find_in_search']
|
||||||
for sr in self.srs:
|
for sr in self.srs:
|
||||||
for locvalue in locations:
|
for locvalue in locations:
|
||||||
accessor = q[locvalue]
|
accessor = q[locvalue]
|
||||||
@ -182,7 +183,7 @@ class SearchFilter(SearchQueryParser):
|
|||||||
m = matchkind
|
m = matchkind
|
||||||
|
|
||||||
vals = [accessor(sr)]
|
vals = [accessor(sr)]
|
||||||
if _match(query, vals, m):
|
if _match(query, vals, m, use_primary_find_in_search=upf):
|
||||||
matches.add(sr)
|
matches.add(sr)
|
||||||
break
|
break
|
||||||
except ValueError: # Unicode errors
|
except ValueError: # Unicode errors
|
||||||
|
@ -6,7 +6,7 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import re, itertools, time, traceback, locale
|
import itertools, time, traceback, locale
|
||||||
from itertools import repeat, izip, imap
|
from itertools import repeat, izip, imap
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
@ -16,10 +16,10 @@ from calibre.utils.date import parse_date, now, UNDEFINED_DATE, clean_date_for_s
|
|||||||
from calibre.utils.search_query_parser import SearchQueryParser
|
from calibre.utils.search_query_parser import SearchQueryParser
|
||||||
from calibre.utils.pyparsing import ParseException
|
from calibre.utils.pyparsing import ParseException
|
||||||
from calibre.utils.localization import (canonicalize_lang, lang_map, get_udc)
|
from calibre.utils.localization import (canonicalize_lang, lang_map, get_udc)
|
||||||
|
from calibre.db.search import CONTAINS_MATCH, EQUALS_MATCH, REGEXP_MATCH, _match
|
||||||
from calibre.ebooks.metadata import title_sort, author_to_author_sort
|
from calibre.ebooks.metadata import title_sort, author_to_author_sort
|
||||||
from calibre.ebooks.metadata.opf2 import metadata_to_opf
|
from calibre.ebooks.metadata.opf2 import metadata_to_opf
|
||||||
from calibre import prints
|
from calibre import prints
|
||||||
from calibre.utils.icu import primary_find
|
|
||||||
|
|
||||||
class MetadataBackup(Thread): # {{{
|
class MetadataBackup(Thread): # {{{
|
||||||
'''
|
'''
|
||||||
@ -118,7 +118,6 @@ class MetadataBackup(Thread): # {{{
|
|||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
### Global utility function for get_match here and in gui2/library.py
|
### Global utility function for get_match here and in gui2/library.py
|
||||||
# This is a global for performance
|
# This is a global for performance
|
||||||
pref_use_primary_find_in_search = False
|
pref_use_primary_find_in_search = False
|
||||||
@ -127,47 +126,6 @@ def set_use_primary_find_in_search(toWhat):
|
|||||||
global pref_use_primary_find_in_search
|
global pref_use_primary_find_in_search
|
||||||
pref_use_primary_find_in_search = toWhat
|
pref_use_primary_find_in_search = toWhat
|
||||||
|
|
||||||
CONTAINS_MATCH = 0
|
|
||||||
EQUALS_MATCH = 1
|
|
||||||
REGEXP_MATCH = 2
|
|
||||||
def _match(query, value, matchkind):
|
|
||||||
if query.startswith('..'):
|
|
||||||
query = query[1:]
|
|
||||||
sq = query[1:]
|
|
||||||
internal_match_ok = True
|
|
||||||
else:
|
|
||||||
internal_match_ok = False
|
|
||||||
for t in value:
|
|
||||||
try: ### ignore regexp exceptions, required because search-ahead tries before typing is finished
|
|
||||||
t = icu_lower(t)
|
|
||||||
if (matchkind == EQUALS_MATCH):
|
|
||||||
if internal_match_ok:
|
|
||||||
if query == t:
|
|
||||||
return True
|
|
||||||
comps = [c.strip() for c in t.split('.') if c.strip()]
|
|
||||||
for comp in comps:
|
|
||||||
if sq == comp:
|
|
||||||
return True
|
|
||||||
elif query[0] == '.':
|
|
||||||
if t.startswith(query[1:]):
|
|
||||||
ql = len(query) - 1
|
|
||||||
if (len(t) == ql) or (t[ql:ql+1] == '.'):
|
|
||||||
return True
|
|
||||||
elif query == t:
|
|
||||||
return True
|
|
||||||
elif matchkind == REGEXP_MATCH:
|
|
||||||
if re.search(query, t, re.I|re.UNICODE):
|
|
||||||
return True
|
|
||||||
elif matchkind == CONTAINS_MATCH:
|
|
||||||
if pref_use_primary_find_in_search:
|
|
||||||
if primary_find(query, t)[0] != -1:
|
|
||||||
return True
|
|
||||||
elif query in t:
|
|
||||||
return True
|
|
||||||
except re.error:
|
|
||||||
pass
|
|
||||||
return False
|
|
||||||
|
|
||||||
def force_to_bool(val):
|
def force_to_bool(val):
|
||||||
if isinstance(val, (str, unicode)):
|
if isinstance(val, (str, unicode)):
|
||||||
try:
|
try:
|
||||||
@ -576,7 +534,8 @@ class ResultCache(SearchQueryParser): # {{{
|
|||||||
continue
|
continue
|
||||||
k = parts[:1]
|
k = parts[:1]
|
||||||
v = parts[1:]
|
v = parts[1:]
|
||||||
if keyq and not _match(keyq, k, keyq_mkind):
|
if keyq and not _match(keyq, k, keyq_mkind,
|
||||||
|
use_primary_find_in_search=pref_use_primary_find_in_search):
|
||||||
continue
|
continue
|
||||||
if valq:
|
if valq:
|
||||||
if valq == 'true':
|
if valq == 'true':
|
||||||
@ -586,7 +545,8 @@ class ResultCache(SearchQueryParser): # {{{
|
|||||||
if v:
|
if v:
|
||||||
add_if_nothing_matches = False
|
add_if_nothing_matches = False
|
||||||
continue
|
continue
|
||||||
elif not _match(valq, v, valq_mkind):
|
elif not _match(valq, v, valq_mkind,
|
||||||
|
use_primary_find_in_search=pref_use_primary_find_in_search):
|
||||||
continue
|
continue
|
||||||
matches.add(id_)
|
matches.add(id_)
|
||||||
|
|
||||||
@ -851,7 +811,8 @@ class ResultCache(SearchQueryParser): # {{{
|
|||||||
vals = [v.strip() for v in item[loc].split(is_multiple_cols[loc])]
|
vals = [v.strip() for v in item[loc].split(is_multiple_cols[loc])]
|
||||||
else:
|
else:
|
||||||
vals = [item[loc]] ### make into list to make _match happy
|
vals = [item[loc]] ### make into list to make _match happy
|
||||||
if _match(q, vals, matchkind):
|
if _match(q, vals, matchkind,
|
||||||
|
use_primary_find_in_search=pref_use_primary_find_in_search):
|
||||||
matches.add(item[0])
|
matches.add(item[0])
|
||||||
continue
|
continue
|
||||||
current_candidates -= matches
|
current_candidates -= matches
|
||||||
|
Loading…
x
Reference in New Issue
Block a user