Feature/search multiple tags (#187)
* Implement feature "Search multiple tags, exclusion in tag search" Add failing test for searching any multiple set of tags Search any tags works; passing tests Add failing test for searching all multiple tags Search all tags works Do not allow combination of search logics Adds support for tag exclusion search Update Search Options Group Update README Clean up Fix man/readme language Fix search_by_tag docstring Split tags by ' - ' not '-': allows for hyphenation Remove unnecessary else Change search_logic lang to search_operator Update exclusion language in readme and man Print warning if ',' and '+' are both used in a search * Add documentation and examples to manpage, improve documentation in README and buku.py * Enforce space separation of '+' and document * Fix under-indent in tests that causes build failure * Add test to check that search_by_tag constructs correct sqlite query; also remove unittest.skips from search_by_tag tests * Move tag search preparation work into prep_tag_search helper function; add test to test_buku.py * Use pytest.mark.parametrize for test_prep_tag_search
This commit is contained in:
parent
281613bc40
commit
2b7f142245
52
README.md
52
README.md
@ -189,9 +189,12 @@ SEARCH OPTIONS:
|
||||
"immutable": entries with locked title
|
||||
--deep match substrings ('pen' matches 'opens')
|
||||
-r, --sreg run a regex search
|
||||
-t, --stag search bookmarks by a tag
|
||||
-t, --stag [tag [,|+] ...] [- tag, ...]
|
||||
search bookmarks by tags
|
||||
use ',' to find entries matching ANY tag
|
||||
use '+' to find entries matching ALL tags
|
||||
excludes entries matching tags following ' - '
|
||||
list all tags, if no search keywords
|
||||
|
||||
ENCRYPTION OPTIONS:
|
||||
-l, --lock [N] encrypt DB file with N (> 0, default 8)
|
||||
hash iterations to generate key
|
||||
@ -242,7 +245,7 @@ PROMPT KEYS:
|
||||
S keyword [...] search for records with ALL keywords
|
||||
d match substrings ('pen' matches 'opened')
|
||||
r expression run a regex search
|
||||
t [...] search bookmarks by a tag or show taglist
|
||||
t [...] search bookmarks by tags or show taglist
|
||||
list index after a tag listing shows records with the tag
|
||||
o id|range [...] browse bookmarks by indices and/or ranges
|
||||
p id|range [...] print bookmarks by indices and/or ranges
|
||||
@ -284,7 +287,7 @@ PROMPT KEYS:
|
||||
- --sall : match all the keywords in URL, title or tags.
|
||||
- --deep : match **substrings** (`match` matches `rematched`) in URL, title and tags.
|
||||
- --sreg : match a regular expression (ignores --deep).
|
||||
- --stag : search bookmarks by a tag, or list all tags alphabetically with usage count (if no arguments).
|
||||
- --stag : search bookmarks by tags, or list all tags alphabetically with usage count (if no arguments). Delimit the list of tags in the query with `,` to search for bookmarks that match ANY of the listed tags. Delimit tags with `+` to search for bookmarks that match ALL of the listed tags. Note that `,` and `+` cannot be used together in the same search. Exclude bookmarks matching certain tags from the results by using ` - ` followed by the tags. Note that the ` - ` operator and the ` + ` delimiter must be space separated: ` - ` instead of `-` and ` + ` instead of `+`. This is to distinguish them from hyphenated tags (e.g., `some-tag-name`) and tags with '+'s (e.g., `some+tag+name`).
|
||||
- Search results are indexed serially. This index is different from actual database index of a bookmark record which is shown within `[]` after the title.
|
||||
- **Import**:
|
||||
- URLs starting with `place:`, `file://` and `apt:` are ignored during import.
|
||||
@ -498,54 +501,63 @@ for resp, url in zip(gr_results, urls):
|
||||
18. **Search** bookmarks **tagged** `general kernel concepts`:
|
||||
|
||||
$ buku --stag general kernel concepts
|
||||
19. List **all unique tags** alphabetically:
|
||||
19. **Search** for bookmarks matching **ANY** of the tags `kernel`, `debugging`, `general kernel concepts`:
|
||||
|
||||
$ buku --stag kernel, debugging, general kernel concepts
|
||||
20. **Search** for bookmarks matching **ALL** of the tags `kernel`, `debugging`, `general kernel concepts`:
|
||||
|
||||
$ buku --stag kernel + debugging + general kernel concepts
|
||||
21. **Search** for bookmarks matching both the tags `kernel` and `debugging`, but **excluding** bookmarks matching the tag `general kernel concepts`:
|
||||
|
||||
$ buku --stag kernel + debugging - general kernel concepts
|
||||
22. List **all unique tags** alphabetically:
|
||||
|
||||
$ buku --stag
|
||||
20. Run a **search and update** the results:
|
||||
23. Run a **search and update** the results:
|
||||
|
||||
$ buku -s kernel debugging -u --tag + linux kernel
|
||||
21. Run a **search and delete** the results:
|
||||
24. Run a **search and delete** the results:
|
||||
|
||||
$ buku -s kernel debugging -d
|
||||
22. **Encrypt or decrypt** DB with **custom number of iterations** (15) to generate key:
|
||||
25. **Encrypt or decrypt** DB with **custom number of iterations** (15) to generate key:
|
||||
|
||||
$ buku -l 15
|
||||
$ buku -k 15
|
||||
The same number of iterations must be specified for one lock & unlock instance. Default is 8, if omitted.
|
||||
23. **Show details** of bookmarks at index 15012014 and ranges 20-30, 40-50:
|
||||
26. **Show details** of bookmarks at index 15012014 and ranges 20-30, 40-50:
|
||||
|
||||
$ buku -p 20-30 15012014 40-50
|
||||
24. Show details of the **last 10 bookmarks**:
|
||||
27. Show details of the **last 10 bookmarks**:
|
||||
|
||||
$ buku -p -10
|
||||
25. **Show all** bookmarks with real index from database:
|
||||
28. **Show all** bookmarks with real index from database:
|
||||
|
||||
$ buku -p
|
||||
$ buku -p | more
|
||||
26. **Replace tag** 'old tag' with 'new tag':
|
||||
29. **Replace tag** 'old tag' with 'new tag':
|
||||
|
||||
$ buku --replace 'old tag' 'new tag'
|
||||
27. **Delete tag** 'old tag' from DB:
|
||||
30. **Delete tag** 'old tag' from DB:
|
||||
|
||||
$ buku --replace 'old tag'
|
||||
28. **Append (or delete) tags** 'tag 1', 'tag 2' to (or from) existing tags of bookmark at index 15012014:
|
||||
31. **Append (or delete) tags** 'tag 1', 'tag 2' to (or from) existing tags of bookmark at index 15012014:
|
||||
|
||||
$ buku -u 15012014 --tag + tag 1, tag 2
|
||||
$ buku -u 15012014 --tag - tag 1, tag 2
|
||||
29. **Open URL** at index 15012014 in browser:
|
||||
32. **Open URL** at index 15012014 in browser:
|
||||
|
||||
$ buku -o 15012014
|
||||
30. List bookmarks with **no title or tags** for bookkeeping:
|
||||
33. List bookmarks with **no title or tags** for bookkeeping:
|
||||
|
||||
$ buku -S blank
|
||||
31. List bookmarks with **immutable title**:
|
||||
34. List bookmarks with **immutable title**:
|
||||
|
||||
$ buku -S immutable
|
||||
32. **Shorten URL** www.google.com and the URL at index 20:
|
||||
35. **Shorten URL** www.google.com and the URL at index 20:
|
||||
|
||||
$ buku --shorten www.google.com
|
||||
$ buku --shorten 20
|
||||
33. **Append, remove tags at prompt** (taglist index to the left, bookmark index to the right):
|
||||
36. **Append, remove tags at prompt** (taglist index to the left, bookmark index to the right):
|
||||
|
||||
// append tags at taglist indices 4 and 6-9 to existing tags in bookmarks at indices 5 and 2-3
|
||||
buku (? for help) g 4 9-6 >> 5 3-2
|
||||
@ -555,7 +567,7 @@ for resp, url in zip(gr_results, urls):
|
||||
buku (? for help) g > 5 3-2
|
||||
// remove tags at taglist indices 4 and 6-9 from tags in bookmarks at indices 5 and 2-3
|
||||
buku (? for help) g 4 9-6 << 5 3-2
|
||||
34. More **help**:
|
||||
37. More **help**:
|
||||
|
||||
$ buku -h
|
||||
$ man buku
|
||||
|
66
buku.1
66
buku.1
@ -69,7 +69,7 @@ Bookmarks with immutable titles are listed with '(L)' after the title.
|
||||
- --sall : match all the keywords in URL, title or tags.
|
||||
- --deep : match \fBsubstrings\fR (`match` matches `rematched`) in URL, title and tags.
|
||||
- --sreg : match a regular expression (ignores --deep).
|
||||
- --stag : search bookmarks by a tag, or list all tags alphabetically with usage count (if no arguments).
|
||||
- --stag : search bookmarks by tags, or list all tags alphabetically with usage count (if no arguments). Delimit the list of tags in the query with `,` to search for bookmarks that match ANY of the listed tags. Delimit tags with `+` to search for bookmarks that match ALL of the listed tags. Note that `,` and `+` cannot be used together in the same search. Exclude bookmarks matching certain tags from the results by using ` - ` followed by the tags. Note that the ` - ` operator and the ` + ` delimiter must be space separated: ` - ` instead of `-` and ` + ` instead of `+`. This is to distinguish them from hyphenated tags (e.g., `some-tag-name`) and tags with '+'s (e.g., `some+tag+name`).
|
||||
- Search results are indexed serially. This index is different from actual database index of a bookmark record which is shown within '[]' after the title.
|
||||
.PP
|
||||
.IP 9. 4
|
||||
@ -154,8 +154,18 @@ Search modifier to match substrings. Works with --sany, --sall.
|
||||
.BI \-r " " \--sreg " expression"
|
||||
Scan for a regular expression match.
|
||||
.TP
|
||||
.BI \-t " " \--stag " [...]"
|
||||
Search bookmarks by a tag. List all tags alphabetically, if no arguments. The usage count (number of bookmarks having the tag) is shown within first brackets.
|
||||
.BI \-t " " \--stag " [tag [,|+] ...] [\- tag, ...]"
|
||||
Search bookmarks by tags.
|
||||
.br
|
||||
Use ',' delimiter to find entries matching ANY of the tags
|
||||
.br
|
||||
Use ' + ' delimiter to find entries matching ALL of the tags. (Note that the ' + ' delimiter must be space separated)
|
||||
.br
|
||||
NOTE: Cannot combine ',' and '+' in the same search
|
||||
.br
|
||||
Use ' - ' to exclude bookmarks that match the tags that follow. (Note that the '-' operator must be space separated).
|
||||
.br
|
||||
List all tags alphabetically, if no arguments. The usage count (number of bookmarks having the tag) is shown within first brackets.
|
||||
.SH ENCRYPTION OPTIONS
|
||||
.TP
|
||||
.BI \-l " " \--lock " [N]"
|
||||
@ -506,6 +516,28 @@ The last index is moved to the deleted index to keep the DB compact.
|
||||
.EE
|
||||
.PP
|
||||
.IP 19. 4
|
||||
\fBSearch\fR for bookmarks matching \fBANY\fR of the tags 'kernel', 'debugging', 'general kernel concepts':
|
||||
.PP
|
||||
.EX
|
||||
.IP
|
||||
.B buku --stag kernel, debugging, general kernel concepts
|
||||
.EE
|
||||
.PP
|
||||
.IP 20.4
|
||||
\fBSearch\fR for bookmarks matching \fBALL\fR of the tags 'kernel', 'debugging', 'general kernel concepts':
|
||||
.PP
|
||||
.EX
|
||||
.IP
|
||||
.B buku --stag kernel + debugging + general kernel concepts
|
||||
.EE
|
||||
.PP
|
||||
.IP 21.4
|
||||
\fBSearch\fR for bookmarks matching both the tags `kernel` and `debugging`, but \fBexcluding\fR bookmarks matching the tag 'general kernel concepts':
|
||||
.PP
|
||||
.EX
|
||||
.IP
|
||||
.B buku --stag kernel + debugging - general kernel concepts
|
||||
.IP 22.4
|
||||
List \fBall unique tags\fR alphabetically:
|
||||
.PP
|
||||
.EX
|
||||
@ -513,7 +545,7 @@ List \fBall unique tags\fR alphabetically:
|
||||
.B buku --stag
|
||||
.EE
|
||||
.PP
|
||||
.IP 20. 4
|
||||
.IP 23. 4
|
||||
Run a \fBsearch and update\fR the results:
|
||||
.PP
|
||||
.EX
|
||||
@ -521,7 +553,7 @@ Run a \fBsearch and update\fR the results:
|
||||
.B buku -s kernel debugging -u --tag + linux kernel
|
||||
.EE
|
||||
.PP
|
||||
.IP 21. 4
|
||||
.IP 24. 4
|
||||
Run a \fBsearch and delete\fR the results:
|
||||
.PP
|
||||
.EX
|
||||
@ -529,7 +561,7 @@ Run a \fBsearch and delete\fR the results:
|
||||
.B buku -s kernel debugging -d
|
||||
.EE
|
||||
.PP
|
||||
.IP 22. 4
|
||||
.IP 25. 4
|
||||
\fBEncrypt or decrypt\fR DB with \fBcustom number of iterations\fR (15) to generate key:
|
||||
.PP
|
||||
.EX
|
||||
@ -542,7 +574,7 @@ Run a \fBsearch and delete\fR the results:
|
||||
.IP "" 4
|
||||
The same number of iterations must be specified for one lock & unlock instance. Default is 8, if omitted.
|
||||
.PP
|
||||
.IP 23. 4
|
||||
.IP 26. 4
|
||||
\fBShow details\fR of bookmarks at index 15012014 and ranges 20-30, 40-50:
|
||||
.PP
|
||||
.EX
|
||||
@ -550,7 +582,7 @@ The same number of iterations must be specified for one lock & unlock instance.
|
||||
.B buku -p 20-30 15012014 40-50
|
||||
.EE
|
||||
.PP
|
||||
.IP 24. 4
|
||||
.IP 27. 4
|
||||
Show details of the \fBlast 10 bookmarks\fR:
|
||||
.PP
|
||||
.EX
|
||||
@ -558,7 +590,7 @@ Show details of the \fBlast 10 bookmarks\fR:
|
||||
.B buku -p -10
|
||||
.EE
|
||||
.PP
|
||||
.IP 25. 4
|
||||
.IP 28. 4
|
||||
\fBShow all\fR bookmarks with real index from database:
|
||||
.PP
|
||||
.EX
|
||||
@ -567,7 +599,7 @@ Show details of the \fBlast 10 bookmarks\fR:
|
||||
.B buku -p | more
|
||||
.EE
|
||||
.PP
|
||||
.IP 26. 4
|
||||
.IP 29. 4
|
||||
\fBReplace tag\fR 'old tag' with 'new tag':
|
||||
.PP
|
||||
.EX
|
||||
@ -575,7 +607,7 @@ Show details of the \fBlast 10 bookmarks\fR:
|
||||
.B buku --replace 'old tag' 'new tag'
|
||||
.EE
|
||||
.PP
|
||||
.IP 27. 4
|
||||
.IP 30. 4
|
||||
\fBDelete tag\fR 'old tag' from DB:
|
||||
.PP
|
||||
.EX
|
||||
@ -583,7 +615,7 @@ Show details of the \fBlast 10 bookmarks\fR:
|
||||
.B buku --replace 'old tag'
|
||||
.EE
|
||||
.PP
|
||||
.IP 28. 4
|
||||
.IP 31. 4
|
||||
\fBAppend (or delete) tags\fR 'tag 1', 'tag 2' to (or from) existing tags of bookmark at index 15012014:
|
||||
.PP
|
||||
.EX
|
||||
@ -592,7 +624,7 @@ Show details of the \fBlast 10 bookmarks\fR:
|
||||
.B buku -u 15012014 --tag - tag 1, tag 2
|
||||
.EE
|
||||
.PP
|
||||
.IP 29. 4
|
||||
.IP 32. 4
|
||||
\fBOpen URL\fR at index 15012014 in browser:
|
||||
.PP
|
||||
.EX
|
||||
@ -600,7 +632,7 @@ Show details of the \fBlast 10 bookmarks\fR:
|
||||
.B buku -o 15012014
|
||||
.EE
|
||||
.PP
|
||||
.IP 30. 4
|
||||
.IP 33. 4
|
||||
List bookmarks with \fBno title or tags\fR for bookkeeping:
|
||||
.PP
|
||||
.EX
|
||||
@ -608,7 +640,7 @@ List bookmarks with \fBno title or tags\fR for bookkeeping:
|
||||
.B buku -S blank
|
||||
.EE
|
||||
.PP
|
||||
.IP 31. 4
|
||||
.IP 34. 4
|
||||
List bookmarks with \fBimmutable title\fR:
|
||||
.PP
|
||||
.EX
|
||||
@ -616,7 +648,7 @@ List bookmarks with \fBimmutable title\fR:
|
||||
.B buku -S immutable
|
||||
.EE
|
||||
.PP
|
||||
.IP 32. 4
|
||||
.IP 35. 4
|
||||
\fBShorten\fR the URL www.google.com and the URL at index 20:
|
||||
.PP
|
||||
.EX
|
||||
@ -625,7 +657,7 @@ List bookmarks with \fBimmutable title\fR:
|
||||
.B buku --shorten 20
|
||||
.EE
|
||||
.PP
|
||||
.IP 33. 4
|
||||
.IP 36. 4
|
||||
\fBAppend, remove tags at prompt\fR (taglist index to the left, bookmark index to the right):
|
||||
.PP
|
||||
.EX
|
||||
|
65
buku.py
65
buku.py
@ -1079,18 +1079,32 @@ class BukuDb:
|
||||
|
||||
return self.cur.fetchall()
|
||||
|
||||
def search_by_tag(self, tag):
|
||||
def search_by_tag(self, tags):
|
||||
'''Search and list bookmarks with a tag
|
||||
|
||||
:param tag: a tag to search as string
|
||||
:param tags: list of tags to search as string
|
||||
:return: search results, or None, if no matches
|
||||
'''
|
||||
|
||||
tag = delim_wrap(tag.strip(DELIM))
|
||||
query = "SELECT id, url, metadata, tags, desc FROM bookmarks WHERE tags LIKE '%' || ? || '%' ORDER BY id ASC"
|
||||
logdbg('query: "%s", args: %s', query, tag)
|
||||
# do not allow combination of search logics
|
||||
if ' + ' in tags and ',' in tags:
|
||||
logerr("Cannot use both '+' and ',' in same search")
|
||||
return
|
||||
|
||||
self.cur.execute(query, (tag,))
|
||||
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'
|
||||
|
||||
logdbg('query: "%s", args: %s', query, tags)
|
||||
|
||||
self.cur.execute(query, tuple(tags, ))
|
||||
return self.cur.fetchall()
|
||||
|
||||
def compactdb(self, index, delay_commit=False):
|
||||
@ -2042,7 +2056,7 @@ PROMPT KEYS:
|
||||
S keyword [...] search for records with ALL keywords
|
||||
d match substrings ('pen' matches 'opened')
|
||||
r expression run a regex search
|
||||
t [...] search bookmarks by a tag or show taglist
|
||||
t [...] search bookmarks by tags or show taglist
|
||||
list index after a tag listing shows records with the tag
|
||||
o id|range [...] browse bookmarks by indices and/or ranges
|
||||
p id|range [...] print bookmarks by indices and/or ranges
|
||||
@ -2326,6 +2340,37 @@ def parse_tags(keywords=[]):
|
||||
return delim_wrap(DELIM.join(sorted_tags))
|
||||
|
||||
|
||||
def prep_tag_search(tags):
|
||||
"""Prepare list of tags to search and determine search operator
|
||||
|
||||
:param tags: list of tags to search as string
|
||||
:return: tuple (
|
||||
list of formatted tags to search,
|
||||
a string indicating query search operator (either OR or AND),
|
||||
a regex string of tags (used to exclude matching bookmarks),
|
||||
),
|
||||
None, if ' - ' operator is not in the tags argument
|
||||
"""
|
||||
|
||||
excluded_tags = None
|
||||
if ' - ' in tags:
|
||||
tags, excluded_tags = tags.split(' - ', 1)
|
||||
|
||||
excluded_taglist = [delim_wrap(t.strip()) for t in excluded_tags.split(',')]
|
||||
# join with pipe to construct regex string
|
||||
excluded_tags = '|'.join(excluded_taglist)
|
||||
|
||||
search_operator = 'OR'
|
||||
tag_delim = ','
|
||||
if ' + ' in tags:
|
||||
search_operator = 'AND'
|
||||
tag_delim = ' + '
|
||||
|
||||
tags = [delim_wrap(t.strip()) for t in tags.split(tag_delim)]
|
||||
|
||||
return tags, search_operator, excluded_tags
|
||||
|
||||
|
||||
def edit_at_prompt(obj, nav):
|
||||
'''Edit and add or update a bookmark
|
||||
|
||||
@ -3101,7 +3146,11 @@ POSITIONAL ARGUMENTS:
|
||||
"immutable": entries with locked title
|
||||
--deep match substrings ('pen' matches 'opens')
|
||||
-r, --sreg run a regex search
|
||||
-t, --stag search bookmarks by a tag
|
||||
-t, --stag [tag [,|+] ...] [- tag, ...]
|
||||
search bookmarks by tags
|
||||
use ',' to find entries matching ANY tag
|
||||
use '+' to find entries matching ALL tags
|
||||
excludes entries matching tags following ' - '
|
||||
list all tags, if no search keywords''')
|
||||
addarg = search_grp.add_argument
|
||||
addarg('-s', '--sany', action='store_true', help=HIDE)
|
||||
|
@ -8,7 +8,7 @@ import unittest
|
||||
|
||||
import pytest
|
||||
|
||||
from buku import is_int, parse_tags
|
||||
from buku import is_int, parse_tags, prep_tag_search
|
||||
|
||||
only_python_3_5 = pytest.mark.skipif(
|
||||
sys.version_info < (3, 5), reason="requires Python 3.5 or later")
|
||||
@ -107,6 +107,30 @@ def test_parse_tags(keywords, exp_res):
|
||||
assert res == exp_res
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'taglist, exp_res',
|
||||
[
|
||||
[
|
||||
'tag1, tag2+3',
|
||||
([',tag1,', ',tag2+3,'], 'OR', None)
|
||||
],
|
||||
[
|
||||
'tag1 + tag2-3 + tag4',
|
||||
([',tag1,', ',tag2-3,', ',tag4,'], 'AND', None)
|
||||
],
|
||||
[
|
||||
'tag1, tag2-3 - tag4, tag5',
|
||||
([',tag1,', ',tag2-3,'], 'OR', ',tag4,|,tag5,')
|
||||
]
|
||||
]
|
||||
)
|
||||
def test_prep_tag_search(taglist, exp_res):
|
||||
"""test prep_tag_search helper function"""
|
||||
|
||||
results = prep_tag_search(taglist)
|
||||
assert results == exp_res
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'nav, is_editor_valid_retval, edit_rec_retval',
|
||||
product(
|
||||
|
@ -273,6 +273,169 @@ class TestBukuDb(unittest.TestCase):
|
||||
expected = [(i + 1,) + tuple(self.bookmarks[i])]
|
||||
self.assertEqual(results, expected)
|
||||
|
||||
def test_search_by_multiple_tags_search_any(self):
|
||||
# adding bookmarks
|
||||
for bookmark in self.bookmarks:
|
||||
self.bdb.add_rec(*bookmark)
|
||||
|
||||
new_bookmark = ['https://newbookmark.com',
|
||||
'New Bookmark',
|
||||
parse_tags(['test,old,new']),
|
||||
'additional bookmark to test multiple tag search']
|
||||
|
||||
self.bdb.add_rec(*new_bookmark)
|
||||
|
||||
with mock.patch('buku.prompt'):
|
||||
# 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
|
||||
expected = [
|
||||
(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')
|
||||
]
|
||||
self.assertEqual(results, expected)
|
||||
|
||||
def test_search_by_multiple_tags_search_all(self):
|
||||
# adding bookmarks
|
||||
for bookmark in self.bookmarks:
|
||||
self.bdb.add_rec(*bookmark)
|
||||
|
||||
new_bookmark = ['https://newbookmark.com',
|
||||
'New Bookmark',
|
||||
parse_tags(['test,old,new']),
|
||||
'additional bookmark to test multiple tag search']
|
||||
|
||||
self.bdb.add_rec(*new_bookmark)
|
||||
|
||||
with mock.patch('buku.prompt'):
|
||||
# search for bookmarks matching ALL 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
|
||||
expected = [
|
||||
(4, 'https://newbookmark.com', 'New Bookmark',
|
||||
parse_tags([',test,old,new,']),
|
||||
'additional bookmark to test multiple tag search')
|
||||
]
|
||||
self.assertEqual(results, expected)
|
||||
|
||||
def test_search_by_tags_enforces_space_seprations_search_all(self):
|
||||
|
||||
bookmark1 = ['https://bookmark1.com',
|
||||
'Bookmark One',
|
||||
parse_tags(['tag, two,tag+two']),
|
||||
"test case for bookmark with '+' in tag"]
|
||||
|
||||
bookmark2 = ['https://bookmark2.com',
|
||||
'Bookmark Two',
|
||||
parse_tags(['tag,two, tag-two']),
|
||||
"test case for bookmark with hyphenated tag"]
|
||||
|
||||
self.bdb.add_rec(*bookmark1)
|
||||
self.bdb.add_rec(*bookmark2)
|
||||
|
||||
with mock.patch('buku.prompt'):
|
||||
# check that space separation for ' + ' operator is enforced
|
||||
results = self.bdb.search_by_tag('tag+two')
|
||||
# Expect a list of five-element tuples containing all bookmark data
|
||||
# db index, URL, title, tags, description
|
||||
expected = [
|
||||
(1, 'https://bookmark1.com', 'Bookmark One',
|
||||
parse_tags([',tag,two,tag+two,']),
|
||||
"test case for bookmark with '+' in tag")
|
||||
]
|
||||
self.assertEqual(results, expected)
|
||||
results = self.bdb.search_by_tag('tag + two')
|
||||
# Expect a list of five-element tuples containing all bookmark data
|
||||
# db index, URL, title, tags, description
|
||||
expected = [
|
||||
(1, 'https://bookmark1.com', 'Bookmark One',
|
||||
parse_tags([',tag,two,tag+two,']),
|
||||
"test case for bookmark with '+' in tag"),
|
||||
(2, 'https://bookmark2.com', 'Bookmark Two',
|
||||
parse_tags([',tag,two,tag-two,']),
|
||||
"test case for bookmark with hyphenated tag"),
|
||||
]
|
||||
self.assertEqual(results, expected)
|
||||
|
||||
def test_search_by_tags_exclusion(self):
|
||||
# adding bookmarks
|
||||
for bookmark in self.bookmarks:
|
||||
self.bdb.add_rec(*bookmark)
|
||||
|
||||
new_bookmark = ['https://newbookmark.com',
|
||||
'New Bookmark',
|
||||
parse_tags(['test,old,new']),
|
||||
'additional bookmark to test multiple tag search']
|
||||
|
||||
self.bdb.add_rec(*new_bookmark)
|
||||
|
||||
with mock.patch('buku.prompt'):
|
||||
# search for bookmarks matching ANY of the supplied tags
|
||||
# while excluding bookmarks from results that match a given tag
|
||||
results = self.bdb.search_by_tag('test, old - est')
|
||||
# Expect a list of five-element tuples containing all bookmark data
|
||||
# db index, URL, title, tags, description
|
||||
expected = [
|
||||
(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)
|
||||
|
||||
def test_search_by_tags_enforces_space_seprations_exclusion(self):
|
||||
|
||||
bookmark1 = ['https://bookmark1.com',
|
||||
'Bookmark One',
|
||||
parse_tags(['tag, two,tag+two']),
|
||||
"test case for bookmark with '+' in tag"]
|
||||
|
||||
bookmark2 = ['https://bookmark2.com',
|
||||
'Bookmark Two',
|
||||
parse_tags(['tag,two, tag-two']),
|
||||
"test case for bookmark with hyphenated tag"]
|
||||
|
||||
bookmark3 = ['https://bookmark3.com',
|
||||
'Bookmark Three',
|
||||
parse_tags(['tag, tag three']),
|
||||
"second test case for bookmark with hyphenated tag"]
|
||||
|
||||
self.bdb.add_rec(*bookmark1)
|
||||
self.bdb.add_rec(*bookmark2)
|
||||
self.bdb.add_rec(*bookmark3)
|
||||
|
||||
with mock.patch('buku.prompt'):
|
||||
# check that space separation for ' - ' operator is enforced
|
||||
results = self.bdb.search_by_tag('tag-two')
|
||||
# Expect a list of five-element tuples containing all bookmark data
|
||||
# db index, URL, title, tags, description
|
||||
expected = [
|
||||
(2, 'https://bookmark2.com', 'Bookmark Two',
|
||||
parse_tags([',tag,two,tag-two,']),
|
||||
"test case for bookmark with hyphenated tag"),
|
||||
]
|
||||
self.assertEqual(results, expected)
|
||||
results = self.bdb.search_by_tag('tag - two')
|
||||
# Expect a list of five-element tuples containing all bookmark data
|
||||
# db index, URL, title, tags, description
|
||||
expected = [
|
||||
(3, 'https://bookmark3.com', 'Bookmark Three',
|
||||
parse_tags([',tag,tag three,']),
|
||||
"second test case for bookmark with hyphenated tag"),
|
||||
]
|
||||
self.assertEqual(results, expected)
|
||||
|
||||
# @unittest.skip('skipping')
|
||||
def test_search_and_open_in_broswer_by_range(self):
|
||||
# adding bookmarks
|
||||
@ -427,7 +590,6 @@ class TestBukuDb(unittest.TestCase):
|
||||
# def test_import_bookmark(self):
|
||||
# self.fail()
|
||||
|
||||
|
||||
@given(
|
||||
index=st.integers(min_value=-10, max_value=10),
|
||||
low=st.integers(min_value=-10, max_value=10),
|
||||
@ -824,6 +986,45 @@ def test_update_rec_exec_arg(caplog, kwargs, exp_query, exp_arguments):
|
||||
assert caplog.records[-1].levelname == 'DEBUG'
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'tags_to_search, exp_query, exp_arguments',
|
||||
[
|
||||
[
|
||||
'tag1, tag2',
|
||||
"SELECT id, url, metadata, tags, desc FROM bookmarks WHERE tags LIKE '%' || ? || '%' "
|
||||
"OR tags LIKE '%' || ? || '%' ORDER BY id ASC",
|
||||
[',tag1,', ',tag2,']
|
||||
|
||||
],
|
||||
[
|
||||
'tag1+tag2,tag3, tag4',
|
||||
"SELECT id, url, metadata, tags, desc FROM bookmarks WHERE tags LIKE '%' || ? || '%' "
|
||||
"OR tags LIKE '%' || ? || '%' OR tags LIKE '%' || ? || '%' ORDER BY id ASC",
|
||||
[',tag1+tag2,', ',tag3,', ',tag4,']
|
||||
],
|
||||
[
|
||||
'tag1 + tag2+tag3',
|
||||
"SELECT id, url, metadata, tags, desc FROM bookmarks WHERE tags LIKE '%' || ? || '%' "
|
||||
"AND tags LIKE '%' || ? || '%' ORDER BY id ASC",
|
||||
[',tag1,', ',tag2+tag3,']
|
||||
],
|
||||
[
|
||||
'tag1-tag2 + tag 3 - tag4',
|
||||
"SELECT id, url, metadata, tags, desc FROM bookmarks WHERE (tags LIKE '%' || ? || '%' "
|
||||
"AND tags LIKE '%' || ? || '%' ) AND tags NOT REGEXP ? ORDER BY id ASC",
|
||||
[',tag1-tag2,', ',tag 3,', ',tag4,']
|
||||
]
|
||||
]
|
||||
)
|
||||
def test_search_by_tag_query(caplog, tags_to_search, exp_query, exp_arguments):
|
||||
"""test that the correct query and argments are constructed"""
|
||||
bdb = BukuDb()
|
||||
bdb.search_by_tag(tags_to_search)
|
||||
exp_log = 'query: "{}", args: {}'.format(exp_query, exp_arguments)
|
||||
assert caplog.records[-1].getMessage() == exp_log
|
||||
assert caplog.records[-1].levelname == 'DEBUG'
|
||||
|
||||
|
||||
def test_update_rec_only_index():
|
||||
"""test method."""
|
||||
bdb = BukuDb()
|
||||
|
Loading…
x
Reference in New Issue
Block a user