Add a 'search limit' feature where the user can specify a set of fields (columns) to search when non-prefixed terms are used

This commit is contained in:
Charles Haley 2011-02-07 11:54:07 +00:00
parent b8b6c83a1d
commit 1d8e98122c
8 changed files with 77 additions and 7 deletions

View File

@ -202,6 +202,15 @@ class SearchBar(QWidget): # {{{
l.addWidget(x)
x.setVisible(False)
x = parent.search_limit_to = QCheckBox()
x.setText(_('&Limit'))
x.setToolTip('<p>'+_('When searching for text without using lookup '
'prefixes, as for example someword instead of title:someword, '
'limit the columns searched to those named in the option '
'Preferences -> Look and Feel -> Limit non-prefixed searches to columns.'))
x.setVisible(False)
l.addWidget(x)
x = parent.saved_search = SavedSearchBox(self)
x.setMaximumSize(QSize(150, 16777215))
x.setMinimumContentsLength(15)

View File

@ -62,6 +62,8 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
r('tags_browser_partition_method', gprefs, choices=choices)
r('tags_browser_collapse_at', gprefs)
r('search_box_limit_to', prefs)
self.current_font = None
self.change_font_button.clicked.connect(self.change_font)
@ -119,6 +121,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
gui.search.search_as_you_type(config['search_as_you_type'])
self.update_font_display()
gui.tags_view.reread_collapse_parameters()
gui.search_limit_to.setEnabled(bool(prefs['search_box_limit_to']))
if __name__ == '__main__':
app = QApplication([])

View File

@ -200,6 +200,26 @@ up into sub-categories. If the partition method is set to disable, this value is
</item>
</layout>
</item>
<item row="8" column="0">
<widget class="QLabel" name="label_81">
<property name="text">
<string>Limit non-&amp;prefixed searches to columns:</string>
</property>
<property name="buddy">
<cstring>opt_search_box_limit_to</cstring>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QLineEdit" name="opt_search_box_limit_to">
<property name="toolTip">
<string>Choose columns to be searched when not using prefixes, as for
example when searching for someword instead of title:someword.
Enter a list of search/lookup names separated by commas. You
must check the 'Limit' box on the GUI for this option to take effect.</string>
</property>
</widget>
</item>
<item row="15" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox_2">
<property name="title">

View File

@ -16,7 +16,7 @@ from calibre.gui2 import config
from calibre.gui2.dialogs.confirm_delete import confirm
from calibre.gui2.dialogs.saved_search_editor import SavedSearchEditor
from calibre.gui2.dialogs.search import SearchDialog
from calibre.utils.config import dynamic
from calibre.utils.config import dynamic, prefs
from calibre.utils.search_query_parser import saved_searches
from calibre.utils.icu import sort_key
@ -379,6 +379,12 @@ class SearchBoxMixin(object): # {{{
self.search_highlight_only.stateChanged.connect(self.highlight_only_changed)
self.search_highlight_only.setChecked(
dynamic.get('search_highlight_only', False))
self.search_limit_to.stateChanged.connect(self.search_limit_to_changed)
self.search_limit_to.setVisible(True)
chk = dynamic.get('use_search_box_limit', False)
self.search_limit_to.setChecked(chk)
prefs['use_search_box_limit'] = chk
self.search_limit_to.setEnabled(bool(prefs['search_box_limit_to']))
def focus_search_box(self, *args):
self.search.setFocus(Qt.OtherFocusReason)
@ -410,6 +416,11 @@ class SearchBoxMixin(object): # {{{
self.current_view().model().set_highlight_only(toWhat)
self.focus_to_library()
def search_limit_to_changed(self, toWhat):
dynamic.set('use_search_box_limit', toWhat)
prefs['use_search_box_limit'] = toWhat
self.search.do_search()
# }}}
class SavedSearchBoxMixin(object): # {{{

View File

@ -1214,7 +1214,7 @@ class TagBrowserMixin(object): # {{{
db.field_metadata.remove_user_categories()
for k in d.categories:
db.field_metadata.add_user_category('@' + k, k)
db.data.sqp_change_locations(db.field_metadata.get_search_terms())
db.data.change_search_locations(db.field_metadata.get_search_terms())
self.tags_view.set_new_model()
self.tags_view.recount()

View File

@ -482,8 +482,10 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
for action in self.iactions.values():
action.location_selected(location)
if location == 'library':
self.search_limit_to.setVisible(True)
self.search_restriction.setEnabled(True)
else:
self.search_limit_to.setVisible(False)
self.search_restriction.setEnabled(False)
# Reset the view in case something changed while it was invisible
self.current_view().reset()

View File

@ -11,7 +11,7 @@ from itertools import repeat
from datetime import timedelta
from threading import Thread
from calibre.utils.config import tweaks
from calibre.utils.config import tweaks, prefs
from calibre.utils.date import parse_date, now, UNDEFINED_DATE
from calibre.utils.search_query_parser import SearchQueryParser
from calibre.utils.pyparsing import ParseException
@ -182,15 +182,16 @@ class ResultCache(SearchQueryParser): # {{{
self.first_sort = True
self.search_restriction = ''
self.field_metadata = field_metadata
all_search_locations = field_metadata.get_search_terms()
SearchQueryParser.__init__(self, all_search_locations, optimize=True)
self.all_search_locations = field_metadata.get_search_terms()
SearchQueryParser.__init__(self, self.all_search_locations, optimize=True)
self.build_date_relop_dict()
self.build_numeric_relop_dict()
def break_cycles(self):
self._data = self.field_metadata = self.FIELD_MAP = \
self.numeric_search_relops = self.date_search_relops = \
self.db_prefs = None
self.db_prefs = self.all_search_locations = None
self.sqp_change_locations([])
def __getitem__(self, row):
@ -218,6 +219,10 @@ class ResultCache(SearchQueryParser): # {{{
def universal_set(self):
return set([i[0] for i in self._data if i is not None])
def change_search_locations(self, locations):
self.sqp_change_locations(locations)
self.all_search_locations = locations
def build_date_relop_dict(self):
'''
Because the database dates have time in them, we can't use direct
@ -432,6 +437,7 @@ class ResultCache(SearchQueryParser): # {{{
# get metadata key associated with the search term. Eliminates
# dealing with plurals and other aliases
location = self.field_metadata.search_term_to_field_key(icu_lower(location.strip()))
# grouped search terms
if isinstance(location, list):
if allow_recursion:
for loc in location:
@ -440,6 +446,20 @@ class ResultCache(SearchQueryParser): # {{{
return matches
raise ParseException(query, len(query), 'Recursive query group detected', self)
# apply the limit if appropriate
if location == 'all' and prefs['use_search_box_limit'] and \
prefs['search_box_limit_to']:
for l in prefs['search_box_limit_to'].split(','):
l = icu_lower(l.strip())
if not l or l == 'all':
continue
if l not in self.all_search_locations:
raise ParseException(l, len(l),
'Unknown field "%s" in search column limit'%l, self)
matches |= self.get_matches(l, query,
candidates=candidates, allow_recursion=allow_recursion)
return matches
if location in self.field_metadata:
fm = self.field_metadata[location]
# take care of dates special case

View File

@ -729,6 +729,11 @@ def _prefs():
c.add_opt('manage_device_metadata', default='manual',
help=_('How and when calibre updates metadata on the device.'))
c.add_opt('search_box_limit_to', default='',
help=_('Comma-separated list of fields to search when no prefix'))
c.add_opt('use_search_box_limit', default=False,
help=_('Set to true to apply the search box limit'))
c.add_opt('migrated', default=False, help='For Internal use. Don\'t modify.')
return c