From e12d2f95f067f44276413455424af78796809994 Mon Sep 17 00:00:00 2001 From: SaltyCatFish Date: Sun, 25 Mar 2018 13:40:06 -0400 Subject: [PATCH] Support keyword filtering (records having keywords a and b but not c and d) (#256) * Support keyword filtering (records having keywords a and b but not c and d) * Remove debug statements. * Left a pass statement by mistake. * Update cli help to show word list '[...]' after search options. * Add space after method doc. * Change cli option '-W --without' to '-x --exclude' --- buku.py | 39 ++++++++++++++++++++++++++++++++++++--- tests/test_bukuDb.py | 21 +++++++++++++++++++++ 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/buku.py b/buku.py index e9aebad..c9edc3c 100755 --- a/buku.py +++ b/buku.py @@ -1273,6 +1273,25 @@ class BukuDb: stag_results = self.search_by_tag(''.join(stag)) return list(set(keyword_results) & set(stag_results)) + def exclude_results_from_search(self, search_results, without, deep): + """Excludes records that match keyword search using without parameters + + Parameters + ---------- + search_results : list + List of search results + without : list of str + Keywords to search. + deep : bool, optional + True to search for matching substrings. + Returns + ------- + list or None + List of search results, or None if no matches. + """ + + return list(set(search_results) - set(self.searchdb(without, False, deep))) + def compactdb(self, index, delay_commit=False): """When an entry at index is deleted, move the last entry in DB to index, if index is lesser. @@ -4089,14 +4108,16 @@ POSITIONAL ARGUMENTS: search_grp = argparser.add_argument_group( title='SEARCH OPTIONS', - description=''' -s, --sany find records with ANY matching keyword + description=''' -s, --sany [...] find records with ANY matching keyword this is the default search option - -S, --sall find records matching ALL the keywords + -S, --sall [...] find records matching ALL the keywords special keywords - "blank": entries with empty title/tag "immutable": entries with locked title + -x, --exclude [...] combine with keyword search to exclude + records --deep match substrings ('pen' matches 'opens') - -r, --sreg run a regex search + -r, --sreg [...] run a regex search -t, --stag [tag [,|+] ...] [- tag, ...] search bookmarks by tags use ',' to find entries matching ANY tag @@ -4109,6 +4130,7 @@ POSITIONAL ARGUMENTS: addarg('-r', '--sreg', nargs='*', help=HIDE) addarg('--deep', action='store_true', help=HIDE) addarg('-t', '--stag', nargs='*', help=HIDE) + addarg('-x', '--exclude', nargs='*', help=HIDE) # ------------------------ # ENCRYPTION OPTIONS GROUP @@ -4352,6 +4374,7 @@ POSITIONAL ARGUMENTS: search_opted = True update_search_results = False tags_search = True if (args.stag is not None and len(args.stag)) else False + exclude_results = True if (args.exclude is not None and len(args.exclude)) else False if args.sany is not None: if len(args.sany): @@ -4361,6 +4384,8 @@ POSITIONAL ARGUMENTS: else: # Search URLs, titles, tags for any keyword search_results = bdb.searchdb(args.sany, False, args.deep) + if exclude_results: + search_results = bdb.exclude_results_from_search(search_results, args.exclude, args.deep) else: logerr('no keyword') elif args.sall is not None: @@ -4371,8 +4396,16 @@ POSITIONAL ARGUMENTS: else: # Search URLs, titles, tags with all keywords search_results = bdb.searchdb(args.sall, True, args.deep) + if exclude_results: + search_results = bdb.exclude_results_from_search(search_results, args.exclude, args.deep) else: logerr('no keyword') + + elif args.exclude is not None: + if len(args.exclude) and len(search_results): + exclude_results = bdb.search_keywords_and_filter_by_tags(args.exclude, True, args.deep, False, None) + search_results = list(set(search_results) - set(exclude_results)) + elif args.sreg is not None: if len(args.sreg): # Apply tag filtering, if opted diff --git a/tests/test_bukuDb.py b/tests/test_bukuDb.py index 7856b05..920e57d 100644 --- a/tests/test_bukuDb.py +++ b/tests/test_bukuDb.py @@ -1330,6 +1330,27 @@ def test_search_keywords_and_filter_by_tags(keyword_results, stag_results, exp_r assert exp_res == res +@pytest.mark.parametrize( + 'search_results, exclude_results, exp_res', + [ + ([], [], []), + (['item1', 'item2'], ['item2'], ['item1']), + (['item2'], ['item1'], ['item2']), + (['item1', 'item2'], ['item1', 'item2'], []), + ] +) +def test_exclude_results_from_search(search_results, exclude_results, exp_res): + """test method.""" + # init + import buku + bdb = buku.BukuDb() + bdb.searchdb = mock.Mock(return_value=exclude_results) + # test + res = bdb.exclude_results_from_search( + search_results, [], True) + assert exp_res == res + + # Helper functions for testcases