зеркало из https://github.com/mozilla/TUID.git
lib updates
This commit is contained in:
Родитель
2ccf177fde
Коммит
5f82c7a06c
|
@ -5,7 +5,7 @@
|
|||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Author: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
|
@ -15,12 +15,16 @@ from jx_base.expressions import jx_expression
|
|||
from jx_python.expressions import Literal, Python
|
||||
from mo_dots import coalesce, listwrap, wrap
|
||||
from mo_dots.datas import register_data
|
||||
from mo_future import is_text, text_type
|
||||
from mo_json import value2json
|
||||
from mo_dots.lists import last
|
||||
from mo_future import is_text, text
|
||||
from mo_json import value2json, true, false, null
|
||||
from mo_logs import Log
|
||||
from mo_logs.strings import expand_template, quote
|
||||
|
||||
|
||||
ENABLE_CONSTRAINTS = True
|
||||
|
||||
|
||||
def generateGuid():
|
||||
"""Gets a random GUID.
|
||||
Note: python's UUID generation library is used here.
|
||||
|
@ -33,7 +37,7 @@ def generateGuid():
|
|||
print(a)
|
||||
print(uuid.UUID(a).hex)
|
||||
"""
|
||||
return text_type(uuid4())
|
||||
return text(uuid4())
|
||||
|
||||
|
||||
def _exec(code, name):
|
||||
|
@ -47,7 +51,7 @@ def _exec(code, name):
|
|||
Log.error("Can not make class\n{{code}}", code=code, cause=e)
|
||||
|
||||
|
||||
_ = listwrap
|
||||
_ = listwrap, last, true, false, null
|
||||
|
||||
|
||||
def DataClass(name, columns, constraint=None):
|
||||
|
@ -104,10 +108,16 @@ class {{class_name}}(Mapping):
|
|||
|
||||
|
||||
def _constraint(row, rownum, rows):
|
||||
try:
|
||||
return {{constraint_expr}}
|
||||
except Exception as e:
|
||||
return False
|
||||
code = {{constraint_expr|quote}}
|
||||
if {{constraint_expr}}:
|
||||
return
|
||||
Log.error(
|
||||
"constraint\\n{" + "{code}}\\nnot satisfied {" + "{expect}}\\n{" + "{value|indent}}",
|
||||
code={{constraint_expr|quote}},
|
||||
expect={{constraint}},
|
||||
value=row,
|
||||
cause=e
|
||||
)
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
if not kwargs:
|
||||
|
@ -124,8 +134,7 @@ class {{class_name}}(Mapping):
|
|||
if illegal:
|
||||
Log.error("{"+"{names}} are not a valid properties", names=illegal)
|
||||
|
||||
if not self._constraint(0, [self]):
|
||||
Log.error("constraint not satisfied {"+"{expect}}\\n{"+"{value|indent}}", expect={{constraint}}, value=self)
|
||||
self._constraint(0, [self])
|
||||
|
||||
def __getitem__(self, item):
|
||||
return getattr(self, item)
|
||||
|
@ -137,9 +146,12 @@ class {{class_name}}(Mapping):
|
|||
def __setattr__(self, item, value):
|
||||
if item not in {{slots}}:
|
||||
Log.error("{"+"{item|quote}} not valid attribute", item=item)
|
||||
|
||||
if value==None and item in {{required}}:
|
||||
Log.error("Expecting property {"+"{item}}", item=item)
|
||||
|
||||
object.__setattr__(self, item, value)
|
||||
if not self._constraint(0, [self]):
|
||||
Log.error("constraint not satisfied {"+"{expect}}\\n{"+"{value|indent}}", expect={{constraint}}, value=self)
|
||||
self._constraint(0, [self])
|
||||
|
||||
def __getattr__(self, item):
|
||||
Log.error("{"+"{item|quote}} not valid attribute", item=item)
|
||||
|
@ -188,7 +200,7 @@ class {{class_name}}(Mapping):
|
|||
"types": "{"
|
||||
+ (",".join(quote(k) + ": " + v.__name__ for k, v in types.items()))
|
||||
+ "}",
|
||||
"constraint_expr": Python[jx_expression(constraint)].to_python(),
|
||||
"constraint_expr": Python[jx_expression(not ENABLE_CONSTRAINTS or constraint)].to_python(),
|
||||
"constraint": value2json(constraint),
|
||||
},
|
||||
)
|
||||
|
@ -200,16 +212,8 @@ class {{class_name}}(Mapping):
|
|||
|
||||
TableDesc = DataClass(
|
||||
"Table",
|
||||
[
|
||||
"name",
|
||||
"url",
|
||||
"query_path",
|
||||
{"name": "last_updated", "nulls": False},
|
||||
"columns"
|
||||
],
|
||||
constraint={"and": [
|
||||
{"eq": [{"last": "query_path"}, {"literal": "."}]}
|
||||
]}
|
||||
["name", "url", "query_path", {"name": "last_updated", "nulls": False}, "columns"],
|
||||
constraint={"and": [{"eq": [{"last": "query_path"}, {"literal": "."}]}]},
|
||||
)
|
||||
|
||||
|
||||
|
@ -225,7 +229,7 @@ Column = DataClass(
|
|||
"nested_path", # AN ARRAY OF PATHS (FROM DEEPEST TO SHALLOWEST) INDICATING THE JSON SUB-ARRAYS
|
||||
{"name": "count", "nulls": True},
|
||||
{"name": "cardinality", "nulls": True},
|
||||
{"name": "multi", "nulls": True},
|
||||
{"name": "multi", "nulls": False},
|
||||
{"name": "partitions", "nulls": True},
|
||||
"last_updated",
|
||||
],
|
||||
|
@ -235,14 +239,24 @@ Column = DataClass(
|
|||
{"not": {"eq": {"es_column": "string"}}},
|
||||
{"not": {"eq": {"es_type": "object", "jx_type": "exists"}}},
|
||||
{"eq": [{"last": "nested_path"}, {"literal": "."}]},
|
||||
{
|
||||
"when": {"eq": [{"literal": ".~N~"}, {"right": {"es_column": 4}}]},
|
||||
"then": {"gt": {"multi": 1}},
|
||||
"else": True,
|
||||
},
|
||||
{
|
||||
"when": {"gte": [{"count": "nested_path"}, 2]},
|
||||
"then": {"ne": [{"first": {"right": {"nested_path": 2}}}, {"literal": "."}]}, # SECOND-LAST ELEMENT
|
||||
"else": True
|
||||
}
|
||||
]
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
from jx_base.container import Container
|
||||
from jx_base.namespace import Namespace
|
||||
from jx_base.facts import Facts
|
||||
from jx_base.snowflake import Snowflake
|
||||
from jx_base.table import Table
|
||||
from jx_base.schema import Schema
|
||||
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Author: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
|
@ -42,7 +42,7 @@ def _delayed_imports():
|
|||
|
||||
class Container(object):
|
||||
"""
|
||||
CONTAINERS HOLD MULTIPLE FACTS AND CAN HANDLE
|
||||
CONTAINERS HOLD MULTIPLE INDICES AND CAN HANDLE
|
||||
GENERAL JSON QUERY EXPRESSIONS ON ITS CONTENTS
|
||||
METADATA FOR A Container IS CALLED A Namespace
|
||||
"""
|
||||
|
@ -114,10 +114,6 @@ class Container(object):
|
|||
def window(self, window):
|
||||
raise NotImplementedError()
|
||||
|
||||
def having(self, having):
|
||||
_ = having
|
||||
raise NotImplementedError()
|
||||
|
||||
def format(self, format):
|
||||
_ = format
|
||||
raise NotImplementedError()
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
# License, v. 2.0. If a copy of the MPL was not distributed with self file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Author: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
# License, v. 2.0. If a copy of the MPL was not distributed with self file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Author: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
|
@ -15,7 +15,7 @@ from numbers import Number
|
|||
from jx_base.expressions import jx_expression
|
||||
from mo_collections.unique_index import UniqueIndex
|
||||
from mo_dots import Data, FlatList, Null, coalesce, is_container, is_data, listwrap, set_default, unwrap, wrap
|
||||
from mo_future import text_type
|
||||
from mo_future import text
|
||||
from mo_logs import Log
|
||||
from mo_math import MAX, MIN
|
||||
from mo_times.dates import Date
|
||||
|
@ -224,7 +224,7 @@ class SimpleSetDomain(Domain):
|
|||
if isinstance(self.key, set):
|
||||
Log.error("problem")
|
||||
|
||||
if not desc.key and (len(desc.partitions)==0 or isinstance(desc.partitions[0], (text_type, Number, tuple))):
|
||||
if not desc.key and (len(desc.partitions)==0 or isinstance(desc.partitions[0], (text, Number, tuple))):
|
||||
# ASSUME PARTS ARE STRINGS, CONVERT TO REAL PART OBJECTS
|
||||
self.key = "value"
|
||||
self.map = {}
|
||||
|
@ -235,7 +235,7 @@ class SimpleSetDomain(Domain):
|
|||
self.map[p] = part
|
||||
self.order[p] = i
|
||||
if isinstance(p, (int, float)):
|
||||
text_part = text_type(float(p)) # ES CAN NOT HANDLE NUMERIC PARTS
|
||||
text_part = text(float(p)) # ES CAN NOT HANDLE NUMERIC PARTS
|
||||
self.map[text_part] = part
|
||||
self.order[text_part] = i
|
||||
self.label = coalesce(self.label, "name")
|
||||
|
@ -378,7 +378,7 @@ class SetDomain(Domain):
|
|||
if isinstance(self.key, set):
|
||||
Log.error("problem")
|
||||
|
||||
if isinstance(desc.partitions[0], (int, float, text_type)):
|
||||
if isinstance(desc.partitions[0], (int, float, text)):
|
||||
# ASSMUE PARTS ARE STRINGS, CONVERT TO REAL PART OBJECTS
|
||||
self.key = "value"
|
||||
self.order[None] = len(desc.partitions)
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,169 @@
|
|||
from jx_base.expressions._utils import simplified, extend, jx_expression, merge_types, operators, language, _jx_expression
|
||||
from jx_base.expressions.abs_op import AbsOp
|
||||
from jx_base.expressions.add_op import AddOp
|
||||
from jx_base.expressions.and_op import AndOp
|
||||
from jx_base.expressions.base_binary_op import BaseBinaryOp
|
||||
from jx_base.expressions.base_inequality_op import BaseInequalityOp
|
||||
from jx_base.expressions.base_multi_op import BaseMultiOp
|
||||
from jx_base.expressions.basic_add_op import BasicAddOp
|
||||
from jx_base.expressions.basic_eq_op import BasicEqOp
|
||||
from jx_base.expressions.basic_index_of_op import BasicIndexOfOp
|
||||
from jx_base.expressions.basic_mul_op import BasicMulOp
|
||||
from jx_base.expressions.basic_multi_op import BasicMultiOp
|
||||
from jx_base.expressions.basic_starts_with_op import BasicStartsWithOp
|
||||
from jx_base.expressions.basic_substring_op import BasicSubstringOp
|
||||
from jx_base.expressions.between_op import BetweenOp
|
||||
from jx_base.expressions.boolean_op import BooleanOp
|
||||
from jx_base.expressions.case_op import CaseOp
|
||||
from jx_base.expressions.coalesce_op import CoalesceOp
|
||||
from jx_base.expressions.concat_op import ConcatOp
|
||||
from jx_base.expressions.count_op import CountOp
|
||||
from jx_base.expressions.date_op import DateOp
|
||||
from jx_base.expressions.div_op import DivOp
|
||||
from jx_base.expressions.eq_op import EqOp
|
||||
from jx_base.expressions.es_nested_op import EsNestedOp
|
||||
from jx_base.expressions.es_script import EsScript
|
||||
from jx_base.expressions.exists_op import ExistsOp
|
||||
from jx_base.expressions.exp_op import ExpOp
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.false_op import FalseOp, FALSE
|
||||
from jx_base.expressions.find_op import FindOp
|
||||
from jx_base.expressions.first_op import FirstOp
|
||||
from jx_base.expressions.floor_op import FloorOp
|
||||
from jx_base.expressions.from_unix_op import FromUnixOp
|
||||
from jx_base.expressions.get_op import GetOp
|
||||
from jx_base.expressions.gt_op import GtOp
|
||||
from jx_base.expressions.gte_op import GteOp
|
||||
from jx_base.expressions.in_op import InOp
|
||||
from jx_base.expressions.integer_op import IntegerOp
|
||||
from jx_base.expressions.is_boolean_op import IsBooleanOp
|
||||
from jx_base.expressions.is_integer_op import IsIntegerOp
|
||||
from jx_base.expressions.is_number_op import IsNumberOp
|
||||
from jx_base.expressions.is_string_op import IsStringOp
|
||||
from jx_base.expressions.last_op import LastOp
|
||||
from jx_base.expressions.leaves_op import LeavesOp
|
||||
from jx_base.expressions.left_op import LeftOp
|
||||
from jx_base.expressions.length_op import LengthOp
|
||||
from jx_base.expressions.literal import Literal, ONE, ZERO, register_literal, is_literal
|
||||
from jx_base.expressions.lt_op import LtOp
|
||||
from jx_base.expressions.lte_op import LteOp
|
||||
from jx_base.expressions.max_op import MaxOp
|
||||
from jx_base.expressions.min_op import MinOp
|
||||
from jx_base.expressions.missing_op import MissingOp
|
||||
from jx_base.expressions.mod_op import ModOp
|
||||
from jx_base.expressions.mul_op import MulOp
|
||||
from jx_base.expressions.ne_op import NeOp
|
||||
from jx_base.expressions.not_left_op import NotLeftOp
|
||||
from jx_base.expressions.not_op import NotOp
|
||||
from jx_base.expressions.not_right_op import NotRightOp
|
||||
from jx_base.expressions.null_op import NullOp, NULL
|
||||
from jx_base.expressions.number_op import NumberOp
|
||||
from jx_base.expressions.offset_op import OffsetOp
|
||||
from jx_base.expressions.or_op import OrOp
|
||||
from jx_base.expressions.prefix_op import PrefixOp
|
||||
from jx_base.expressions.python_script import PythonScript
|
||||
from jx_base.expressions.query_op import QueryOp
|
||||
from jx_base.expressions.range_op import RangeOp
|
||||
from jx_base.expressions.reg_exp_op import RegExpOp
|
||||
from jx_base.expressions.right_op import RightOp
|
||||
from jx_base.expressions.rows_op import RowsOp
|
||||
from jx_base.expressions.script_op import ScriptOp
|
||||
from jx_base.expressions.select_op import SelectOp
|
||||
from jx_base.expressions.split_op import SplitOp
|
||||
from jx_base.expressions.sql_eq_op import SqlEqOp
|
||||
from jx_base.expressions.sql_instr_op import SqlInstrOp
|
||||
from jx_base.expressions.sql_script import SQLScript
|
||||
from jx_base.expressions.sql_substr_op import SqlSubstrOp
|
||||
from jx_base.expressions.string_op import StringOp
|
||||
from jx_base.expressions.sub_op import SubOp
|
||||
from jx_base.expressions.suffix_op import SuffixOp
|
||||
from jx_base.expressions.true_op import TrueOp, TRUE
|
||||
from jx_base.expressions.tuple_op import TupleOp
|
||||
from jx_base.expressions.union_op import UnionOp
|
||||
from jx_base.expressions.unix_op import UnixOp
|
||||
from jx_base.expressions.variable import Variable, IDENTITY
|
||||
from jx_base.expressions.when_op import WhenOp
|
||||
from mo_dots import set_default
|
||||
|
||||
set_default(operators, {
|
||||
"abs": AbsOp,
|
||||
"add": AddOp,
|
||||
"and": AndOp,
|
||||
"basic.add": BasicAddOp,
|
||||
"basic.mul": BasicMulOp,
|
||||
"between": BetweenOp,
|
||||
"case": CaseOp,
|
||||
"coalesce": CoalesceOp,
|
||||
"concat": ConcatOp,
|
||||
"count": CountOp,
|
||||
"date": DateOp,
|
||||
"div": DivOp,
|
||||
"divide": DivOp,
|
||||
"eq": EqOp,
|
||||
"exists": ExistsOp,
|
||||
"exp": ExpOp,
|
||||
"find": FindOp,
|
||||
"first": FirstOp,
|
||||
"floor": FloorOp,
|
||||
"from_unix": FromUnixOp,
|
||||
"get": GetOp,
|
||||
"gt": GtOp,
|
||||
"gte": GteOp,
|
||||
"in": InOp,
|
||||
"instr": FindOp,
|
||||
"is_number": IsNumberOp,
|
||||
"is_string": IsStringOp,
|
||||
"last": LastOp,
|
||||
"left": LeftOp,
|
||||
"length": LengthOp,
|
||||
"literal": Literal,
|
||||
"lt": LtOp,
|
||||
"lte": LteOp,
|
||||
"match_all": TrueOp,
|
||||
"max": MaxOp,
|
||||
"minus": SubOp,
|
||||
"missing": MissingOp,
|
||||
"mod": ModOp,
|
||||
"mul": MulOp,
|
||||
"mult": MulOp,
|
||||
"multiply": MulOp,
|
||||
"ne": NeOp,
|
||||
"neq": NeOp,
|
||||
"not": NotOp,
|
||||
"not_left": NotLeftOp,
|
||||
"not_right": NotRightOp,
|
||||
"null": NullOp,
|
||||
"number": NumberOp,
|
||||
"offset": OffsetOp,
|
||||
"or": OrOp,
|
||||
"postfix": SuffixOp,
|
||||
"prefix": PrefixOp,
|
||||
"range": RangeOp,
|
||||
"regex": RegExpOp,
|
||||
"regexp": RegExpOp,
|
||||
"right": RightOp,
|
||||
"rows": RowsOp,
|
||||
"script": ScriptOp,
|
||||
"select": SelectOp,
|
||||
"split": SplitOp,
|
||||
"string": StringOp,
|
||||
"suffix": SuffixOp,
|
||||
"sub": SubOp,
|
||||
"subtract": SubOp,
|
||||
"sum": AddOp,
|
||||
"term": EqOp,
|
||||
"terms": InOp,
|
||||
"tuple": TupleOp,
|
||||
"union": UnionOp,
|
||||
"unix": UnixOp,
|
||||
"when": WhenOp,
|
||||
})
|
||||
|
||||
language.register_ops(vars())
|
||||
|
||||
register_literal(NullOp)
|
||||
register_literal(FalseOp)
|
||||
register_literal(TrueOp)
|
||||
register_literal(DateOp)
|
||||
register_literal(Literal)
|
||||
|
|
@ -0,0 +1,180 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
import operator
|
||||
|
||||
from jx_base.language import is_expression, Language
|
||||
from mo_dots import Null, is_sequence
|
||||
from mo_future import (
|
||||
first,
|
||||
get_function_name,
|
||||
is_text,
|
||||
items as items_,
|
||||
text,
|
||||
utf8_json_encoder,
|
||||
)
|
||||
from mo_json import BOOLEAN, INTEGER, IS_NULL, NUMBER, OBJECT, STRING, scrub
|
||||
from mo_logs import Except, Log
|
||||
from mo_math import is_number
|
||||
from mo_times import Date
|
||||
|
||||
ALLOW_SCRIPTING = False
|
||||
EMPTY_DICT = {}
|
||||
|
||||
Literal, TRUE, NULL, TupleOp, Variable = [None] * 5
|
||||
|
||||
def extend(cls):
|
||||
"""
|
||||
DECORATOR TO ADD METHODS TO CLASSES
|
||||
:param cls: THE CLASS TO ADD THE METHOD TO
|
||||
:return:
|
||||
"""
|
||||
|
||||
def extender(func):
|
||||
setattr(cls, get_function_name(func), func)
|
||||
return func
|
||||
|
||||
return extender
|
||||
|
||||
|
||||
def simplified(func):
|
||||
def mark_as_simple(self):
|
||||
if self.simplified:
|
||||
return self
|
||||
|
||||
output = func(self)
|
||||
output.simplified = True
|
||||
return output
|
||||
|
||||
func_name = get_function_name(func)
|
||||
mark_as_simple.__name__ = func_name
|
||||
return mark_as_simple
|
||||
|
||||
|
||||
def jx_expression(expr, schema=None):
|
||||
if expr == None:
|
||||
return None
|
||||
|
||||
# UPDATE THE VARIABLE WITH THIER KNOWN TYPES
|
||||
if not schema:
|
||||
output = _jx_expression(expr, language)
|
||||
return output
|
||||
output = _jx_expression(expr, language)
|
||||
for v in output.vars():
|
||||
leaves = schema.leaves(v.var)
|
||||
if len(leaves) == 0:
|
||||
v.data_type = IS_NULL
|
||||
if len(leaves) == 1:
|
||||
v.data_type = first(leaves).jx_type
|
||||
return output
|
||||
|
||||
|
||||
def _jx_expression(expr, lang):
|
||||
"""
|
||||
WRAP A JSON EXPRESSION WITH OBJECT REPRESENTATION
|
||||
"""
|
||||
if is_expression(expr):
|
||||
# CONVERT TO lang
|
||||
new_op = lang[expr]
|
||||
if not new_op:
|
||||
# CAN NOT BE FOUND, TRY SOME PARTIAL EVAL
|
||||
return language[expr.get_id()].partial_eval()
|
||||
return expr
|
||||
# return new_op(expr.args) # THIS CAN BE DONE, BUT IT NEEDS MORE CODING, AND I WOULD EXPECT IT TO BE SLOW
|
||||
|
||||
if expr is None:
|
||||
return TRUE
|
||||
elif is_text(expr):
|
||||
return Variable(expr)
|
||||
elif expr in (True, False, None) or expr == None or is_number(expr):
|
||||
return Literal(expr)
|
||||
elif expr.__class__ is Date:
|
||||
return Literal(expr.unix)
|
||||
elif is_sequence(expr):
|
||||
return lang[TupleOp([_jx_expression(e, lang) for e in expr])]
|
||||
|
||||
# expr = wrap(expr)
|
||||
try:
|
||||
items = items_(expr)
|
||||
|
||||
for op, term in items:
|
||||
# ONE OF THESE IS THE OPERATOR
|
||||
full_op = operators.get(op)
|
||||
if full_op:
|
||||
class_ = lang.ops[full_op.get_id()]
|
||||
if class_:
|
||||
return class_.define(expr)
|
||||
|
||||
# THIS LANGUAGE DOES NOT SUPPORT THIS OPERATOR, GOTO BASE LANGUAGE AND GET THE MACRO
|
||||
class_ = language[op.get_id()]
|
||||
output = class_.define(expr).partial_eval()
|
||||
return _jx_expression(output, lang)
|
||||
else:
|
||||
if not items:
|
||||
return NULL
|
||||
raise Log.error("{{instruction|json}} is not known", instruction=expr)
|
||||
|
||||
except Exception as e:
|
||||
Log.error("programmer error expr = {{value|quote}}", value=expr, cause=e)
|
||||
|
||||
|
||||
language = Language(None)
|
||||
|
||||
|
||||
_json_encoder = utf8_json_encoder
|
||||
|
||||
|
||||
def value2json(value):
|
||||
try:
|
||||
scrubbed = scrub(value, scrub_number=float)
|
||||
return text(_json_encoder(scrubbed))
|
||||
except Exception as e:
|
||||
e = Except.wrap(e)
|
||||
Log.warning("problem serializing {{type}}", type=text(repr(value)), cause=e)
|
||||
raise e
|
||||
|
||||
|
||||
def merge_types(jx_types):
|
||||
"""
|
||||
:param jx_types: ITERABLE OF jx TYPES
|
||||
:return: ONE TYPE TO RULE THEM ALL
|
||||
"""
|
||||
return _merge_types[max(_merge_score[t] for t in jx_types)]
|
||||
|
||||
|
||||
_merge_score = {IS_NULL: 0, BOOLEAN: 1, INTEGER: 2, NUMBER: 3, STRING: 4, OBJECT: 5}
|
||||
_merge_types = {v: k for k, v in _merge_score.items()}
|
||||
|
||||
builtin_ops = {
|
||||
"ne": operator.ne,
|
||||
"eq": operator.eq,
|
||||
"gte": operator.ge,
|
||||
"gt": operator.gt,
|
||||
"lte": operator.le,
|
||||
"lt": operator.lt,
|
||||
"add": operator.add,
|
||||
"sub": operator.sub,
|
||||
"mul": operator.mul,
|
||||
"max": lambda *v: max(v),
|
||||
"min": lambda *v: min(v),
|
||||
}
|
||||
|
||||
operators = {}
|
|
@ -0,0 +1,54 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions._utils import simplified
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.language import is_op
|
||||
from mo_json import NUMBER
|
||||
|
||||
|
||||
class AbsOp(Expression):
|
||||
data_type = NUMBER
|
||||
|
||||
def __init__(self, term):
|
||||
Expression.__init__(self, term)
|
||||
self.term = term
|
||||
|
||||
def __data__(self):
|
||||
return {"abs": self.term.__data__()}
|
||||
|
||||
def __eq__(self, other):
|
||||
if not is_op(other, AbsOp):
|
||||
return False
|
||||
return self.term == other.term
|
||||
|
||||
def vars(self):
|
||||
return self.term.vars()
|
||||
|
||||
def map(self, map_):
|
||||
return self.lang[AbsOp(self.term.map(map_))]
|
||||
|
||||
def missing(self):
|
||||
return self.term.missing()
|
||||
|
||||
@simplified
|
||||
def partial_eval(self):
|
||||
return AbsOp(self.term.partial_eval())
|
|
@ -0,0 +1,26 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions.base_multi_op import BaseMultiOp
|
||||
|
||||
|
||||
class AddOp(BaseMultiOp):
|
||||
op = "add"
|
|
@ -0,0 +1,115 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions._utils import simplified
|
||||
from jx_base.expressions.boolean_op import BooleanOp
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.false_op import FALSE
|
||||
from jx_base.expressions.true_op import TRUE
|
||||
from jx_base.language import is_op
|
||||
from mo_dots import is_many
|
||||
from mo_future import zip_longest
|
||||
from mo_json import BOOLEAN
|
||||
|
||||
NotOp = None
|
||||
OrOp = None
|
||||
|
||||
class AndOp(Expression):
|
||||
data_type = BOOLEAN
|
||||
|
||||
def __init__(self, terms):
|
||||
Expression.__init__(self, terms)
|
||||
if terms == None:
|
||||
self.terms = []
|
||||
elif is_many(terms):
|
||||
self.terms = terms
|
||||
else:
|
||||
self.terms = [terms]
|
||||
|
||||
def __data__(self):
|
||||
return {"and": [t.__data__() for t in self.terms]}
|
||||
|
||||
def __eq__(self, other):
|
||||
if is_op(other, AndOp):
|
||||
return all(a == b for a, b in zip_longest(self.terms, other.terms))
|
||||
return False
|
||||
|
||||
def vars(self):
|
||||
output = set()
|
||||
for t in self.terms:
|
||||
output |= t.vars()
|
||||
return output
|
||||
|
||||
def map(self, map_):
|
||||
return self.lang[AndOp([t.map(map_) for t in self.terms])]
|
||||
|
||||
def missing(self):
|
||||
return FALSE
|
||||
|
||||
@simplified
|
||||
def partial_eval(self):
|
||||
or_terms = [[]] # LIST OF TUPLES FOR or-ing and and-ing
|
||||
for i, t in enumerate(self.terms):
|
||||
simple = self.lang[BooleanOp(t)].partial_eval()
|
||||
if simple.type != BOOLEAN:
|
||||
simple = simple.exists()
|
||||
|
||||
if simple is self.lang[TRUE]:
|
||||
continue
|
||||
elif simple is FALSE:
|
||||
return FALSE
|
||||
elif is_op(simple, AndOp):
|
||||
for and_terms in or_terms:
|
||||
and_terms.extend([tt for tt in simple.terms if tt not in and_terms])
|
||||
continue
|
||||
elif is_op(simple, OrOp):
|
||||
or_terms = [
|
||||
and_terms + ([o] if o not in and_terms else [])
|
||||
for o in simple.terms
|
||||
for and_terms in or_terms
|
||||
if self.lang[NotOp(o)].partial_eval() not in and_terms
|
||||
]
|
||||
continue
|
||||
for and_terms in list(or_terms):
|
||||
if self.lang[NotOp(simple)].partial_eval() in and_terms:
|
||||
or_terms.remove(and_terms)
|
||||
elif simple not in and_terms:
|
||||
and_terms.append(simple)
|
||||
|
||||
if len(or_terms) == 0:
|
||||
return FALSE
|
||||
elif len(or_terms) == 1:
|
||||
and_terms = or_terms[0]
|
||||
if len(and_terms) == 0:
|
||||
return TRUE
|
||||
elif len(and_terms) == 1:
|
||||
return and_terms[0]
|
||||
else:
|
||||
return self.lang[AndOp(and_terms)]
|
||||
|
||||
return self.lang[
|
||||
OrOp(
|
||||
[
|
||||
AndOp(and_terms) if len(and_terms) > 1 else and_terms[0]
|
||||
for and_terms in or_terms
|
||||
]
|
||||
)
|
||||
].partial_eval()
|
|
@ -0,0 +1,78 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions._utils import builtin_ops, simplified
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.false_op import FALSE
|
||||
from jx_base.expressions.literal import Literal
|
||||
from jx_base.expressions.literal import is_literal
|
||||
from jx_base.expressions.null_op import NULL
|
||||
from jx_base.expressions.or_op import OrOp
|
||||
from jx_base.expressions.variable import Variable
|
||||
from jx_base.language import is_op
|
||||
from mo_json import NUMBER
|
||||
|
||||
|
||||
class BaseBinaryOp(Expression):
|
||||
has_simple_form = True
|
||||
data_type = NUMBER
|
||||
op = None
|
||||
|
||||
def __init__(self, terms, default=NULL):
|
||||
Expression.__init__(self, terms)
|
||||
self.lhs, self.rhs = terms
|
||||
self.default = default
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self.op
|
||||
|
||||
def __data__(self):
|
||||
if is_op(self.lhs, Variable) and is_literal(self.rhs):
|
||||
return {self.op: {self.lhs.var, self.rhs.value}, "default": self.default}
|
||||
else:
|
||||
return {
|
||||
self.op: [self.lhs.__data__(), self.rhs.__data__()],
|
||||
"default": self.default,
|
||||
}
|
||||
|
||||
def vars(self):
|
||||
return self.lhs.vars() | self.rhs.vars() | self.default.vars()
|
||||
|
||||
def map(self, map_):
|
||||
return self.__class__(
|
||||
[self.lhs.map(map_), self.rhs.map(map_)], default=self.default.map(map_)
|
||||
)
|
||||
|
||||
def missing(self):
|
||||
if self.default.exists():
|
||||
return FALSE
|
||||
else:
|
||||
return self.lang[OrOp([self.lhs.missing(), self.rhs.missing()])]
|
||||
|
||||
@simplified
|
||||
def partial_eval(self):
|
||||
lhs = self.lhs.partial_eval()
|
||||
rhs = self.rhs.partial_eval()
|
||||
default = self.default.partial_eval()
|
||||
if is_literal(lhs) and is_literal(rhs):
|
||||
return Literal(builtin_ops[self.op](lhs.value, rhs.value))
|
||||
return self.__class__([lhs, rhs], default=default)
|
|
@ -0,0 +1,73 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions._utils import builtin_ops, simplified
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.false_op import FALSE
|
||||
from jx_base.expressions.literal import Literal
|
||||
from jx_base.expressions.literal import is_literal
|
||||
from jx_base.expressions.variable import Variable
|
||||
from jx_base.language import is_op
|
||||
from mo_json import BOOLEAN
|
||||
|
||||
|
||||
class BaseInequalityOp(Expression):
|
||||
has_simple_form = True
|
||||
data_type = BOOLEAN
|
||||
op = None
|
||||
|
||||
def __init__(self, terms):
|
||||
Expression.__init__(self, terms)
|
||||
self.lhs, self.rhs = terms
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self.op
|
||||
|
||||
def __data__(self):
|
||||
if is_op(self.lhs, Variable) and is_literal(self.rhs):
|
||||
return {self.op: {self.lhs.var, self.rhs.value}}
|
||||
else:
|
||||
return {self.op: [self.lhs.__data__(), self.rhs.__data__()]}
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, self.__class__):
|
||||
return False
|
||||
return self.op == other.op and self.lhs == other.lhs and self.rhs == other.rhs
|
||||
|
||||
def vars(self):
|
||||
return self.lhs.vars() | self.rhs.vars()
|
||||
|
||||
def map(self, map_):
|
||||
return self.__class__([self.lhs.map(map_), self.rhs.map(map_)])
|
||||
|
||||
def missing(self):
|
||||
return FALSE
|
||||
|
||||
@simplified
|
||||
def partial_eval(self):
|
||||
lhs = self.lhs.partial_eval()
|
||||
rhs = self.rhs.partial_eval()
|
||||
|
||||
if is_literal(lhs) and is_literal(rhs):
|
||||
return Literal(builtin_ops[self.op](lhs, rhs))
|
||||
|
||||
return self.__class__([lhs, rhs])
|
|
@ -0,0 +1,142 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions._utils import simplified, builtin_ops, operators
|
||||
from jx_base.expressions.and_op import AndOp
|
||||
from jx_base.expressions.coalesce_op import CoalesceOp
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.false_op import FALSE
|
||||
from jx_base.expressions.literal import Literal, ZERO, ONE, is_literal
|
||||
from jx_base.expressions.null_op import NULL
|
||||
from jx_base.expressions.or_op import OrOp
|
||||
from jx_base.expressions.true_op import TRUE
|
||||
from jx_base.expressions.when_op import WhenOp
|
||||
from mo_dots import coalesce
|
||||
from mo_json import NUMBER
|
||||
|
||||
|
||||
class BaseMultiOp(Expression):
|
||||
has_simple_form = True
|
||||
data_type = NUMBER
|
||||
op = None
|
||||
|
||||
def __init__(self, terms, **clauses):
|
||||
Expression.__init__(self, terms)
|
||||
self.terms = terms
|
||||
self.default = coalesce(clauses.get("default"), NULL)
|
||||
self.nulls = coalesce(
|
||||
clauses.get("nulls"), FALSE
|
||||
) # nulls==True WILL HAVE OP RETURN null ONLY IF ALL OPERANDS ARE null
|
||||
|
||||
def __data__(self):
|
||||
return {
|
||||
self.op: [t.__data__() for t in self.terms],
|
||||
"default": self.default,
|
||||
"nulls": self.nulls,
|
||||
}
|
||||
|
||||
def vars(self):
|
||||
output = set()
|
||||
for t in self.terms:
|
||||
output |= t.vars()
|
||||
return output
|
||||
|
||||
def map(self, map_):
|
||||
return self.__class__(
|
||||
[t.map(map_) for t in self.terms],
|
||||
**{"default": self.default, "nulls": self.nulls}
|
||||
)
|
||||
|
||||
def missing(self):
|
||||
if self.nulls:
|
||||
if self.default is NULL:
|
||||
return self.lang[AndOp([t.missing() for t in self.terms])]
|
||||
else:
|
||||
return TRUE
|
||||
else:
|
||||
if self.default is NULL:
|
||||
return self.lang[OrOp([t.missing() for t in self.terms])]
|
||||
else:
|
||||
return FALSE
|
||||
|
||||
def exists(self):
|
||||
if self.nulls:
|
||||
return self.lang[OrOp([t.exists() for t in self.terms])]
|
||||
else:
|
||||
return self.lang[AndOp([t.exists() for t in self.terms])]
|
||||
|
||||
@simplified
|
||||
def partial_eval(self):
|
||||
acc = None
|
||||
terms = []
|
||||
for t in self.terms:
|
||||
simple = t.partial_eval()
|
||||
if simple is NULL:
|
||||
pass
|
||||
elif is_literal(simple):
|
||||
if acc is None:
|
||||
acc = simple.value
|
||||
else:
|
||||
acc = builtin_ops[self.op](acc, simple.value)
|
||||
else:
|
||||
terms.append(simple)
|
||||
|
||||
lang = self.lang
|
||||
if len(terms) == 0:
|
||||
if acc == None:
|
||||
return self.default.partial_eval()
|
||||
else:
|
||||
return lang[Literal(acc)]
|
||||
elif self.nulls:
|
||||
# DECISIVE
|
||||
if acc is not None:
|
||||
terms.append(Literal(acc))
|
||||
|
||||
output = lang[
|
||||
WhenOp(
|
||||
AndOp([t.missing() for t in terms]),
|
||||
**{
|
||||
"then": self.default,
|
||||
"else": operators["basic." + self.op](
|
||||
[CoalesceOp([t, _jx_identity[self.op]]) for t in terms]
|
||||
),
|
||||
}
|
||||
)
|
||||
].partial_eval()
|
||||
else:
|
||||
# CONSERVATIVE
|
||||
if acc is not None:
|
||||
terms.append(lang[Literal(acc)])
|
||||
|
||||
output = lang[
|
||||
WhenOp(
|
||||
lang[OrOp([t.missing() for t in terms])],
|
||||
**{
|
||||
"then": self.default,
|
||||
"else": operators["basic." + self.op](terms),
|
||||
}
|
||||
)
|
||||
].partial_eval()
|
||||
|
||||
return output
|
||||
|
||||
|
||||
_jx_identity = {"add": ZERO, "mul": ONE}
|
|
@ -0,0 +1,26 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions.basic_multi_op import BasicMultiOp
|
||||
|
||||
|
||||
class BasicAddOp(BasicMultiOp):
|
||||
op = "basic.add"
|
|
@ -0,0 +1,48 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.false_op import FALSE
|
||||
from jx_base.language import is_op
|
||||
from mo_json import BOOLEAN
|
||||
|
||||
|
||||
class BasicEqOp(Expression):
|
||||
"""
|
||||
PLACEHOLDER FOR BASIC `==` OPERATOR (CAN NOT DEAL WITH NULLS)
|
||||
"""
|
||||
|
||||
data_type = BOOLEAN
|
||||
|
||||
def __init__(self, terms):
|
||||
Expression.__init__(self, terms)
|
||||
self.lhs, self.rhs = terms
|
||||
|
||||
def __data__(self):
|
||||
return {"basic.eq": [self.lhs.__data__(), self.rhs.__data__()]}
|
||||
|
||||
def missing(self):
|
||||
return FALSE
|
||||
|
||||
def __eq__(self, other):
|
||||
if not is_op(other, BasicEqOp):
|
||||
return False
|
||||
return self.lhs == other.lhs and self.rhs == other.rhs
|
|
@ -0,0 +1,67 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.language import is_op
|
||||
|
||||
from jx_base.expressions._utils import simplified
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.false_op import FALSE
|
||||
from jx_base.expressions.integer_op import IntegerOp
|
||||
from jx_base.expressions.literal import ZERO
|
||||
from jx_base.expressions.max_op import MaxOp
|
||||
from jx_base.expressions.string_op import StringOp
|
||||
from mo_json import INTEGER
|
||||
|
||||
|
||||
class BasicIndexOfOp(Expression):
|
||||
"""
|
||||
PLACEHOLDER FOR BASIC value.indexOf(find, start) (CAN NOT DEAL WITH NULLS)
|
||||
"""
|
||||
|
||||
data_type = INTEGER
|
||||
|
||||
def __init__(self, params):
|
||||
Expression.__init__(self, params)
|
||||
self.value, self.find, self.start = params
|
||||
|
||||
def __data__(self):
|
||||
return {
|
||||
"basic.indexOf": [
|
||||
self.value.__data__(),
|
||||
self.find.__data__(),
|
||||
self.start.__data__(),
|
||||
]
|
||||
}
|
||||
|
||||
def vars(self):
|
||||
return self.value.vars() | self.find.vars() | self.start.vars()
|
||||
|
||||
def missing(self):
|
||||
return FALSE
|
||||
|
||||
@simplified
|
||||
def partial_eval(self):
|
||||
start = IntegerOp(MaxOp([ZERO, self.start])).partial_eval()
|
||||
return self.lang[
|
||||
BasicIndexOfOp(
|
||||
[
|
||||
StringOp(self.value).partial_eval(),
|
||||
StringOp(self.find).partial_eval(),
|
||||
start,
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
def __eq__(self, other):
|
||||
if not is_op(other, BasicIndexOfOp):
|
||||
return False
|
||||
return self.value == self.value and self.find == other.find and self.start == other.start
|
|
@ -0,0 +1,26 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions.basic_multi_op import BasicMultiOp
|
||||
|
||||
|
||||
class BasicMulOp(BasicMultiOp):
|
||||
op = "basic.mul"
|
|
@ -0,0 +1,82 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions._utils import simplified, builtin_ops
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.false_op import FALSE
|
||||
from jx_base.expressions.literal import Literal
|
||||
from jx_base.expressions.null_op import NULL
|
||||
from jx_base.language import is_op
|
||||
from mo_json import NUMBER
|
||||
|
||||
|
||||
class BasicMultiOp(Expression):
|
||||
"""
|
||||
PLACEHOLDER FOR BASIC OPERATOR (CAN NOT DEAL WITH NULLS)
|
||||
"""
|
||||
|
||||
data_type = NUMBER
|
||||
op = None
|
||||
|
||||
def __init__(self, terms):
|
||||
Expression.__init__(self, terms)
|
||||
self.terms = terms
|
||||
|
||||
def vars(self):
|
||||
output = set()
|
||||
for t in self.terms:
|
||||
output.update(t.vars())
|
||||
return output
|
||||
|
||||
def map(self, map):
|
||||
return self.__class__([t.map(map) for t in self.terms])
|
||||
|
||||
def __data__(self):
|
||||
return {self.op: [t.__data__() for t in self.terms]}
|
||||
|
||||
def missing(self):
|
||||
return FALSE
|
||||
|
||||
@simplified
|
||||
def partial_eval(self):
|
||||
acc = None
|
||||
terms = []
|
||||
for t in self.terms:
|
||||
simple = t.partial_eval()
|
||||
if simple is NULL:
|
||||
pass
|
||||
elif is_op(simple, Literal):
|
||||
if acc is None:
|
||||
acc = simple.value
|
||||
else:
|
||||
acc = builtin_ops[self.op](acc, simple.value)
|
||||
else:
|
||||
terms.append(simple)
|
||||
if len(terms) == 0:
|
||||
if acc == None:
|
||||
return self.default.partial_eval()
|
||||
else:
|
||||
return Literal(acc)
|
||||
else:
|
||||
if acc is not None:
|
||||
terms.append(Literal(acc))
|
||||
|
||||
return self.__class__(terms)
|
|
@ -0,0 +1,63 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions._utils import simplified
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.false_op import FALSE
|
||||
from jx_base.expressions.string_op import StringOp
|
||||
from jx_base.language import is_op
|
||||
from mo_json import BOOLEAN
|
||||
|
||||
|
||||
class BasicStartsWithOp(Expression):
|
||||
"""
|
||||
PLACEHOLDER FOR BASIC value.startsWith(find, start) (CAN NOT DEAL WITH NULLS)
|
||||
"""
|
||||
|
||||
data_type = BOOLEAN
|
||||
|
||||
def __init__(self, params):
|
||||
Expression.__init__(self, params)
|
||||
self.value, self.prefix = params
|
||||
|
||||
def __data__(self):
|
||||
return {"basic.startsWith": [self.value.__data__(), self.prefix.__data__()]}
|
||||
|
||||
def __eq__(self, other):
|
||||
if is_op(other, BasicStartsWithOp):
|
||||
return self.value == other.value and self.prefix == other.prefix
|
||||
|
||||
def vars(self):
|
||||
return self.value.vars() | self.prefix.vars()
|
||||
|
||||
def missing(self):
|
||||
return FALSE
|
||||
|
||||
@simplified
|
||||
def partial_eval(self):
|
||||
return self.lang[
|
||||
BasicStartsWithOp(
|
||||
[
|
||||
StringOp(self.value).partial_eval(),
|
||||
StringOp(self.prefix).partial_eval(),
|
||||
]
|
||||
)
|
||||
]
|
|
@ -0,0 +1,48 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.false_op import FALSE
|
||||
from mo_json import STRING
|
||||
|
||||
|
||||
class BasicSubstringOp(Expression):
|
||||
"""
|
||||
PLACEHOLDER FOR BASIC value.substring(start, end) (CAN NOT DEAL WITH NULLS)
|
||||
"""
|
||||
|
||||
data_type = STRING
|
||||
|
||||
def __init__(self, terms):
|
||||
Expression.__init__(self, terms)
|
||||
self.value, self.start, self.end = terms
|
||||
|
||||
def __data__(self):
|
||||
return {
|
||||
"basic.substring": [
|
||||
self.value.__data__(),
|
||||
self.start.__data__(),
|
||||
self.end.__data__(),
|
||||
]
|
||||
}
|
||||
|
||||
def missing(self):
|
||||
return FALSE
|
|
@ -0,0 +1,183 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions._utils import jx_expression, simplified
|
||||
from jx_base.expressions.add_op import AddOp
|
||||
from jx_base.expressions.basic_substring_op import BasicSubstringOp
|
||||
from jx_base.expressions.case_op import CaseOp
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.find_op import FindOp
|
||||
from jx_base.expressions.is_number_op import IsNumberOp
|
||||
from jx_base.expressions.length_op import LengthOp
|
||||
from jx_base.expressions.literal import Literal, ZERO, is_literal
|
||||
from jx_base.expressions.max_op import MaxOp
|
||||
from jx_base.expressions.min_op import MinOp
|
||||
from jx_base.expressions.null_op import NULL
|
||||
from jx_base.expressions.variable import Variable
|
||||
from jx_base.expressions.when_op import WhenOp
|
||||
from jx_base.language import is_op
|
||||
from mo_dots import is_data, is_sequence, wrap, coalesce
|
||||
from mo_json import STRING
|
||||
from mo_logs import Log
|
||||
|
||||
|
||||
class BetweenOp(Expression):
|
||||
data_type = STRING
|
||||
|
||||
def __init__(self, value, prefix, suffix, default=NULL, start=NULL):
|
||||
Expression.__init__(self, [])
|
||||
self.value = value
|
||||
self.prefix = coalesce(prefix, NULL)
|
||||
self.suffix = coalesce(suffix, NULL)
|
||||
self.default = coalesce(default, NULL)
|
||||
self.start = coalesce(start, NULL)
|
||||
if is_literal(self.prefix) and is_literal(self.suffix):
|
||||
pass
|
||||
else:
|
||||
Log.error("Expecting literal prefix and suffix only")
|
||||
|
||||
@classmethod
|
||||
def define(cls, expr):
|
||||
term = expr.between
|
||||
if is_sequence(term):
|
||||
return cls.lang[
|
||||
BetweenOp(
|
||||
value=jx_expression(term[0]),
|
||||
prefix=jx_expression(term[1]),
|
||||
suffix=jx_expression(term[2]),
|
||||
default=jx_expression(expr.default),
|
||||
start=jx_expression(expr.start),
|
||||
)
|
||||
]
|
||||
elif is_data(term):
|
||||
var, vals = term.items()[0]
|
||||
if is_sequence(vals) and len(vals) == 2:
|
||||
return cls.lang[
|
||||
BetweenOp(
|
||||
value=Variable(var),
|
||||
prefix=Literal(vals[0]),
|
||||
suffix=Literal(vals[1]),
|
||||
default=jx_expression(expr.default),
|
||||
start=jx_expression(expr.start),
|
||||
)
|
||||
]
|
||||
else:
|
||||
Log.error(
|
||||
"`between` parameters are expected to be in {var: [prefix, suffix]} form"
|
||||
)
|
||||
else:
|
||||
Log.error(
|
||||
"`between` parameters are expected to be in {var: [prefix, suffix]} form"
|
||||
)
|
||||
|
||||
def vars(self):
|
||||
return (
|
||||
self.value.vars()
|
||||
| self.prefix.vars()
|
||||
| self.suffix.vars()
|
||||
| self.default.vars()
|
||||
| self.start.vars()
|
||||
)
|
||||
|
||||
def map(self, map_):
|
||||
return BetweenOp(
|
||||
self.value.map(map_),
|
||||
self.prefix.map(map_),
|
||||
self.suffix.map(map_),
|
||||
default=self.default.map(map_),
|
||||
start=self.start.map(map_),
|
||||
)
|
||||
|
||||
def __data__(self):
|
||||
if (
|
||||
is_op(self.value, Variable)
|
||||
and is_literal(self.prefix)
|
||||
and is_literal(self.suffix)
|
||||
):
|
||||
output = wrap(
|
||||
{"between": {self.value.var: [self.prefix.value, self.suffix.value]}}
|
||||
)
|
||||
else:
|
||||
output = wrap(
|
||||
{
|
||||
"between": [
|
||||
self.value.__data__(),
|
||||
self.prefix.__data__(),
|
||||
self.suffix.__data__(),
|
||||
]
|
||||
}
|
||||
)
|
||||
if self.start:
|
||||
output.start = self.start.__data__()
|
||||
if self.default:
|
||||
output.default = self.default.__data__()
|
||||
return output
|
||||
|
||||
@simplified
|
||||
def partial_eval(self):
|
||||
value = self.value.partial_eval()
|
||||
|
||||
start_index = self.lang[
|
||||
CaseOp(
|
||||
[
|
||||
WhenOp(self.prefix.missing(), **{"then": ZERO}),
|
||||
WhenOp(
|
||||
IsNumberOp(self.prefix), **{"then": MaxOp([ZERO, self.prefix])}
|
||||
),
|
||||
FindOp([value, self.prefix], start=self.start),
|
||||
]
|
||||
)
|
||||
].partial_eval()
|
||||
|
||||
len_prefix = self.lang[
|
||||
CaseOp(
|
||||
[
|
||||
WhenOp(self.prefix.missing(), **{"then": ZERO}),
|
||||
WhenOp(IsNumberOp(self.prefix), **{"then": ZERO}),
|
||||
LengthOp(self.prefix),
|
||||
]
|
||||
)
|
||||
].partial_eval()
|
||||
|
||||
end_index = self.lang[
|
||||
CaseOp(
|
||||
[
|
||||
WhenOp(start_index.missing(), **{"then": NULL}),
|
||||
WhenOp(self.suffix.missing(), **{"then": LengthOp(value)}),
|
||||
WhenOp(
|
||||
IsNumberOp(self.suffix),
|
||||
**{"then": MinOp([self.suffix, LengthOp(value)])}
|
||||
),
|
||||
FindOp(
|
||||
[value, self.suffix], start=AddOp([start_index, len_prefix])
|
||||
),
|
||||
]
|
||||
)
|
||||
].partial_eval()
|
||||
|
||||
start_index = AddOp([start_index, len_prefix]).partial_eval()
|
||||
substring = BasicSubstringOp([value, start_index, end_index]).partial_eval()
|
||||
|
||||
between = self.lang[
|
||||
WhenOp(end_index.missing(), **{"then": self.default, "else": substring})
|
||||
].partial_eval()
|
||||
|
||||
return between
|
|
@ -0,0 +1,62 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions._utils import simplified
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.false_op import FALSE
|
||||
from jx_base.expressions.null_op import NULL
|
||||
from jx_base.expressions.true_op import TRUE
|
||||
from mo_json import BOOLEAN
|
||||
|
||||
|
||||
class BooleanOp(Expression):
|
||||
data_type = BOOLEAN
|
||||
|
||||
def __init__(self, term):
|
||||
Expression.__init__(self, [term])
|
||||
self.term = term
|
||||
|
||||
def __data__(self):
|
||||
return {"boolean": self.term.__data__()}
|
||||
|
||||
def vars(self):
|
||||
return self.term.vars()
|
||||
|
||||
def map(self, map_):
|
||||
return self.lang[BooleanOp(self.term.map(map_))]
|
||||
|
||||
def missing(self):
|
||||
return self.term.missing()
|
||||
|
||||
@simplified
|
||||
def partial_eval(self):
|
||||
term = self.lang[self.term].partial_eval()
|
||||
if term is TRUE:
|
||||
return TRUE
|
||||
elif term in (FALSE, NULL):
|
||||
return FALSE
|
||||
elif term.type is BOOLEAN:
|
||||
return term
|
||||
elif term is self.term:
|
||||
return self
|
||||
|
||||
exists = self.lang[term].exists().partial_eval()
|
||||
return exists
|
|
@ -0,0 +1,124 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions import first_op, not_op, eq_op
|
||||
from jx_base.expressions._utils import simplified
|
||||
from jx_base.expressions.and_op import AndOp
|
||||
from jx_base.expressions.not_op import NotOp
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.false_op import FALSE
|
||||
from jx_base.expressions.literal import NULL
|
||||
from jx_base.expressions.or_op import OrOp
|
||||
from jx_base.expressions.true_op import TRUE
|
||||
from jx_base.expressions.when_op import WhenOp
|
||||
from jx_base.language import is_op
|
||||
from mo_dots import is_sequence
|
||||
from mo_future import first
|
||||
from mo_json import OBJECT, BOOLEAN
|
||||
from mo_logs import Log
|
||||
|
||||
|
||||
class CaseOp(Expression):
|
||||
def __init__(self, terms, **clauses):
|
||||
if not is_sequence(terms):
|
||||
Log.error("case expression requires a list of `when` sub-clauses")
|
||||
Expression.__init__(self, terms)
|
||||
if len(terms) == 0:
|
||||
Log.error("Expecting at least one clause")
|
||||
|
||||
for w in terms[:-1]:
|
||||
if not is_op(w, WhenOp) or w.els_ is not NULL:
|
||||
Log.error(
|
||||
"case expression does not allow `else` clause in `when` sub-clause"
|
||||
)
|
||||
self.whens = terms
|
||||
|
||||
def __data__(self):
|
||||
return {"case": [w.__data__() for w in self.whens]}
|
||||
|
||||
def __eq__(self, other):
|
||||
if is_op(other, CaseOp):
|
||||
return all(s == o for s, o in zip(self.whens, other.whens))
|
||||
|
||||
def vars(self):
|
||||
output = set()
|
||||
for w in self.whens:
|
||||
output |= w.vars()
|
||||
return output
|
||||
|
||||
def map(self, map_):
|
||||
return self.lang[CaseOp([w.map(map_) for w in self.whens])]
|
||||
|
||||
def missing(self):
|
||||
m = self.whens[-1].missing()
|
||||
for w in reversed(self.whens[0:-1]):
|
||||
when = w.when.partial_eval()
|
||||
if when is FALSE:
|
||||
pass
|
||||
elif when is TRUE:
|
||||
m = w.then.partial_eval().missing()
|
||||
else:
|
||||
m = self.lang[OrOp([AndOp([when, w.then.partial_eval().missing()]), m])]
|
||||
return m.partial_eval()
|
||||
|
||||
@simplified
|
||||
def partial_eval(self):
|
||||
if self.type == BOOLEAN:
|
||||
nots = []
|
||||
ors = []
|
||||
for w in self.whens[:-1]:
|
||||
ors.append(AndOp(nots + [w.when, w.then]))
|
||||
nots.append(NotOp(w.when))
|
||||
ors.append(AndOp(nots + [self.whens[-1]]))
|
||||
return self.lang[OrOp(ors)].partial_eval()
|
||||
|
||||
whens = []
|
||||
for w in self.whens[:-1]:
|
||||
when = self.lang[w.when].partial_eval()
|
||||
if when is TRUE:
|
||||
whens.append(self.lang[w.then].partial_eval())
|
||||
break
|
||||
elif when is FALSE:
|
||||
pass
|
||||
else:
|
||||
whens.append(self.lang[WhenOp(when, **{"then": w.then.partial_eval()})])
|
||||
else:
|
||||
whens.append(self.lang[self.whens[-1]].partial_eval())
|
||||
|
||||
if len(whens) == 1:
|
||||
return whens[0]
|
||||
elif len(whens) == 2:
|
||||
return self.lang[WhenOp(whens[0].when, **{"then": whens[0].then, "else": whens[1]})]
|
||||
else:
|
||||
return self.lang[CaseOp(whens)]
|
||||
|
||||
@property
|
||||
def type(self):
|
||||
types = set(w.then.type if is_op(w, WhenOp) else w.type for w in self.whens)
|
||||
if len(types) > 1:
|
||||
return OBJECT
|
||||
else:
|
||||
return first(types)
|
||||
|
||||
|
||||
first_op.CaseOp = CaseOp
|
||||
not_op.CaseOp = CaseOp
|
||||
eq_op.CaseOp = CaseOp
|
|
@ -0,0 +1,78 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions.literal import is_literal
|
||||
from jx_base.expressions.null_op import NULL
|
||||
from jx_base.expressions._utils import simplified
|
||||
from jx_base.expressions.and_op import AndOp
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.first_op import FirstOp
|
||||
from jx_base.language import is_op
|
||||
|
||||
|
||||
class CoalesceOp(Expression):
|
||||
has_simple_form = True
|
||||
|
||||
def __init__(self, terms):
|
||||
Expression.__init__(self, terms)
|
||||
self.terms = terms
|
||||
|
||||
def __data__(self):
|
||||
return {"coalesce": [t.__data__() for t in self.terms]}
|
||||
|
||||
def __eq__(self, other):
|
||||
if is_op(other, CoalesceOp):
|
||||
if len(self.terms) == len(other.terms):
|
||||
return all(s == o for s, o in zip(self.terms, other.terms))
|
||||
return False
|
||||
|
||||
def missing(self):
|
||||
# RETURN true FOR RECORDS THE WOULD RETURN NULL
|
||||
return self.lang[AndOp([v.missing() for v in self.terms])]
|
||||
|
||||
def vars(self):
|
||||
output = set()
|
||||
for v in self.terms:
|
||||
output |= v.vars()
|
||||
return output
|
||||
|
||||
def map(self, map_):
|
||||
return self.lang[CoalesceOp([v.map(map_) for v in self.terms])]
|
||||
|
||||
@simplified
|
||||
def partial_eval(self):
|
||||
terms = []
|
||||
for t in self.terms:
|
||||
simple = self.lang[FirstOp(t)].partial_eval()
|
||||
if simple is NULL:
|
||||
pass
|
||||
elif is_literal(simple):
|
||||
terms.append(simple)
|
||||
break
|
||||
else:
|
||||
terms.append(simple)
|
||||
|
||||
if len(terms) == 0:
|
||||
return NULL
|
||||
elif len(terms) == 1:
|
||||
return terms[0]
|
||||
else:
|
||||
return self.lang[CoalesceOp(terms)]
|
|
@ -0,0 +1,86 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions._utils import jx_expression
|
||||
from jx_base.expressions.and_op import AndOp
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.literal import Literal
|
||||
from jx_base.expressions.literal import is_literal
|
||||
from jx_base.expressions.null_op import NULL
|
||||
from jx_base.expressions.variable import Variable
|
||||
from jx_base.language import is_op
|
||||
from jx_base.utils import is_variable_name
|
||||
from mo_dots import is_data
|
||||
from mo_future import first, is_text
|
||||
from mo_json import STRING
|
||||
from mo_logs import Log
|
||||
|
||||
|
||||
class ConcatOp(Expression):
|
||||
has_simple_form = True
|
||||
data_type = STRING
|
||||
|
||||
def __init__(self, terms, **clauses):
|
||||
Expression.__init__(self, terms)
|
||||
if is_data(terms):
|
||||
self.terms = first(terms.items())
|
||||
else:
|
||||
self.terms = terms
|
||||
self.separator = clauses.get(str("separator"), Literal(""))
|
||||
self.default = clauses.get(str("default"), NULL)
|
||||
if not is_literal(self.separator):
|
||||
Log.error("Expecting a literal separator")
|
||||
|
||||
@classmethod
|
||||
def define(cls, expr):
|
||||
terms = expr["concat"]
|
||||
if is_data(terms):
|
||||
k, v = first(terms.items())
|
||||
terms = [Variable(k), Literal(v)]
|
||||
else:
|
||||
terms = [jx_expression(t) for t in terms]
|
||||
|
||||
return cls.lang[
|
||||
ConcatOp(
|
||||
terms,
|
||||
**{
|
||||
k: Literal(v)
|
||||
if is_text(v) and not is_variable_name(v)
|
||||
else jx_expression(v)
|
||||
for k, v in expr.items()
|
||||
if k in ["default", "separator"]
|
||||
}
|
||||
)
|
||||
]
|
||||
|
||||
def __data__(self):
|
||||
f, s = self.terms[0], self.terms[1]
|
||||
if is_op(f, Variable) and is_literal(s):
|
||||
output = {"concat": {f.var: s.value}}
|
||||
else:
|
||||
output = {"concat": [t.__data__() for t in self.terms]}
|
||||
if self.separator.json != '""':
|
||||
output["separator"] = self.separator.__data__()
|
||||
return output
|
||||
|
||||
def vars(self):
|
||||
if not self.terms:
|
||||
return set()
|
||||
return set.union(*(t.vars() for t in self.terms))
|
||||
|
||||
def map(self, map_):
|
||||
return self.lang[
|
||||
ConcatOp([t.map(map_) for t in self.terms], separator=self.separator)
|
||||
]
|
||||
|
||||
def missing(self):
|
||||
return self.lang[
|
||||
AndOp([t.missing() for t in self.terms] + [self.default.missing()])
|
||||
].partial_eval()
|
|
@ -0,0 +1,55 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions.false_op import FALSE
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.true_op import TrueOp
|
||||
from jx_base.expressions.tuple_op import TupleOp
|
||||
from mo_dots import is_many
|
||||
from mo_json import INTEGER
|
||||
|
||||
|
||||
class CountOp(Expression):
|
||||
has_simple_form = False
|
||||
data_type = INTEGER
|
||||
|
||||
def __init__(self, terms, **clauses):
|
||||
Expression.__init__(self, terms)
|
||||
if is_many(terms):
|
||||
# SHORTCUT: ASSUME AN ARRAY OF IS A TUPLE
|
||||
self.terms = self.lang[TupleOp(terms)]
|
||||
else:
|
||||
self.terms = terms
|
||||
|
||||
def __data__(self):
|
||||
return {"count": self.terms.__data__()}
|
||||
|
||||
def vars(self):
|
||||
return self.terms.vars()
|
||||
|
||||
def map(self, map_):
|
||||
return self.lang[CountOp(self.terms.map(map_))]
|
||||
|
||||
def missing(self):
|
||||
return FALSE
|
||||
|
||||
def exists(self):
|
||||
return TrueOp
|
|
@ -0,0 +1,57 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions import literal
|
||||
from jx_base.expressions.literal import Literal
|
||||
from mo_dots import coalesce
|
||||
from mo_future import is_text
|
||||
from mo_json import NUMBER
|
||||
from mo_times.dates import unicode2Date, Date
|
||||
|
||||
|
||||
class DateOp(Literal):
|
||||
date_type = NUMBER
|
||||
|
||||
def __init__(self, term):
|
||||
if hasattr(self, "date"):
|
||||
return
|
||||
if is_text(term):
|
||||
self.date = term
|
||||
else:
|
||||
self.date = coalesce(term.get("literal"), term)
|
||||
v = unicode2Date(self.date)
|
||||
if isinstance(v, Date):
|
||||
Literal.__init__(self, v.unix)
|
||||
else:
|
||||
Literal.__init__(self, v.seconds)
|
||||
|
||||
@classmethod
|
||||
def define(cls, expr):
|
||||
return cls.lang[DateOp(expr.get("date"))]
|
||||
|
||||
def __data__(self):
|
||||
return {"date": self.date}
|
||||
|
||||
def __call__(self, row=None, rownum=None, rows=None):
|
||||
return Date(self.date)
|
||||
|
||||
|
||||
literal.DateOp=DateOp
|
|
@ -0,0 +1,54 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions._utils import simplified, builtin_ops
|
||||
from jx_base.expressions.and_op import AndOp
|
||||
from jx_base.expressions.base_binary_op import BaseBinaryOp
|
||||
from jx_base.expressions.eq_op import EqOp
|
||||
from jx_base.expressions.literal import Literal, ZERO, is_literal
|
||||
from jx_base.expressions.or_op import OrOp
|
||||
|
||||
|
||||
class DivOp(BaseBinaryOp):
|
||||
op = "div"
|
||||
|
||||
def missing(self):
|
||||
return self.lang[
|
||||
AndOp(
|
||||
[
|
||||
self.default.missing(),
|
||||
OrOp(
|
||||
[self.lhs.missing(), self.rhs.missing(), EqOp([self.rhs, ZERO])]
|
||||
),
|
||||
]
|
||||
)
|
||||
].partial_eval()
|
||||
|
||||
@simplified
|
||||
def partial_eval(self):
|
||||
default = self.default.partial_eval()
|
||||
rhs = self.rhs.partial_eval()
|
||||
if rhs is ZERO:
|
||||
return default
|
||||
lhs = self.lhs.partial_eval()
|
||||
if is_literal(lhs) and is_literal(rhs):
|
||||
return Literal(builtin_ops[self.op](lhs.value, rhs.value))
|
||||
return self.__class__([lhs, rhs], default=default)
|
|
@ -0,0 +1,104 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions._utils import simplified
|
||||
from jx_base.expressions.and_op import AndOp
|
||||
from jx_base.expressions.basic_eq_op import BasicEqOp
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.false_op import FALSE
|
||||
from jx_base.expressions.literal import is_literal
|
||||
from jx_base.expressions.true_op import TRUE
|
||||
from jx_base.expressions.variable import Variable
|
||||
from jx_base.language import is_op, value_compare
|
||||
from mo_dots import is_many
|
||||
from mo_json import BOOLEAN
|
||||
|
||||
CaseOp = None
|
||||
InOp = None
|
||||
WhneOp = None
|
||||
|
||||
class EqOp(Expression):
|
||||
has_simple_form = True
|
||||
data_type = BOOLEAN
|
||||
|
||||
def __new__(cls, terms):
|
||||
if is_many(terms):
|
||||
return object.__new__(cls)
|
||||
|
||||
items = terms.items()
|
||||
if len(items) == 1:
|
||||
if is_many(items[0][1]):
|
||||
return cls.lang[InOp(items[0])]
|
||||
else:
|
||||
return cls.lang[EqOp(items[0])]
|
||||
else:
|
||||
acc = []
|
||||
for lhs, rhs in items:
|
||||
if rhs.json.startswith("["):
|
||||
acc.append(cls.lang[InOp([Variable(lhs), rhs])])
|
||||
else:
|
||||
acc.append(cls.lang[EqOp([Variable(lhs), rhs])])
|
||||
return cls.lang[AndOp(acc)]
|
||||
|
||||
def __init__(self, terms):
|
||||
Expression.__init__(self, terms)
|
||||
self.lhs, self.rhs = terms
|
||||
|
||||
def __data__(self):
|
||||
if is_op(self.lhs, Variable) and is_literal(self.rhs):
|
||||
return {"eq": {self.lhs.var, self.rhs.value}}
|
||||
else:
|
||||
return {"eq": [self.lhs.__data__(), self.rhs.__data__()]}
|
||||
|
||||
def __eq__(self, other):
|
||||
if is_op(other, EqOp):
|
||||
return self.lhs == other.lhs and self.rhs == other.rhs
|
||||
return False
|
||||
|
||||
def vars(self):
|
||||
return self.lhs.vars() | self.rhs.vars()
|
||||
|
||||
def map(self, map_):
|
||||
return self.lang[EqOp([self.lhs.map(map_), self.rhs.map(map_)])]
|
||||
|
||||
def missing(self):
|
||||
return FALSE
|
||||
|
||||
def exists(self):
|
||||
return TRUE
|
||||
|
||||
@simplified
|
||||
def partial_eval(self):
|
||||
lhs = self.lang[self.lhs].partial_eval()
|
||||
rhs = self.lang[self.rhs].partial_eval()
|
||||
|
||||
if is_literal(lhs) and is_literal(rhs):
|
||||
return FALSE if value_compare(lhs.value, rhs.value) else TRUE
|
||||
else:
|
||||
return self.lang[
|
||||
self.lang[CaseOp(
|
||||
[
|
||||
WhenOp(lhs.missing(), **{"then": rhs.missing()}),
|
||||
WhenOp(rhs.missing(), **{"then": FALSE}),
|
||||
BasicEqOp([lhs, rhs]),
|
||||
]
|
||||
)]
|
||||
].partial_eval()
|
|
@ -0,0 +1,50 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions._utils import simplified
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.language import is_op
|
||||
from mo_json import BOOLEAN
|
||||
|
||||
|
||||
class EsNestedOp(Expression):
|
||||
data_type = BOOLEAN
|
||||
has_simple_form = False
|
||||
|
||||
def __init__(self, terms):
|
||||
Expression.__init__(self, terms)
|
||||
self.path, self.query = terms
|
||||
|
||||
@simplified
|
||||
def partial_eval(self):
|
||||
if self.path.var == ".":
|
||||
return self.query.partial_eval()
|
||||
return self.lang[
|
||||
EsNestedOp("es.nested", [self.path, self.query.partial_eval()])
|
||||
]
|
||||
|
||||
def __data__(self):
|
||||
return {"es.nested": {self.path.var: self.query.__data__()}}
|
||||
|
||||
def __eq__(self, other):
|
||||
if is_op(other, EsNestedOp):
|
||||
return self.path.var == other.path.var and self.query == other.query
|
||||
return False
|
|
@ -0,0 +1,30 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions.expression import Expression
|
||||
|
||||
|
||||
class EsScript(Expression):
|
||||
"""
|
||||
REPRESENT A Painless SCRIPT
|
||||
"""
|
||||
|
||||
pass
|
|
@ -0,0 +1,54 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions._utils import simplified
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.false_op import FALSE
|
||||
from mo_json import BOOLEAN
|
||||
|
||||
NotOp = None
|
||||
|
||||
|
||||
class ExistsOp(Expression):
|
||||
data_type = BOOLEAN
|
||||
|
||||
def __init__(self, term):
|
||||
Expression.__init__(self, [term])
|
||||
self.field = term
|
||||
|
||||
def __data__(self):
|
||||
return {"exists": self.field.__data__()}
|
||||
|
||||
def vars(self):
|
||||
return self.field.vars()
|
||||
|
||||
def map(self, map_):
|
||||
return self.lang[ExistsOp(self.field.map(map_))]
|
||||
|
||||
def missing(self):
|
||||
return FALSE
|
||||
|
||||
def exists(self):
|
||||
return TRUE
|
||||
|
||||
@simplified
|
||||
def partial_eval(self):
|
||||
return self.lang[NotOp(self.field.missing())].partial_eval()
|
|
@ -0,0 +1,26 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions.base_binary_op import BaseBinaryOp
|
||||
|
||||
|
||||
class ExpOp(BaseBinaryOp):
|
||||
op = "exp"
|
|
@ -0,0 +1,178 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions._utils import operators, jx_expression, _jx_expression, simplified
|
||||
from jx_base.language import BaseExpression, ID, is_expression, is_op
|
||||
from mo_dots import is_data, is_sequence, is_container
|
||||
from mo_future import items as items_, text
|
||||
from mo_json import BOOLEAN, OBJECT, value2json
|
||||
from mo_logs import Log
|
||||
|
||||
FALSE, Literal, is_literal, MissingOp, NotOp, NULL, Variable = [None]*7
|
||||
|
||||
|
||||
class Expression(BaseExpression):
|
||||
data_type = OBJECT
|
||||
has_simple_form = False
|
||||
|
||||
def __init__(self, args):
|
||||
self.simplified = False
|
||||
# SOME BASIC VERIFICATION THAT THESE ARE REASONABLE PARAMETERS
|
||||
if is_sequence(args):
|
||||
bad = [t for t in args if t != None and not is_expression(t)]
|
||||
if bad:
|
||||
Log.error("Expecting an expression, not {{bad}}", bad=bad)
|
||||
elif is_data(args):
|
||||
if not all(is_op(k, Variable) and is_literal(v) for k, v in args.items()):
|
||||
Log.error("Expecting an {<variable>: <literal>}")
|
||||
elif args == None:
|
||||
pass
|
||||
else:
|
||||
if not is_expression(args):
|
||||
Log.error("Expecting an expression")
|
||||
|
||||
@classmethod
|
||||
def get_id(cls):
|
||||
return getattr(cls, ID)
|
||||
|
||||
@classmethod
|
||||
def define(cls, expr):
|
||||
"""
|
||||
GENERAL SUPPORT FOR BUILDING EXPRESSIONS FROM JSON EXPRESSIONS
|
||||
OVERRIDE THIS IF AN OPERATOR EXPECTS COMPLICATED PARAMETERS
|
||||
:param expr: Data representing a JSON Expression
|
||||
:return: parse tree
|
||||
"""
|
||||
|
||||
try:
|
||||
lang = cls.lang
|
||||
items = items_(expr)
|
||||
for item in items:
|
||||
op, term = item
|
||||
full_op = operators.get(op)
|
||||
if full_op:
|
||||
class_ = lang.ops[full_op.get_id()]
|
||||
clauses = {k: jx_expression(v) for k, v in expr.items() if k != op}
|
||||
break
|
||||
else:
|
||||
if not items:
|
||||
return NULL
|
||||
raise Log.error(
|
||||
"{{operator|quote}} is not a known operator", operator=expr
|
||||
)
|
||||
|
||||
if term == None:
|
||||
return class_([], **clauses)
|
||||
elif is_container(term):
|
||||
terms = [jx_expression(t) for t in term]
|
||||
return class_(terms, **clauses)
|
||||
elif is_data(term):
|
||||
items = items_(term)
|
||||
if class_.has_simple_form:
|
||||
if len(items) == 1:
|
||||
k, v = items[0]
|
||||
return class_([Variable(k), Literal(v)], **clauses)
|
||||
else:
|
||||
return class_({k: Literal(v) for k, v in items}, **clauses)
|
||||
else:
|
||||
return class_(_jx_expression(term, lang), **clauses)
|
||||
else:
|
||||
if op in ["literal", "date", "offset"]:
|
||||
return class_(term, **clauses)
|
||||
else:
|
||||
return class_(_jx_expression(term, lang), **clauses)
|
||||
except Exception as e:
|
||||
Log.error("programmer error expr = {{value|quote}}", value=expr, cause=e)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self.__class__.__name__
|
||||
|
||||
@property
|
||||
def many(self):
|
||||
"""
|
||||
:return: True IF THE EXPRESSION RETURNS A MULTIVALUE (WHICH IS NOT A LIST OR A TUPLE)
|
||||
"""
|
||||
return False
|
||||
|
||||
def __data__(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def vars(self):
|
||||
raise Log.error("{{type}} has no `vars` method", type=self.__class__.__name__)
|
||||
|
||||
def map(self, map):
|
||||
raise Log.error("{{type}} has no `map` method", type=self.__class__.__name__)
|
||||
|
||||
def missing(self):
|
||||
"""
|
||||
THERE IS PLENTY OF OPPORTUNITY TO SIMPLIFY missing EXPRESSIONS
|
||||
OVERRIDE THIS METHOD TO SIMPLIFY
|
||||
:return:
|
||||
"""
|
||||
if self.type == BOOLEAN:
|
||||
Log.error("programmer error")
|
||||
return self.lang[MissingOp(self)]
|
||||
|
||||
def exists(self):
|
||||
"""
|
||||
THERE IS PLENTY OF OPPORTUNITY TO SIMPLIFY exists EXPRESSIONS
|
||||
OVERRIDE THIS METHOD TO SIMPLIFY
|
||||
:return:
|
||||
"""
|
||||
return self.lang[NotOp(self.missing()).partial_eval()]
|
||||
|
||||
def is_true(self):
|
||||
"""
|
||||
:return: True, IF THIS EXPRESSION ALWAYS RETURNS BOOLEAN true
|
||||
"""
|
||||
return FALSE # GOOD DEFAULT ASSUMPTION
|
||||
|
||||
def is_false(self):
|
||||
"""
|
||||
:return: True, IF THIS EXPRESSION ALWAYS RETURNS BOOLEAN false
|
||||
"""
|
||||
return FALSE # GOOD DEFAULT ASSUMPTION
|
||||
|
||||
@simplified
|
||||
def partial_eval(self):
|
||||
"""
|
||||
ATTEMPT TO SIMPLIFY THE EXPRESSION:
|
||||
PREFERABLY RETURNING A LITERAL, BUT MAYBE A SIMPLER EXPRESSION, OR self IF NOT POSSIBLE
|
||||
"""
|
||||
return self
|
||||
|
||||
@property
|
||||
def type(self):
|
||||
return self.data_type
|
||||
|
||||
def __eq__(self, other):
|
||||
if other is None:
|
||||
return False
|
||||
if self.get_id() != other.get_id():
|
||||
return False
|
||||
self_class = self.__class__
|
||||
Log.note("this is slow on {{type}}", type=text(self_class.__name__))
|
||||
return self.__data__() == other.__data__()
|
||||
|
||||
def __str__(self):
|
||||
return value2json(self.__data__(), pretty=True)
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions import literal, expression
|
||||
from jx_base.expressions.literal import Literal
|
||||
from mo_json import BOOLEAN
|
||||
|
||||
TRUE = None
|
||||
|
||||
|
||||
class FalseOp(Literal):
|
||||
data_type = BOOLEAN
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
return object.__new__(cls, *args, **kwargs)
|
||||
|
||||
def __init__(self, op=None, term=None):
|
||||
Literal.__init__(self, False)
|
||||
|
||||
@classmethod
|
||||
def define(cls, expr):
|
||||
return FALSE
|
||||
|
||||
def __nonzero__(self):
|
||||
return False
|
||||
|
||||
def __eq__(self, other):
|
||||
return (other is FALSE) or (other is False)
|
||||
|
||||
def __data__(self):
|
||||
return False
|
||||
|
||||
def vars(self):
|
||||
return set()
|
||||
|
||||
def map(self, map_):
|
||||
return self
|
||||
|
||||
def missing(self):
|
||||
return FALSE
|
||||
|
||||
def is_true(self):
|
||||
return FALSE
|
||||
|
||||
def is_false(self):
|
||||
return TRUE
|
||||
|
||||
def __call__(self, row=None, rownum=None, rows=None):
|
||||
return False
|
||||
|
||||
def __unicode__(self):
|
||||
return "false"
|
||||
|
||||
def __str__(self):
|
||||
return b"false"
|
||||
|
||||
def __bool__(self):
|
||||
return False
|
||||
|
||||
|
||||
FALSE = FalseOp()
|
||||
|
||||
expression.FALSE = FALSE
|
||||
literal.FALSE = FALSE
|
|
@ -0,0 +1,75 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.literal import ZERO
|
||||
from jx_base.expressions.literal import is_literal
|
||||
from jx_base.expressions.null_op import NULL
|
||||
from jx_base.expressions.variable import Variable
|
||||
from jx_base.language import is_op
|
||||
from mo_json import INTEGER
|
||||
|
||||
|
||||
class FindOp(Expression):
|
||||
"""
|
||||
RETURN INDEX OF find IN value, ELSE RETURN null
|
||||
"""
|
||||
|
||||
has_simple_form = True
|
||||
data_type = INTEGER
|
||||
|
||||
def __init__(self, term, **kwargs):
|
||||
Expression.__init__(self, term)
|
||||
self.value, self.find = term
|
||||
self.default = kwargs.get("default", NULL)
|
||||
self.start = kwargs.get("start", ZERO).partial_eval()
|
||||
if self.start is NULL:
|
||||
self.start = ZERO
|
||||
|
||||
def __data__(self):
|
||||
if is_op(self.value, Variable) and is_literal(self.find):
|
||||
output = {
|
||||
"find": {self.value.var, self.find.value},
|
||||
"start": self.start.__data__(),
|
||||
}
|
||||
else:
|
||||
output = {
|
||||
"find": [self.value.__data__(), self.find.__data__()],
|
||||
"start": self.start.__data__(),
|
||||
}
|
||||
if self.default is not NULL:
|
||||
output["default"] = self.default.__data__()
|
||||
return output
|
||||
|
||||
def vars(self):
|
||||
return (
|
||||
self.value.vars()
|
||||
| self.find.vars()
|
||||
| self.default.vars()
|
||||
| self.start.vars()
|
||||
)
|
||||
|
||||
def map(self, map_):
|
||||
return FindOp(
|
||||
[self.value.map(map_), self.find.map(map_)],
|
||||
start=self.start.map(map_),
|
||||
default=self.default.map(map_),
|
||||
)
|
|
@ -0,0 +1,79 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions._utils import simplified
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.last_op import LastOp
|
||||
from jx_base.expressions.literal import is_literal
|
||||
from jx_base.language import is_op
|
||||
from mo_json import OBJECT
|
||||
from mo_logs import Log
|
||||
|
||||
CaseOp = None
|
||||
WhenOp = None
|
||||
|
||||
|
||||
class FirstOp(Expression):
|
||||
def __init__(self, term):
|
||||
Expression.__init__(self, [term])
|
||||
self.term = term
|
||||
self.data_type = self.term.type
|
||||
|
||||
def __data__(self):
|
||||
return {"first": self.term.__data__()}
|
||||
|
||||
def vars(self):
|
||||
return self.term.vars()
|
||||
|
||||
def map(self, map_):
|
||||
return self.lang[LastOp(self.term.map(map_))]
|
||||
|
||||
def missing(self):
|
||||
return self.term.missing()
|
||||
|
||||
@simplified
|
||||
def partial_eval(self):
|
||||
term = self.lang[self.term].partial_eval()
|
||||
if is_op(term, FirstOp):
|
||||
return term
|
||||
elif is_op(term, CaseOp): # REWRITING
|
||||
return self.lang[
|
||||
CaseOp(
|
||||
[
|
||||
WhenOp(t.when, **{"then": FirstOp(t.then)})
|
||||
for t in term.whens[:-1]
|
||||
]
|
||||
+ [FirstOp(term.whens[-1])]
|
||||
)
|
||||
].partial_eval()
|
||||
elif is_op(term, WhenOp):
|
||||
return self.lang[
|
||||
WhenOp(
|
||||
term.when,
|
||||
**{"then": FirstOp(term.then), "else": FirstOp(term.els_)}
|
||||
)
|
||||
].partial_eval()
|
||||
elif term.type != OBJECT and not term.many:
|
||||
return term
|
||||
elif is_literal(term):
|
||||
Log.error("not handled yet")
|
||||
else:
|
||||
return self.lang[FirstOp(term)]
|
|
@ -0,0 +1,72 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions.eq_op import EqOp
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.false_op import FALSE
|
||||
from jx_base.expressions.literal import ZERO, ONE
|
||||
from jx_base.expressions.literal import is_literal
|
||||
from jx_base.expressions.null_op import NULL
|
||||
from jx_base.expressions.or_op import OrOp
|
||||
from jx_base.expressions.variable import Variable
|
||||
from jx_base.language import is_op
|
||||
from mo_json import NUMBER
|
||||
|
||||
|
||||
class FloorOp(Expression):
|
||||
has_simple_form = True
|
||||
data_type = NUMBER
|
||||
|
||||
def __init__(self, terms, default=NULL):
|
||||
Expression.__init__(self, terms)
|
||||
if len(terms) == 1:
|
||||
self.lhs = terms[0]
|
||||
self.rhs = ONE
|
||||
else:
|
||||
self.lhs, self.rhs = terms
|
||||
self.default = default
|
||||
|
||||
def __data__(self):
|
||||
if is_op(self.lhs, Variable) and is_literal(self.rhs):
|
||||
return {"floor": {self.lhs.var, self.rhs.value}, "default": self.default}
|
||||
else:
|
||||
return {
|
||||
"floor": [self.lhs.__data__(), self.rhs.__data__()],
|
||||
"default": self.default,
|
||||
}
|
||||
|
||||
def vars(self):
|
||||
return self.lhs.vars() | self.rhs.vars() | self.default.vars()
|
||||
|
||||
def map(self, map_):
|
||||
return self.lang[
|
||||
FloorOp(
|
||||
[self.lhs.map(map_), self.rhs.map(map_)], default=self.default.map(map_)
|
||||
)
|
||||
]
|
||||
|
||||
def missing(self):
|
||||
if self.default.exists():
|
||||
return FALSE
|
||||
else:
|
||||
return self.lang[
|
||||
OrOp([self.lhs.missing(), self.rhs.missing(), EqOp([self.rhs, ZERO])])
|
||||
]
|
|
@ -0,0 +1,44 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions.expression import Expression
|
||||
from mo_json import NUMBER
|
||||
|
||||
|
||||
class FromUnixOp(Expression):
|
||||
"""
|
||||
FOR USING ON DATABASES WHICH HAVE A DATE COLUMNS: CONVERT TO UNIX
|
||||
"""
|
||||
|
||||
data_type = NUMBER
|
||||
|
||||
def __init__(self, term):
|
||||
Expression.__init__(self, term)
|
||||
self.value = term
|
||||
|
||||
def vars(self):
|
||||
return self.value.vars()
|
||||
|
||||
def map(self, map_):
|
||||
return self.lang[FromUnixOp(self.value.map(map_))]
|
||||
|
||||
def missing(self):
|
||||
return self.value.missing()
|
|
@ -0,0 +1,49 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.literal import is_literal
|
||||
|
||||
|
||||
class GetOp(Expression):
|
||||
has_simple_form = True
|
||||
|
||||
def __init__(self, term):
|
||||
Expression.__init__(self, term)
|
||||
self.var = term[0]
|
||||
self.offsets = term[1:]
|
||||
|
||||
def __data__(self):
|
||||
if is_literal(self.var) and len(self.offsets) == 1 and is_literal(self.offset):
|
||||
return {"get": {self.var.json, self.offsets[0].value}}
|
||||
else:
|
||||
return {"get": [self.var.__data__()] + [o.__data__() for o in self.offsets]}
|
||||
|
||||
def vars(self):
|
||||
output = self.var.vars()
|
||||
for o in self.offsets:
|
||||
output |= o.vars()
|
||||
return output
|
||||
|
||||
def map(self, map_):
|
||||
return self.lang[
|
||||
GetOp([self.var.map(map_)] + [o.map(map_) for o in self.offsets])
|
||||
]
|
|
@ -0,0 +1,26 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions.base_inequality_op import BaseInequalityOp
|
||||
|
||||
|
||||
class GtOp(BaseInequalityOp):
|
||||
op = "gt"
|
|
@ -0,0 +1,26 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions.base_inequality_op import BaseInequalityOp
|
||||
|
||||
|
||||
class GteOp(BaseInequalityOp):
|
||||
op = "gte"
|
|
@ -0,0 +1,85 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions import eq_op
|
||||
from jx_base.expressions._utils import simplified
|
||||
from jx_base.expressions.eq_op import EqOp
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.false_op import FALSE
|
||||
from jx_base.expressions.literal import Literal
|
||||
from jx_base.expressions.literal import is_literal
|
||||
from jx_base.expressions.null_op import NULL
|
||||
from jx_base.expressions.variable import Variable
|
||||
from jx_base.language import is_op
|
||||
from mo_dots import is_many
|
||||
from mo_json import BOOLEAN
|
||||
|
||||
|
||||
class InOp(Expression):
|
||||
has_simple_form = True
|
||||
data_type = BOOLEAN
|
||||
|
||||
def __new__(cls, terms):
|
||||
if is_op(terms[0], Variable) and is_op(terms[1], Literal):
|
||||
name, value = terms
|
||||
if not is_many(value.value):
|
||||
return cls.lang[EqOp([name, Literal([value.value])])]
|
||||
return object.__new__(cls)
|
||||
|
||||
def __init__(self, term):
|
||||
Expression.__init__(self, term)
|
||||
self.value, self.superset = term
|
||||
|
||||
def __data__(self):
|
||||
if is_op(self.value, Variable) and is_literal(self.superset):
|
||||
return {"in": {self.value.var: self.superset.value}}
|
||||
else:
|
||||
return {"in": [self.value.__data__(), self.superset.__data__()]}
|
||||
|
||||
def __eq__(self, other):
|
||||
if is_op(other, InOp):
|
||||
return self.value == other.value and self.superset == other.superset
|
||||
return False
|
||||
|
||||
def vars(self):
|
||||
return self.value.vars()
|
||||
|
||||
def map(self, map_):
|
||||
return self.lang[InOp([self.value.map(map_), self.superset.map(map_)])]
|
||||
|
||||
@simplified
|
||||
def partial_eval(self):
|
||||
value = self.value.partial_eval()
|
||||
superset = self.superset.partial_eval()
|
||||
if superset is NULL:
|
||||
return FALSE
|
||||
elif is_literal(value) and is_literal(superset):
|
||||
return self.lang[Literal(self())]
|
||||
else:
|
||||
return self.lang[InOp([value, superset])]
|
||||
|
||||
def __call__(self):
|
||||
return self.value() in self.superset()
|
||||
|
||||
def missing(self):
|
||||
return FALSE
|
||||
|
||||
eq_op.InOp = InOp
|
|
@ -0,0 +1,56 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions._utils import simplified
|
||||
from jx_base.expressions.coalesce_op import CoalesceOp
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.first_op import FirstOp
|
||||
from jx_base.language import is_op
|
||||
from mo_json import INTEGER
|
||||
|
||||
|
||||
class IntegerOp(Expression):
|
||||
data_type = INTEGER
|
||||
|
||||
def __init__(self, term):
|
||||
Expression.__init__(self, [term])
|
||||
self.term = term
|
||||
|
||||
def __data__(self):
|
||||
return {"integer": self.term.__data__()}
|
||||
|
||||
def vars(self):
|
||||
return self.term.vars()
|
||||
|
||||
def map(self, map_):
|
||||
return self.lang[IntegerOp(self.term.map(map_))]
|
||||
|
||||
def missing(self):
|
||||
return self.term.missing()
|
||||
|
||||
@simplified
|
||||
def partial_eval(self):
|
||||
term = self.lang[FirstOp(self.term)].partial_eval()
|
||||
if is_op(term, CoalesceOp):
|
||||
return self.lang[CoalesceOp([IntegerOp(t) for t in term.terms])]
|
||||
if term.type == INTEGER:
|
||||
return term
|
||||
return self.lang[IntegerOp(term)]
|
|
@ -0,0 +1,44 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.false_op import FALSE
|
||||
from mo_json import BOOLEAN
|
||||
|
||||
|
||||
class IsBooleanOp(Expression):
|
||||
data_type = BOOLEAN
|
||||
|
||||
def __init__(self, term):
|
||||
Expression.__init__(self, [term])
|
||||
self.term = term
|
||||
|
||||
def __data__(self):
|
||||
return {"is_boolean": self.term.__data__()}
|
||||
|
||||
def vars(self):
|
||||
return self.term.vars()
|
||||
|
||||
def map(self, map_):
|
||||
return self.lang[IsBooleanOp(self.term.map(map_))]
|
||||
|
||||
def missing(self):
|
||||
return FALSE
|
|
@ -0,0 +1,44 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.false_op import FALSE
|
||||
from mo_json import BOOLEAN
|
||||
|
||||
|
||||
class IsIntegerOp(Expression):
|
||||
data_type = BOOLEAN
|
||||
|
||||
def __init__(self, term):
|
||||
Expression.__init__(self, [term])
|
||||
self.term = term
|
||||
|
||||
def __data__(self):
|
||||
return {"is_integer": self.term.__data__()}
|
||||
|
||||
def vars(self):
|
||||
return self.term.vars()
|
||||
|
||||
def map(self, map_):
|
||||
return self.lang[IsIntegerOp(self.term.map(map_))]
|
||||
|
||||
def missing(self):
|
||||
return FALSE
|
|
@ -0,0 +1,60 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions._utils import simplified
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.false_op import FALSE
|
||||
from jx_base.expressions.null_op import NULL
|
||||
from jx_base.expressions.true_op import TRUE
|
||||
from mo_json import BOOLEAN, INTEGER, NUMBER, OBJECT, NUMBER_TYPES
|
||||
|
||||
|
||||
class IsNumberOp(Expression):
|
||||
data_type = BOOLEAN
|
||||
|
||||
def __init__(self, term):
|
||||
Expression.__init__(self, [term])
|
||||
self.term = term
|
||||
|
||||
def __data__(self):
|
||||
return {"is_number": self.term.__data__()}
|
||||
|
||||
def vars(self):
|
||||
return self.term.vars()
|
||||
|
||||
def map(self, map_):
|
||||
return self.lang[IsNumberOp(self.term.map(map_))]
|
||||
|
||||
def missing(self):
|
||||
return FALSE
|
||||
|
||||
@simplified
|
||||
def partial_eval(self):
|
||||
term = self.term.partial_eval()
|
||||
|
||||
if term is NULL:
|
||||
return FALSE
|
||||
elif term.type in NUMBER_TYPES:
|
||||
return TRUE
|
||||
elif term.type == OBJECT:
|
||||
return self
|
||||
else:
|
||||
return FALSE
|
|
@ -0,0 +1,44 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.false_op import FALSE
|
||||
from mo_json import BOOLEAN
|
||||
|
||||
|
||||
class IsStringOp(Expression):
|
||||
data_type = BOOLEAN
|
||||
|
||||
def __init__(self, term):
|
||||
Expression.__init__(self, [term])
|
||||
self.term = term
|
||||
|
||||
def __data__(self):
|
||||
return {"is_string": self.term.__data__()}
|
||||
|
||||
def vars(self):
|
||||
return self.term.vars()
|
||||
|
||||
def map(self, map_):
|
||||
return self.lang[IsStringOp(self.term.map(map_))]
|
||||
|
||||
def missing(self):
|
||||
return FALSE
|
|
@ -0,0 +1,62 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions._utils import simplified
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.literal import is_literal
|
||||
from jx_base.expressions.null_op import NULL
|
||||
from jx_base.language import is_op
|
||||
from mo_dots import is_many
|
||||
from mo_dots.lists import last
|
||||
from mo_json import OBJECT
|
||||
|
||||
|
||||
class LastOp(Expression):
|
||||
def __init__(self, term):
|
||||
Expression.__init__(self, [term])
|
||||
self.term = term
|
||||
self.data_type = self.term.type
|
||||
|
||||
def __data__(self):
|
||||
return {"last": self.term.__data__()}
|
||||
|
||||
def vars(self):
|
||||
return self.term.vars()
|
||||
|
||||
def map(self, map_):
|
||||
return self.lang[LastOp(self.term.map(map_))]
|
||||
|
||||
def missing(self):
|
||||
return self.term.missing()
|
||||
|
||||
@simplified
|
||||
def partial_eval(self):
|
||||
term = self.term.partial_eval()
|
||||
if is_op(self.term, LastOp):
|
||||
return term
|
||||
elif term.type != OBJECT and not term.many:
|
||||
return term
|
||||
elif term is NULL:
|
||||
return term
|
||||
elif is_literal(term):
|
||||
return last(term)
|
||||
else:
|
||||
return self.lang[LastOp(term)]
|
|
@ -0,0 +1,48 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.false_op import FALSE
|
||||
from mo_json import OBJECT
|
||||
|
||||
|
||||
class LeavesOp(Expression):
|
||||
date_type = OBJECT
|
||||
|
||||
def __init__(self, term, prefix=None):
|
||||
Expression.__init__(self, term)
|
||||
self.term = term
|
||||
self.prefix = prefix
|
||||
|
||||
def __data__(self):
|
||||
if self.prefix:
|
||||
return {"leaves": self.term.__data__(), "prefix": self.prefix}
|
||||
else:
|
||||
return {"leaves": self.term.__data__()}
|
||||
|
||||
def vars(self):
|
||||
return self.term.vars()
|
||||
|
||||
def map(self, map_):
|
||||
return self.lang[LeavesOp(self.term.map(map_))]
|
||||
|
||||
def missing(self):
|
||||
return FALSE
|
|
@ -0,0 +1,81 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions._utils import simplified
|
||||
from jx_base.expressions.basic_substring_op import BasicSubstringOp
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.length_op import LengthOp
|
||||
from jx_base.expressions.literal import ZERO
|
||||
from jx_base.expressions.literal import is_literal
|
||||
from jx_base.expressions.max_op import MaxOp
|
||||
from jx_base.expressions.min_op import MinOp
|
||||
from jx_base.expressions.or_op import OrOp
|
||||
from jx_base.expressions.variable import Variable
|
||||
from jx_base.expressions.when_op import WhenOp
|
||||
from jx_base.language import is_op
|
||||
from mo_dots import is_data
|
||||
from mo_json import STRING
|
||||
|
||||
|
||||
class LeftOp(Expression):
|
||||
has_simple_form = True
|
||||
data_type = STRING
|
||||
|
||||
def __init__(self, term):
|
||||
Expression.__init__(self, term)
|
||||
if is_data(term):
|
||||
self.value, self.length = term.items()[0]
|
||||
else:
|
||||
self.value, self.length = term
|
||||
|
||||
def __data__(self):
|
||||
if is_op(self.value, Variable) and is_literal(self.length):
|
||||
return {"left": {self.value.var: self.length.value}}
|
||||
else:
|
||||
return {"left": [self.value.__data__(), self.length.__data__()]}
|
||||
|
||||
def vars(self):
|
||||
return self.value.vars() | self.length.vars()
|
||||
|
||||
def map(self, map_):
|
||||
return self.lang[LeftOp([self.value.map(map_), self.length.map(map_)])]
|
||||
|
||||
def missing(self):
|
||||
return self.lang[
|
||||
OrOp([self.value.missing(), self.length.missing()])
|
||||
].partial_eval()
|
||||
|
||||
@simplified
|
||||
def partial_eval(self):
|
||||
value = self.lang[self.value].partial_eval()
|
||||
length = self.lang[self.length].partial_eval()
|
||||
max_length = LengthOp(value)
|
||||
|
||||
return self.lang[
|
||||
WhenOp(
|
||||
self.missing(),
|
||||
**{
|
||||
"else": BasicSubstringOp(
|
||||
[value, ZERO, MaxOp([ZERO, MinOp([length, max_length])])]
|
||||
)
|
||||
}
|
||||
)
|
||||
].partial_eval()
|
|
@ -0,0 +1,64 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions._utils import simplified
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.literal import Literal
|
||||
from jx_base.expressions.literal import is_literal
|
||||
from jx_base.expressions.null_op import NULL
|
||||
from jx_base.language import is_op
|
||||
from mo_future import is_text
|
||||
from mo_json import INTEGER
|
||||
|
||||
|
||||
class LengthOp(Expression):
|
||||
data_type = INTEGER
|
||||
|
||||
def __init__(self, term):
|
||||
Expression.__init__(self, [term])
|
||||
self.term = term
|
||||
|
||||
def __eq__(self, other):
|
||||
if is_op(other, LengthOp):
|
||||
return self.term == other.term
|
||||
|
||||
def __data__(self):
|
||||
return {"length": self.term.__data__()}
|
||||
|
||||
def vars(self):
|
||||
return self.term.vars()
|
||||
|
||||
def map(self, map_):
|
||||
return self.lang[LengthOp(self.term.map(map_))]
|
||||
|
||||
def missing(self):
|
||||
return self.term.missing()
|
||||
|
||||
@simplified
|
||||
def partial_eval(self):
|
||||
term = self.lang[self.term].partial_eval()
|
||||
if is_literal(term):
|
||||
if is_text(term.value):
|
||||
return self.lang[Literal(len(term.value))]
|
||||
else:
|
||||
return NULL
|
||||
else:
|
||||
return self.lang[LengthOp(term)]
|
|
@ -0,0 +1,142 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions import _utils, expression
|
||||
from jx_base.expressions._utils import simplified, value2json
|
||||
from jx_base.expressions.expression import Expression
|
||||
from mo_dots import Null, is_data
|
||||
from mo_json import python_type_to_json_type
|
||||
|
||||
DateOp, FALSE, TRUE, NULL = [None]*4
|
||||
|
||||
class Literal(Expression):
|
||||
"""
|
||||
A literal JSON document
|
||||
"""
|
||||
|
||||
def __new__(cls, term):
|
||||
if term == None:
|
||||
return NULL
|
||||
if term is True:
|
||||
return TRUE
|
||||
if term is False:
|
||||
return FALSE
|
||||
if is_data(term) and term.get("date"):
|
||||
# SPECIAL CASE
|
||||
return cls.lang[DateOp(term.get("date"))]
|
||||
return object.__new__(cls)
|
||||
|
||||
def __init__(self, value):
|
||||
Expression.__init__(self, None)
|
||||
self.simplified = True
|
||||
self._value = value
|
||||
|
||||
@classmethod
|
||||
def define(cls, expr):
|
||||
return Literal(expr.get("literal"))
|
||||
|
||||
def __nonzero__(self):
|
||||
return True
|
||||
|
||||
def __eq__(self, other):
|
||||
if other == None:
|
||||
if self._value == None:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
elif self._value == None:
|
||||
return False
|
||||
|
||||
if is_literal(other):
|
||||
return (self._value == other._value) or (self.json == other.json)
|
||||
|
||||
def __data__(self):
|
||||
return {"literal": self.value}
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
return self._value
|
||||
|
||||
@property
|
||||
def json(self):
|
||||
if self._value == "":
|
||||
self._json = '""'
|
||||
else:
|
||||
self._json = value2json(self._value)
|
||||
|
||||
return self._json
|
||||
|
||||
def vars(self):
|
||||
return set()
|
||||
|
||||
def map(self, map_):
|
||||
return self
|
||||
|
||||
def missing(self):
|
||||
if self._value in [None, Null]:
|
||||
return TRUE
|
||||
if self.value == "":
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
def __call__(self, row=None, rownum=None, rows=None):
|
||||
return self.value
|
||||
|
||||
def __unicode__(self):
|
||||
return self._json
|
||||
|
||||
def __str__(self):
|
||||
return str(self._json)
|
||||
|
||||
@property
|
||||
def type(self):
|
||||
return python_type_to_json_type[self._value.__class__]
|
||||
|
||||
@simplified
|
||||
def partial_eval(self):
|
||||
return self
|
||||
|
||||
def str(self):
|
||||
return str(self.value)
|
||||
|
||||
|
||||
ZERO = Literal(0)
|
||||
ONE = Literal(1)
|
||||
|
||||
|
||||
literal_op_ids = tuple()
|
||||
|
||||
|
||||
def register_literal(op):
|
||||
global literal_op_ids
|
||||
literal_op_ids = literal_op_ids+(op.get_id(),)
|
||||
|
||||
|
||||
def is_literal(l):
|
||||
try:
|
||||
return l.get_id() in literal_op_ids
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
_utils.Literal = Literal
|
||||
expression.Literal = Literal
|
||||
expression.is_literal=is_literal
|
|
@ -0,0 +1,26 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions.base_inequality_op import BaseInequalityOp
|
||||
|
||||
|
||||
class LtOp(BaseInequalityOp):
|
||||
op = "lt"
|
|
@ -0,0 +1,26 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions.base_inequality_op import BaseInequalityOp
|
||||
|
||||
|
||||
class LteOp(BaseInequalityOp):
|
||||
op = "lte"
|
|
@ -0,0 +1,82 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions._utils import simplified
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.false_op import FALSE
|
||||
from jx_base.expressions.literal import Literal, is_literal
|
||||
from jx_base.expressions.null_op import NULL
|
||||
from mo_dots import is_many
|
||||
from mo_json import NUMBER
|
||||
from mo_math import MAX
|
||||
|
||||
|
||||
class MaxOp(Expression):
|
||||
data_type = NUMBER
|
||||
|
||||
def __init__(self, terms):
|
||||
Expression.__init__(self, terms)
|
||||
if terms == None:
|
||||
self.terms = []
|
||||
elif is_many(terms):
|
||||
self.terms = [t for t in terms if t != None]
|
||||
else:
|
||||
self.terms = [terms]
|
||||
|
||||
def __data__(self):
|
||||
return {"max": [t.__data__() for t in self.terms]}
|
||||
|
||||
def vars(self):
|
||||
output = set()
|
||||
for t in self.terms:
|
||||
output |= t.vars()
|
||||
return output
|
||||
|
||||
def map(self, map_):
|
||||
return self.lang[MaxOp([t.map(map_) for t in self.terms])]
|
||||
|
||||
def missing(self):
|
||||
return FALSE
|
||||
|
||||
@simplified
|
||||
def partial_eval(self):
|
||||
maximum = None
|
||||
terms = []
|
||||
for t in self.terms:
|
||||
simple = t.partial_eval()
|
||||
if simple is NULL:
|
||||
pass
|
||||
elif is_literal(simple):
|
||||
maximum = MAX([maximum, simple.value])
|
||||
else:
|
||||
terms.append(simple)
|
||||
if len(terms) == 0:
|
||||
if maximum == None:
|
||||
return NULL
|
||||
else:
|
||||
return Literal(maximum)
|
||||
else:
|
||||
if maximum == None:
|
||||
output = self.lang[MaxOp(terms)]
|
||||
else:
|
||||
output = self.lang[MaxOp([Literal(maximum)] + terms)]
|
||||
|
||||
return output
|
|
@ -0,0 +1,85 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions._utils import simplified
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.false_op import FALSE
|
||||
from jx_base.expressions.literal import Literal
|
||||
from jx_base.expressions.literal import is_literal
|
||||
from jx_base.expressions.null_op import NULL
|
||||
from jx_base.expressions.null_op import NullOp
|
||||
from jx_base.language import is_op
|
||||
from mo_dots import is_many
|
||||
from mo_json import NUMBER
|
||||
from mo_math import MIN
|
||||
|
||||
|
||||
class MinOp(Expression):
|
||||
data_type = NUMBER
|
||||
|
||||
def __init__(self, terms):
|
||||
Expression.__init__(self, terms)
|
||||
if terms == None:
|
||||
self.terms = []
|
||||
elif is_many(terms):
|
||||
self.terms = terms
|
||||
else:
|
||||
self.terms = [terms]
|
||||
|
||||
def __data__(self):
|
||||
return {"min": [t.__data__() for t in self.terms]}
|
||||
|
||||
def vars(self):
|
||||
output = set()
|
||||
for t in self.terms:
|
||||
output |= t.vars()
|
||||
return output
|
||||
|
||||
def map(self, map_):
|
||||
return self.lang[MinOp([t.map(map_) for t in self.terms])]
|
||||
|
||||
def missing(self):
|
||||
return FALSE
|
||||
|
||||
@simplified
|
||||
def partial_eval(self):
|
||||
minimum = None
|
||||
terms = []
|
||||
for t in self.terms:
|
||||
simple = t.partial_eval()
|
||||
if is_op(simple, NullOp):
|
||||
pass
|
||||
elif is_literal(simple):
|
||||
minimum = MIN([minimum, simple.value])
|
||||
else:
|
||||
terms.append(simple)
|
||||
if len(terms) == 0:
|
||||
if minimum == None:
|
||||
return NULL
|
||||
else:
|
||||
return Literal(minimum)
|
||||
else:
|
||||
if minimum == None:
|
||||
output = self.lang[MinOp(terms)]
|
||||
else:
|
||||
output = self.lang[MinOp([Literal(minimum)] + terms)]
|
||||
|
||||
return output
|
|
@ -0,0 +1,68 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions import expression
|
||||
from jx_base.expressions._utils import simplified
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.false_op import FALSE
|
||||
from jx_base.expressions.true_op import TRUE
|
||||
from jx_base.language import is_op
|
||||
from mo_json import BOOLEAN
|
||||
|
||||
|
||||
class MissingOp(Expression):
|
||||
data_type = BOOLEAN
|
||||
|
||||
def __init__(self, term):
|
||||
Expression.__init__(self, term)
|
||||
self.expr = term
|
||||
|
||||
def __data__(self):
|
||||
return {"missing": self.expr.__data__()}
|
||||
|
||||
def __eq__(self, other):
|
||||
if not is_op(other, MissingOp):
|
||||
return False
|
||||
else:
|
||||
return self.expr == other.expr
|
||||
|
||||
def vars(self):
|
||||
return self.expr.vars()
|
||||
|
||||
def map(self, map_):
|
||||
return self.lang[MissingOp(self.expr.map(map_))]
|
||||
|
||||
def missing(self):
|
||||
return FALSE
|
||||
|
||||
def exists(self):
|
||||
return TRUE
|
||||
|
||||
@simplified
|
||||
def partial_eval(self):
|
||||
output = self.lang[self.expr].partial_eval().missing()
|
||||
if is_op(output, MissingOp):
|
||||
return output
|
||||
else:
|
||||
return output.partial_eval()
|
||||
|
||||
|
||||
expression.MissingOp = MissingOp
|
|
@ -0,0 +1,26 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions.base_binary_op import BaseBinaryOp
|
||||
|
||||
|
||||
class ModOp(BaseBinaryOp):
|
||||
op = "mod"
|
|
@ -0,0 +1,26 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions.base_multi_op import BaseMultiOp
|
||||
|
||||
|
||||
class MulOp(BaseMultiOp):
|
||||
op = "mul"
|
|
@ -0,0 +1,71 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions import not_op
|
||||
from jx_base.expressions._utils import simplified
|
||||
from jx_base.expressions.eq_op import EqOp
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.false_op import FALSE
|
||||
from jx_base.expressions.literal import is_literal
|
||||
from jx_base.expressions.not_op import NotOp
|
||||
from jx_base.expressions.variable import Variable
|
||||
from jx_base.language import is_op
|
||||
from mo_dots import is_data, is_sequence
|
||||
from mo_json import BOOLEAN
|
||||
from mo_logs import Log
|
||||
|
||||
|
||||
class NeOp(Expression):
|
||||
has_simple_form = True
|
||||
data_type = BOOLEAN
|
||||
|
||||
def __init__(self, terms):
|
||||
Expression.__init__(self, terms)
|
||||
if is_sequence(terms):
|
||||
self.lhs, self.rhs = terms
|
||||
elif is_data(terms):
|
||||
self.rhs, self.lhs = terms.items()[0]
|
||||
else:
|
||||
Log.error("logic error")
|
||||
|
||||
def __data__(self):
|
||||
if is_op(self.lhs, Variable) and is_literal(self.rhs):
|
||||
return {"ne": {self.lhs.var, self.rhs.value}}
|
||||
else:
|
||||
return {"ne": [self.lhs.__data__(), self.rhs.__data__()]}
|
||||
|
||||
def vars(self):
|
||||
return self.lhs.vars() | self.rhs.vars()
|
||||
|
||||
def map(self, map_):
|
||||
return self.lang[NeOp([self.lhs.map(map_), self.rhs.map(map_)])]
|
||||
|
||||
def missing(self):
|
||||
return (
|
||||
FALSE
|
||||
) # USING THE decisive EQUAILTY https://github.com/mozilla/jx-sqlite/blob/master/docs/Logical%20Equality.md#definitions
|
||||
|
||||
@simplified
|
||||
def partial_eval(self):
|
||||
output = self.lang[NotOp(EqOp([self.lhs, self.rhs]))].partial_eval()
|
||||
return output
|
||||
|
||||
not_op.NeOp = NeOp
|
|
@ -0,0 +1,83 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions._utils import simplified
|
||||
from jx_base.expressions.basic_substring_op import BasicSubstringOp
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.length_op import LengthOp
|
||||
from jx_base.expressions.literal import ZERO
|
||||
from jx_base.expressions.literal import is_literal
|
||||
from jx_base.expressions.max_op import MaxOp
|
||||
from jx_base.expressions.min_op import MinOp
|
||||
from jx_base.expressions.or_op import OrOp
|
||||
from jx_base.expressions.variable import Variable
|
||||
from jx_base.expressions.when_op import WhenOp
|
||||
from jx_base.language import is_op
|
||||
from mo_dots import is_data
|
||||
from mo_json import STRING
|
||||
|
||||
|
||||
class NotLeftOp(Expression):
|
||||
has_simple_form = True
|
||||
data_type = STRING
|
||||
|
||||
def __init__(self, term):
|
||||
Expression.__init__(self, term)
|
||||
if is_data(term):
|
||||
self.value, self.length = term.items()[0]
|
||||
else:
|
||||
self.value, self.length = term
|
||||
|
||||
def __data__(self):
|
||||
if is_op(self.value, Variable) and is_literal(self.length):
|
||||
return {"not_left": {self.value.var: self.length.value}}
|
||||
else:
|
||||
return {"not_left": [self.value.__data__(), self.length.__data__()]}
|
||||
|
||||
def vars(self):
|
||||
return self.value.vars() | self.length.vars()
|
||||
|
||||
def map(self, map_):
|
||||
return self.lang[NotLeftOp([self.value.map(map_), self.length.map(map_)])]
|
||||
|
||||
def missing(self):
|
||||
return self.lang[OrOp([self.value.missing(), self.length.missing()])]
|
||||
|
||||
@simplified
|
||||
def partial_eval(self):
|
||||
value = self.lang[self.value].partial_eval()
|
||||
length = self.length.partial_eval()
|
||||
|
||||
if length is ZERO:
|
||||
return value
|
||||
|
||||
max_length = LengthOp(value)
|
||||
output = self.lang[
|
||||
WhenOp(
|
||||
self.missing(),
|
||||
**{
|
||||
"else": BasicSubstringOp(
|
||||
[value, MaxOp([ZERO, MinOp([length, max_length])]), max_length]
|
||||
)
|
||||
}
|
||||
)
|
||||
].partial_eval()
|
||||
return output
|
|
@ -0,0 +1,126 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions import and_op, exists_op, expression
|
||||
from jx_base.expressions._utils import simplified
|
||||
from jx_base.expressions.and_op import AndOp
|
||||
from jx_base.expressions.basic_index_of_op import BasicIndexOfOp
|
||||
from jx_base.expressions.basic_substring_op import BasicSubstringOp
|
||||
from jx_base.expressions.eq_op import EqOp
|
||||
from jx_base.expressions.exists_op import ExistsOp
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.false_op import FALSE
|
||||
from jx_base.expressions.literal import is_literal
|
||||
from jx_base.expressions.missing_op import MissingOp
|
||||
from jx_base.expressions.null_op import NULL
|
||||
from jx_base.expressions.or_op import OrOp
|
||||
from jx_base.expressions.true_op import TRUE
|
||||
from jx_base.language import is_op
|
||||
from mo_json import BOOLEAN
|
||||
from mo_logs import Log
|
||||
|
||||
CaseOp = None
|
||||
NeOp = None
|
||||
WhenOp = None
|
||||
|
||||
class NotOp(Expression):
|
||||
data_type = BOOLEAN
|
||||
|
||||
def __init__(self, term):
|
||||
Expression.__init__(self, term)
|
||||
self.term = term
|
||||
|
||||
def __data__(self):
|
||||
return {"not": self.term.__data__()}
|
||||
|
||||
def __eq__(self, other):
|
||||
if not is_op(other, NotOp):
|
||||
return False
|
||||
return self.term == other.term
|
||||
|
||||
def vars(self):
|
||||
return self.term.vars()
|
||||
|
||||
def map(self, map_):
|
||||
return self.lang[NotOp(self.term.map(map_))]
|
||||
|
||||
def missing(self):
|
||||
return self.term.missing()
|
||||
|
||||
@simplified
|
||||
def partial_eval(self):
|
||||
def inverse(term):
|
||||
if term is TRUE:
|
||||
return FALSE
|
||||
elif term is FALSE:
|
||||
return TRUE
|
||||
elif term is NULL:
|
||||
return TRUE
|
||||
elif is_literal(term):
|
||||
Log.error("`not` operator expects a Boolean term")
|
||||
elif is_op(term, WhenOp):
|
||||
output = self.lang[
|
||||
WhenOp(
|
||||
term.when,
|
||||
**{"then": inverse(term.then), "else": inverse(term.els_)}
|
||||
)
|
||||
].partial_eval()
|
||||
elif is_op(term, CaseOp): # REWRITING
|
||||
output = self.lang[
|
||||
CaseOp(
|
||||
[
|
||||
WhenOp(w.when, **{"then": inverse(w.then)})
|
||||
if is_op(w, WhenOp)
|
||||
else inverse(w)
|
||||
for w in term.whens
|
||||
]
|
||||
)
|
||||
].partial_eval()
|
||||
elif is_op(term, AndOp):
|
||||
output = self.lang[
|
||||
OrOp([inverse(t) for t in term.terms])
|
||||
].partial_eval()
|
||||
elif is_op(term, OrOp):
|
||||
output = self.lang[
|
||||
AndOp([inverse(t) for t in term.terms])
|
||||
].partial_eval()
|
||||
elif is_op(term, MissingOp):
|
||||
output = self.lang[NotOp(term.expr.missing())]
|
||||
elif is_op(term, ExistsOp):
|
||||
output = term.field.missing().partial_eval()
|
||||
elif is_op(term, NotOp):
|
||||
output = self.lang[term.term].partial_eval()
|
||||
elif is_op(term, NeOp):
|
||||
output = self.lang[EqOp([term.lhs, term.rhs])].partial_eval()
|
||||
elif is_op(term, BasicIndexOfOp) or is_op(term, BasicSubstringOp):
|
||||
return FALSE
|
||||
else:
|
||||
output = self.lang[NotOp(term)]
|
||||
|
||||
return output
|
||||
|
||||
output = inverse(self.lang[self.term].partial_eval())
|
||||
return output
|
||||
|
||||
|
||||
and_op.NotOp = NotOp
|
||||
exists_op.NotOp = NotOp
|
||||
expression.NotOp =NotOp
|
|
@ -0,0 +1,81 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions._utils import simplified
|
||||
from jx_base.expressions.basic_substring_op import BasicSubstringOp
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.length_op import LengthOp
|
||||
from jx_base.expressions.literal import ZERO
|
||||
from jx_base.expressions.literal import is_literal
|
||||
from jx_base.expressions.max_op import MaxOp
|
||||
from jx_base.expressions.min_op import MinOp
|
||||
from jx_base.expressions.or_op import OrOp
|
||||
from jx_base.expressions.sub_op import SubOp
|
||||
from jx_base.expressions.variable import Variable
|
||||
from jx_base.expressions.when_op import WhenOp
|
||||
from jx_base.language import is_op
|
||||
from mo_dots import is_data
|
||||
from mo_json import STRING
|
||||
|
||||
|
||||
class NotRightOp(Expression):
|
||||
has_simple_form = True
|
||||
data_type = STRING
|
||||
|
||||
def __init__(self, term):
|
||||
Expression.__init__(self, term)
|
||||
if is_data(term):
|
||||
self.value, self.length = term.items()[0]
|
||||
else:
|
||||
self.value, self.length = term
|
||||
|
||||
def __data__(self):
|
||||
if is_op(self.value, Variable) and is_literal(self.length):
|
||||
return {"not_right": {self.value.var: self.length.value}}
|
||||
else:
|
||||
return {"not_right": [self.value.__data__(), self.length.__data__()]}
|
||||
|
||||
def vars(self):
|
||||
return self.value.vars() | self.length.vars()
|
||||
|
||||
def map(self, map_):
|
||||
return self.lang[NotRightOp([self.value.map(map_), self.length.map(map_)])]
|
||||
|
||||
def missing(self):
|
||||
return self.lang[OrOp([self.value.missing(), self.length.missing()])]
|
||||
|
||||
@simplified
|
||||
def partial_eval(self):
|
||||
value = self.lang[self.value].partial_eval()
|
||||
length = self.length.partial_eval()
|
||||
|
||||
if length is ZERO:
|
||||
return value
|
||||
|
||||
max_length = LengthOp(value)
|
||||
part = BasicSubstringOp(
|
||||
[
|
||||
value,
|
||||
ZERO,
|
||||
MaxOp([ZERO, MinOp([max_length, SubOp([max_length, length])])]),
|
||||
]
|
||||
)
|
||||
return self.lang[WhenOp(self.missing(), **{"else": part})].partial_eval()
|
|
@ -0,0 +1,113 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions import literal, _utils, expression
|
||||
from jx_base.expressions.false_op import FALSE
|
||||
from jx_base.expressions.literal import Literal
|
||||
from jx_base.expressions.true_op import TRUE
|
||||
from jx_base.language import TYPE_ORDER
|
||||
from mo_dots import Null
|
||||
from mo_json import IS_NULL, OBJECT
|
||||
from mo_logs import Log
|
||||
|
||||
|
||||
class NullOp(Literal):
|
||||
"""
|
||||
FOR USE WHEN EVERYTHING IS EXPECTED TO BE AN Expression
|
||||
USE IT TO EXPECT A NULL VALUE IN assertAlmostEqual
|
||||
"""
|
||||
|
||||
data_type = OBJECT
|
||||
|
||||
@classmethod
|
||||
def define(cls, expr):
|
||||
return NULL
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
return object.__new__(cls, *args, **kwargs)
|
||||
|
||||
def __init__(self, op=None, term=None):
|
||||
Literal.__init__(self, None)
|
||||
|
||||
def __nonzero__(self):
|
||||
return True
|
||||
|
||||
def __eq__(self, other):
|
||||
return other is NULL
|
||||
|
||||
def __gt__(self, other):
|
||||
return False
|
||||
|
||||
def __lt__(self, other):
|
||||
return False
|
||||
|
||||
def __ge__(self, other):
|
||||
if other == None:
|
||||
return True
|
||||
return False
|
||||
|
||||
def __le__(self, other):
|
||||
if other == None:
|
||||
return True
|
||||
return False
|
||||
|
||||
def __data__(self):
|
||||
return {"null": {}}
|
||||
|
||||
def vars(self):
|
||||
return set()
|
||||
|
||||
def map(self, map_):
|
||||
return self
|
||||
|
||||
def missing(self):
|
||||
return TRUE
|
||||
|
||||
def exists(self):
|
||||
return FALSE
|
||||
|
||||
def __call__(self, row=None, rownum=None, rows=None):
|
||||
return Null
|
||||
|
||||
def __unicode__(self):
|
||||
return "null"
|
||||
|
||||
def __str__(self):
|
||||
return b"null"
|
||||
|
||||
@property
|
||||
def type(self):
|
||||
return IS_NULL
|
||||
|
||||
def __hash__(self):
|
||||
return id(None)
|
||||
|
||||
def __bool__(self):
|
||||
Log.error("Detecting truthiness of NullOp is too confusing to be allowed")
|
||||
|
||||
|
||||
NULL = NullOp()
|
||||
TYPE_ORDER[NullOp] = 9
|
||||
TYPE_ORDER[NULL] = 9
|
||||
|
||||
literal.NULL = NULL
|
||||
_utils.NULL = NULL
|
||||
expression.NULL=NULL
|
|
@ -0,0 +1,94 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions._utils import simplified
|
||||
from jx_base.expressions.case_op import CaseOp
|
||||
from jx_base.expressions.coalesce_op import CoalesceOp
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.false_op import FALSE
|
||||
from jx_base.expressions.first_op import FirstOp
|
||||
from jx_base.expressions.literal import Literal, ZERO, ONE
|
||||
from jx_base.expressions.literal import is_literal
|
||||
from jx_base.expressions.null_op import NULL
|
||||
from jx_base.expressions.true_op import TRUE
|
||||
from jx_base.expressions.when_op import WhenOp
|
||||
from jx_base.language import is_op
|
||||
from mo_future import text
|
||||
from mo_json import NUMBER
|
||||
from mo_logs import Log
|
||||
|
||||
|
||||
class NumberOp(Expression):
|
||||
data_type = NUMBER
|
||||
|
||||
def __init__(self, term):
|
||||
Expression.__init__(self, [term])
|
||||
self.term = term
|
||||
|
||||
def __data__(self):
|
||||
return {"number": self.term.__data__()}
|
||||
|
||||
def vars(self):
|
||||
return self.term.vars()
|
||||
|
||||
def map(self, map_):
|
||||
return self.lang[NumberOp(self.term.map(map_))]
|
||||
|
||||
def missing(self):
|
||||
return self.term.missing()
|
||||
|
||||
@simplified
|
||||
def partial_eval(self):
|
||||
term = self.lang[FirstOp(self.term)].partial_eval()
|
||||
|
||||
if is_literal(term):
|
||||
if term is NULL:
|
||||
return NULL
|
||||
elif term is FALSE:
|
||||
return ZERO
|
||||
elif term is TRUE:
|
||||
return ONE
|
||||
elif isinstance(term.value, text):
|
||||
return Literal(float(text))
|
||||
elif isinstance(term.value, (int, float)):
|
||||
return term
|
||||
else:
|
||||
Log.error("can not convert {{value|json}} to number", value=term.value)
|
||||
elif is_op(term, CaseOp): # REWRITING
|
||||
return self.lang[
|
||||
CaseOp(
|
||||
[
|
||||
WhenOp(t.when, **{"then": NumberOp(t.then)})
|
||||
for t in term.whens[:-1]
|
||||
]
|
||||
+ [NumberOp(term.whens[-1])]
|
||||
)
|
||||
].partial_eval()
|
||||
elif is_op(term, WhenOp): # REWRITING
|
||||
return self.lang[
|
||||
WhenOp(
|
||||
term.when,
|
||||
**{"then": NumberOp(term.then), "else": NumberOp(term.els_)}
|
||||
)
|
||||
].partial_eval()
|
||||
elif is_op(term, CoalesceOp):
|
||||
return self.lang[CoalesceOp([NumberOp(t) for t in term.terms])]
|
||||
return self.lang[NumberOp(term)]
|
|
@ -0,0 +1,61 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions.expression import Expression
|
||||
from mo_future import text
|
||||
from mo_logs import Log
|
||||
from mo_math import is_integer
|
||||
|
||||
|
||||
class OffsetOp(Expression):
|
||||
"""
|
||||
OFFSET INDEX INTO A TUPLE
|
||||
"""
|
||||
|
||||
def __init__(self, var):
|
||||
Expression.__init__(self, None)
|
||||
if not is_integer(var):
|
||||
Log.error("Expecting an integer")
|
||||
self.var = var
|
||||
|
||||
def __call__(self, row, rownum=None, rows=None):
|
||||
try:
|
||||
return row[self.var]
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def __data__(self):
|
||||
return {"offset": self.var}
|
||||
|
||||
def vars(self):
|
||||
return {}
|
||||
|
||||
def __hash__(self):
|
||||
return self.var.__hash__()
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.var == other
|
||||
|
||||
def __unicode__(self):
|
||||
return text(self.var)
|
||||
|
||||
def __str__(self):
|
||||
return str(self.var)
|
|
@ -0,0 +1,99 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions import and_op
|
||||
from jx_base.expressions._utils import simplified
|
||||
from jx_base.expressions.and_op import AndOp
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.false_op import FALSE
|
||||
from jx_base.expressions.true_op import TRUE
|
||||
from jx_base.language import is_op
|
||||
from mo_json import BOOLEAN
|
||||
|
||||
|
||||
class OrOp(Expression):
|
||||
data_type = BOOLEAN
|
||||
|
||||
def __init__(self, terms):
|
||||
Expression.__init__(self, terms)
|
||||
self.terms = terms
|
||||
|
||||
def __data__(self):
|
||||
return {"or": [t.__data__() for t in self.terms]}
|
||||
|
||||
def vars(self):
|
||||
output = set()
|
||||
for t in self.terms:
|
||||
output |= t.vars()
|
||||
return output
|
||||
|
||||
def map(self, map_):
|
||||
return self.lang[OrOp([t.map(map_) for t in self.terms])]
|
||||
|
||||
def missing(self):
|
||||
return FALSE
|
||||
|
||||
def __call__(self, row=None, rownum=None, rows=None):
|
||||
return any(t(row, rownum, rows) for t in self.terms)
|
||||
|
||||
def __eq__(self, other):
|
||||
if not is_op(other, OrOp):
|
||||
return False
|
||||
if len(self.terms) != len(other.terms):
|
||||
return False
|
||||
return all(t == u for t, u in zip(self.terms, other.terms))
|
||||
|
||||
@simplified
|
||||
def partial_eval(self):
|
||||
terms = []
|
||||
ands = []
|
||||
for t in self.terms:
|
||||
simple = self.lang[t].partial_eval()
|
||||
if simple.type != BOOLEAN:
|
||||
simple = simple.exists()
|
||||
|
||||
if simple is TRUE:
|
||||
return TRUE
|
||||
elif simple is FALSE:
|
||||
pass
|
||||
elif is_op(simple, OrOp):
|
||||
terms.extend([tt for tt in simple.terms if tt not in terms])
|
||||
elif is_op(simple, AndOp):
|
||||
ands.append(simple)
|
||||
elif simple not in terms:
|
||||
terms.append(simple)
|
||||
|
||||
if ands: # REMOVE TERMS THAT ARE MORE RESTRICTIVE THAN OTHERS
|
||||
for a in ands:
|
||||
for tt in a.terms:
|
||||
if tt in terms:
|
||||
break
|
||||
else:
|
||||
terms.append(a)
|
||||
|
||||
if len(terms) == 0:
|
||||
return FALSE
|
||||
if len(terms) == 1:
|
||||
return terms[0]
|
||||
return self.lang[OrOp(terms)]
|
||||
|
||||
|
||||
and_op.OrOp = OrOp
|
|
@ -0,0 +1,88 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions._utils import simplified
|
||||
from jx_base.expressions.basic_starts_with_op import BasicStartsWithOp
|
||||
from jx_base.expressions.case_op import CaseOp
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.false_op import FALSE
|
||||
from jx_base.expressions.literal import is_literal
|
||||
from jx_base.expressions.null_op import NULL
|
||||
from jx_base.expressions.true_op import TRUE
|
||||
from jx_base.expressions.variable import Variable
|
||||
from jx_base.expressions.when_op import WhenOp
|
||||
from jx_base.language import is_op
|
||||
from mo_dots import is_data
|
||||
from mo_json import BOOLEAN
|
||||
|
||||
|
||||
class PrefixOp(Expression):
|
||||
has_simple_form = True
|
||||
data_type = BOOLEAN
|
||||
|
||||
def __init__(self, term):
|
||||
Expression.__init__(self, term)
|
||||
if not term:
|
||||
self.expr = NULL
|
||||
self.prefix = NULL
|
||||
elif is_data(term):
|
||||
self.expr, self.prefix = term.items()[0]
|
||||
else:
|
||||
self.expr, self.prefix = term
|
||||
|
||||
def __data__(self):
|
||||
if not self.expr:
|
||||
return {"prefix": {}}
|
||||
elif is_op(self.expr, Variable) and is_literal(self.prefix):
|
||||
return {"prefix": {self.expr.var: self.prefix.value}}
|
||||
else:
|
||||
return {"prefix": [self.expr.__data__(), self.prefix.__data__()]}
|
||||
|
||||
def vars(self):
|
||||
if self.expr is NULL:
|
||||
return set()
|
||||
return self.expr.vars() | self.prefix.vars()
|
||||
|
||||
def map(self, map_):
|
||||
if not self.expr:
|
||||
return self
|
||||
else:
|
||||
return self.lang[PrefixOp([self.expr.map(map_), self.prefix.map(map_)])]
|
||||
|
||||
def missing(self):
|
||||
return FALSE
|
||||
|
||||
@simplified
|
||||
def partial_eval(self):
|
||||
return self.lang[
|
||||
CaseOp(
|
||||
[
|
||||
WhenOp(self.prefix.missing(), then=TRUE),
|
||||
WhenOp(self.expr.missing(), then=FALSE),
|
||||
BasicStartsWithOp([self.expr, self.prefix]),
|
||||
]
|
||||
)
|
||||
].partial_eval()
|
||||
|
||||
def __eq__(self, other):
|
||||
if not is_op(other, PrefixOp):
|
||||
return False
|
||||
return self.expr == other.expr and self.prefix == other.prefix
|
|
@ -0,0 +1,30 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions.expression import Expression
|
||||
|
||||
|
||||
class PythonScript(Expression):
|
||||
"""
|
||||
REPRESENT A Python SCRIPT
|
||||
"""
|
||||
|
||||
pass
|
|
@ -0,0 +1,26 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions.expression import Expression
|
||||
|
||||
|
||||
class QueryOp(Expression):
|
||||
pass
|
|
@ -0,0 +1,47 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions._utils import operators
|
||||
from jx_base.expressions.and_op import AndOp
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.literal import Literal
|
||||
from mo_json import BOOLEAN
|
||||
from mo_logs import Log
|
||||
|
||||
|
||||
class RangeOp(Expression):
|
||||
has_simple_form = True
|
||||
data_type = BOOLEAN
|
||||
|
||||
def __new__(cls, term, *args):
|
||||
Expression.__new__(cls, *args)
|
||||
field, comparisons = term # comparisons IS A Literal()
|
||||
return cls.lang[
|
||||
AndOp(
|
||||
[
|
||||
getattr(cls.lang, operators[op])([field, Literal(value)])
|
||||
for op, value in comparisons.value.items()
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
def __init__(self, term):
|
||||
Log.error("Should never happen!")
|
|
@ -0,0 +1,49 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.false_op import FALSE
|
||||
from jx_base.expressions.true_op import TRUE
|
||||
from mo_json import BOOLEAN
|
||||
|
||||
|
||||
class RegExpOp(Expression):
|
||||
has_simple_form = True
|
||||
data_type = BOOLEAN
|
||||
|
||||
def __init__(self, terms):
|
||||
Expression.__init__(self, terms)
|
||||
self.var, self.pattern = terms
|
||||
|
||||
def __data__(self):
|
||||
return {"regexp": {self.var.var: self.pattern}}
|
||||
|
||||
def vars(self):
|
||||
return {self.var}
|
||||
|
||||
def map(self, map_):
|
||||
return self.lang[RegExpOp([self.var.map(map_), self.pattern])]
|
||||
|
||||
def missing(self):
|
||||
return FALSE
|
||||
|
||||
def exists(self):
|
||||
return TRUE
|
|
@ -0,0 +1,89 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions._utils import simplified
|
||||
from jx_base.expressions.basic_substring_op import BasicSubstringOp
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.length_op import LengthOp
|
||||
from jx_base.expressions.literal import ZERO
|
||||
from jx_base.expressions.literal import is_literal
|
||||
from jx_base.expressions.max_op import MaxOp
|
||||
from jx_base.expressions.min_op import MinOp
|
||||
from jx_base.expressions.or_op import OrOp
|
||||
from jx_base.expressions.sub_op import SubOp
|
||||
from jx_base.expressions.variable import Variable
|
||||
from jx_base.expressions.when_op import WhenOp
|
||||
from jx_base.language import is_op
|
||||
from mo_dots import is_data
|
||||
from mo_json import STRING
|
||||
|
||||
|
||||
class RightOp(Expression):
|
||||
has_simple_form = True
|
||||
data_type = STRING
|
||||
|
||||
def __init__(self, term):
|
||||
Expression.__init__(self, term)
|
||||
if is_data(term):
|
||||
self.value, self.length = term.items()[0]
|
||||
else:
|
||||
self.value, self.length = term
|
||||
|
||||
if is_literal(self.value):
|
||||
Log.note("")
|
||||
|
||||
def __data__(self):
|
||||
if is_op(self.value, Variable) and is_literal(self.length):
|
||||
return {"right": {self.value.var: self.length.value}}
|
||||
else:
|
||||
return {"right": [self.value.__data__(), self.length.__data__()]}
|
||||
|
||||
def vars(self):
|
||||
return self.value.vars() | self.length.vars()
|
||||
|
||||
def map(self, map_):
|
||||
return self.lang[RightOp([self.value.map(map_), self.length.map(map_)])]
|
||||
|
||||
def missing(self):
|
||||
return self.lang[OrOp([self.value.missing(), self.length.missing()])]
|
||||
|
||||
@simplified
|
||||
def partial_eval(self):
|
||||
value = self.lang[self.value].partial_eval()
|
||||
length = self.lang[self.length].partial_eval()
|
||||
max_length = LengthOp(value)
|
||||
|
||||
return self.lang[
|
||||
WhenOp(
|
||||
self.missing(),
|
||||
**{
|
||||
"else": BasicSubstringOp(
|
||||
[
|
||||
value,
|
||||
MaxOp(
|
||||
[ZERO, MinOp([max_length, SubOp([max_length, length])])]
|
||||
),
|
||||
max_length,
|
||||
]
|
||||
)
|
||||
}
|
||||
)
|
||||
].partial_eval()
|
|
@ -0,0 +1,56 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.literal import Literal
|
||||
from jx_base.expressions.literal import is_literal
|
||||
from jx_base.expressions.variable import Variable
|
||||
from jx_base.language import is_op
|
||||
from mo_logs import Log
|
||||
|
||||
|
||||
class RowsOp(Expression):
|
||||
has_simple_form = True
|
||||
|
||||
def __init__(self, term):
|
||||
Expression.__init__(self, term)
|
||||
self.var, self.offset = term
|
||||
if is_op(self.var, Variable):
|
||||
if is_op(self.var, Variable) and not any(
|
||||
self.var.var.startswith(p) for p in ["row.", "rows.", "rownum"]
|
||||
): # VARIABLES ARE INTERPRETED LITERALLY
|
||||
self.var = Literal(self.var.var)
|
||||
else:
|
||||
Log.error("can not handle")
|
||||
else:
|
||||
Log.error("can not handle")
|
||||
|
||||
def __data__(self):
|
||||
if is_literal(self.var) and is_literal(self.offset):
|
||||
return {"rows": {self.var.json, self.offset.value}}
|
||||
else:
|
||||
return {"rows": [self.var.__data__(), self.offset.__data__()]}
|
||||
|
||||
def vars(self):
|
||||
return self.var.vars() | self.offset.vars() | {"rows", "rownum"}
|
||||
|
||||
def map(self, map_):
|
||||
return self.lang[RowsOp([self.var.map(map_), self.offset.map(map_)])]
|
|
@ -0,0 +1,62 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions.expression import Expression
|
||||
from mo_future import is_text
|
||||
from mo_json import OBJECT
|
||||
from mo_logs import Log
|
||||
|
||||
|
||||
class ScriptOp(Expression):
|
||||
"""
|
||||
ONLY FOR WHEN YOU TRUST THE SCRIPT SOURCE
|
||||
"""
|
||||
|
||||
def __init__(self, script, data_type=OBJECT):
|
||||
Expression.__init__(self, None)
|
||||
if not is_text(script):
|
||||
Log.error("expecting text of a script")
|
||||
self.simplified = True
|
||||
self.script = script
|
||||
self.data_type = data_type
|
||||
|
||||
@classmethod
|
||||
def define(cls, expr):
|
||||
if ALLOW_SCRIPTING:
|
||||
Log.warning(
|
||||
"Scripting has been activated: This has known security holes!!\nscript = {{script|quote}}",
|
||||
script=expr.script.term,
|
||||
)
|
||||
return cls.lang[ScriptOp(expr.script)]
|
||||
else:
|
||||
Log.error("scripting is disabled")
|
||||
|
||||
def vars(self):
|
||||
return set()
|
||||
|
||||
def map(self, map_):
|
||||
return self
|
||||
|
||||
def __unicode__(self):
|
||||
return self.script
|
||||
|
||||
def __str__(self):
|
||||
return str(self.script)
|
|
@ -0,0 +1,91 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions.expression import jx_expression, Expression, _jx_expression
|
||||
from jx_base.utils import is_variable_name
|
||||
from mo_dots import wrap, is_container
|
||||
from mo_future import is_text
|
||||
from mo_logs import Log
|
||||
from mo_math import UNION
|
||||
|
||||
|
||||
class SelectOp(Expression):
|
||||
has_simple_form = True
|
||||
|
||||
def __init__(self, terms):
|
||||
"""
|
||||
:param terms: list OF {"name":name, "value":value} DESCRIPTORS
|
||||
"""
|
||||
self.terms = terms
|
||||
|
||||
@classmethod
|
||||
def define(cls, expr):
|
||||
expr = wrap(expr)
|
||||
term = expr.select
|
||||
terms = []
|
||||
if not is_container(term):
|
||||
raise Log.error("Expecting a list")
|
||||
for t in term:
|
||||
if is_text(t):
|
||||
if not is_variable_name(t):
|
||||
Log.error(
|
||||
"expecting {{value}} a simple dot-delimited path name", value=t
|
||||
)
|
||||
terms.append({"name": t, "value": _jx_expression(t, cls.lang)})
|
||||
elif t.name == None:
|
||||
if t.value == None:
|
||||
Log.error(
|
||||
"expecting select parameters to have name and value properties"
|
||||
)
|
||||
elif is_text(t.value):
|
||||
if not is_variable_name(t):
|
||||
Log.error(
|
||||
"expecting {{value}} a simple dot-delimited path name",
|
||||
value=t.value,
|
||||
)
|
||||
else:
|
||||
terms.append(
|
||||
{
|
||||
"name": t.value,
|
||||
"value": _jx_expression(t.value, cls.lang),
|
||||
}
|
||||
)
|
||||
else:
|
||||
Log.error("expecting a name property")
|
||||
else:
|
||||
terms.append({"name": t.name, "value": jx_expression(t.value)})
|
||||
return cls.lang[SelectOp(terms)]
|
||||
|
||||
def __data__(self):
|
||||
return {
|
||||
"select": [
|
||||
{"name": t.name.__data__(), "value": t.value.__data__()}
|
||||
for t in self.terms
|
||||
]
|
||||
}
|
||||
|
||||
def vars(self):
|
||||
return UNION(t.value for t in self.terms)
|
||||
|
||||
def map(self, map_):
|
||||
return SelectOp(
|
||||
[{"name": t.name, "value": t.value.map(map_)} for t in self.terms]
|
||||
)
|
|
@ -0,0 +1,84 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions.and_op import AndOp
|
||||
from jx_base.expressions.eq_op import EqOp
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.find_op import FindOp
|
||||
from jx_base.expressions.literal import Literal
|
||||
from jx_base.expressions.literal import is_literal
|
||||
from jx_base.expressions.or_op import OrOp
|
||||
from jx_base.expressions.script_op import ScriptOp
|
||||
from jx_base.expressions.true_op import TRUE
|
||||
from jx_base.expressions.variable import Variable
|
||||
from jx_base.language import is_op
|
||||
|
||||
|
||||
class SplitOp(Expression):
|
||||
has_simple_form = True
|
||||
|
||||
def __init__(self, term, **kwargs):
|
||||
Expression.__init__(self, term)
|
||||
self.value, self.find = term
|
||||
|
||||
def __data__(self):
|
||||
if is_op(self.value, Variable) and is_literal(self.find):
|
||||
return {"split": {self.value.var, self.find.value}}
|
||||
else:
|
||||
return {"split": [self.value.__data__(), self.find.__data__()]}
|
||||
|
||||
def vars(self):
|
||||
return (
|
||||
self.value.vars()
|
||||
| self.find.vars()
|
||||
| self.default.vars()
|
||||
| self.start.vars()
|
||||
)
|
||||
|
||||
def map(self, map_):
|
||||
return FindOp(
|
||||
[self.value.map(map_), self.find.map(map_)],
|
||||
start=self.start.map(map_),
|
||||
default=self.default.map(map_),
|
||||
)
|
||||
|
||||
def missing(self):
|
||||
v = self.value.to_es_script(not_null=True)
|
||||
find = self.find.to_es_script(not_null=True)
|
||||
index = v + ".indexOf(" + find + ", " + self.start.to_es_script() + ")"
|
||||
|
||||
return self.lang[
|
||||
AndOp(
|
||||
[
|
||||
self.default.missing(),
|
||||
OrOp(
|
||||
[
|
||||
self.value.missing(),
|
||||
self.find.missing(),
|
||||
EqOp([ScriptOp(index), Literal(-1)]),
|
||||
]
|
||||
),
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
def exists(self):
|
||||
return TRUE
|
|
@ -0,0 +1,45 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions.eq_op import EqOp
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.false_op import FALSE
|
||||
from jx_base.language import is_op
|
||||
from mo_json import BOOLEAN
|
||||
|
||||
|
||||
class SqlEqOp(Expression):
|
||||
data_type = BOOLEAN
|
||||
|
||||
def __init__(self, terms):
|
||||
Expression.__init__(self, terms)
|
||||
self.lhs, self.rhs = terms
|
||||
|
||||
def __data__(self):
|
||||
return {"sql.eq": [self.lhs.__data__(), self.rhs.__data__()]}
|
||||
|
||||
def missing(self):
|
||||
return FALSE
|
||||
|
||||
def __eq__(self, other):
|
||||
if not is_op(other, EqOp):
|
||||
return False
|
||||
return self.lhs == other.lhs and self.rhs == other.rhs
|
|
@ -0,0 +1,41 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.false_op import FALSE
|
||||
from mo_json import INTEGER
|
||||
|
||||
|
||||
class SqlInstrOp(Expression):
|
||||
data_type = INTEGER
|
||||
|
||||
def __init__(self, params):
|
||||
Expression.__init__(self, params)
|
||||
self.value, self.find = params
|
||||
|
||||
def __data__(self):
|
||||
return {"sql.instr": [self.value.__data__(), self.find.__data__()]}
|
||||
|
||||
def vars(self):
|
||||
return self.value.vars() | self.find.vars()
|
||||
|
||||
def missing(self):
|
||||
return FALSE
|
|
@ -0,0 +1,30 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions.expression import Expression
|
||||
|
||||
|
||||
class SQLScript(Expression):
|
||||
"""
|
||||
REPRESENT A SQL SCRIPT
|
||||
"""
|
||||
|
||||
pass
|
|
@ -0,0 +1,47 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.false_op import FALSE
|
||||
from mo_json import INTEGER
|
||||
|
||||
|
||||
class SqlSubstrOp(Expression):
|
||||
data_type = INTEGER
|
||||
|
||||
def __init__(self, params):
|
||||
Expression.__init__(self, params)
|
||||
self.value, self.start, self.length = params
|
||||
|
||||
def __data__(self):
|
||||
return {
|
||||
"sql.substr": [
|
||||
self.value.__data__(),
|
||||
self.start.__data__(),
|
||||
self.length.__data__(),
|
||||
]
|
||||
}
|
||||
|
||||
def vars(self):
|
||||
return self.value.vars() | self.start.vars() | self.length.vars()
|
||||
|
||||
def missing(self):
|
||||
return FALSE
|
|
@ -0,0 +1,75 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
import mo_json
|
||||
from jx_base.expressions._utils import simplified
|
||||
from jx_base.expressions.coalesce_op import CoalesceOp
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.first_op import FirstOp
|
||||
from jx_base.expressions.literal import Literal
|
||||
from jx_base.expressions.literal import is_literal
|
||||
from jx_base.expressions.null_op import NULL
|
||||
from jx_base.language import is_op
|
||||
from mo_json import STRING, IS_NULL
|
||||
|
||||
|
||||
class StringOp(Expression):
|
||||
data_type = STRING
|
||||
|
||||
def __init__(self, term):
|
||||
Expression.__init__(self, [term])
|
||||
self.term = term
|
||||
|
||||
def __data__(self):
|
||||
return {"string": self.term.__data__()}
|
||||
|
||||
def vars(self):
|
||||
return self.term.vars()
|
||||
|
||||
def map(self, map_):
|
||||
return self.lang[StringOp(self.term.map(map_))]
|
||||
|
||||
def missing(self):
|
||||
return self.term.missing()
|
||||
|
||||
@simplified
|
||||
def partial_eval(self):
|
||||
term = self.term
|
||||
if term.type is IS_NULL:
|
||||
return NULL
|
||||
term = self.lang[FirstOp(term)].partial_eval()
|
||||
if is_op(term, StringOp):
|
||||
return term.term.partial_eval()
|
||||
elif is_op(term, CoalesceOp):
|
||||
return self.lang[
|
||||
CoalesceOp([self.lang[StringOp(t)].partial_eval() for t in term.terms])
|
||||
]
|
||||
elif is_literal(term):
|
||||
if term.type == STRING:
|
||||
return term
|
||||
else:
|
||||
return self.lang[Literal(mo_json.value2json(term.value))]
|
||||
return self
|
||||
|
||||
def __eq__(self, other):
|
||||
if not is_op(other, StringOp):
|
||||
return False
|
||||
return self.term == other.term
|
|
@ -0,0 +1,26 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions.base_binary_op import BaseBinaryOp
|
||||
|
||||
|
||||
class SubOp(BaseBinaryOp):
|
||||
op = "sub"
|
|
@ -0,0 +1,94 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
import re
|
||||
|
||||
from jx_base.expressions._utils import simplified
|
||||
from jx_base.expressions.and_op import AndOp
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.false_op import FALSE
|
||||
from jx_base.expressions.literal import Literal, is_literal
|
||||
from jx_base.expressions.reg_exp_op import RegExpOp
|
||||
from jx_base.expressions.true_op import TRUE
|
||||
from jx_base.expressions.variable import Variable
|
||||
from jx_base.expressions.when_op import WhenOp
|
||||
from jx_base.language import is_op
|
||||
from mo_dots import is_data
|
||||
from mo_json import BOOLEAN, STRING
|
||||
from mo_logs import Log
|
||||
|
||||
|
||||
class SuffixOp(Expression):
|
||||
has_simple_form = True
|
||||
data_type = BOOLEAN
|
||||
|
||||
def __init__(self, term):
|
||||
Expression.__init__(self, term)
|
||||
if not term:
|
||||
self.expr = self.suffix = None
|
||||
elif is_data(term):
|
||||
self.expr, self.suffix = term.items()[0]
|
||||
else:
|
||||
self.expr, self.suffix = term
|
||||
|
||||
def __data__(self):
|
||||
if self.expr is None:
|
||||
return {"suffix": {}}
|
||||
elif is_op(self.expr, Variable) and is_literal(self.suffix):
|
||||
return {"suffix": {self.expr.var: self.suffix.value}}
|
||||
else:
|
||||
return {"suffix": [self.expr.__data__(), self.suffix.__data__()]}
|
||||
|
||||
def missing(self):
|
||||
"""
|
||||
THERE IS PLENTY OF OPPORTUNITY TO SIMPLIFY missing EXPRESSIONS
|
||||
OVERRIDE THIS METHOD TO SIMPLIFY
|
||||
:return:
|
||||
"""
|
||||
return FALSE
|
||||
|
||||
def vars(self):
|
||||
if self.expr is None:
|
||||
return set()
|
||||
return self.expr.vars() | self.suffix.vars()
|
||||
|
||||
def map(self, map_):
|
||||
if self.expr is None:
|
||||
return TRUE
|
||||
else:
|
||||
return self.lang[SuffixOp([self.expr.map(map_), self.suffix.map(map_)])]
|
||||
|
||||
@simplified
|
||||
def partial_eval(self):
|
||||
if self.expr is None:
|
||||
return TRUE
|
||||
if not is_literal(self.suffix) and self.suffix.type == STRING:
|
||||
Log.error("can only hanlde literal suffix ")
|
||||
|
||||
return WhenOp(
|
||||
self.lang[AndOp([self.expr.exists(), self.suffix.exists()])],
|
||||
**{
|
||||
"then": self.lang[
|
||||
RegExpOp([self.expr, Literal(".*" + re.escape(self.suffix.value))])
|
||||
],
|
||||
"else": FALSE,
|
||||
}
|
||||
).partial_eval()
|
|
@ -0,0 +1,82 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions import literal, false_op, _utils
|
||||
from jx_base.expressions.literal import Literal
|
||||
from jx_base.expressions.false_op import FALSE
|
||||
from mo_json import BOOLEAN
|
||||
|
||||
|
||||
class TrueOp(Literal):
|
||||
data_type = BOOLEAN
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
return object.__new__(cls, *args, **kwargs)
|
||||
|
||||
def __init__(self, op=None, term=None):
|
||||
Literal.__init__(self, True)
|
||||
|
||||
@classmethod
|
||||
def define(cls, expr):
|
||||
return TRUE
|
||||
|
||||
def __nonzero__(self):
|
||||
return True
|
||||
|
||||
def __eq__(self, other):
|
||||
return (other is TRUE) or (other is True)
|
||||
|
||||
def __data__(self):
|
||||
return True
|
||||
|
||||
def vars(self):
|
||||
return set()
|
||||
|
||||
def map(self, map_):
|
||||
return self
|
||||
|
||||
def missing(self):
|
||||
return FALSE
|
||||
|
||||
def is_true(self):
|
||||
return TRUE
|
||||
|
||||
def is_false(self):
|
||||
return FALSE
|
||||
|
||||
def __call__(self, row=None, rownum=None, rows=None):
|
||||
return True
|
||||
|
||||
def __unicode__(self):
|
||||
return "true"
|
||||
|
||||
def __str__(self):
|
||||
return b"true"
|
||||
|
||||
def __bool__(self):
|
||||
return True
|
||||
|
||||
|
||||
TRUE = TrueOp()
|
||||
|
||||
literal.TRUE = TRUE
|
||||
false_op.TRUE = TRUE
|
||||
_utils.TRUE = TRUE
|
|
@ -0,0 +1,70 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions import _utils
|
||||
from jx_base.expressions._utils import simplified
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.false_op import FALSE
|
||||
from jx_base.expressions.literal import Literal
|
||||
from jx_base.expressions.literal import is_literal
|
||||
from mo_dots import is_many
|
||||
from mo_json import OBJECT
|
||||
|
||||
|
||||
class TupleOp(Expression):
|
||||
date_type = OBJECT
|
||||
|
||||
def __init__(self, terms):
|
||||
Expression.__init__(self, terms)
|
||||
if terms == None:
|
||||
self.terms = []
|
||||
elif is_many(terms):
|
||||
self.terms = terms
|
||||
else:
|
||||
self.terms = [terms]
|
||||
|
||||
def __iter__(self):
|
||||
return self.terms.__iter__()
|
||||
|
||||
def __data__(self):
|
||||
return {"tuple": [t.__data__() for t in self.terms]}
|
||||
|
||||
def vars(self):
|
||||
output = set()
|
||||
for t in self.terms:
|
||||
output |= t.vars()
|
||||
return output
|
||||
|
||||
def map(self, map_):
|
||||
return self.lang[TupleOp([t.map(map_) for t in self.terms])]
|
||||
|
||||
def missing(self):
|
||||
return FALSE
|
||||
|
||||
@simplified
|
||||
def partial_eval(self):
|
||||
if all(is_literal(t) for t in self.terms):
|
||||
return self.lang[Literal([t.value for t in self.terms])]
|
||||
|
||||
return self
|
||||
|
||||
|
||||
_utils.TupleOp=TupleOp
|
|
@ -0,0 +1,84 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions._utils import simplified, merge_types
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.false_op import FALSE
|
||||
from jx_base.expressions.literal import Literal
|
||||
from jx_base.expressions.null_op import NULL
|
||||
from jx_base.language import is_op
|
||||
from mo_dots import is_many
|
||||
from mo_math import MIN
|
||||
|
||||
|
||||
class UnionOp(Expression):
|
||||
def __init__(self, terms):
|
||||
Expression.__init__(self, terms)
|
||||
if terms == None:
|
||||
self.terms = []
|
||||
elif is_many(terms):
|
||||
self.terms = terms
|
||||
else:
|
||||
self.terms = [terms]
|
||||
|
||||
def __data__(self):
|
||||
return {"union": [t.__data__() for t in self.terms]}
|
||||
|
||||
@property
|
||||
def type(self):
|
||||
return merge_types(t.type for t in self.terms)
|
||||
|
||||
def vars(self):
|
||||
output = set()
|
||||
for t in self.terms:
|
||||
output |= t.vars()
|
||||
return output
|
||||
|
||||
def map(self, map_):
|
||||
return self.lang[UnionOp([t.map(map_) for t in self.terms])]
|
||||
|
||||
def missing(self):
|
||||
return FALSE
|
||||
|
||||
@simplified
|
||||
def partial_eval(self):
|
||||
minimum = None
|
||||
terms = []
|
||||
for t in self.terms:
|
||||
simple = t.partial_eval()
|
||||
if simple is NULL:
|
||||
pass
|
||||
elif is_op(simple, Literal):
|
||||
minimum = MIN([minimum, simple.value])
|
||||
else:
|
||||
terms.append(simple)
|
||||
if len(terms) == 0:
|
||||
if minimum == None:
|
||||
return NULL
|
||||
else:
|
||||
return Literal(minimum)
|
||||
else:
|
||||
if minimum == None:
|
||||
output = self.lang[UnionOp(terms)]
|
||||
else:
|
||||
output = self.lang[UnionOp([Literal(minimum)] + terms)]
|
||||
|
||||
return output
|
|
@ -0,0 +1,45 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions.expression import Expression
|
||||
from mo_json import NUMBER
|
||||
|
||||
|
||||
class UnixOp(Expression):
|
||||
"""
|
||||
FOR USING ON DATABASES WHICH HAVE A DATE COLUMNS: CONVERT TO UNIX
|
||||
"""
|
||||
|
||||
has_simple_form = True
|
||||
data_type = NUMBER
|
||||
|
||||
def __init__(self, term):
|
||||
Expression.__init__(self, term)
|
||||
self.value = term
|
||||
|
||||
def vars(self):
|
||||
return self.value.vars()
|
||||
|
||||
def map(self, map_):
|
||||
return self.lang[UnixOp(self.value.map(map_))]
|
||||
|
||||
def missing(self):
|
||||
return self.value.missing()
|
|
@ -0,0 +1,98 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions import _utils, expression
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.false_op import FALSE
|
||||
from jx_base.expressions.missing_op import MissingOp
|
||||
from jx_base.language import is_op
|
||||
from jx_base.utils import get_property_name
|
||||
from mo_dots import coalesce, is_sequence, split_field
|
||||
from mo_dots.lists import last
|
||||
from mo_future import is_text
|
||||
from mo_json.typed_encoder import inserter_type_to_json_type
|
||||
|
||||
|
||||
class Variable(Expression):
|
||||
def __init__(self, var):
|
||||
"""
|
||||
:param var: DOT DELIMITED PATH INTO A DOCUMENT
|
||||
|
||||
"""
|
||||
Expression.__init__(self, None)
|
||||
|
||||
# if self.lang != self.__class_.lang:
|
||||
# pass
|
||||
self.var = get_property_name(var)
|
||||
jx_type = inserter_type_to_json_type.get(last(split_field(var)))
|
||||
if jx_type:
|
||||
self.data_type = jx_type
|
||||
|
||||
def __call__(self, row, rownum=None, rows=None):
|
||||
path = split_field(self.var)
|
||||
for p in path:
|
||||
row = row.get(p)
|
||||
if row is None:
|
||||
return None
|
||||
if is_sequence(row) and len(row) == 1:
|
||||
return row[0]
|
||||
return row
|
||||
|
||||
def __data__(self):
|
||||
return self.var
|
||||
|
||||
@property
|
||||
def many(self):
|
||||
return True
|
||||
|
||||
def vars(self):
|
||||
return {self}
|
||||
|
||||
def map(self, map_):
|
||||
return Variable(coalesce(map_.get(self.var), self.var))
|
||||
|
||||
def __hash__(self):
|
||||
return self.var.__hash__()
|
||||
|
||||
def __eq__(self, other):
|
||||
if is_op(other, Variable):
|
||||
return self.var == other.var
|
||||
elif is_text(other):
|
||||
return self.var == other
|
||||
return False
|
||||
|
||||
def __unicode__(self):
|
||||
return self.var
|
||||
|
||||
def __str__(self):
|
||||
return str(self.var)
|
||||
|
||||
def missing(self):
|
||||
if self.var == "_id":
|
||||
return FALSE
|
||||
else:
|
||||
return self.lang[MissingOp(self)]
|
||||
|
||||
|
||||
IDENTITY = Variable(".")
|
||||
|
||||
_utils.Variable = Variable
|
||||
expression.Variable = Variable
|
|
@ -0,0 +1,116 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
|
||||
"""
|
||||
# NOTE:
|
||||
|
||||
THE self.lang[operator] PATTERN IS CASTING NEW OPERATORS TO OWN LANGUAGE;
|
||||
KEEPING Python AS# Python, ES FILTERS AS ES FILTERS, AND Painless AS
|
||||
Painless. WE COULD COPY partial_eval(), AND OTHERS, TO THIER RESPECTIVE
|
||||
LANGUAGE, BUT WE KEEP CODE HERE SO THERE IS LESS OF IT
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from jx_base.expressions import first_op, eq_op, not_op
|
||||
from jx_base.expressions._utils import simplified
|
||||
from jx_base.expressions.and_op import AndOp
|
||||
from jx_base.expressions.boolean_op import BooleanOp
|
||||
from jx_base.expressions.expression import Expression
|
||||
from jx_base.expressions.false_op import FALSE
|
||||
from jx_base.expressions.literal import Literal
|
||||
from jx_base.expressions.not_op import NotOp
|
||||
from jx_base.expressions.null_op import NULL
|
||||
from jx_base.expressions.or_op import OrOp
|
||||
from jx_base.expressions.true_op import TRUE
|
||||
from jx_base.language import is_op
|
||||
from mo_dots import coalesce
|
||||
from mo_json import INTEGER, NUMBER, OBJECT, NUMBER_TYPES
|
||||
from mo_logs import Log
|
||||
|
||||
|
||||
class WhenOp(Expression):
|
||||
def __init__(self, term, **clauses):
|
||||
Expression.__init__(self, [term])
|
||||
|
||||
self.when = term
|
||||
self.then = coalesce(clauses.get("then"), NULL)
|
||||
self.els_ = coalesce(clauses.get("else"), NULL)
|
||||
|
||||
if self.then is NULL:
|
||||
self.data_type = self.els_.type
|
||||
elif self.els_ is NULL:
|
||||
self.data_type = self.then.type
|
||||
elif self.then.type == self.els_.type:
|
||||
self.data_type = self.then.type
|
||||
elif self.then.type in NUMBER_TYPES and self.els_.type in NUMBER_TYPES:
|
||||
self.data_type = NUMBER
|
||||
else:
|
||||
self.data_type = OBJECT
|
||||
|
||||
def __data__(self):
|
||||
return {
|
||||
"when": self.when.__data__(),
|
||||
"then": None if self.then is NULL else self.then.__data__(),
|
||||
"else": None if self.els_ is NULL else self.els_.__data__()
|
||||
}
|
||||
|
||||
def vars(self):
|
||||
return self.when.vars() | self.then.vars() | self.els_.vars()
|
||||
|
||||
def map(self, map_):
|
||||
return self.lang[
|
||||
WhenOp(
|
||||
self.when.map(map_),
|
||||
**{"then": self.then.map(map_), "else": self.els_.map(map_)}
|
||||
)
|
||||
]
|
||||
|
||||
def missing(self):
|
||||
return self.lang[
|
||||
OrOp(
|
||||
[
|
||||
AndOp([self.when, self.then.missing()]),
|
||||
AndOp([NotOp(self.when), self.els_.missing()]),
|
||||
]
|
||||
)
|
||||
].partial_eval()
|
||||
|
||||
@simplified
|
||||
def partial_eval(self):
|
||||
when = self.lang[BooleanOp(self.when)].partial_eval()
|
||||
|
||||
if when is TRUE:
|
||||
return self.lang[self.then].partial_eval()
|
||||
elif when in [FALSE, NULL]:
|
||||
return self.lang[self.els_].partial_eval()
|
||||
elif is_op(when, Literal):
|
||||
Log.error("Expecting `when` clause to return a Boolean, or `null`")
|
||||
|
||||
then = self.lang[self.then].partial_eval()
|
||||
els_ = self.lang[self.els_].partial_eval()
|
||||
|
||||
if then is TRUE:
|
||||
if els_ is FALSE:
|
||||
return when
|
||||
elif els_ is TRUE:
|
||||
return TRUE
|
||||
elif then is FALSE:
|
||||
if els_ is FALSE:
|
||||
return FALSE
|
||||
elif els_ is TRUE:
|
||||
return self.lang[NotOp(when)].partial_eval()
|
||||
|
||||
return self.lang[WhenOp(when, **{"then": then, "else": els_})]
|
||||
|
||||
|
||||
first_op.WhenOp = WhenOp
|
||||
eq_op.WhenOp = WhenOp
|
||||
not_op.WhenOp = WhenOp
|
|
@ -5,10 +5,13 @@
|
|||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Author: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from mo_future import is_text
|
||||
from mo_logs import Log
|
||||
|
||||
|
||||
class Facts(object):
|
||||
"""
|
||||
|
@ -16,14 +19,21 @@ class Facts(object):
|
|||
WITH THE RELATIONS THAT CONNECT THEM ALL, BUT LIMITED TO A TREE
|
||||
"""
|
||||
|
||||
def __init__(self, container, snowflake):
|
||||
def __init__(self, name, container):
|
||||
if not is_text(name):
|
||||
Log.error("parameter is wrong")
|
||||
self.container = container
|
||||
self.snowflake = snowflake
|
||||
self.name = name
|
||||
|
||||
@property
|
||||
def namespace(self):
|
||||
return self.container.namespace
|
||||
|
||||
@property
|
||||
def snowflake(self):
|
||||
return self.schema.snowflake
|
||||
|
||||
@property
|
||||
def schema(self):
|
||||
return self.snowflake.schema
|
||||
return self.container.ns.get_schema(self.name)
|
||||
|
||||
|
|
|
@ -5,23 +5,24 @@
|
|||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Author: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from copy import copy
|
||||
from decimal import Decimal
|
||||
from math import isnan
|
||||
|
||||
from mo_dots import Data, data_types, listwrap
|
||||
from mo_dots import Data, data_types, listwrap, NullType, startswith_field
|
||||
from mo_dots.lists import list_types, is_many
|
||||
from mo_future import boolean_type, long, none_type, text_type, transpose
|
||||
from mo_future import boolean_type, long, none_type, text, transpose
|
||||
from mo_logs import Log
|
||||
from mo_times import Date
|
||||
|
||||
builtin_tuple = tuple
|
||||
|
||||
Expression = None
|
||||
expression_module = None
|
||||
expression_module = "jx_base.expressions"
|
||||
JX = None
|
||||
ID = "_op_id"
|
||||
|
||||
|
@ -48,7 +49,7 @@ class LanguageElement(type):
|
|||
def __new__(cls, name, bases, dct):
|
||||
x = type.__new__(cls, name, bases, dct)
|
||||
x.lang = None
|
||||
if x.__module__ == expression_module:
|
||||
if startswith_field(x.__module__, expression_module):
|
||||
# ALL OPS IN expression_module ARE GIVEN AN ID, NO OTHERS
|
||||
setattr(x, ID, next_id())
|
||||
return x
|
||||
|
@ -68,9 +69,47 @@ BaseExpression = LanguageElement(str("BaseExpression"), (object,), {})
|
|||
class Language(object):
|
||||
|
||||
def __init__(self, name):
|
||||
global JX
|
||||
if not name:
|
||||
name = "JX"
|
||||
JX = self
|
||||
self.name = name
|
||||
self.ops = None
|
||||
|
||||
def register_ops(self, module_vars):
|
||||
global JX
|
||||
|
||||
if self.name != "JX":
|
||||
self.ops = copy(JX.ops)
|
||||
else:
|
||||
num_ops = 1 + max(
|
||||
obj.get_id()
|
||||
for obj in module_vars.values()
|
||||
if isinstance(obj, type) and hasattr(obj, ID)
|
||||
)
|
||||
self.ops = [None] * num_ops
|
||||
|
||||
for _, new_op in module_vars.items():
|
||||
if isinstance(new_op, type) and hasattr(new_op, ID):
|
||||
# EXPECT OPERATORS TO HAVE id
|
||||
# EXPECT NEW DEFINED OPS IN THIS MODULE TO HAVE lang NOT SET
|
||||
curr = getattr(new_op, "lang")
|
||||
if not curr:
|
||||
old_op = self.ops[new_op.get_id()]
|
||||
if old_op is not None and old_op.__name__ != new_op.__name__:
|
||||
Log.error("Logic error")
|
||||
self.ops[new_op.get_id()] = new_op
|
||||
setattr(new_op, "lang", self)
|
||||
|
||||
if self.name:
|
||||
# ENSURE THE ALL OPS ARE DEFINED ON THE NEW LANGUAGE
|
||||
for base_op, new_op in transpose(JX.ops, self.ops):
|
||||
if new_op is base_op:
|
||||
# MISSED DEFINITION, ADD ONE
|
||||
new_op = type(base_op.__name__, (base_op,), {})
|
||||
self.ops[new_op.get_id()] = new_op
|
||||
setattr(new_op, "lang", self)
|
||||
|
||||
def __getitem__(self, item):
|
||||
if item == None:
|
||||
Log.error("expecting operator")
|
||||
|
@ -84,47 +123,6 @@ class Language(object):
|
|||
return self.name
|
||||
|
||||
|
||||
def define_language(lang_name, module_vars):
|
||||
# LET ALL EXPRESSIONS POINT TO lang OBJECT WITH ALL EXPRESSIONS
|
||||
# ENSURE THIS IS BELOW ALL SUB_CLASS DEFINITIONS SO var() CAPTURES ALL EXPRESSIONS
|
||||
global JX
|
||||
|
||||
if lang_name:
|
||||
language = Language(lang_name)
|
||||
language.ops = copy(JX.ops)
|
||||
else:
|
||||
num_ops = 1 + max(
|
||||
obj.get_id()
|
||||
for obj in module_vars.values()
|
||||
if isinstance(obj, type) and hasattr(obj, ID)
|
||||
)
|
||||
language = JX = Language("JX")
|
||||
language.ops = [None] * num_ops
|
||||
|
||||
for _, new_op in module_vars.items():
|
||||
if isinstance(new_op, type) and hasattr(new_op, ID):
|
||||
# EXPECT OPERATORS TO HAVE id
|
||||
# EXPECT NEW DEFINED OPS IN THIS MODULE TO HAVE lang NOT SET
|
||||
curr = getattr(new_op, "lang")
|
||||
if not curr:
|
||||
old_op = language.ops[new_op.get_id()]
|
||||
if old_op is not None and old_op.__name__ != new_op.__name__:
|
||||
Log.error("Logic error")
|
||||
language.ops[new_op.get_id()] = new_op
|
||||
setattr(new_op, "lang", language)
|
||||
|
||||
if lang_name:
|
||||
# ENSURE THE ALL OPS ARE DEFINED ON THE NEW LANGUAGE
|
||||
for base_op, new_op in transpose(JX.ops, language.ops):
|
||||
if new_op is base_op:
|
||||
# MISSED DEFINITION, ADD ONE
|
||||
new_op = type(base_op.__name__, (base_op,), {})
|
||||
language.ops[new_op.get_id()] = new_op
|
||||
setattr(new_op, "lang", language)
|
||||
|
||||
return language
|
||||
|
||||
|
||||
def is_op(call, op):
|
||||
"""
|
||||
:param call: The specific operator instance (a method call)
|
||||
|
@ -189,15 +187,14 @@ def value_compare(left, right, ordering=1):
|
|||
right = None
|
||||
rtype = none_type
|
||||
|
||||
null_order = ordering*10
|
||||
ltype_num = TYPE_ORDER.get(ltype, null_order)
|
||||
rtype_num = TYPE_ORDER.get(rtype, null_order)
|
||||
ltype_num = type_order(ltype, ordering)
|
||||
rtype_num = type_order(rtype, ordering)
|
||||
|
||||
type_diff = ltype_num - rtype_num
|
||||
if type_diff != 0:
|
||||
return ordering if type_diff > 0 else -ordering
|
||||
|
||||
if ltype_num == null_order:
|
||||
if ltype_num in (-10, 10):
|
||||
return 0
|
||||
elif ltype is builtin_tuple:
|
||||
for a, b in zip(left, right):
|
||||
|
@ -221,17 +218,33 @@ def value_compare(left, right, ordering=1):
|
|||
Log.error("Can not compare values {{left}} to {{right}}", left=left, right=right, cause=e)
|
||||
|
||||
|
||||
def type_order(dtype, ordering):
|
||||
o = TYPE_ORDER.get(dtype)
|
||||
if o is None:
|
||||
if dtype in NULL_TYPES:
|
||||
return ordering * 10
|
||||
else:
|
||||
Log.warning("type will be treated as its own type while sorting")
|
||||
TYPE_ORDER[dtype] = 6
|
||||
return 6
|
||||
return o
|
||||
|
||||
|
||||
NULL_TYPES = (none_type, NullType)
|
||||
|
||||
|
||||
TYPE_ORDER = {
|
||||
boolean_type: 0,
|
||||
int: 1,
|
||||
float: 1,
|
||||
Decimal: 1,
|
||||
Date: 1,
|
||||
long: 1,
|
||||
text_type: 2,
|
||||
list: 3,
|
||||
builtin_tuple: 3,
|
||||
dict: 4,
|
||||
Data: 4
|
||||
text: 3,
|
||||
list: 4,
|
||||
builtin_tuple: 4,
|
||||
dict: 5,
|
||||
Data: 5
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -5,10 +5,11 @@
|
|||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Author: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
import datetime
|
||||
from collections import Mapping
|
||||
|
||||
from jx_base import Column, TableDesc
|
||||
|
@ -26,7 +27,7 @@ from mo_dots import (
|
|||
split_field,
|
||||
unwraplist,
|
||||
wrap)
|
||||
from mo_future import binary_type, items, long, none_type, reduce, text_type
|
||||
from mo_future import binary_type, items, long, none_type, reduce, text
|
||||
from mo_json import INTEGER, NUMBER, STRING, python_type_to_json_type
|
||||
from mo_times.dates import Date
|
||||
|
||||
|
@ -272,8 +273,9 @@ _merge_order = {
|
|||
int: 3,
|
||||
long: 3,
|
||||
Date: 4,
|
||||
datetime: 4,
|
||||
float: 5,
|
||||
text_type: 6,
|
||||
text: 6,
|
||||
binary_type: 6,
|
||||
object: 7,
|
||||
dict: 8,
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Author: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
|
@ -34,7 +34,6 @@ class Namespace(object):
|
|||
output.where = self.convert(query.where)
|
||||
output["from"] = self._convert_from(query["from"])
|
||||
output.edges = self._convert_clause(query.edges)
|
||||
output.having = convert_list(self._convert_having, query.having)
|
||||
output.window = convert_list(self._convert_window, query.window)
|
||||
output.sort = self._convert_clause(query.sort)
|
||||
output.format = query.format
|
||||
|
@ -47,9 +46,6 @@ class Namespace(object):
|
|||
def _convert_clause(self, clause):
|
||||
raise NotImplementedError()
|
||||
|
||||
def _convert_having(self, clause):
|
||||
raise NotImplementedError()
|
||||
|
||||
def _convert_window(self, clause):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
|
|
@ -5,25 +5,27 @@
|
|||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Author: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
from collections import Mapping
|
||||
from copy import copy
|
||||
from importlib import import_module
|
||||
|
||||
import jx_base
|
||||
import mo_math
|
||||
from jx_base.dimensions import Dimension
|
||||
from jx_base.domains import DefaultDomain, Domain, SetDomain
|
||||
from jx_base.expressions import Expression, FALSE, LeavesOp, QueryOp as QueryOp_, ScriptOp, TRUE, Variable, jx_expression
|
||||
from jx_base.utils import is_variable_name
|
||||
from jx_base.expressions import Expression, FALSE, LeavesOp, QueryOp as QueryOp_, ScriptOp, Variable, jx_expression
|
||||
from jx_base.language import is_expression, is_op
|
||||
from mo_dots import Data, FlatList, Null, coalesce, concat_field, is_container, is_data, is_list, listwrap, literal_field, relative_field, set_default, unwrap, unwraplist, wrap, is_many
|
||||
from mo_future import is_text, text_type
|
||||
from jx_base.utils import is_variable_name
|
||||
from mo_dots import Data, FlatList, Null, coalesce, concat_field, is_container, is_data, is_list, listwrap, \
|
||||
literal_field, relative_field, set_default, unwrap, unwraplist, wrap
|
||||
from mo_future import is_text, text
|
||||
from mo_json import STRUCT
|
||||
from mo_json.typed_encoder import untype_path
|
||||
from mo_logs import Log
|
||||
import mo_math
|
||||
from mo_math import AND, UNION, is_number
|
||||
|
||||
DEFAULT_LIMIT = 10
|
||||
|
@ -46,15 +48,9 @@ def _late_import():
|
|||
|
||||
|
||||
class QueryOp(QueryOp_):
|
||||
__slots__ = ["frum", "select", "edges", "groupby", "where", "window", "sort", "limit", "having", "format", "isLean"]
|
||||
__slots__ = ["frum", "select", "edges", "groupby", "where", "window", "sort", "limit", "format", "chunk_size", "destination"]
|
||||
|
||||
# def __new__(cls, op=None, frum=None, select=None, edges=None, groupby=None, window=None, where=None, sort=None, limit=None, format=None):
|
||||
# output = object.__new__(cls)
|
||||
# for s in QueryOp.__slots__:
|
||||
# setattr(output, s, None)
|
||||
# return output
|
||||
|
||||
def __init__(self,frum, select=None, edges=None, groupby=None, window=None, where=None, sort=None, limit=None, format=None):
|
||||
def __init__(self,frum, select=None, edges=None, groupby=None, window=None, where=None, sort=None, limit=None, format=None, chunk_size=None, destination=None):
|
||||
if isinstance(frum, jx_base.Table):
|
||||
pass
|
||||
else:
|
||||
|
@ -68,6 +64,8 @@ class QueryOp(QueryOp_):
|
|||
self.sort = sort
|
||||
self.limit = limit
|
||||
self.format = format
|
||||
self.chunk_size = chunk_size
|
||||
self.destination = destination
|
||||
|
||||
def __data__(self):
|
||||
def select___data__():
|
||||
|
@ -210,9 +208,13 @@ class QueryOp(QueryOp_):
|
|||
output = QueryOp(
|
||||
frum=table,
|
||||
format=query.format,
|
||||
limit=mo_math.min(MAX_LIMIT, coalesce(query.limit, DEFAULT_LIMIT))
|
||||
chunk_size=query.chunk_size,
|
||||
destination=query.destination,
|
||||
)
|
||||
|
||||
_import_temper_limit()
|
||||
output.limit = temper_limit(query.limit, query)
|
||||
|
||||
if query.select or isinstance(query.select, (Mapping, list)):
|
||||
output.select = _normalize_selects(query.select, query.frum, schema=schema)
|
||||
else:
|
||||
|
@ -235,13 +237,10 @@ class QueryOp(QueryOp_):
|
|||
|
||||
output.where = _normalize_where({"and": listwrap(query.where)}, schema=schema)
|
||||
output.window = [_normalize_window(w) for w in listwrap(query.window)]
|
||||
output.having = None
|
||||
output.sort = _normalize_sort(query.sort)
|
||||
if not mo_math.is_integer(output.limit) or output.limit < 0:
|
||||
if output.limit != None and (not mo_math.is_integer(output.limit) or output.limit < 0):
|
||||
Log.error("Expecting limit >= 0")
|
||||
|
||||
output.isLean = query.isLean
|
||||
|
||||
return output
|
||||
|
||||
|
||||
|
@ -273,6 +272,18 @@ class QueryOp(QueryOp_):
|
|||
return output
|
||||
|
||||
|
||||
def temper_limit(limit, query):
|
||||
return coalesce(query.limit, 10)
|
||||
|
||||
|
||||
def _import_temper_limit():
|
||||
global temper_limit
|
||||
try:
|
||||
temper_limit = import_module("jx_elasticsearch.es52").temper_limit
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
|
||||
canonical_aggregates = wrap({
|
||||
"cardinality": {"name":"cardinality", "default": 0},
|
||||
"count": {"name": "count", "default": 0},
|
||||
|
@ -285,7 +296,7 @@ canonical_aggregates = wrap({
|
|||
|
||||
|
||||
def _normalize_selects(selects, frum, schema=None, ):
|
||||
if frum == None or isinstance(frum, (list, set, text_type)):
|
||||
if frum == None or isinstance(frum, (list, set, text)):
|
||||
if is_list(selects):
|
||||
if len(selects) == 0:
|
||||
return Null
|
||||
|
@ -406,7 +417,7 @@ def _normalize_select_no_context(select, schema=None):
|
|||
output.value = jx_expression(select.value, schema=schema)
|
||||
elif is_number(output.value):
|
||||
if not output.name:
|
||||
output.name = text_type(output.value)
|
||||
output.name = text(output.value)
|
||||
output.value = jx_expression(select.value, schema=schema)
|
||||
else:
|
||||
output.value = jx_expression(select.value, schema=schema)
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Author: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http:# mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Author: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# Author: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
|
||||
#
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# `jx_elasticsearch`
|
||||
|
||||
This library implements [JSON Query Expressions]() atop an Elasticsearch.
|
||||
This library implements [JSON Query Expressions]() atop an Elasticsearch instance.
|
||||
|
||||
|
||||
## Contribution
|
||||
|
@ -14,3 +14,29 @@ There are two directories in the git history that may help for old versions.
|
|||
|
||||
Both of these directories are too old to be used directly, but they do have code templates for their respective scripting language, and they do have other hints about how to construct queries with the limitations of the older versions.
|
||||
|
||||
|
||||
|
||||
## elasticsearch.py
|
||||
|
||||
This module handles the lifecycle of an Elasticsearch index in the context of
|
||||
ETL. You only need this module if you are creating and retiring indexes. You
|
||||
do not need this module for simply searching; for that I suggest using the
|
||||
rest API directly.
|
||||
|
||||
### Settings
|
||||
|
||||
Both ```Cluster``` and ```Index``` objects accept the same settings dict,
|
||||
selecting only the properties it requires.
|
||||
|
||||
{
|
||||
"host" : "http://192.168.0.98",
|
||||
"port" : 9200,
|
||||
"index" : "b2g_tests",
|
||||
"type" : "test_result",
|
||||
"debug" : true,
|
||||
"limit_replicas" : true,
|
||||
"schema_file" : "resources/schema/test_schema.json"
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче