diff --git a/redash/models/__init__.py b/redash/models/__init__.py index 7cc551b659..7f0518cf4d 100644 --- a/redash/models/__init__.py +++ b/redash/models/__init__.py @@ -2,6 +2,7 @@ import datetime import logging import numbers +import re import time import pytz @@ -644,6 +645,43 @@ def outdated_queries(cls): return list(outdated_queries.values()) + @classmethod + def _do_multi_byte_search(cls, all_queries, term, limit=None): + # term examples: + # - word + # - name:word + # - query:word + # - "multiple words" + # - name:"multiple words" + # - word1 word2 word3 + # - word1 "multiple word" query:"select foo" + tokens = re.findall(r'(?:([^:\s]+):)?(?:"([^"]+)"|(\S+))', term) + conditions = [] + for token in tokens: + key = None + if token[0]: + key = token[0] + + if token[1]: + value = token[1] + else: + value = token[2] + + pattern = f"%{value}%" + + if key == "id" and value.isdigit(): + conditions.append(cls.id.equal(int(value))) + elif key == "name": + conditions.append(cls.name.ilike(pattern)) + elif key == "query": + conditions.append(cls.query_text.ilike(pattern)) + elif key == "description": + conditions.append(cls.description.ilike(pattern)) + else: + conditions.append(or_(cls.name.ilike(pattern), cls.description.ilike(pattern))) + + return all_queries.filter(and_(*conditions)).order_by(Query.id).limit(limit) + @classmethod def search( cls, @@ -664,12 +702,7 @@ def search( if multi_byte_search: # Since tsvector doesn't work well with CJK languages, use `ilike` too - pattern = "%{}%".format(term) - return ( - all_queries.filter(or_(cls.name.ilike(pattern), cls.description.ilike(pattern))) - .order_by(Query.id) - .limit(limit) - ) + return cls._do_multi_byte_search(all_queries, term, limit) # sort the result using the weight as defined in the search vector column return all_queries.search(term, sort=True).limit(limit) @@ -678,13 +711,7 @@ def search( def search_by_user(cls, term, user, limit=None, multi_byte_search=False): if multi_byte_search: # Since tsvector doesn't work well with CJK languages, use `ilike` too - pattern = "%{}%".format(term) - return ( - cls.by_user(user) - .filter(or_(cls.name.ilike(pattern), cls.description.ilike(pattern))) - .order_by(Query.id) - .limit(limit) - ) + return cls._do_multi_byte_search(cls.by_user(user), term, limit) return cls.by_user(user).search(term, sort=True).limit(limit)