Skip to content

Commit

Permalink
Speed up search query parser
Browse files Browse the repository at this point in the history
  • Loading branch information
kovidgoyal committed Apr 20, 2013
2 parents 3ee801d + 4fc4145 commit 79eeec9
Show file tree
Hide file tree
Showing 9 changed files with 208 additions and 3,899 deletions.
50 changes: 0 additions & 50 deletions COPYRIGHT
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,6 @@ License: GPL2+
The full text of the GPL is distributed as in
/usr/share/common-licenses/GPL-2 on Debian systems.

Files: src/pyPdf/*
Copyright: Copyright (c) 2006, Mathieu Fenniak
Copyright: Copyright (c) 2007, Ashish Kulkarni <[email protected]>
License: BSD
The full text of the BSD license is distributed as in
/usr/share/common-licenses/BSD on Debian systems.

Files: src/calibre/utils/lzx/*
Copyright: Copyright (C) 2002, Matthew T. Russotto
Copyright: Copyright (C) 2008, Marshall T. Vandegrift <[email protected]>
Expand All @@ -100,49 +93,6 @@ License: BSD
The full text of the BSD license is distributed as in
/usr/share/common-licenses/BSD on Debian systems.

Files: src/calibre/utils/pyparsing.py
Copyright: Copyright (c) 2003-2008, Paul T. McGuire
License: MIT
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Files: src/calibre/utils/PythonMagickWand.py
Copyright: (c) 2007 - Achim Domma - [email protected]
License: MIT
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

Files: src/calibre/utils/msdes/d3des.h:
Files: src/calibre/utils/msdes/des.c:
Copyright: Copyright (C) 1988,1989,1990,1991,1992, Richard Outerbridge
Expand Down
2 changes: 1 addition & 1 deletion setup/check.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def get_files(self, cache):
if cache.get(y, 0) == mtime:
continue
if (f.endswith('.py') and f not in (
'feedparser.py', 'pyparsing.py', 'markdown.py') and
'feedparser.py', 'markdown.py') and
'prs500/driver.py' not in y):
yield y, mtime
if f.endswith('.coffee'):
Expand Down
15 changes: 8 additions & 7 deletions src/calibre/db/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,13 +195,13 @@ def __call__(self, query, field_iter):
try:
qd = now() - timedelta(int(num))
except:
raise ParseException(query, len(query), 'Number conversion error')
raise ParseException(_('Number conversion error: {0}').format(num))
field_count = 3
else:
try:
qd = parse_date(query, as_utc=False)
except:
raise ParseException(query, len(query), 'Date conversion error')
raise ParseException(_('Date conversion error: {0}').format(query))
if '-' in query:
field_count = query.count('-') + 1
else:
Expand Down Expand Up @@ -285,8 +285,8 @@ def __call__(self, query, field_iter, location, datatype, candidates, is_many=Fa
try:
q = cast(query) * mult
except:
raise ParseException(query, len(query),
'Non-numeric value in query: %r'%query)
raise ParseException(
_('Non-numeric value in query: {0}').format(query))

for val, book_ids in field_iter():
if val is None:
Expand Down Expand Up @@ -351,8 +351,8 @@ def __call__(self, query, field_iter, candidates, use_primary_find):
if ':' in query:
q = [q.strip() for q in query.split(':')]
if len(q) != 2:
raise ParseException(query, len(query),
'Invalid query format for colon-separated search')
raise ParseException(
_('Invalid query format for colon-separated search: {0}').format(query))
keyq, valq = q
keyq_mkind, keyq = _matchkind(keyq)
valq_mkind, valq = _matchkind(valq)
Expand Down Expand Up @@ -465,7 +465,8 @@ def get_matches(self, location, query, candidates=None,
if invert:
matches = self.all_book_ids - matches
return matches
raise ParseException(query, len(query), 'Recursive query group detected')
raise ParseException(
_('Recursive query group detected: {0}').format(query))

# If the user has asked to restrict searching over all field, apply
# that restriction
Expand Down
2 changes: 1 addition & 1 deletion src/calibre/gui2/library/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
QModelIndex, QVariant, QDateTime, QColor, QPixmap)

from calibre.gui2 import NONE, UNDEFINED_QDATETIME, error_dialog
from calibre.utils.pyparsing import ParseException
from calibre.utils.search_query_parser import ParseException
from calibre.ebooks.metadata import fmt_sidx, authors_to_string, string_to_authors
from calibre.ebooks.metadata.book.base import SafeFormat
from calibre.ptempfile import PersistentTemporaryFile
Expand Down
59 changes: 30 additions & 29 deletions src/calibre/gui2/search_restriction_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from calibre.gui2 import error_dialog, question_dialog
from calibre.gui2.widgets import ComboBoxWithHelp
from calibre.utils.icu import sort_key
from calibre.utils.pyparsing import ParseException
from calibre.utils.search_query_parser import ParseException
from calibre.utils.search_query_parser import saved_searches

class SelectNames(QDialog): # {{{
Expand Down Expand Up @@ -299,7 +299,7 @@ class SearchRestrictionMixin(object):

def __init__(self):
self.checked = QIcon(I('ok.png'))
self.empty = QIcon()
self.empty = QIcon(I('blank.png'))
self.search_based_vl_name = None
self.search_based_vl = None

Expand All @@ -315,38 +315,39 @@ def __init__(self):
self.search_restriction.setVisible(False)
self.search_count.setText(_("(all books)"))
self.ar_menu = QMenu(_('Additional restriction'))
self.edit_menu = QMenu(_('Edit Virtual Library'))
self.rm_menu = QMenu(_('Remove Virtual Library'))


def add_virtual_library(self, db, name, search):
virt_libs = db.prefs.get('virtual_libraries', {})
virt_libs[name] = search
db.prefs.set('virtual_libraries', virt_libs)

def do_create_edit(self, editing=None):
def do_create_edit(self, name=None):
db = self.library_view.model().db
virt_libs = db.prefs.get('virtual_libraries', {})
cd = CreateVirtualLibrary(self, virt_libs.keys(), editing=editing)
cd = CreateVirtualLibrary(self, virt_libs.keys(), editing=name)
if cd.exec_() == cd.Accepted:
if editing:
self._remove_vl(editing, reapply=False)
if name:
self._remove_vl(name, reapply=False)
self.add_virtual_library(db, cd.library_name, cd.library_search)
if not editing or editing == db.data.get_base_restriction_name():
if not name or name == db.data.get_base_restriction_name():
self.apply_virtual_library(cd.library_name)

def virtual_library_clicked(self):
m = self.virtual_library_menu
m.clear()

a = m.addAction(_('Create Virtual Library'))
a.triggered.connect(partial(self.do_create_edit, editing=None))
a.triggered.connect(partial(self.do_create_edit, name=None))

self.edit_menu = a = QMenu()
a.setTitle(_('Edit Virtual Library'))
a.aboutToShow.connect(partial(self.build_virtual_library_list, remove=False))
a = self.edit_menu
self.build_virtual_library_list(a, self.do_create_edit)
m.addMenu(a)

self.rm_menu = a = QMenu()
a.setTitle(_('Remove Virtual Library'))
a.aboutToShow.connect(partial(self.build_virtual_library_list, remove=True))
a = self.rm_menu
self.build_virtual_library_list(a, self.remove_vl_triggered)
m.addMenu(a)

m.addSeparator()
Expand All @@ -356,7 +357,7 @@ def virtual_library_clicked(self):
a = self.ar_menu
a.clear()
a.setIcon(self.checked if db.data.get_search_restriction_name() else self.empty)
a.aboutToShow.connect(self.build_search_restriction_list)
self.build_search_restriction_list()
m.addMenu(a)

m.addSeparator()
Expand Down Expand Up @@ -426,24 +427,24 @@ def apply_virtual_library(self, library=None):
self._apply_search_restriction(db.data.get_search_restriction(),
db.data.get_search_restriction_name())

def build_virtual_library_list(self, remove=False):
def build_virtual_library_list(self, menu, handler):
db = self.library_view.model().db
virt_libs = db.prefs.get('virtual_libraries', {})
if remove:
m = self.rm_menu
else:
m = self.edit_menu
m.clear()
menu.clear()
menu.setIcon(self.empty)

def add_action(name, search):
a = m.addAction(name)
if remove:
a.triggered.connect(partial(self.remove_vl_triggered, name=name))
else:
a.triggered.connect(partial(self.do_create_edit, editing=name))

for n in sorted(virt_libs.keys(), key=sort_key):
add_action(n, virt_libs[n])
a = menu.addAction(name)
a.triggered.connect(partial(handler, name=name))
a.setIcon(self.empty)

libs = sorted(virt_libs.keys(), key=sort_key)
if libs:
menu.setEnabled(True)
for n in libs:
add_action(n, virt_libs[n])
else:
menu.setEnabled(False)

def remove_vl_triggered(self, name=None):
if not question_dialog(self, _('Are you sure?'),
Expand Down
24 changes: 8 additions & 16 deletions src/calibre/library/caches.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from calibre.utils.config import tweaks, prefs
from calibre.utils.date import parse_date, now, UNDEFINED_DATE, clean_date_for_sort
from calibre.utils.search_query_parser import SearchQueryParser
from calibre.utils.pyparsing import ParseException
from calibre.utils.search_query_parser import ParseException
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
Expand Down Expand Up @@ -366,25 +366,18 @@ def get_dates_matches(self, location, query, candidates):
elif query in self.local_thismonth:
qd = now()
field_count = 2
elif query.endswith(self.local_daysago):
elif query.endswith(self.local_daysago) or query.endswith(self.untrans_daysago):
num = query[0:-self.local_daysago_len]
try:
qd = now() - timedelta(int(num))
except:
raise ParseException(query, len(query), 'Number conversion error', self)
field_count = 3
elif query.endswith(self.untrans_daysago):
num = query[0:-self.untrans_daysago_len]
try:
qd = now() - timedelta(int(num))
except:
raise ParseException(query, len(query), 'Number conversion error', self)
raise ParseException(_('Number conversion error: {0}').format(num))
field_count = 3
else:
try:
qd = parse_date(query, as_utc=False)
except:
raise ParseException(query, len(query), 'Date conversion error', self)
raise ParseException(_('Date conversion error: {0}').format(query))
if '-' in query:
field_count = query.count('-') + 1
else:
Expand Down Expand Up @@ -460,8 +453,7 @@ def get_numeric_matches(self, location, query, candidates, val_func = None):
try:
q = cast(query) * mult
except:
raise ParseException(query, len(query),
'Non-numeric value in query', self)
raise ParseException(_('Non-numeric value in query: {0}').format(query))

for id_ in candidates:
item = self._data[id_]
Expand Down Expand Up @@ -505,8 +497,8 @@ def get_keypair_matches(self, location, query, candidates):
if query.find(':') >= 0:
q = [q.strip() for q in query.split(':')]
if len(q) != 2:
raise ParseException(query, len(query),
'Invalid query format for colon-separated search', self)
raise ParseException(
_('Invalid query format for colon-separated search: {0}').format(query))
(keyq, valq) = q
keyq_mkind, keyq = self._matchkind(keyq)
valq_mkind, valq = self._matchkind(valq)
Expand Down Expand Up @@ -655,7 +647,7 @@ def get_matches(self, location, query, candidates=None,
if invert:
matches = self.universal_set() - matches
return matches
raise ParseException(query, len(query), 'Recursive query group detected', self)
raise ParseException(_('Recursive query group detected: {0}').format(query))

# apply the limit if appropriate
if location == 'all' and prefs['limit_search_columns'] and \
Expand Down
Loading

0 comments on commit 79eeec9

Please sign in to comment.