Skip to content

Added new operators support #27

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Dec 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions translator/app/translator/const.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
from os.path import abspath, dirname
from typing import Union, List

APP_PATH = dirname(abspath(__file__))

CTI_MIN_LIMIT_QUERY = 10000

DEFAULT_VALUE_TYPE = Union[Union[int, str, List[int], List[str]]]
33 changes: 27 additions & 6 deletions translator/app/translator/core/render.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from abc import ABC
from typing import Union, List, Dict

from app.translator.const import DEFAULT_VALUE_TYPE
from app.translator.core.exceptions.core import NotImplementedException, StrictPlatformException
from app.translator.core.exceptions.parser import UnsupportedOperatorException
from app.translator.core.functions import PlatformFunctions
Expand All @@ -37,6 +38,11 @@ class BaseQueryFieldValue(ABC):
def __init__(self, or_token):
self.field_value = {
OperatorType.EQ: self.equal_modifier,
OperatorType.LT: self.less_modifier,
OperatorType.LTE: self.less_or_equal_modifier,
OperatorType.GT: self.greater_modifier,
OperatorType.GTE: self.greater_or_equal_modifier,
OperatorType.NEQ: self.not_equal_modifier,
OperatorType.CONTAINS: self.contains_modifier,
OperatorType.ENDSWITH: self.endswith_modifier,
OperatorType.STARTSWITH: self.startswith_modifier,
Expand All @@ -45,22 +51,37 @@ def __init__(self, or_token):
}
self.or_token = f" {or_token} "

def equal_modifier(self, field, value):
def equal_modifier(self, field: str, value: DEFAULT_VALUE_TYPE) -> str:
raise NotImplementedException

def contains_modifier(self, field, value):
def less_modifier(self, field: str, value: Union[int, str]) -> str:
raise NotImplementedException

def endswith_modifier(self, field, value):
def less_or_equal_modifier(self, field: str, value: Union[int, str]) -> str:
raise NotImplementedException

def startswith_modifier(self, field, value):
def greater_modifier(self, field: str, value: Union[int, str]) -> str:
raise NotImplementedException

def regex_modifier(self, field, value):
def greater_or_equal_modifier(self, field: str, value: Union[int, str]) -> str:
raise NotImplementedException

def keywords(self, field, value):
def not_equal_modifier(self, field: str, value: DEFAULT_VALUE_TYPE) -> str:
raise NotImplementedException

def contains_modifier(self, field: str, value: DEFAULT_VALUE_TYPE) -> str:
raise NotImplementedException

def endswith_modifier(self, field: str, value: DEFAULT_VALUE_TYPE) -> str:
raise NotImplementedException

def startswith_modifier(self, field: str, value: DEFAULT_VALUE_TYPE) -> str:
raise NotImplementedException

def regex_modifier(self, field: str, value: DEFAULT_VALUE_TYPE) -> str:
raise NotImplementedException

def keywords(self, field: str, value: DEFAULT_VALUE_TYPE) -> str:
raise NotImplementedException

def apply_field_value(self, field, operator, value):
Expand Down
7 changes: 6 additions & 1 deletion translator/app/translator/core/tokenizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class QueryTokenizer(BaseTokenizer):
field_pattern = r"(?P<field_name>[a-zA-Z\._\-]+)"
operator_pattern = r"\s?(?P<operator>and|or|not|AND|OR|NOT)\s?"
field_value_pattern = r"""^___field___\s*___match_operator___\s*___value___"""
match_operator_pattern = r"""(?:___field___\s?(?P<match_operator>ilike|contains|endswith|startswith|in|==|=|=~|!=|:|\:))\s?"""
match_operator_pattern = r"""(?:___field___\s?(?P<match_operator>ilike|contains|endswith|startswith|in|>=|<=|==|>|<|=~|!=|=|:|\:))\s?"""
base_value_pattern = r"(?:___value_pattern___)"
_value_pattern = r"""(?:\"|\')*(?P<value>[:a-zA-Z\*0-9=+%#\-_\/\\'\,.&^@!\(\s]*)(?:\*|\'|\"|\s|\$)*"""
value_pattern = base_value_pattern.replace('___value_pattern___', _value_pattern)
Expand All @@ -60,6 +60,11 @@ class QueryTokenizer(BaseTokenizer):
operators_map = {
"=": OperatorType.EQ,
"in": OperatorType.EQ,
"<": OperatorType.LT,
"<=": OperatorType.LTE,
">": OperatorType.GT,
">=": OperatorType.GTE,
"!=": OperatorType.NEQ,
"contains": OperatorType.CONTAINS,
"startswith": OperatorType.STARTSWITH,
"endswith": OperatorType.ENDSWITH
Expand Down
31 changes: 25 additions & 6 deletions translator/app/translator/platforms/athena/renders/athena.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
limitations under the License.
-----------------------------------------------------------------
"""
from typing import Union

from app.translator.const import DEFAULT_VALUE_TYPE
from app.translator.platforms.athena.const import athena_details
from app.translator.platforms.athena.mapping import AthenaMappings, athena_mappings
from app.translator.core.exceptions.render import UnsupportedRenderMethod
Expand All @@ -28,32 +30,49 @@
class AthenaFieldValue(BaseQueryFieldValue):
details: PlatformDetails = athena_details

def equal_modifier(self, field, value):
def equal_modifier(self, field: str, value: DEFAULT_VALUE_TYPE) -> str:
if isinstance(value, list):
return f"({self.or_token.join([self.equal_modifier(field=field, value=v) for v in value])})"
return f"{field} = '{value}'"

def contains_modifier(self, field, value):
def less_modifier(self, field: str, value: Union[int, str]) -> str:
return f"{field} < '{value}'"

def less_or_equal_modifier(self, field: str, value: Union[int, str]) -> str:
return f"{field} <= '{value}'"

def greater_modifier(self, field: str, value: Union[int, str]) -> str:
return f"{field} > '{value}'"

def greater_or_equal_modifier(self, field: str, value: Union[int, str]) -> str:
return f"{field} >= '{value}'"

def not_equal_modifier(self, field: str, value: DEFAULT_VALUE_TYPE) -> str:
if isinstance(value, list):
return f"({self.or_token.join([self.not_equal_modifier(field=field, value=v) for v in value])})"
return f"{field} != '{value}'"

def contains_modifier(self, field: str, value: DEFAULT_VALUE_TYPE) -> str:
if isinstance(value, list):
return f"({self.or_token.join(self.contains_modifier(field=field, value=v) for v in value)})"
return f"{field} ILIKE '%{value}%' ESCAPE '\\'"

def endswith_modifier(self, field, value):
def endswith_modifier(self, field: str, value: DEFAULT_VALUE_TYPE) -> str:
if isinstance(value, list):
return f"({self.or_token.join(self.endswith_modifier(field=field, value=v) for v in value)})"
return f"{field} ILIKE '%{value}' ESCAPE '\\'"

def startswith_modifier(self, field, value):
def startswith_modifier(self, field: str, value: DEFAULT_VALUE_TYPE) -> str:
if isinstance(value, list):
return f"({self.or_token.join(self.startswith_modifier(field=field, value=v) for v in value)})"
return f"{field} ILIKE '{value}%' ESCAPE '\\'"

def regex_modifier(self, field, value):
def regex_modifier(self, field: str, value: DEFAULT_VALUE_TYPE) -> str:
if isinstance(value, list):
return f"({self.or_token.join(self.regex_modifier(field=field, value=v) for v in value)})"
return f"{field} ILIKE '{value}' ESCAPE '\\'"

def keywords(self, field, value):
def keywords(self, field: str, value: DEFAULT_VALUE_TYPE) -> str:
raise UnsupportedRenderMethod(platform_name=self.details.name, method="Keywords")


Expand Down
4 changes: 2 additions & 2 deletions translator/app/translator/platforms/athena/tokenizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

class AthenaTokenizer(QueryTokenizer):
field_pattern = r'(?P<field_name>"[a-zA-Z\._\-\s]+"|[a-zA-Z\._\-]+)'
match_operator_pattern = r"""(?:___field___\s?(?P<match_operator>like|in|=|>|<|>=|<=|<>|!=))\s?"""
match_operator_pattern = r"""(?:___field___\s?(?P<match_operator>like|in|<=|>=|==|>|<|<>|!=|=))\s?"""
num_value_pattern = r"(?P<num_value>\d+(?:\.\d+)*)\s*"
bool_value_pattern = r"(?P<bool_value>true|false)\s*"
single_quotes_value_pattern = r"""'(?P<s_q_value>(?:[:a-zA-Z\*0-9=+%#\-\/\\,_".$&^@!\(\)\{\}\s]|'')*)'"""
Expand Down Expand Up @@ -66,7 +66,7 @@ def search_field_value(self, query):
should_process_value_wildcard_symbols = self.should_process_value_wildcard_symbols(operator)
query, operator, value = self.search_value(query=query, operator=operator, field_name=field_name)

operator_token = Identifier(token_type=OperatorType.EQ)
operator_token = Identifier(token_type=operator)
if should_process_value_wildcard_symbols:
value, operator_token = self.process_value_wildcard_symbols(
value=value,
Expand Down
11 changes: 11 additions & 0 deletions translator/app/translator/platforms/base/lucene/const.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

COMPARISON_OPERATORS_MAP = {
":[* TO": {
"replace": [":\[\*\sTO"],
"default_op": "<="
},
":[": {
"replace": [":\[", "TO\s\*"],
"default_op": ">="
},
}
31 changes: 25 additions & 6 deletions translator/app/translator/platforms/base/lucene/renders/lucene.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"""
from typing import Union

from app.translator.const import DEFAULT_VALUE_TYPE
from app.translator.core.render import BaseQueryRender
from app.translator.core.render import BaseQueryFieldValue

Expand All @@ -27,39 +28,57 @@ class LuceneFieldValue(BaseQueryFieldValue):
def apply_value(self, value: Union[str, int]):
return value

def equal_modifier(self, field, value):
def equal_modifier(self, field: str, value: DEFAULT_VALUE_TYPE) -> str:
if isinstance(value, list):
values = self.or_token.join(self.apply_value(f'{v}') for v in value)
return f"{field}:({values})"
return f'{field}:{self.apply_value(value)}'

def contains_modifier(self, field, value):
def less_modifier(self, field: str, value: Union[int, str]) -> str:
return f'{field}:<{self.apply_value(value)}'

def less_or_equal_modifier(self, field: str, value: Union[int, str]) -> str:
return f'{field}:[* TO {self.apply_value(value)}]'

def greater_modifier(self, field: str, value: Union[int, str]) -> str:
return f'{field}:>{self.apply_value(value)}'

def greater_or_equal_modifier(self, field: str, value: Union[int, str]) -> str:
return f'{field}:[{self.apply_value(value)} TO *]'

def not_equal_modifier(self, field: str, value: DEFAULT_VALUE_TYPE) -> str:
if isinstance(value, list):
values = self.or_token.join(self.apply_value(f'{v}') for v in value)
return f"NOT ({field} = ({values})"
return f'NOT ({field} = {self.apply_value(value)})'

def contains_modifier(self, field: str, value: DEFAULT_VALUE_TYPE) -> str:
if isinstance(value, list):
values = self.or_token.join(self.apply_value(f'*{v}*') for v in value)
return f"{field}:({values})"
prepared_value = self.apply_value(f"*{value}*")
return f'{field}:{prepared_value}'

def endswith_modifier(self, field, value):
def endswith_modifier(self, field: str, value: DEFAULT_VALUE_TYPE) -> str:
if isinstance(value, list):
values = self.or_token.join(self.apply_value(f'*{v}') for v in value)
return f"{field}:({values})"
prepared_value = self.apply_value(f"*{value}")
return f'{field}:{prepared_value}'

def startswith_modifier(self, field, value):
def startswith_modifier(self, field: str, value: DEFAULT_VALUE_TYPE) -> str:
if isinstance(value, list):
values = self.or_token.join(self.apply_value(f'{v}*') for v in value)
return f"{field}:({values})"
prepared_value = self.apply_value(f"{value}*")
return f'{field}:{prepared_value}'

def regex_modifier(self, field, value):
def regex_modifier(self, field: str, value: DEFAULT_VALUE_TYPE) -> str:
if isinstance(value, list):
return f"({self.or_token.join(self.regex_modifier(field=field, value=v) for v in value)})"
return f'{field}:/{value}/'

def keywords(self, field, value):
def keywords(self, field: str, value: DEFAULT_VALUE_TYPE) -> str:
if isinstance(value, list):
return f"({self.or_token.join(self.keywords(field=field, value=v) for v in value)})"
return self.apply_value(f"*{value}*")
Expand Down
20 changes: 16 additions & 4 deletions translator/app/translator/platforms/base/lucene/tokenizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@
from app.translator.core.tokenizer import QueryTokenizer
from app.translator.core.custom_types.tokens import OperatorType
from app.translator.tools.utils import get_match_group
from app.translator.platforms.base.lucene.const import COMPARISON_OPERATORS_MAP


class LuceneTokenizer(QueryTokenizer, ANDLogicOperatorMixin):
field_pattern = r"(?P<field_name>[a-zA-Z\.\-_]+)"
match_operator_pattern = r"(?:___field___\s*(?P<match_operator>:))\s*"

match_operator_pattern = r"(?:___field___\s*(?P<match_operator>:\[\*\sTO|:\[|:<|:>|:))\s*"
num_value_pattern = r"(?P<num_value>\d+(?:\.\d+)*)\s*"
double_quotes_value_pattern = r'"(?P<d_q_value>(?:[:a-zA-Z\*0-9=+%#\-_/,\'\.$&^@!\(\)\{\}\s]|\\\"|\\)*)"\s*'
no_quotes_value_pattern = r"(?P<n_q_value>(?:[a-zA-Z\*0-9=%#_/,\'\.$@]|\\\"|\\\\)+)\s*"
Expand All @@ -46,6 +46,8 @@ class LuceneTokenizer(QueryTokenizer, ANDLogicOperatorMixin):

operators_map = {
":": OperatorType.EQ,
":>": OperatorType.GT,
":<": OperatorType.LT
}

def __init__(self):
Expand Down Expand Up @@ -77,9 +79,11 @@ def get_operator_and_value(self, match: re.Match, operator: str = OperatorType.E
elif (d_q_value := get_match_group(match, group_name='d_q_value')) is not None:
return operator, d_q_value

return super().get_operator_and_value(match)
return super().get_operator_and_value(match, operator)

def search_value(self, query: str, operator: str, field_name: str) -> Tuple[str, str, Union[str, List[str]]]:
if operator in COMPARISON_OPERATORS_MAP.keys():
return self.search_value_gte_lte(query, operator, field_name)
check_pattern = self.multi_value_check_pattern
check_regex = check_pattern.replace('___field___', field_name).replace('___operator___', operator)
if re.match(check_regex, query):
Expand All @@ -96,11 +100,19 @@ def search_value(self, query: str, operator: str, field_name: str) -> Tuple[str,
if field_value_search is None:
raise TokenizerGeneralException(error=f"Value couldn't be found in query part: {query}")

operator, value = self.get_operator_and_value(field_value_search)
operator, value = self.get_operator_and_value(field_value_search, self.map_operator(operator))
value = [self.clean_quotes(v) for v in re.split(r"\s+OR\s+", value)] if is_multi else value
pos = field_value_search.end()
return query[pos:], operator, value

def search_value_gte_lte(self, query: str, operator: str, field_name: str) -> Tuple[str, str, Union[str, List[str]]]:
query_list = query.split("]")
to_replace = [v for val in COMPARISON_OPERATORS_MAP.values() for v in val["replace"]]
to_replace.append(field_name)
regex = re.compile('|'.join(to_replace))
value = re.sub(regex, '', query_list.pop(0))
return "".join(query_list), COMPARISON_OPERATORS_MAP.get(operator, {}).get("default_op"), value.strip()

def search_keyword(self, query: str) -> Tuple[Keyword, str]:
keyword_search = re.search(self.keyword_pattern, query)
_, value = self.get_operator_and_value(keyword_search)
Expand Down
31 changes: 25 additions & 6 deletions translator/app/translator/platforms/base/spl/renders/spl.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,39 +16,58 @@
limitations under the License.
-----------------------------------------------------------------
"""
from typing import Union

from app.translator.const import DEFAULT_VALUE_TYPE
from app.translator.core.exceptions.render import UnsupportedRenderMethod
from app.translator.core.render import BaseQueryRender, BaseQueryFieldValue


class SplFieldValue(BaseQueryFieldValue):

def equal_modifier(self, field, value):
def equal_modifier(self, field: str, value: DEFAULT_VALUE_TYPE) -> str:
if isinstance(value, list):
return f"({self.or_token.join([self.equal_modifier(field=field, value=v) for v in value])})"
return f'{field}="{value}"'

def contains_modifier(self, field, value):
def less_modifier(self, field: str, value: Union[int, str]) -> str:
return f'{field}<"{value}"'

def less_or_equal_modifier(self, field: str, value: Union[int, str]) -> str:
return f'{field}<="{value}"'

def greater_modifier(self, field: str, value: Union[int, str]) -> str:
return f'{field}>"{value}"'

def greater_or_equal_modifier(self, field: str, value: Union[int, str]) -> str:
return f'{field}>="{value}"'

def not_equal_modifier(self, field: str, value: DEFAULT_VALUE_TYPE) -> str:
if isinstance(value, list):
return f"({self.or_token.join([self.not_equal_modifier(field=field, value=v) for v in value])})"
return f'{field}!="{value}"'

def contains_modifier(self, field: str, value: DEFAULT_VALUE_TYPE) -> str:
if isinstance(value, list):
return f"({self.or_token.join([self.contains_modifier(field=field, value=v) for v in value])})"
return f'{field}="*{value}*"'

def endswith_modifier(self, field, value):
def endswith_modifier(self, field: str, value: DEFAULT_VALUE_TYPE) -> str:
if isinstance(value, list):
return f"({self.or_token.join([self.endswith_modifier(field=field, value=v) for v in value])})"
return f'{field}="*{value}"'

def startswith_modifier(self, field, value):
def startswith_modifier(self, field: str, value: DEFAULT_VALUE_TYPE) -> str:
if isinstance(value, list):
return f"({self.or_token.join([self.startswith_modifier(field=field, value=v) for v in value])})"
return f'{field}="{value}*"'

def keywords(self, field, value):
def keywords(self, field: str, value: DEFAULT_VALUE_TYPE) -> str:
if isinstance(value, list):
return f"({self.or_token.join(self.keywords(field=field, value=v) for v in value)})"
return f'"{value}"'

def regex_modifier(self, field, value):
def regex_modifier(self, field: str, value: DEFAULT_VALUE_TYPE) -> str:
raise UnsupportedRenderMethod(platform_name=self.details.name, method="Regex Expression")


Expand Down
Loading
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy