teaql 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- teaql/__init__.py +0 -0
- teaql/core/__init__.py +63 -0
- teaql/core/entity.py +35 -0
- teaql/core/eval.py +84 -0
- teaql/core/expr.py +345 -0
- teaql/core/graph.py +94 -0
- teaql/core/list.py +80 -0
- teaql/core/meta.py +68 -0
- teaql/core/mutation.py +149 -0
- teaql/core/query.py +424 -0
- teaql/core/safe_expression.py +86 -0
- teaql/core/value.py +259 -0
- teaql/core/xls.py +142 -0
- teaql/data_service/__init__.py +187 -0
- teaql/provider/__init__.py +0 -0
- teaql/provider/mysql/__init__.py +8 -0
- teaql/provider/mysql/dialect.py +31 -0
- teaql/provider/mysql/transport.py +101 -0
- teaql/provider/postgres/__init__.py +8 -0
- teaql/provider/postgres/dialect.py +42 -0
- teaql/provider/postgres/transport.py +90 -0
- teaql/provider/redis/__init__.py +3 -0
- teaql/provider/redis/remote.py +57 -0
- teaql/provider/redis/store.py +50 -0
- teaql/provider/sqlite/__init__.py +26 -0
- teaql/provider/sqlite/dialect.py +47 -0
- teaql/provider/sqlite/transport.py +89 -0
- teaql/provider/tfp_client/__init__.py +127 -0
- teaql/py.typed +1 -0
- teaql/runtime/__init__.py +7 -0
- teaql/runtime/audit.py +70 -0
- teaql/runtime/context.py +657 -0
- teaql/runtime/env.py +29 -0
- teaql/runtime/module.py +67 -0
- teaql/runtime/store.py +13 -0
- teaql/sql/__init__.py +25 -0
- teaql/sql/dialect.py +471 -0
- teaql/sql/executor.py +377 -0
- teaql/sql/types.py +78 -0
- teaql/web_integration/__init__.py +1 -0
- teaql/web_integration/fastapi/__init__.py +93 -0
- teaql-0.1.0.dist-info/METADATA +71 -0
- teaql-0.1.0.dist-info/RECORD +46 -0
- teaql-0.1.0.dist-info/WHEEL +5 -0
- teaql-0.1.0.dist-info/licenses/LICENSE +201 -0
- teaql-0.1.0.dist-info/top_level.txt +1 -0
teaql/__init__.py
ADDED
|
File without changes
|
teaql/core/__init__.py
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
from .value import Value, DataType, Timestamp
|
|
2
|
+
from .entity import BaseEntityData
|
|
3
|
+
from .expr import (
|
|
4
|
+
Expr, ExprBuilder, BinaryOp, ExprFunction,
|
|
5
|
+
ColumnExpr, ValueExpr, FunctionExpr, BinaryExpr,
|
|
6
|
+
SubQueryExpr, BetweenExpr, IsNullExpr, IsNotNullExpr,
|
|
7
|
+
AndExpr, OrExpr, NotExpr
|
|
8
|
+
)
|
|
9
|
+
from .query import (
|
|
10
|
+
SelectQuery, SortDirection, OrderBy, Aggregate, AggregateFunction,
|
|
11
|
+
Slice, RelationLoad, RawSqlProjection, ObjectGroupBy,
|
|
12
|
+
AggregationCacheOptions, StreamConfig, NamedExpr
|
|
13
|
+
)
|
|
14
|
+
from .mutation import (
|
|
15
|
+
InsertCommand, UpdateCommand, DeleteCommand, RecoverCommand,
|
|
16
|
+
BatchInsertCommand, BatchUpdateCommand, TraceNode,
|
|
17
|
+
InsertCommand, UpdateCommand, DeleteCommand, RecoverCommand,
|
|
18
|
+
BatchInsertCommand, BatchUpdateCommand, MutationRequest
|
|
19
|
+
)
|
|
20
|
+
from .graph import GraphNode
|
|
21
|
+
|
|
22
|
+
from .meta import EntityDescriptor, PropertyDescriptor
|
|
23
|
+
from .list import SmartList
|
|
24
|
+
from .eval import LoadState, EvalResult
|
|
25
|
+
from .safe_expression import SafeExpression
|
|
26
|
+
from .xls import XlsWorkbook, XlsPage, XlsBlock, XlsBlockBuildContext
|
|
27
|
+
|
|
28
|
+
__all__ = [
|
|
29
|
+
"Value", "DataType", "Timestamp",
|
|
30
|
+
"BaseEntityData",
|
|
31
|
+
"Expr", "ExprBuilder", "BinaryOp", "ExprFunction",
|
|
32
|
+
"SelectQuery", "SortDirection", "OrderBy", "Aggregate", "AggregateFunction",
|
|
33
|
+
"Slice", "RelationLoad", "RawSqlProjection", "ObjectGroupBy",
|
|
34
|
+
"AggregationCacheOptions", "StreamConfig", "NamedExpr",
|
|
35
|
+
"InsertCommand", "UpdateCommand", "DeleteCommand", "RecoverCommand",
|
|
36
|
+
"BatchInsertCommand", "BatchUpdateCommand", "TraceNode",
|
|
37
|
+
"InsertCommand", "UpdateCommand", "DeleteCommand", "RecoverCommand",
|
|
38
|
+
"BatchInsertCommand", "BatchUpdateCommand", "MutationRequest",
|
|
39
|
+
"GraphNode",
|
|
40
|
+
"EntityDescriptor", "PropertyDescriptor", "SmartList",
|
|
41
|
+
"LoadState", "EvalResult", "SafeExpression",
|
|
42
|
+
"XlsWorkbook", "XlsPage", "XlsBlock", "XlsBlockBuildContext"
|
|
43
|
+
]
|
|
44
|
+
import builtins
|
|
45
|
+
builtins.uint64 = int
|
|
46
|
+
builtins.int64 = int
|
|
47
|
+
builtins.string = str
|
|
48
|
+
builtins.time = type('time', (), {'Time': str})
|
|
49
|
+
builtins.typing = __import__('typing')
|
|
50
|
+
builtins.decimal = __import__('decimal')
|
|
51
|
+
TypeU64 = "U64"
|
|
52
|
+
TypeI64 = "I64"
|
|
53
|
+
TypeText = "Text"
|
|
54
|
+
TypeTimestamp = "Timestamp"
|
|
55
|
+
TypeDecimal = "Decimal"
|
|
56
|
+
TypeJson = "Json"
|
|
57
|
+
TypeBool = "Bool"
|
|
58
|
+
TypeDate = "Date"
|
|
59
|
+
def NewInsertCommand(entity: str):
|
|
60
|
+
return InsertCommand(entity=entity)
|
|
61
|
+
|
|
62
|
+
def NewUpdateCommand(entity: str, id_val):
|
|
63
|
+
return UpdateCommand(entity=entity, id=Value.from_any(id_val))
|
teaql/core/entity.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from typing import Dict, Optional, Any
|
|
2
|
+
from .value import Value
|
|
3
|
+
|
|
4
|
+
class BaseEntityData:
|
|
5
|
+
def __init__(self, id: int = 0, version: int = 0, dynamic: Optional[Dict[str, Value]] = None):
|
|
6
|
+
self.id = id
|
|
7
|
+
self.version = version
|
|
8
|
+
self.dynamic = dynamic or {}
|
|
9
|
+
|
|
10
|
+
@classmethod
|
|
11
|
+
def new(cls) -> 'BaseEntityData':
|
|
12
|
+
return cls()
|
|
13
|
+
|
|
14
|
+
def with_id(self, id: int) -> 'BaseEntityData':
|
|
15
|
+
self.id = id
|
|
16
|
+
return self
|
|
17
|
+
|
|
18
|
+
def with_version(self, version: int) -> 'BaseEntityData':
|
|
19
|
+
self.version = version
|
|
20
|
+
return self
|
|
21
|
+
|
|
22
|
+
def with_dynamic(self, key: str, value: Any) -> 'BaseEntityData':
|
|
23
|
+
self.dynamic[key] = Value.from_any(value)
|
|
24
|
+
return self
|
|
25
|
+
|
|
26
|
+
def to_record(self) -> Dict[str, Any]:
|
|
27
|
+
rec = {"id": self.id, "version": self.version}
|
|
28
|
+
if self.dynamic:
|
|
29
|
+
for k, v in self.dynamic.items():
|
|
30
|
+
rec[k] = v.to_json_value()
|
|
31
|
+
return rec
|
|
32
|
+
def get_dynamic(self, key: str) -> Optional[Value]:
|
|
33
|
+
return self.dynamic.get(key)
|
|
34
|
+
def put_dynamic(self, key, value):
|
|
35
|
+
self.dynamic[key] = value
|
teaql/core/eval.py
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
from enum import Enum, auto
|
|
2
|
+
from typing import Set, Any, Generic, TypeVar, Callable
|
|
3
|
+
|
|
4
|
+
class LoadStateType(Enum):
|
|
5
|
+
NotLoaded = auto()
|
|
6
|
+
Partial = auto()
|
|
7
|
+
FullyLoaded = auto()
|
|
8
|
+
|
|
9
|
+
class LoadState:
|
|
10
|
+
def __init__(self, state_type: LoadStateType = LoadStateType.NotLoaded, fields: Set[str] = None):
|
|
11
|
+
self.state_type = state_type
|
|
12
|
+
self.fields = fields or set()
|
|
13
|
+
|
|
14
|
+
@classmethod
|
|
15
|
+
def NotLoaded(cls) -> 'LoadState':
|
|
16
|
+
return cls(LoadStateType.NotLoaded)
|
|
17
|
+
|
|
18
|
+
@classmethod
|
|
19
|
+
def Partial(cls, fields: Set[str]) -> 'LoadState':
|
|
20
|
+
return cls(LoadStateType.Partial, fields)
|
|
21
|
+
|
|
22
|
+
@classmethod
|
|
23
|
+
def FullyLoaded(cls) -> 'LoadState':
|
|
24
|
+
return cls(LoadStateType.FullyLoaded)
|
|
25
|
+
|
|
26
|
+
def is_loaded(self, field_or_relation: str) -> bool:
|
|
27
|
+
if self.state_type == LoadStateType.NotLoaded:
|
|
28
|
+
return False
|
|
29
|
+
if self.state_type == LoadStateType.FullyLoaded:
|
|
30
|
+
return True
|
|
31
|
+
return field_or_relation in self.fields
|
|
32
|
+
|
|
33
|
+
T = TypeVar('T')
|
|
34
|
+
U = TypeVar('U')
|
|
35
|
+
|
|
36
|
+
class EvalResultType(Enum):
|
|
37
|
+
Value = auto()
|
|
38
|
+
Null = auto()
|
|
39
|
+
NotLoaded = auto()
|
|
40
|
+
|
|
41
|
+
class EvalResult(Generic[T]):
|
|
42
|
+
def __init__(self, result_type: EvalResultType, value: T = None, failed_node: str = None, attempted_path: str = None):
|
|
43
|
+
self.result_type = result_type
|
|
44
|
+
self.value = value
|
|
45
|
+
self.failed_node = failed_node
|
|
46
|
+
self.attempted_path = attempted_path
|
|
47
|
+
|
|
48
|
+
@classmethod
|
|
49
|
+
def Value(cls, value: T) -> 'EvalResult[T]':
|
|
50
|
+
return cls(EvalResultType.Value, value=value)
|
|
51
|
+
|
|
52
|
+
@classmethod
|
|
53
|
+
def Null(cls) -> 'EvalResult[T]':
|
|
54
|
+
return cls(EvalResultType.Null)
|
|
55
|
+
|
|
56
|
+
@classmethod
|
|
57
|
+
def NotLoaded(cls, failed_node: str, attempted_path: str) -> 'EvalResult[T]':
|
|
58
|
+
return cls(EvalResultType.NotLoaded, failed_node=failed_node, attempted_path=attempted_path)
|
|
59
|
+
|
|
60
|
+
def and_then(self, field_name: str, f: Callable[[T], 'EvalResult[U]']) -> 'EvalResult[U]':
|
|
61
|
+
if self.result_type == EvalResultType.Value:
|
|
62
|
+
res = f(self.value)
|
|
63
|
+
if res.result_type == EvalResultType.NotLoaded:
|
|
64
|
+
new_path = res.attempted_path
|
|
65
|
+
if new_path == field_name:
|
|
66
|
+
pass
|
|
67
|
+
elif not new_path:
|
|
68
|
+
new_path = field_name
|
|
69
|
+
else:
|
|
70
|
+
new_path = f"{field_name}.{new_path}"
|
|
71
|
+
return EvalResult.NotLoaded(res.failed_node, new_path)
|
|
72
|
+
return res
|
|
73
|
+
elif self.result_type == EvalResultType.Null:
|
|
74
|
+
return EvalResult.Null()
|
|
75
|
+
else:
|
|
76
|
+
return EvalResult.NotLoaded(self.failed_node, self.attempted_path)
|
|
77
|
+
|
|
78
|
+
def map(self, f: Callable[[T], U]) -> 'EvalResult[U]':
|
|
79
|
+
if self.result_type == EvalResultType.Value:
|
|
80
|
+
return EvalResult.Value(f(self.value))
|
|
81
|
+
elif self.result_type == EvalResultType.Null:
|
|
82
|
+
return EvalResult.Null()
|
|
83
|
+
else:
|
|
84
|
+
return EvalResult.NotLoaded(self.failed_node, self.attempted_path)
|
teaql/core/expr.py
ADDED
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
from enum import Enum, auto
|
|
2
|
+
from typing import List, Optional, Any, Union
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from .value import Value
|
|
5
|
+
|
|
6
|
+
class BinaryOp(Enum):
|
|
7
|
+
Eq = auto()
|
|
8
|
+
Ne = auto()
|
|
9
|
+
Gt = auto()
|
|
10
|
+
Gte = auto()
|
|
11
|
+
Lt = auto()
|
|
12
|
+
Lte = auto()
|
|
13
|
+
Like = auto()
|
|
14
|
+
NotLike = auto()
|
|
15
|
+
In = auto()
|
|
16
|
+
NotIn = auto()
|
|
17
|
+
InLarge = auto()
|
|
18
|
+
NotInLarge = auto()
|
|
19
|
+
|
|
20
|
+
class ExprFunction(Enum):
|
|
21
|
+
Soundex = auto()
|
|
22
|
+
Gbk = auto()
|
|
23
|
+
Count = auto()
|
|
24
|
+
Sum = auto()
|
|
25
|
+
Avg = auto()
|
|
26
|
+
Min = auto()
|
|
27
|
+
Max = auto()
|
|
28
|
+
Stddev = auto()
|
|
29
|
+
StddevPop = auto()
|
|
30
|
+
VarSamp = auto()
|
|
31
|
+
VarPop = auto()
|
|
32
|
+
BitAnd = auto()
|
|
33
|
+
BitOr = auto()
|
|
34
|
+
BitXor = auto()
|
|
35
|
+
|
|
36
|
+
class Expr:
|
|
37
|
+
@staticmethod
|
|
38
|
+
def eq(field: str, value: Any) -> 'Expr':
|
|
39
|
+
return BinaryExpr(ExprBuilder.column(field), BinaryOp.Eq, ExprBuilder.value(value) if not isinstance(value, Expr) else value)
|
|
40
|
+
@staticmethod
|
|
41
|
+
def ne(field: str, value: Any) -> 'Expr':
|
|
42
|
+
return BinaryExpr(ExprBuilder.column(field), BinaryOp.Ne, ExprBuilder.value(value) if not isinstance(value, Expr) else value)
|
|
43
|
+
@staticmethod
|
|
44
|
+
def gt(field: str, value: Any) -> 'Expr':
|
|
45
|
+
return BinaryExpr(ExprBuilder.column(field), BinaryOp.Gt, ExprBuilder.value(value) if not isinstance(value, Expr) else value)
|
|
46
|
+
@staticmethod
|
|
47
|
+
def gte(field: str, value: Any) -> 'Expr':
|
|
48
|
+
return BinaryExpr(ExprBuilder.column(field), BinaryOp.Gte, ExprBuilder.value(value) if not isinstance(value, Expr) else value)
|
|
49
|
+
@staticmethod
|
|
50
|
+
def lt(field: str, value: Any) -> 'Expr':
|
|
51
|
+
return BinaryExpr(ExprBuilder.column(field), BinaryOp.Lt, ExprBuilder.value(value) if not isinstance(value, Expr) else value)
|
|
52
|
+
@staticmethod
|
|
53
|
+
def lte(field: str, value: Any) -> 'Expr':
|
|
54
|
+
return BinaryExpr(ExprBuilder.column(field), BinaryOp.Lte, ExprBuilder.value(value) if not isinstance(value, Expr) else value)
|
|
55
|
+
@staticmethod
|
|
56
|
+
def like(field: str, value: Any) -> 'Expr':
|
|
57
|
+
return BinaryExpr(ExprBuilder.column(field), BinaryOp.Like, ExprBuilder.value(value) if not isinstance(value, Expr) else value)
|
|
58
|
+
@staticmethod
|
|
59
|
+
def not_like(field: str, value: Any) -> 'Expr':
|
|
60
|
+
return BinaryExpr(ExprBuilder.column(field), BinaryOp.NotLike, ExprBuilder.value(value) if not isinstance(value, Expr) else value)
|
|
61
|
+
|
|
62
|
+
@staticmethod
|
|
63
|
+
def new_and(left: 'Expr', right: 'Expr') -> 'Expr':
|
|
64
|
+
return AndExpr([left, right])
|
|
65
|
+
|
|
66
|
+
@staticmethod
|
|
67
|
+
def new_or(left: 'Expr', right: 'Expr') -> 'Expr':
|
|
68
|
+
return OrExpr([left, right])
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@dataclass
|
|
72
|
+
class ColumnExpr(Expr):
|
|
73
|
+
name: str
|
|
74
|
+
|
|
75
|
+
@dataclass
|
|
76
|
+
class ValueExpr(Expr):
|
|
77
|
+
value: Value
|
|
78
|
+
|
|
79
|
+
@dataclass
|
|
80
|
+
class FunctionExpr(Expr):
|
|
81
|
+
function: ExprFunction
|
|
82
|
+
args: List[Expr]
|
|
83
|
+
|
|
84
|
+
@dataclass
|
|
85
|
+
class BinaryExpr(Expr):
|
|
86
|
+
left: Expr
|
|
87
|
+
op: BinaryOp
|
|
88
|
+
right: Expr
|
|
89
|
+
|
|
90
|
+
@dataclass
|
|
91
|
+
class SubQueryExpr(Expr):
|
|
92
|
+
left: Expr
|
|
93
|
+
op: BinaryOp
|
|
94
|
+
entity: Any # EntityDescriptor
|
|
95
|
+
query: Any # SelectQuery
|
|
96
|
+
|
|
97
|
+
@dataclass
|
|
98
|
+
class BetweenExpr(Expr):
|
|
99
|
+
expr: Expr
|
|
100
|
+
lower: Expr
|
|
101
|
+
upper: Expr
|
|
102
|
+
|
|
103
|
+
@dataclass
|
|
104
|
+
class IsNullExpr(Expr):
|
|
105
|
+
expr: Expr
|
|
106
|
+
|
|
107
|
+
@dataclass
|
|
108
|
+
class IsNotNullExpr(Expr):
|
|
109
|
+
expr: Expr
|
|
110
|
+
|
|
111
|
+
@dataclass
|
|
112
|
+
class AndExpr(Expr):
|
|
113
|
+
exprs: List[Expr]
|
|
114
|
+
|
|
115
|
+
@dataclass
|
|
116
|
+
class OrExpr(Expr):
|
|
117
|
+
exprs: List[Expr]
|
|
118
|
+
|
|
119
|
+
@dataclass
|
|
120
|
+
class NotExpr(Expr):
|
|
121
|
+
expr: Expr
|
|
122
|
+
|
|
123
|
+
class ExprBuilder:
|
|
124
|
+
@staticmethod
|
|
125
|
+
def column(name: str) -> Expr:
|
|
126
|
+
return ColumnExpr(name)
|
|
127
|
+
|
|
128
|
+
@staticmethod
|
|
129
|
+
def value(value: Any) -> Expr:
|
|
130
|
+
return ValueExpr(Value.from_any(value))
|
|
131
|
+
|
|
132
|
+
@staticmethod
|
|
133
|
+
def function(function: ExprFunction, args: List[Expr]) -> Expr:
|
|
134
|
+
return FunctionExpr(function, args)
|
|
135
|
+
|
|
136
|
+
@staticmethod
|
|
137
|
+
def soundex(expr: Expr) -> Expr:
|
|
138
|
+
return ExprBuilder.function(ExprFunction.Soundex, [expr])
|
|
139
|
+
|
|
140
|
+
@staticmethod
|
|
141
|
+
def gbk(expr: Expr) -> Expr:
|
|
142
|
+
return ExprBuilder.function(ExprFunction.Gbk, [expr])
|
|
143
|
+
|
|
144
|
+
@staticmethod
|
|
145
|
+
def count_all() -> Expr:
|
|
146
|
+
return ExprBuilder.function(ExprFunction.Count, [])
|
|
147
|
+
|
|
148
|
+
@staticmethod
|
|
149
|
+
def count_expr(expr: Expr) -> Expr:
|
|
150
|
+
return ExprBuilder.function(ExprFunction.Count, [expr])
|
|
151
|
+
|
|
152
|
+
@staticmethod
|
|
153
|
+
def sum_expr(expr: Expr) -> Expr:
|
|
154
|
+
return ExprBuilder.function(ExprFunction.Sum, [expr])
|
|
155
|
+
|
|
156
|
+
@staticmethod
|
|
157
|
+
def avg_expr(expr: Expr) -> Expr:
|
|
158
|
+
return ExprBuilder.function(ExprFunction.Avg, [expr])
|
|
159
|
+
|
|
160
|
+
@staticmethod
|
|
161
|
+
def min_expr(expr: Expr) -> Expr:
|
|
162
|
+
return ExprBuilder.function(ExprFunction.Min, [expr])
|
|
163
|
+
|
|
164
|
+
@staticmethod
|
|
165
|
+
def max_expr(expr: Expr) -> Expr:
|
|
166
|
+
return ExprBuilder.function(ExprFunction.Max, [expr])
|
|
167
|
+
|
|
168
|
+
@staticmethod
|
|
169
|
+
def stddev_expr(expr: Expr) -> Expr:
|
|
170
|
+
return ExprBuilder.function(ExprFunction.Stddev, [expr])
|
|
171
|
+
|
|
172
|
+
@staticmethod
|
|
173
|
+
def stddev_pop_expr(expr: Expr) -> Expr:
|
|
174
|
+
return ExprBuilder.function(ExprFunction.StddevPop, [expr])
|
|
175
|
+
|
|
176
|
+
@staticmethod
|
|
177
|
+
def var_samp_expr(expr: Expr) -> Expr:
|
|
178
|
+
return ExprBuilder.function(ExprFunction.VarSamp, [expr])
|
|
179
|
+
|
|
180
|
+
@staticmethod
|
|
181
|
+
def var_pop_expr(expr: Expr) -> Expr:
|
|
182
|
+
return ExprBuilder.function(ExprFunction.VarPop, [expr])
|
|
183
|
+
|
|
184
|
+
@staticmethod
|
|
185
|
+
def bit_and_expr(expr: Expr) -> Expr:
|
|
186
|
+
return ExprBuilder.function(ExprFunction.BitAnd, [expr])
|
|
187
|
+
|
|
188
|
+
@staticmethod
|
|
189
|
+
def bit_or_expr(expr: Expr) -> Expr:
|
|
190
|
+
return ExprBuilder.function(ExprFunction.BitOr, [expr])
|
|
191
|
+
|
|
192
|
+
@staticmethod
|
|
193
|
+
def bit_xor_expr(expr: Expr) -> Expr:
|
|
194
|
+
return ExprBuilder.function(ExprFunction.BitXor, [expr])
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def column(name: str) -> Expr:
|
|
198
|
+
return ExprBuilder.column(name)
|
|
199
|
+
|
|
200
|
+
def value(val: Any) -> Expr:
|
|
201
|
+
return ExprBuilder.value(val)
|
|
202
|
+
|
|
203
|
+
def function(func: ExprFunction, args: List[Expr]) -> Expr:
|
|
204
|
+
return ExprBuilder.function(func, args)
|
|
205
|
+
|
|
206
|
+
def soundex(expr: Expr) -> Expr:
|
|
207
|
+
return ExprBuilder.soundex(expr)
|
|
208
|
+
|
|
209
|
+
def gbk(expr: Expr) -> Expr:
|
|
210
|
+
return ExprBuilder.gbk(expr)
|
|
211
|
+
|
|
212
|
+
def count_all() -> Expr:
|
|
213
|
+
return ExprBuilder.count_all()
|
|
214
|
+
|
|
215
|
+
def count_expr(expr: Expr) -> Expr:
|
|
216
|
+
return ExprBuilder.count_expr(expr)
|
|
217
|
+
|
|
218
|
+
def sum_expr(expr: Expr) -> Expr:
|
|
219
|
+
return ExprBuilder.sum_expr(expr)
|
|
220
|
+
|
|
221
|
+
def avg_expr(expr: Expr) -> Expr:
|
|
222
|
+
return ExprBuilder.avg_expr(expr)
|
|
223
|
+
|
|
224
|
+
def min_expr(expr: Expr) -> Expr:
|
|
225
|
+
return ExprBuilder.min_expr(expr)
|
|
226
|
+
|
|
227
|
+
def max_expr(expr: Expr) -> Expr:
|
|
228
|
+
return ExprBuilder.max_expr(expr)
|
|
229
|
+
|
|
230
|
+
def stddev_expr(expr: Expr) -> Expr:
|
|
231
|
+
return ExprBuilder.stddev_expr(expr)
|
|
232
|
+
|
|
233
|
+
def stddev_pop_expr(expr: Expr) -> Expr:
|
|
234
|
+
return ExprBuilder.stddev_pop_expr(expr)
|
|
235
|
+
|
|
236
|
+
def var_samp_expr(expr: Expr) -> Expr:
|
|
237
|
+
return ExprBuilder.var_samp_expr(expr)
|
|
238
|
+
|
|
239
|
+
def var_pop_expr(expr: Expr) -> Expr:
|
|
240
|
+
return ExprBuilder.var_pop_expr(expr)
|
|
241
|
+
|
|
242
|
+
def bit_and_expr(expr: Expr) -> Expr:
|
|
243
|
+
return ExprBuilder.bit_and_expr(expr)
|
|
244
|
+
|
|
245
|
+
def bit_or_expr(expr: Expr) -> Expr:
|
|
246
|
+
return ExprBuilder.bit_or_expr(expr)
|
|
247
|
+
|
|
248
|
+
def bit_xor_expr(expr: Expr) -> Expr:
|
|
249
|
+
return ExprBuilder.bit_xor_expr(expr)
|
|
250
|
+
|
|
251
|
+
def binary(left: Expr, op: BinaryOp, right: Expr) -> Expr:
|
|
252
|
+
return BinaryExpr(left, op, right)
|
|
253
|
+
|
|
254
|
+
def eq(field: str, val: Any) -> Expr:
|
|
255
|
+
return Expr.eq(field, val)
|
|
256
|
+
|
|
257
|
+
def ne(field: str, val: Any) -> Expr:
|
|
258
|
+
return Expr.ne(field, val)
|
|
259
|
+
|
|
260
|
+
def gt(field: str, val: Any) -> Expr:
|
|
261
|
+
return Expr.gt(field, val)
|
|
262
|
+
|
|
263
|
+
def gte(field: str, val: Any) -> Expr:
|
|
264
|
+
return Expr.gte(field, val)
|
|
265
|
+
|
|
266
|
+
def lt(field: str, val: Any) -> Expr:
|
|
267
|
+
return Expr.lt(field, val)
|
|
268
|
+
|
|
269
|
+
def lte(field: str, val: Any) -> Expr:
|
|
270
|
+
return Expr.lte(field, val)
|
|
271
|
+
|
|
272
|
+
def like(field: str, val: Any) -> Expr:
|
|
273
|
+
return Expr.like(field, val)
|
|
274
|
+
|
|
275
|
+
def not_like(field: str, val: Any) -> Expr:
|
|
276
|
+
return Expr.not_like(field, val)
|
|
277
|
+
|
|
278
|
+
def contain(field: str, val: Any) -> Expr:
|
|
279
|
+
return Expr.like(field, f"%{val}%")
|
|
280
|
+
|
|
281
|
+
def not_contain(field: str, val: Any) -> Expr:
|
|
282
|
+
return Expr.not_like(field, f"%{val}%")
|
|
283
|
+
|
|
284
|
+
def begin_with(field: str, val: Any) -> Expr:
|
|
285
|
+
return Expr.like(field, f"{val}%")
|
|
286
|
+
|
|
287
|
+
def not_begin_with(field: str, val: Any) -> Expr:
|
|
288
|
+
return Expr.not_like(field, f"{val}%")
|
|
289
|
+
|
|
290
|
+
def end_with(field: str, val: Any) -> Expr:
|
|
291
|
+
return Expr.like(field, f"%{val}")
|
|
292
|
+
|
|
293
|
+
def not_end_with(field: str, val: Any) -> Expr:
|
|
294
|
+
return Expr.not_like(field, f"%{val}")
|
|
295
|
+
|
|
296
|
+
def sound_like(field: str, val: Any) -> Expr:
|
|
297
|
+
return eq(soundex(column(field)), soundex(value(val)))
|
|
298
|
+
|
|
299
|
+
def in_list(field: str, vals: List[Any]) -> Expr:
|
|
300
|
+
return BinaryExpr(column(field), BinaryOp.In, value(vals))
|
|
301
|
+
|
|
302
|
+
def not_in_list(field: str, vals: List[Any]) -> Expr:
|
|
303
|
+
return BinaryExpr(column(field), BinaryOp.NotIn, value(vals))
|
|
304
|
+
|
|
305
|
+
def in_large(field: str, vals: List[Any]) -> Expr:
|
|
306
|
+
return BinaryExpr(column(field), BinaryOp.InLarge, value(vals))
|
|
307
|
+
|
|
308
|
+
def not_in_large(field: str, vals: List[Any]) -> Expr:
|
|
309
|
+
return BinaryExpr(column(field), BinaryOp.NotInLarge, value(vals))
|
|
310
|
+
|
|
311
|
+
def is_null(expr: Expr) -> Expr:
|
|
312
|
+
return IsNullExpr(expr)
|
|
313
|
+
|
|
314
|
+
def is_not_null(expr: Expr) -> Expr:
|
|
315
|
+
return IsNotNullExpr(expr)
|
|
316
|
+
|
|
317
|
+
def between(expr: Expr, lower: Expr, upper: Expr) -> Expr:
|
|
318
|
+
return BetweenExpr(expr, lower, upper)
|
|
319
|
+
|
|
320
|
+
def compare_columns(left: str, op: BinaryOp, right: str) -> Expr:
|
|
321
|
+
return BinaryExpr(column(left), op, column(right))
|
|
322
|
+
|
|
323
|
+
def subquery(left: Expr, op: BinaryOp, entity: Any, query: Any) -> Expr:
|
|
324
|
+
return SubQueryExpr(left, op, entity, query)
|
|
325
|
+
|
|
326
|
+
def in_subquery(left: Expr, entity: Any, query: Any) -> Expr:
|
|
327
|
+
return subquery(left, BinaryOp.In, entity, query)
|
|
328
|
+
|
|
329
|
+
def not_in_subquery(left: Expr, entity: Any, query: Any) -> Expr:
|
|
330
|
+
return subquery(left, BinaryOp.NotIn, entity, query)
|
|
331
|
+
|
|
332
|
+
def negate(expr: Expr) -> Expr:
|
|
333
|
+
return NotExpr(expr)
|
|
334
|
+
|
|
335
|
+
def and_expr(left: Expr, right: Expr) -> Expr:
|
|
336
|
+
return Expr.new_and(left, right)
|
|
337
|
+
|
|
338
|
+
def or_expr(left: Expr, right: Expr) -> Expr:
|
|
339
|
+
return OrExpr([left, right])
|
|
340
|
+
|
|
341
|
+
def and_(*exprs: Expr) -> Expr:
|
|
342
|
+
return AndExpr(list(exprs))
|
|
343
|
+
|
|
344
|
+
def or_(*exprs: Expr) -> Expr:
|
|
345
|
+
return OrExpr(list(exprs))
|
teaql/core/graph.py
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
from typing import Any, Dict, List, Optional
|
|
2
|
+
from enum import Enum
|
|
3
|
+
|
|
4
|
+
class EntityGraphOperation(Enum):
|
|
5
|
+
SAVE = 1
|
|
6
|
+
DELETE = 2
|
|
7
|
+
|
|
8
|
+
class GraphNode:
|
|
9
|
+
def __init__(self, entity: str):
|
|
10
|
+
self.entity = entity
|
|
11
|
+
self.fields: Dict[str, Any] = {}
|
|
12
|
+
self.is_deleted: bool = False
|
|
13
|
+
self.comment_text: Optional[str] = None
|
|
14
|
+
self.children: Dict[str, List['GraphNode']] = {}
|
|
15
|
+
|
|
16
|
+
def child(self, rel: str) -> 'GraphNode':
|
|
17
|
+
if rel not in self.children:
|
|
18
|
+
self.children[rel] = []
|
|
19
|
+
node = GraphNode(rel)
|
|
20
|
+
self.children[rel].append(node)
|
|
21
|
+
return node
|
|
22
|
+
|
|
23
|
+
def relation(self, rel: str) -> 'GraphNode':
|
|
24
|
+
return self.child(rel)
|
|
25
|
+
|
|
26
|
+
def relations(self) -> Dict[str, List['GraphNode']]:
|
|
27
|
+
return self.children
|
|
28
|
+
|
|
29
|
+
def remove(self, rel: str):
|
|
30
|
+
if rel in self.children:
|
|
31
|
+
del self.children[rel]
|
|
32
|
+
|
|
33
|
+
def set(self, field: str, val: Any) -> 'GraphNode':
|
|
34
|
+
self.fields[field] = val
|
|
35
|
+
return self
|
|
36
|
+
|
|
37
|
+
def value(self, field: str) -> Optional[Any]:
|
|
38
|
+
return self.fields.get(field)
|
|
39
|
+
|
|
40
|
+
def delete(self) -> 'GraphNode':
|
|
41
|
+
self.is_deleted = True
|
|
42
|
+
return self
|
|
43
|
+
|
|
44
|
+
def comment(self, text: str) -> 'GraphNode':
|
|
45
|
+
self.comment_text = text
|
|
46
|
+
return self
|
|
47
|
+
|
|
48
|
+
def set_comment(self, text: str):
|
|
49
|
+
self.comment_text = text
|
|
50
|
+
|
|
51
|
+
def id(self) -> Optional[Any]:
|
|
52
|
+
return self.fields.get('id')
|
|
53
|
+
|
|
54
|
+
def operation(self) -> Optional['EntityGraphOperation']:
|
|
55
|
+
if self.is_deleted:
|
|
56
|
+
return EntityGraphOperation.DELETE
|
|
57
|
+
return EntityGraphOperation.SAVE
|
|
58
|
+
|
|
59
|
+
def reference(self, rel: str, ref_id: Any) -> 'GraphNode':
|
|
60
|
+
node = self.child(rel)
|
|
61
|
+
node.set('id', ref_id)
|
|
62
|
+
return node
|
|
63
|
+
|
|
64
|
+
class EntityGraphBuilder:
|
|
65
|
+
def __init__(self, entity: str):
|
|
66
|
+
self.node = GraphNode(entity)
|
|
67
|
+
|
|
68
|
+
def set(self, key: str, value: Any) -> 'EntityGraphBuilder':
|
|
69
|
+
self.node.set(key, value)
|
|
70
|
+
return self
|
|
71
|
+
|
|
72
|
+
def delete(self) -> 'EntityGraphBuilder':
|
|
73
|
+
self.node.delete()
|
|
74
|
+
return self
|
|
75
|
+
|
|
76
|
+
def comment(self, text: str) -> 'EntityGraphBuilder':
|
|
77
|
+
self.node.comment(text)
|
|
78
|
+
return self
|
|
79
|
+
|
|
80
|
+
def child(self, child_node: GraphNode) -> 'EntityGraphBuilder':
|
|
81
|
+
self.node.children.append(child_node)
|
|
82
|
+
return self
|
|
83
|
+
|
|
84
|
+
def save(self) -> 'EntityGraphBuilder':
|
|
85
|
+
self.node.operation = EntityGraphOperation.SAVE
|
|
86
|
+
return self
|
|
87
|
+
|
|
88
|
+
def build(self) -> GraphNode:
|
|
89
|
+
return self.node
|
|
90
|
+
|
|
91
|
+
class EntityGraph:
|
|
92
|
+
@staticmethod
|
|
93
|
+
def new(entity: str) -> EntityGraphBuilder:
|
|
94
|
+
return EntityGraphBuilder(entity)
|
teaql/core/list.py
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
from typing import Any, List, Dict, Iterator
|
|
2
|
+
|
|
3
|
+
class SmartList:
|
|
4
|
+
def __init__(self, data: List[Any], facets: Dict[str, Any] = None, total_count: int = None):
|
|
5
|
+
self.data = data
|
|
6
|
+
self.facets = facets or {}
|
|
7
|
+
self.total_count = total_count if total_count is not None else len(data)
|
|
8
|
+
|
|
9
|
+
def facet(self, name: str) -> Any:
|
|
10
|
+
return self.facets.get(name)
|
|
11
|
+
|
|
12
|
+
def __iter__(self) -> Iterator[Any]:
|
|
13
|
+
return iter(self.data)
|
|
14
|
+
|
|
15
|
+
def __len__(self) -> int:
|
|
16
|
+
return len(self.data)
|
|
17
|
+
|
|
18
|
+
def map(self, f) -> 'SmartList':
|
|
19
|
+
return SmartList([f(x) for x in self.data], self.facets, self.total_count)
|
|
20
|
+
|
|
21
|
+
def filter(self, f) -> 'SmartList':
|
|
22
|
+
return SmartList([x for x in self.data if f(x)], self.facets, self.total_count)
|
|
23
|
+
|
|
24
|
+
def flat_map(self, f) -> 'SmartList':
|
|
25
|
+
result = []
|
|
26
|
+
for x in self.data:
|
|
27
|
+
result.extend(f(x))
|
|
28
|
+
return SmartList(result, self.facets, self.total_count)
|
|
29
|
+
|
|
30
|
+
def first(self) -> Any:
|
|
31
|
+
return self.data[0] if self.data else None
|
|
32
|
+
|
|
33
|
+
def last(self) -> Any:
|
|
34
|
+
return self.data[-1] if self.data else None
|
|
35
|
+
|
|
36
|
+
def is_empty(self) -> bool:
|
|
37
|
+
return len(self.data) == 0
|
|
38
|
+
|
|
39
|
+
def into_vec(self) -> List[Any]:
|
|
40
|
+
return self.data
|
|
41
|
+
|
|
42
|
+
def get(self, index: int) -> Any:
|
|
43
|
+
if 0 <= index < len(self.data):
|
|
44
|
+
return self.data[index]
|
|
45
|
+
return None
|
|
46
|
+
|
|
47
|
+
def retain(self, f):
|
|
48
|
+
self.data = [x for x in self.data if f(x)]
|
|
49
|
+
|
|
50
|
+
def to_list(lst: SmartList) -> List[Any]:
|
|
51
|
+
return lst.data
|
|
52
|
+
|
|
53
|
+
def to_set(lst: SmartList) -> set:
|
|
54
|
+
return set(lst.data)
|
|
55
|
+
|
|
56
|
+
def identity_map(lst: SmartList) -> Dict[Any, Any]:
|
|
57
|
+
return {x: x for x in lst.data}
|
|
58
|
+
|
|
59
|
+
def group_by(lst: SmartList, key_func) -> Dict[Any, List[Any]]:
|
|
60
|
+
result = {}
|
|
61
|
+
for item in lst.data:
|
|
62
|
+
key = key_func(item)
|
|
63
|
+
if key not in result:
|
|
64
|
+
result[key] = []
|
|
65
|
+
result[key].append(item)
|
|
66
|
+
return result
|
|
67
|
+
|
|
68
|
+
def into_records(lst: SmartList) -> List[Dict[str, Any]]:
|
|
69
|
+
# Assuming the elements are entities with a .values dict or similar, or they are just dicts
|
|
70
|
+
return [x.values if hasattr(x, 'values') else dict(x) for x in lst.data]
|
|
71
|
+
|
|
72
|
+
def ids(lst: SmartList) -> List[Any]:
|
|
73
|
+
# Assuming elements have a .get('id') or similar
|
|
74
|
+
return [x.get('id') if isinstance(x, dict) else getattr(x, 'id', None) for x in lst.data]
|
|
75
|
+
|
|
76
|
+
def map_by_id(lst: SmartList) -> Dict[Any, Any]:
|
|
77
|
+
return { (x.get('id') if isinstance(x, dict) else getattr(x, 'id', None)): x for x in lst.data }
|
|
78
|
+
|
|
79
|
+
def versions(lst: SmartList) -> List[Any]:
|
|
80
|
+
return [x.get('version') if isinstance(x, dict) else getattr(x, 'version', None) for x in lst.data]
|