diff --git a/asl_articles/search.py b/asl_articles/search.py index 35bf77c..9c8c353 100644 --- a/asl_articles/search.py +++ b/asl_articles/search.py @@ -393,7 +393,15 @@ def _make_fts_query_string( query_string, search_aliases ): #pylint: disable=too return "({})".format( " OR ".join( quote(v) for v in val ) ) def quote( val ): """Quote a string, if necessary.""" - if not val.startswith( '"' ) or not val.endswith( '"' ): + # NOTE: We used to check for fully-quoted values i.e. + # not ( startswith " and endswith " ) + # which becomes: + # not startswith " or not endswith " + # but this doesn't work with quoted multi-word phrases that contain special characters + # e.g. "J. R. Tracy", since we see that the first phrase ("J.) is not fully-quoted, + # and so we wrap it in quotes :-/ Instead, if we see a quote at either end of the word, + # we treat it as part of a quoted phrase (either single- or multi-word), and use it verbatim. + if not val.startswith( '"' ) and not val.endswith( '"' ): if any( ch in val for ch in _SQLITE_FTS_SPECIAL_CHARS+" " ): val = '"{}"'.format( val ) return val.replace( "'", "''" ) diff --git a/asl_articles/tests/test_search.py b/asl_articles/tests/test_search.py index 9010680..a38dbc0 100644 --- a/asl_articles/tests/test_search.py +++ b/asl_articles/tests/test_search.py @@ -583,6 +583,11 @@ def test_make_fts_query_string(): # 'foo AND "xyz 123" AND bar' # ) + # test some quoted phrases that wrap special characters + do_test( 'Mr. Jones', '"Mr." AND Jones' ) + do_test( '"Mr. Jones"', '"Mr. Jones"' ) + do_test( 'foo "Mr. Jones" bar', 'foo AND "Mr. Jones" AND bar' ) + # test some incorrectly quoted phrases do_test( '"', '' ) do_test( ' " " " ', '' )