diff options
-rw-r--r-- | CHANGELOG.rst | 2 | ||||
-rw-r--r-- | docs/source/man/lesana-search.rst | 5 | ||||
-rw-r--r-- | docs/source/user/search.rst | 18 | ||||
-rw-r--r-- | docs/source/user/settings.rst | 3 | ||||
-rw-r--r-- | lesana/collection.py | 7 | ||||
-rw-r--r-- | lesana/command.py | 15 | ||||
-rw-r--r-- | tests/data/complex/settings.yaml | 3 | ||||
-rw-r--r-- | tests/test_collection.py | 12 | ||||
-rw-r--r-- | tests/test_commands.py | 18 |
9 files changed, 80 insertions, 3 deletions
diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 48486ff..8b2d9c4 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -24,6 +24,8 @@ Unreleased contents of a field. * New property ``precision`` for ``decimal`` fields, to force rounding values to that number of decimal digits. +* New settings property ``search_aliases`` with a dict of values to be + filled in in a jinja2 template for a search query. Bugfixes -------- diff --git a/docs/source/man/lesana-search.rst b/docs/source/man/lesana-search.rst index fff4b8e..f7bcf9c 100644 --- a/docs/source/man/lesana-search.rst +++ b/docs/source/man/lesana-search.rst @@ -7,7 +7,8 @@ SYNOPSIS lesana search [--help] [--collection COLLECTION] [--template TEMPLATE] \ [--offset OFFSET] [--pagesize PAGESIZE] [--all] \ - [--sort FIELD1 [--sort FIELD2 ...]] [query [query ...]] + [--expand-query-template] [--sort FIELD1 [--sort FIELD2 ...]] \ + [query [query ...]] DESCRIPTION =========== @@ -50,4 +51,6 @@ OPTIONS This option can be added multiple times; prefix the name of the field with ``-`` to reverse the results (e.g. ``--sort='-date'``). +expand-query-template + Render search_aliases in the query as a jinja2 template diff --git a/docs/source/user/search.rst b/docs/source/user/search.rst index 41746a3..d14f37f 100644 --- a/docs/source/user/search.rst +++ b/docs/source/user/search.rst @@ -22,3 +22,21 @@ xapian for details. .. _`Query Parser`: https://getting-started-with-xapian.readthedocs.io/en/latest/concepts/search/queryparser.html +.. _search aliases: + +Search templates and ``search_aliases`` +======================================= + +In some contexts, search queries are rendered as jinja2 templates with +the contents of the ``search_aliases`` property as set in +``settings.yaml``. + +The values of those search aliases should be valid search snippets with +the syntax documented above; it's usually a good idea to wrap them in +parenthesis, so that they are easier to use in complex queries; e.g.:: + + my_alias: '(name:object OR name:thing)' + +can correctly be used in a query like:: + + {{ my_alias }} AND description:shiny diff --git a/docs/source/user/settings.rst b/docs/source/user/settings.rst index 2d3a3fa..ba14e37 100644 --- a/docs/source/user/settings.rst +++ b/docs/source/user/settings.rst @@ -20,6 +20,9 @@ It is a yaml file with a dict of properties and their values. a list of field names (possibly prefixed by + or -) that are used by default to sort results of searches in the collection. The fields must be marked as sortable in their definition, see below. +``search_aliases``: + a dict of <name>: <search snippet> which can be used in a query + template. For more details see :ref:`search aliases` ``fields``: The list of fields used by the collection, as described below. diff --git a/lesana/collection.py b/lesana/collection.py index d51187f..e428ae1 100644 --- a/lesana/collection.py +++ b/lesana/collection.py @@ -334,6 +334,13 @@ class Collection(object): ) return cache + def render_query_template(self, query): + """ + Render a query template, filling it with search_aliases. + """ + t = jinja2.Template(query) + return t.render(**self.settings.get('search_aliases', {})) + def start_search(self, querystring, sort_by=None): """ Prepare a search for querystring. diff --git a/lesana/command.py b/lesana/command.py index 92f82df..ac7eba2 100644 --- a/lesana/command.py +++ b/lesana/command.py @@ -291,6 +291,14 @@ class Search(Command): dict(action='append', help='Sort results by a sortable field'), ), ( + ['--expand-query-template', '-e'], + { + 'action': 'store_true', + 'help': + 'Render search_aliases in the query as a jinja2 template', + }, + ), + ( ['query'], { 'help': 'Xapian query to search in the collection', @@ -315,16 +323,19 @@ class Search(Command): offset = self.args.offset or 0 pagesize = self.args.pagesize or 12 collection = self.collection_class(self.args.collection) + query = self.args.query + if self.args.expand_query_template: + query = collection.render_query_template(query) # sorted results require a less efficient full search rather # than being able to use the list of all documents. - if self.args.query == ['*'] and not ( + if query == ['*'] and not ( self.args.sort or getattr(collection.settings, 'default_sort', False) ): results = collection.get_all_documents() else: collection.start_search( - ' '.join(self.args.query), + ' '.join(query), sort_by=self.args.sort ) if self.args.all: diff --git a/tests/data/complex/settings.yaml b/tests/data/complex/settings.yaml index f4ad574..09e9c98 100644 --- a/tests/data/complex/settings.yaml +++ b/tests/data/complex/settings.yaml @@ -63,3 +63,6 @@ fields: - name: price type: decimal precision: 2 +search_aliases: + nice: '(category:first OR category:second)' + bad: category:third diff --git a/tests/test_collection.py b/tests/test_collection.py index de349ed..1400f57 100644 --- a/tests/test_collection.py +++ b/tests/test_collection.py @@ -550,6 +550,18 @@ class testComplexCollection(unittest.TestCase): entry = self.collection.entry_from_eid(eid) self.assertEqual(entry.data['price'], "1.90") + def test_search_aliases(self): + search_query = "{{ nice }}" + search_query = self.collection.render_query_template(search_query) + print("QUERY IS", search_query) + self.collection.start_search(search_query) + res = self.collection.get_search_results() + matches = list(res) + self.assertEqual(len(matches), 2) + matches_ids = [m.eid for m in matches] + self.assertIn('8e9fa1ed3c1b4a30a6be7a98eda0cfa7', matches_ids) + self.assertIn('5084bc6e94f24dc6976629282ef30419', matches_ids) + class testCollectionWithErrors(unittest.TestCase): def setUp(self): diff --git a/tests/test_commands.py b/tests/test_commands.py index 1a7d83b..91a8894 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -115,6 +115,7 @@ class testCommandsSimple(unittest.TestCase, CommandsMixin): 'offset': None, 'pagesize': None, 'sort': None, + 'expand_query_template': False, 'all': False, } streams = self._run_command(command.Search(), args) @@ -205,6 +206,23 @@ class testCommandsComplex(unittest.TestCase, CommandsMixin): self.assertIn('this: 1', streams['stdout'].getvalue()) self.assertEqual(streams['stderr'].getvalue(), '') + def test_search_template(self): + args = { + 'collection': self.tmpdir.name, + 'git': True, + 'template': False, + 'query': '{{ nice }}', + 'expand_query_template': True, + 'offset': None, + 'pagesize': None, + 'sort': None, + 'all': False, + } + streams = self._run_command(command.Search(), args) + self.assertIn('8e9fa1ed', streams['stdout'].getvalue()) + self.assertIn('5084bc6e', streams['stdout'].getvalue()) + self.assertEqual(streams['stderr'].getvalue(), '') + if __name__ == '__main__': unittest.main() |