Feature/order search results by keyword matches (#244)

* Add case statements to search_by_tag

* Add subselect so we don't return score column

* update tests to change expected order of results

* Add case statements to searchdb

* Add docstring to wrap_in_case_statement

* Add to comment in search_by_multiple_tags_search_any test

* Switch clause to fragment

* Change case_select variable to case_statement

* Remove wrap_in_case_statement function and use lambda instead
This commit is contained in:
Alex 2018-02-17 20:22:59 -05:00 committed by Arun Prakash Jana
parent 19e8fff33d
commit c2d21b8c24
2 changed files with 45 additions and 26 deletions

54
buku.py
View File

@ -1121,7 +1121,6 @@ class BukuDb:
if not keywords:
return None
q0 = 'SELECT id, url, metadata, tags, desc FROM bookmarks WHERE '
# Deep query string
q1 = ("(tags LIKE ('%' || ? || '%') OR "
"URL LIKE ('%' || ? || '%') OR "
@ -1134,11 +1133,13 @@ class BukuDb:
'desc REGEXP ?) ')
qargs = []
case_statement = lambda x: "CASE WHEN " + x + " THEN 1 ELSE 0 END"
if regex:
q0 = 'SELECT id, url, metadata, tags, desc FROM (SELECT *, '
for token in keywords:
q0 += q2 + 'OR '
q0 += case_statement(q2) + ' + '
qargs += (token, token, token, token,)
q0 = q0[:-3]
q0 = q0[:-3] + ' AS score FROM bookmarks WHERE score > 0 ORDER BY score DESC)'
elif all_keywords:
if len(keywords) == 1 and keywords[0] == 'blank':
q0 = "SELECT * FROM bookmarks WHERE metadata = '' OR tags = ? "
@ -1146,6 +1147,7 @@ class BukuDb:
elif len(keywords) == 1 and keywords[0] == 'immutable':
q0 = 'SELECT * FROM bookmarks WHERE flags & 1 == 1 '
else:
q0 = 'SELECT id, url, metadata, tags, desc FROM bookmarks WHERE '
for token in keywords:
if deep:
q0 += q1 + 'AND '
@ -1155,21 +1157,21 @@ class BukuDb:
qargs += (token, token, token, token,)
q0 = q0[:-4]
q0 += 'ORDER BY id ASC'
elif not all_keywords:
q0 = 'SELECT id, url, metadata, tags, desc FROM (SELECT *, '
for token in keywords:
if deep:
q0 += q1 + 'OR '
q0 += case_statement(q1) + ' + '
else:
token = '\\b' + token.rstrip('/') + '\\b'
q0 += q2 + 'OR '
q0 += case_statement(q2) + ' + '
qargs += (token, token, token, token,)
q0 = q0[:-3]
q0 = q0[:-3] + ' AS score FROM bookmarks WHERE score > 0 ORDER BY score DESC)'
else:
logerr('Invalid search option')
return None
q0 += 'ORDER BY id ASC'
logdbg('query: "%s", args: %s', q0, qargs)
try:
@ -1205,17 +1207,34 @@ class BukuDb:
tags, search_operator, excluded_tags = prep_tag_search(tags)
query = "SELECT id, url, metadata, tags, desc FROM bookmarks WHERE tags LIKE '%' || ? || '%' "
for tag in tags[1:]:
query += "{} tags LIKE '%' || ? || '%' ".format(search_operator)
if excluded_tags:
tags.append(excluded_tags)
query = query.replace('WHERE tags', 'WHERE (tags')
query += ') AND tags NOT REGEXP ? '
query += 'ORDER BY id ASC'
if search_operator == 'AND':
query = "SELECT id, url, metadata, tags, desc FROM bookmarks WHERE tags LIKE '%' || ? || '%' "
for tag in tags[1:]:
query += "{} tags LIKE '%' || ? || '%' ".format(search_operator)
if excluded_tags:
tags.append(excluded_tags)
query = query.replace('WHERE tags', 'WHERE (tags')
query += ') AND tags NOT REGEXP ? '
query += 'ORDER BY id ASC'
else:
query = "SELECT id, url, metadata, tags, desc FROM (SELECT *, "
case_statement = "CASE WHEN tags LIKE '%' || ? || '%' THEN 1 ELSE 0 END"
query += case_statement
for tag in tags[1:]:
query += ' + ' + case_statement
query += ' AS score FROM bookmarks WHERE score > 0'
if excluded_tags:
tags.append(excluded_tags)
query += ' AND tags NOT REGEXP ? '
query += ' ORDER BY score DESC)'
logdbg('query: "%s", args: %s', query, tags)
self.cur.execute(query, tuple(tags, ))
return self.cur.fetchall()
@ -2946,7 +2965,6 @@ def prep_tag_search(tags):
return tags, search_operator, excluded_tags
def gen_auto_tag():
"""Generate a tag in Year-Month-Date format.

View File

@ -309,17 +309,18 @@ class TestBukuDb(unittest.TestCase):
# search for bookmarks matching ANY of the supplied tags
results = self.bdb.search_by_tag('test, old')
# Expect a list of five-element tuples containing all bookmark data
# db index, URL, title, tags, description
# db index, URL, title, tags, description, ordered by records with
# the most number of matches.
expected = [
(4, 'https://newbookmark.com', 'New Bookmark',
parse_tags([',test,old,new,']),
'additional bookmark to test multiple tag search'),
(1, 'http://slashdot.org', 'SLASHDOT',
parse_tags([',news,old,']),
"News for old nerds, stuff that doesn't matter"),
(3, 'https://test.com:8080', 'test',
parse_tags([',test,tes,est,es,']),
"a case for replace_tag test"),
(4, 'https://newbookmark.com', 'New Bookmark',
parse_tags([',test,old,new,']),
'additional bookmark to test multiple tag search')
"a case for replace_tag test")
]
self.assertEqual(results, expected)
@ -405,12 +406,12 @@ class TestBukuDb(unittest.TestCase):
# Expect a list of five-element tuples containing all bookmark data
# db index, URL, title, tags, description
expected = [
(4, 'https://newbookmark.com', 'New Bookmark',
parse_tags([',test,old,new,']),
'additional bookmark to test multiple tag search'),
(1, 'http://slashdot.org', 'SLASHDOT',
parse_tags([',news,old,']),
"News for old nerds, stuff that doesn't matter"),
(4, 'https://newbookmark.com', 'New Bookmark',
parse_tags([',test,old,new,']),
'additional bookmark to test multiple tag search')
]
self.assertEqual(results, expected)