diff --git a/news/139.bugfix b/news/139.bugfix new file mode 100644 index 0000000..ec4772e --- /dev/null +++ b/news/139.bugfix @@ -0,0 +1,2 @@ +Handle parenthesis inside quotes +[erral] diff --git a/plone/app/querystring/querybuilder.py b/plone/app/querystring/querybuilder.py index d9ef15b..bd8978f 100644 --- a/plone/app/querystring/querybuilder.py +++ b/plone/app/querystring/querybuilder.py @@ -44,10 +44,11 @@ def _quote(term): # being parsed as logical query atoms. if term.lower() in ("and", "or", "not"): term = '"%s"' % term - return term + return _quote_chars(term) def munge_search_term(query): + original_query = query for char in _BAD_CHARS: query = query.replace(char, " ") @@ -66,7 +67,7 @@ def munge_search_term(query): r += map(_quote, query.strip().split()) r = " AND ".join(r) - r = _quote_chars(r) + ("*" if r and not r.endswith('"') else "") + r = r + ("*" if r and not original_query.endswith('"') else "") return r diff --git a/plone/app/querystring/tests/testQueryBuilder.py b/plone/app/querystring/tests/testQueryBuilder.py index 4c39838..6a79bf0 100644 --- a/plone/app/querystring/tests/testQueryBuilder.py +++ b/plone/app/querystring/tests/testQueryBuilder.py @@ -325,6 +325,80 @@ def testQueryBuilderCustomQueryDoNotOverrideValues(self): self.assertEqual(len(results), 1) self.assertEqual(results[0].Title(), "Collectionstestpage 2") + def test_munge_search_term(self): + from plone.app.querystring.querybuilder import _BAD_CHARS + from plone.app.querystring.querybuilder import munge_search_term + + search_term_tests = [ + ( + # search term + "spam ham", + "spam AND ham*", + ), + ( + # quoted term + '"spam ham"', + '"spam ham"', + ), + ( + # cleanup quoted terms + '" spam ham "', + '"spam ham"', + ), + ( + # quoted term with inner parenthesis + '"spam (ham)"', + '"spam (ham)"', + ), + ( + # quoted term with inner parenthesis + '"spam" (ham)', + '"spam" AND "("ham")"*', + ), + ( + # quoted term with inner parenthesis + '"(spam ham)"', + '"(spam ham)"', + ), + ( + # mixed cases + "Spam hAm", + "Spam AND hAm*", + ), + ( + # mix quoting and unquoted + 'let\'s eat some "ham and eggs " without spam ', + '"ham and eggs" AND let\'s AND eat AND some ' "AND without AND spam*", + ), + ( + 'test "Welcome" to "Plone" retest', + '"Welcome" AND "Plone" AND test AND to AND retest*', + ), + ( + # parentheses + "spam (ham)", + 'spam AND "("ham")"*', + ), + ( + # special keywords + "spam or not ham and eggs", + 'spam AND "or" AND "not" AND ham AND "and" AND eggs*', + ), + ( + # bad characters + " ".join(_BAD_CHARS), + "", + ), + ( + # weird input + 'test ""Welcome" to "Plone"" retest', + '"to" AND test AND WelcomePlone AND retest*', + ), + ] + + for _in, _out in search_term_tests: + self.assertEqual(munge_search_term(_in), _out) + class TestQuerybuilderResultTypes(unittest.TestCase): layer = TEST_PROFILE_PLONEAPPQUERYSTRING_INTEGRATION_TESTING