relationalai 0.13.5__py3-none-any.whl → 1.0.0a2__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.
- relationalai/__init__.py +1 -256
- relationalai/config/__init__.py +56 -0
- relationalai/config/config.py +289 -0
- relationalai/config/config_fields.py +86 -0
- relationalai/config/connections/__init__.py +46 -0
- relationalai/config/connections/base.py +23 -0
- relationalai/config/connections/duckdb.py +29 -0
- relationalai/config/connections/snowflake.py +243 -0
- relationalai/config/external/__init__.py +17 -0
- relationalai/config/external/dbt_converter.py +101 -0
- relationalai/config/external/dbt_models.py +93 -0
- relationalai/config/external/snowflake_converter.py +41 -0
- relationalai/config/external/snowflake_models.py +85 -0
- relationalai/config/external/utils.py +19 -0
- relationalai/config/shims.py +1 -0
- relationalai/semantics/__init__.py +146 -22
- relationalai/semantics/backends/lqp/annotations.py +11 -0
- relationalai/semantics/backends/sql/sql_compiler.py +327 -0
- relationalai/semantics/frontend/base.py +1719 -0
- relationalai/semantics/frontend/core.py +179 -0
- relationalai/semantics/frontend/front_compiler.py +1316 -0
- relationalai/semantics/frontend/pprint.py +408 -0
- relationalai/semantics/metamodel/__init__.py +6 -40
- relationalai/semantics/metamodel/builtins.py +206 -772
- relationalai/semantics/metamodel/metamodel.py +465 -0
- relationalai/semantics/metamodel/metamodel_analyzer.py +519 -0
- relationalai/semantics/metamodel/pprint.py +414 -0
- relationalai/semantics/metamodel/rewriter.py +266 -0
- relationalai/semantics/metamodel/typer.py +1213 -0
- relationalai/semantics/std/__init__.py +60 -40
- relationalai/semantics/std/aggregates.py +148 -0
- relationalai/semantics/std/common.py +44 -0
- relationalai/semantics/std/constraints.py +37 -43
- relationalai/semantics/std/datetime.py +249 -135
- relationalai/semantics/std/decimals.py +45 -52
- relationalai/semantics/std/floats.py +13 -5
- relationalai/semantics/std/integers.py +26 -11
- relationalai/semantics/std/math.py +183 -112
- relationalai/semantics/std/numbers.py +86 -0
- relationalai/semantics/std/re.py +80 -62
- relationalai/semantics/std/strings.py +101 -46
- relationalai/shims/executor.py +179 -0
- relationalai/shims/helpers.py +126 -0
- relationalai/shims/hoister.py +221 -0
- relationalai/shims/mm2v0.py +1394 -0
- relationalai/tools/cli/__init__.py +6 -0
- relationalai/tools/cli/cli.py +90 -0
- relationalai/tools/cli/components/__init__.py +5 -0
- relationalai/tools/cli/components/progress_reader.py +1524 -0
- relationalai/tools/cli/components/utils.py +58 -0
- relationalai/tools/cli/config_template.py +45 -0
- relationalai/tools/cli/dev.py +19 -0
- relationalai/tools/debugger.py +289 -183
- relationalai/tools/typer_debugger.py +93 -0
- relationalai/util/dataclasses.py +43 -0
- relationalai/util/docutils.py +40 -0
- relationalai/util/error.py +199 -0
- relationalai/util/format.py +48 -109
- relationalai/util/naming.py +145 -0
- relationalai/util/python.py +35 -0
- relationalai/util/runtime.py +156 -0
- relationalai/util/schema.py +197 -0
- relationalai/util/source.py +185 -0
- relationalai/util/structures.py +163 -0
- relationalai/util/tracing.py +261 -0
- relationalai-1.0.0a2.dist-info/METADATA +44 -0
- relationalai-1.0.0a2.dist-info/RECORD +489 -0
- relationalai-1.0.0a2.dist-info/WHEEL +5 -0
- relationalai-1.0.0a2.dist-info/entry_points.txt +3 -0
- relationalai-1.0.0a2.dist-info/top_level.txt +2 -0
- v0/relationalai/__init__.py +216 -0
- v0/relationalai/clients/__init__.py +5 -0
- v0/relationalai/clients/azure.py +477 -0
- v0/relationalai/clients/client.py +912 -0
- v0/relationalai/clients/config.py +673 -0
- v0/relationalai/clients/direct_access_client.py +118 -0
- v0/relationalai/clients/hash_util.py +31 -0
- v0/relationalai/clients/local.py +571 -0
- v0/relationalai/clients/profile_polling.py +73 -0
- v0/relationalai/clients/result_helpers.py +420 -0
- v0/relationalai/clients/snowflake.py +3869 -0
- v0/relationalai/clients/types.py +113 -0
- v0/relationalai/clients/use_index_poller.py +980 -0
- v0/relationalai/clients/util.py +356 -0
- v0/relationalai/debugging.py +389 -0
- v0/relationalai/dsl.py +1749 -0
- v0/relationalai/early_access/builder/__init__.py +30 -0
- v0/relationalai/early_access/builder/builder/__init__.py +35 -0
- v0/relationalai/early_access/builder/snowflake/__init__.py +12 -0
- v0/relationalai/early_access/builder/std/__init__.py +25 -0
- v0/relationalai/early_access/builder/std/decimals/__init__.py +12 -0
- v0/relationalai/early_access/builder/std/integers/__init__.py +12 -0
- v0/relationalai/early_access/builder/std/math/__init__.py +12 -0
- v0/relationalai/early_access/builder/std/strings/__init__.py +14 -0
- v0/relationalai/early_access/devtools/__init__.py +12 -0
- v0/relationalai/early_access/devtools/benchmark_lqp/__init__.py +12 -0
- v0/relationalai/early_access/devtools/extract_lqp/__init__.py +12 -0
- v0/relationalai/early_access/dsl/adapters/orm/adapter_qb.py +427 -0
- v0/relationalai/early_access/dsl/adapters/orm/parser.py +636 -0
- v0/relationalai/early_access/dsl/adapters/owl/adapter.py +176 -0
- v0/relationalai/early_access/dsl/adapters/owl/parser.py +160 -0
- v0/relationalai/early_access/dsl/bindings/common.py +402 -0
- v0/relationalai/early_access/dsl/bindings/csv.py +170 -0
- v0/relationalai/early_access/dsl/bindings/legacy/binding_models.py +143 -0
- v0/relationalai/early_access/dsl/bindings/snowflake.py +64 -0
- v0/relationalai/early_access/dsl/codegen/binder.py +411 -0
- v0/relationalai/early_access/dsl/codegen/common.py +79 -0
- v0/relationalai/early_access/dsl/codegen/helpers.py +23 -0
- v0/relationalai/early_access/dsl/codegen/relations.py +700 -0
- v0/relationalai/early_access/dsl/codegen/weaver.py +417 -0
- v0/relationalai/early_access/dsl/core/builders/__init__.py +47 -0
- v0/relationalai/early_access/dsl/core/builders/logic.py +19 -0
- v0/relationalai/early_access/dsl/core/builders/scalar_constraint.py +11 -0
- v0/relationalai/early_access/dsl/core/constraints/predicate/atomic.py +455 -0
- v0/relationalai/early_access/dsl/core/constraints/predicate/universal.py +73 -0
- v0/relationalai/early_access/dsl/core/constraints/scalar.py +310 -0
- v0/relationalai/early_access/dsl/core/context.py +13 -0
- v0/relationalai/early_access/dsl/core/cset.py +132 -0
- v0/relationalai/early_access/dsl/core/exprs/__init__.py +116 -0
- v0/relationalai/early_access/dsl/core/exprs/relational.py +18 -0
- v0/relationalai/early_access/dsl/core/exprs/scalar.py +412 -0
- v0/relationalai/early_access/dsl/core/instances.py +44 -0
- v0/relationalai/early_access/dsl/core/logic/__init__.py +193 -0
- v0/relationalai/early_access/dsl/core/logic/aggregation.py +98 -0
- v0/relationalai/early_access/dsl/core/logic/exists.py +223 -0
- v0/relationalai/early_access/dsl/core/logic/helper.py +163 -0
- v0/relationalai/early_access/dsl/core/namespaces.py +32 -0
- v0/relationalai/early_access/dsl/core/relations.py +276 -0
- v0/relationalai/early_access/dsl/core/rules.py +112 -0
- v0/relationalai/early_access/dsl/core/std/__init__.py +45 -0
- v0/relationalai/early_access/dsl/core/temporal/recall.py +6 -0
- v0/relationalai/early_access/dsl/core/types/__init__.py +270 -0
- v0/relationalai/early_access/dsl/core/types/concepts.py +128 -0
- v0/relationalai/early_access/dsl/core/types/constrained/__init__.py +267 -0
- v0/relationalai/early_access/dsl/core/types/constrained/nominal.py +143 -0
- v0/relationalai/early_access/dsl/core/types/constrained/subtype.py +124 -0
- v0/relationalai/early_access/dsl/core/types/standard.py +92 -0
- v0/relationalai/early_access/dsl/core/types/unconstrained.py +50 -0
- v0/relationalai/early_access/dsl/core/types/variables.py +203 -0
- v0/relationalai/early_access/dsl/ir/compiler.py +318 -0
- v0/relationalai/early_access/dsl/ir/executor.py +260 -0
- v0/relationalai/early_access/dsl/ontologies/constraints.py +88 -0
- v0/relationalai/early_access/dsl/ontologies/export.py +30 -0
- v0/relationalai/early_access/dsl/ontologies/models.py +453 -0
- v0/relationalai/early_access/dsl/ontologies/python_printer.py +303 -0
- v0/relationalai/early_access/dsl/ontologies/readings.py +60 -0
- v0/relationalai/early_access/dsl/ontologies/relationships.py +322 -0
- v0/relationalai/early_access/dsl/ontologies/roles.py +87 -0
- v0/relationalai/early_access/dsl/ontologies/subtyping.py +55 -0
- v0/relationalai/early_access/dsl/orm/constraints.py +438 -0
- v0/relationalai/early_access/dsl/orm/measures/dimensions.py +200 -0
- v0/relationalai/early_access/dsl/orm/measures/initializer.py +16 -0
- v0/relationalai/early_access/dsl/orm/measures/measure_rules.py +275 -0
- v0/relationalai/early_access/dsl/orm/measures/measures.py +299 -0
- v0/relationalai/early_access/dsl/orm/measures/role_exprs.py +268 -0
- v0/relationalai/early_access/dsl/orm/models.py +256 -0
- v0/relationalai/early_access/dsl/orm/object_oriented_printer.py +344 -0
- v0/relationalai/early_access/dsl/orm/printer.py +469 -0
- v0/relationalai/early_access/dsl/orm/reasoners.py +480 -0
- v0/relationalai/early_access/dsl/orm/relations.py +19 -0
- v0/relationalai/early_access/dsl/orm/relationships.py +251 -0
- v0/relationalai/early_access/dsl/orm/types.py +42 -0
- v0/relationalai/early_access/dsl/orm/utils.py +79 -0
- v0/relationalai/early_access/dsl/orm/verb.py +204 -0
- v0/relationalai/early_access/dsl/physical_metadata/tables.py +133 -0
- v0/relationalai/early_access/dsl/relations.py +170 -0
- v0/relationalai/early_access/dsl/rulesets.py +69 -0
- v0/relationalai/early_access/dsl/schemas/__init__.py +450 -0
- v0/relationalai/early_access/dsl/schemas/builder.py +48 -0
- v0/relationalai/early_access/dsl/schemas/comp_names.py +51 -0
- v0/relationalai/early_access/dsl/schemas/components.py +203 -0
- v0/relationalai/early_access/dsl/schemas/contexts.py +156 -0
- v0/relationalai/early_access/dsl/schemas/exprs.py +89 -0
- v0/relationalai/early_access/dsl/schemas/fragments.py +464 -0
- v0/relationalai/early_access/dsl/serialization.py +79 -0
- v0/relationalai/early_access/dsl/serialize/exporter.py +163 -0
- v0/relationalai/early_access/dsl/snow/api.py +104 -0
- v0/relationalai/early_access/dsl/snow/common.py +76 -0
- v0/relationalai/early_access/dsl/state_mgmt/__init__.py +129 -0
- v0/relationalai/early_access/dsl/state_mgmt/state_charts.py +125 -0
- v0/relationalai/early_access/dsl/state_mgmt/transitions.py +130 -0
- v0/relationalai/early_access/dsl/types/__init__.py +40 -0
- v0/relationalai/early_access/dsl/types/concepts.py +12 -0
- v0/relationalai/early_access/dsl/types/entities.py +135 -0
- v0/relationalai/early_access/dsl/types/values.py +17 -0
- v0/relationalai/early_access/dsl/utils.py +102 -0
- v0/relationalai/early_access/graphs/__init__.py +13 -0
- v0/relationalai/early_access/lqp/__init__.py +12 -0
- v0/relationalai/early_access/lqp/compiler/__init__.py +12 -0
- v0/relationalai/early_access/lqp/constructors/__init__.py +18 -0
- v0/relationalai/early_access/lqp/executor/__init__.py +12 -0
- v0/relationalai/early_access/lqp/ir/__init__.py +12 -0
- v0/relationalai/early_access/lqp/passes/__init__.py +12 -0
- v0/relationalai/early_access/lqp/pragmas/__init__.py +12 -0
- v0/relationalai/early_access/lqp/primitives/__init__.py +12 -0
- v0/relationalai/early_access/lqp/types/__init__.py +12 -0
- v0/relationalai/early_access/lqp/utils/__init__.py +12 -0
- v0/relationalai/early_access/lqp/validators/__init__.py +12 -0
- v0/relationalai/early_access/metamodel/__init__.py +58 -0
- v0/relationalai/early_access/metamodel/builtins/__init__.py +12 -0
- v0/relationalai/early_access/metamodel/compiler/__init__.py +12 -0
- v0/relationalai/early_access/metamodel/dependency/__init__.py +12 -0
- v0/relationalai/early_access/metamodel/factory/__init__.py +17 -0
- v0/relationalai/early_access/metamodel/helpers/__init__.py +12 -0
- v0/relationalai/early_access/metamodel/ir/__init__.py +14 -0
- v0/relationalai/early_access/metamodel/rewrite/__init__.py +7 -0
- v0/relationalai/early_access/metamodel/typer/__init__.py +3 -0
- v0/relationalai/early_access/metamodel/typer/typer/__init__.py +12 -0
- v0/relationalai/early_access/metamodel/types/__init__.py +15 -0
- v0/relationalai/early_access/metamodel/util/__init__.py +15 -0
- v0/relationalai/early_access/metamodel/visitor/__init__.py +12 -0
- v0/relationalai/early_access/rel/__init__.py +12 -0
- v0/relationalai/early_access/rel/executor/__init__.py +12 -0
- v0/relationalai/early_access/rel/rel_utils/__init__.py +12 -0
- v0/relationalai/early_access/rel/rewrite/__init__.py +7 -0
- v0/relationalai/early_access/solvers/__init__.py +19 -0
- v0/relationalai/early_access/sql/__init__.py +11 -0
- v0/relationalai/early_access/sql/executor/__init__.py +3 -0
- v0/relationalai/early_access/sql/rewrite/__init__.py +3 -0
- v0/relationalai/early_access/tests/logging/__init__.py +12 -0
- v0/relationalai/early_access/tests/test_snapshot_base/__init__.py +12 -0
- v0/relationalai/early_access/tests/utils/__init__.py +12 -0
- v0/relationalai/environments/__init__.py +35 -0
- v0/relationalai/environments/base.py +381 -0
- v0/relationalai/environments/colab.py +14 -0
- v0/relationalai/environments/generic.py +71 -0
- v0/relationalai/environments/ipython.py +68 -0
- v0/relationalai/environments/jupyter.py +9 -0
- v0/relationalai/environments/snowbook.py +169 -0
- v0/relationalai/errors.py +2478 -0
- v0/relationalai/experimental/SF.py +38 -0
- v0/relationalai/experimental/inspect.py +47 -0
- v0/relationalai/experimental/pathfinder/__init__.py +158 -0
- v0/relationalai/experimental/pathfinder/api.py +160 -0
- v0/relationalai/experimental/pathfinder/automaton.py +584 -0
- v0/relationalai/experimental/pathfinder/bridge.py +226 -0
- v0/relationalai/experimental/pathfinder/compiler.py +416 -0
- v0/relationalai/experimental/pathfinder/datalog.py +214 -0
- v0/relationalai/experimental/pathfinder/diagnostics.py +56 -0
- v0/relationalai/experimental/pathfinder/filter.py +236 -0
- v0/relationalai/experimental/pathfinder/glushkov.py +439 -0
- v0/relationalai/experimental/pathfinder/options.py +265 -0
- v0/relationalai/experimental/pathfinder/rpq.py +344 -0
- v0/relationalai/experimental/pathfinder/transition.py +200 -0
- v0/relationalai/experimental/pathfinder/utils.py +26 -0
- v0/relationalai/experimental/paths/api.py +143 -0
- v0/relationalai/experimental/paths/benchmarks/grid_graph.py +37 -0
- v0/relationalai/experimental/paths/examples/basic_example.py +40 -0
- v0/relationalai/experimental/paths/examples/minimal_engine_warmup.py +3 -0
- v0/relationalai/experimental/paths/examples/movie_example.py +77 -0
- v0/relationalai/experimental/paths/examples/paths_benchmark.py +115 -0
- v0/relationalai/experimental/paths/examples/paths_example.py +116 -0
- v0/relationalai/experimental/paths/examples/pattern_to_automaton.py +28 -0
- v0/relationalai/experimental/paths/find_paths_via_automaton.py +85 -0
- v0/relationalai/experimental/paths/graph.py +185 -0
- v0/relationalai/experimental/paths/path_algorithms/find_paths.py +280 -0
- v0/relationalai/experimental/paths/path_algorithms/one_sided_ball_repetition.py +26 -0
- v0/relationalai/experimental/paths/path_algorithms/one_sided_ball_upto.py +111 -0
- v0/relationalai/experimental/paths/path_algorithms/single.py +59 -0
- v0/relationalai/experimental/paths/path_algorithms/two_sided_balls_repetition.py +39 -0
- v0/relationalai/experimental/paths/path_algorithms/two_sided_balls_upto.py +103 -0
- v0/relationalai/experimental/paths/path_algorithms/usp-old.py +130 -0
- v0/relationalai/experimental/paths/path_algorithms/usp-tuple.py +183 -0
- v0/relationalai/experimental/paths/path_algorithms/usp.py +150 -0
- v0/relationalai/experimental/paths/product_graph.py +93 -0
- v0/relationalai/experimental/paths/rpq/automaton.py +584 -0
- v0/relationalai/experimental/paths/rpq/diagnostics.py +56 -0
- v0/relationalai/experimental/paths/rpq/rpq.py +378 -0
- v0/relationalai/experimental/paths/tests/tests_limit_sp_max_length.py +90 -0
- v0/relationalai/experimental/paths/tests/tests_limit_sp_multiple.py +119 -0
- v0/relationalai/experimental/paths/tests/tests_limit_sp_single.py +104 -0
- v0/relationalai/experimental/paths/tests/tests_limit_walks_multiple.py +113 -0
- v0/relationalai/experimental/paths/tests/tests_limit_walks_single.py +149 -0
- v0/relationalai/experimental/paths/tests/tests_one_sided_ball_repetition_multiple.py +70 -0
- v0/relationalai/experimental/paths/tests/tests_one_sided_ball_repetition_single.py +64 -0
- v0/relationalai/experimental/paths/tests/tests_one_sided_ball_upto_multiple.py +115 -0
- v0/relationalai/experimental/paths/tests/tests_one_sided_ball_upto_single.py +75 -0
- v0/relationalai/experimental/paths/tests/tests_single_paths.py +152 -0
- v0/relationalai/experimental/paths/tests/tests_single_walks.py +208 -0
- v0/relationalai/experimental/paths/tests/tests_single_walks_undirected.py +297 -0
- v0/relationalai/experimental/paths/tests/tests_two_sided_balls_repetition_multiple.py +107 -0
- v0/relationalai/experimental/paths/tests/tests_two_sided_balls_repetition_single.py +76 -0
- v0/relationalai/experimental/paths/tests/tests_two_sided_balls_upto_multiple.py +76 -0
- v0/relationalai/experimental/paths/tests/tests_two_sided_balls_upto_single.py +110 -0
- v0/relationalai/experimental/paths/tests/tests_usp_nsp_multiple.py +229 -0
- v0/relationalai/experimental/paths/tests/tests_usp_nsp_single.py +108 -0
- v0/relationalai/experimental/paths/tree_agg.py +168 -0
- v0/relationalai/experimental/paths/utilities/iterators.py +27 -0
- v0/relationalai/experimental/paths/utilities/prefix_sum.py +91 -0
- v0/relationalai/experimental/solvers.py +1087 -0
- v0/relationalai/loaders/csv.py +195 -0
- v0/relationalai/loaders/loader.py +177 -0
- v0/relationalai/loaders/types.py +23 -0
- v0/relationalai/rel_emitter.py +373 -0
- v0/relationalai/rel_utils.py +185 -0
- v0/relationalai/semantics/__init__.py +29 -0
- v0/relationalai/semantics/devtools/benchmark_lqp.py +536 -0
- v0/relationalai/semantics/devtools/compilation_manager.py +294 -0
- v0/relationalai/semantics/devtools/extract_lqp.py +110 -0
- v0/relationalai/semantics/internal/internal.py +3785 -0
- v0/relationalai/semantics/internal/snowflake.py +325 -0
- v0/relationalai/semantics/lqp/builtins.py +16 -0
- v0/relationalai/semantics/lqp/compiler.py +22 -0
- v0/relationalai/semantics/lqp/constructors.py +68 -0
- v0/relationalai/semantics/lqp/executor.py +474 -0
- v0/relationalai/semantics/lqp/intrinsics.py +24 -0
- v0/relationalai/semantics/lqp/ir.py +124 -0
- v0/relationalai/semantics/lqp/model2lqp.py +877 -0
- v0/relationalai/semantics/lqp/passes.py +680 -0
- v0/relationalai/semantics/lqp/primitives.py +252 -0
- v0/relationalai/semantics/lqp/result_helpers.py +202 -0
- v0/relationalai/semantics/lqp/rewrite/__init__.py +18 -0
- v0/relationalai/semantics/lqp/rewrite/annotate_constraints.py +57 -0
- v0/relationalai/semantics/lqp/rewrite/cdc.py +216 -0
- v0/relationalai/semantics/lqp/rewrite/extract_common.py +338 -0
- v0/relationalai/semantics/lqp/rewrite/extract_keys.py +490 -0
- v0/relationalai/semantics/lqp/rewrite/function_annotations.py +114 -0
- v0/relationalai/semantics/lqp/rewrite/functional_dependencies.py +314 -0
- v0/relationalai/semantics/lqp/rewrite/quantify_vars.py +296 -0
- v0/relationalai/semantics/lqp/rewrite/splinter.py +76 -0
- v0/relationalai/semantics/lqp/types.py +101 -0
- v0/relationalai/semantics/lqp/utils.py +160 -0
- v0/relationalai/semantics/lqp/validators.py +57 -0
- v0/relationalai/semantics/metamodel/__init__.py +40 -0
- v0/relationalai/semantics/metamodel/builtins.py +776 -0
- v0/relationalai/semantics/metamodel/compiler.py +133 -0
- v0/relationalai/semantics/metamodel/dependency.py +862 -0
- v0/relationalai/semantics/metamodel/executor.py +61 -0
- v0/relationalai/semantics/metamodel/factory.py +287 -0
- v0/relationalai/semantics/metamodel/helpers.py +361 -0
- v0/relationalai/semantics/metamodel/ir.py +923 -0
- v0/relationalai/semantics/metamodel/rewrite/__init__.py +7 -0
- v0/relationalai/semantics/metamodel/rewrite/discharge_constraints.py +39 -0
- v0/relationalai/semantics/metamodel/rewrite/dnf_union_splitter.py +210 -0
- v0/relationalai/semantics/metamodel/rewrite/extract_nested_logicals.py +78 -0
- v0/relationalai/semantics/metamodel/rewrite/flatten.py +554 -0
- v0/relationalai/semantics/metamodel/rewrite/format_outputs.py +165 -0
- v0/relationalai/semantics/metamodel/typer/checker.py +353 -0
- v0/relationalai/semantics/metamodel/typer/typer.py +1395 -0
- v0/relationalai/semantics/metamodel/util.py +505 -0
- v0/relationalai/semantics/metamodel/visitor.py +944 -0
- v0/relationalai/semantics/reasoners/__init__.py +10 -0
- v0/relationalai/semantics/reasoners/graph/__init__.py +37 -0
- v0/relationalai/semantics/reasoners/graph/core.py +9019 -0
- v0/relationalai/semantics/reasoners/optimization/__init__.py +68 -0
- v0/relationalai/semantics/reasoners/optimization/common.py +88 -0
- v0/relationalai/semantics/reasoners/optimization/solvers_dev.py +568 -0
- v0/relationalai/semantics/reasoners/optimization/solvers_pb.py +1163 -0
- v0/relationalai/semantics/rel/builtins.py +40 -0
- v0/relationalai/semantics/rel/compiler.py +989 -0
- v0/relationalai/semantics/rel/executor.py +359 -0
- v0/relationalai/semantics/rel/rel.py +482 -0
- v0/relationalai/semantics/rel/rel_utils.py +276 -0
- v0/relationalai/semantics/snowflake/__init__.py +3 -0
- v0/relationalai/semantics/sql/compiler.py +2503 -0
- v0/relationalai/semantics/sql/executor/duck_db.py +52 -0
- v0/relationalai/semantics/sql/executor/result_helpers.py +64 -0
- v0/relationalai/semantics/sql/executor/snowflake.py +145 -0
- v0/relationalai/semantics/sql/rewrite/denormalize.py +222 -0
- v0/relationalai/semantics/sql/rewrite/double_negation.py +49 -0
- v0/relationalai/semantics/sql/rewrite/recursive_union.py +127 -0
- v0/relationalai/semantics/sql/rewrite/sort_output_query.py +246 -0
- v0/relationalai/semantics/sql/sql.py +504 -0
- v0/relationalai/semantics/std/__init__.py +54 -0
- v0/relationalai/semantics/std/constraints.py +43 -0
- v0/relationalai/semantics/std/datetime.py +363 -0
- v0/relationalai/semantics/std/decimals.py +62 -0
- v0/relationalai/semantics/std/floats.py +7 -0
- v0/relationalai/semantics/std/integers.py +22 -0
- v0/relationalai/semantics/std/math.py +141 -0
- v0/relationalai/semantics/std/pragmas.py +11 -0
- v0/relationalai/semantics/std/re.py +83 -0
- v0/relationalai/semantics/std/std.py +14 -0
- v0/relationalai/semantics/std/strings.py +63 -0
- v0/relationalai/semantics/tests/__init__.py +0 -0
- v0/relationalai/semantics/tests/test_snapshot_abstract.py +143 -0
- v0/relationalai/semantics/tests/test_snapshot_base.py +9 -0
- v0/relationalai/semantics/tests/utils.py +46 -0
- v0/relationalai/std/__init__.py +70 -0
- v0/relationalai/tools/__init__.py +0 -0
- v0/relationalai/tools/cli.py +1940 -0
- v0/relationalai/tools/cli_controls.py +1826 -0
- v0/relationalai/tools/cli_helpers.py +390 -0
- v0/relationalai/tools/debugger.py +183 -0
- v0/relationalai/tools/debugger_client.py +109 -0
- v0/relationalai/tools/debugger_server.py +302 -0
- v0/relationalai/tools/dev.py +685 -0
- v0/relationalai/tools/qb_debugger.py +425 -0
- v0/relationalai/util/clean_up_databases.py +95 -0
- v0/relationalai/util/format.py +123 -0
- v0/relationalai/util/list_databases.py +9 -0
- v0/relationalai/util/otel_configuration.py +25 -0
- v0/relationalai/util/otel_handler.py +484 -0
- v0/relationalai/util/snowflake_handler.py +88 -0
- v0/relationalai/util/span_format_test.py +43 -0
- v0/relationalai/util/span_tracker.py +207 -0
- v0/relationalai/util/spans_file_handler.py +72 -0
- v0/relationalai/util/tracing_handler.py +34 -0
- frontend/debugger/dist/.gitignore +0 -2
- frontend/debugger/dist/assets/favicon-Dy0ZgA6N.png +0 -0
- frontend/debugger/dist/assets/index-Cssla-O7.js +0 -208
- frontend/debugger/dist/assets/index-DlHsYx1V.css +0 -9
- frontend/debugger/dist/index.html +0 -17
- relationalai/clients/__init__.py +0 -18
- relationalai/clients/client.py +0 -946
- relationalai/clients/config.py +0 -673
- relationalai/clients/direct_access_client.py +0 -118
- relationalai/clients/exec_txn_poller.py +0 -153
- relationalai/clients/hash_util.py +0 -31
- relationalai/clients/local.py +0 -594
- relationalai/clients/profile_polling.py +0 -73
- relationalai/clients/resources/__init__.py +0 -8
- relationalai/clients/resources/azure/azure.py +0 -502
- relationalai/clients/resources/snowflake/__init__.py +0 -20
- relationalai/clients/resources/snowflake/cli_resources.py +0 -98
- relationalai/clients/resources/snowflake/direct_access_resources.py +0 -739
- relationalai/clients/resources/snowflake/engine_service.py +0 -381
- relationalai/clients/resources/snowflake/engine_state_handlers.py +0 -315
- relationalai/clients/resources/snowflake/error_handlers.py +0 -240
- relationalai/clients/resources/snowflake/export_procedure.py.jinja +0 -249
- relationalai/clients/resources/snowflake/resources_factory.py +0 -99
- relationalai/clients/resources/snowflake/snowflake.py +0 -3193
- relationalai/clients/resources/snowflake/use_index_poller.py +0 -1019
- relationalai/clients/resources/snowflake/use_index_resources.py +0 -188
- relationalai/clients/resources/snowflake/util.py +0 -387
- relationalai/clients/result_helpers.py +0 -420
- relationalai/clients/types.py +0 -118
- relationalai/clients/util.py +0 -356
- relationalai/debugging.py +0 -389
- relationalai/dsl.py +0 -1749
- relationalai/early_access/builder/__init__.py +0 -30
- relationalai/early_access/builder/builder/__init__.py +0 -35
- relationalai/early_access/builder/snowflake/__init__.py +0 -12
- relationalai/early_access/builder/std/__init__.py +0 -25
- relationalai/early_access/builder/std/decimals/__init__.py +0 -12
- relationalai/early_access/builder/std/integers/__init__.py +0 -12
- relationalai/early_access/builder/std/math/__init__.py +0 -12
- relationalai/early_access/builder/std/strings/__init__.py +0 -14
- relationalai/early_access/devtools/__init__.py +0 -12
- relationalai/early_access/devtools/benchmark_lqp/__init__.py +0 -12
- relationalai/early_access/devtools/extract_lqp/__init__.py +0 -12
- relationalai/early_access/dsl/adapters/orm/adapter_qb.py +0 -427
- relationalai/early_access/dsl/adapters/orm/parser.py +0 -636
- relationalai/early_access/dsl/adapters/owl/adapter.py +0 -176
- relationalai/early_access/dsl/adapters/owl/parser.py +0 -160
- relationalai/early_access/dsl/bindings/common.py +0 -402
- relationalai/early_access/dsl/bindings/csv.py +0 -170
- relationalai/early_access/dsl/bindings/legacy/binding_models.py +0 -143
- relationalai/early_access/dsl/bindings/snowflake.py +0 -64
- relationalai/early_access/dsl/codegen/binder.py +0 -411
- relationalai/early_access/dsl/codegen/common.py +0 -79
- relationalai/early_access/dsl/codegen/helpers.py +0 -23
- relationalai/early_access/dsl/codegen/relations.py +0 -700
- relationalai/early_access/dsl/codegen/weaver.py +0 -417
- relationalai/early_access/dsl/core/builders/__init__.py +0 -47
- relationalai/early_access/dsl/core/builders/logic.py +0 -19
- relationalai/early_access/dsl/core/builders/scalar_constraint.py +0 -11
- relationalai/early_access/dsl/core/constraints/predicate/atomic.py +0 -455
- relationalai/early_access/dsl/core/constraints/predicate/universal.py +0 -73
- relationalai/early_access/dsl/core/constraints/scalar.py +0 -310
- relationalai/early_access/dsl/core/context.py +0 -13
- relationalai/early_access/dsl/core/cset.py +0 -132
- relationalai/early_access/dsl/core/exprs/__init__.py +0 -116
- relationalai/early_access/dsl/core/exprs/relational.py +0 -18
- relationalai/early_access/dsl/core/exprs/scalar.py +0 -412
- relationalai/early_access/dsl/core/instances.py +0 -44
- relationalai/early_access/dsl/core/logic/__init__.py +0 -193
- relationalai/early_access/dsl/core/logic/aggregation.py +0 -98
- relationalai/early_access/dsl/core/logic/exists.py +0 -223
- relationalai/early_access/dsl/core/logic/helper.py +0 -163
- relationalai/early_access/dsl/core/namespaces.py +0 -32
- relationalai/early_access/dsl/core/relations.py +0 -276
- relationalai/early_access/dsl/core/rules.py +0 -112
- relationalai/early_access/dsl/core/std/__init__.py +0 -45
- relationalai/early_access/dsl/core/temporal/recall.py +0 -6
- relationalai/early_access/dsl/core/types/__init__.py +0 -270
- relationalai/early_access/dsl/core/types/concepts.py +0 -128
- relationalai/early_access/dsl/core/types/constrained/__init__.py +0 -267
- relationalai/early_access/dsl/core/types/constrained/nominal.py +0 -143
- relationalai/early_access/dsl/core/types/constrained/subtype.py +0 -124
- relationalai/early_access/dsl/core/types/standard.py +0 -92
- relationalai/early_access/dsl/core/types/unconstrained.py +0 -50
- relationalai/early_access/dsl/core/types/variables.py +0 -203
- relationalai/early_access/dsl/ir/compiler.py +0 -318
- relationalai/early_access/dsl/ir/executor.py +0 -260
- relationalai/early_access/dsl/ontologies/constraints.py +0 -88
- relationalai/early_access/dsl/ontologies/export.py +0 -30
- relationalai/early_access/dsl/ontologies/models.py +0 -453
- relationalai/early_access/dsl/ontologies/python_printer.py +0 -303
- relationalai/early_access/dsl/ontologies/readings.py +0 -60
- relationalai/early_access/dsl/ontologies/relationships.py +0 -322
- relationalai/early_access/dsl/ontologies/roles.py +0 -87
- relationalai/early_access/dsl/ontologies/subtyping.py +0 -55
- relationalai/early_access/dsl/orm/constraints.py +0 -438
- relationalai/early_access/dsl/orm/measures/dimensions.py +0 -200
- relationalai/early_access/dsl/orm/measures/initializer.py +0 -16
- relationalai/early_access/dsl/orm/measures/measure_rules.py +0 -275
- relationalai/early_access/dsl/orm/measures/measures.py +0 -299
- relationalai/early_access/dsl/orm/measures/role_exprs.py +0 -268
- relationalai/early_access/dsl/orm/models.py +0 -256
- relationalai/early_access/dsl/orm/object_oriented_printer.py +0 -344
- relationalai/early_access/dsl/orm/printer.py +0 -469
- relationalai/early_access/dsl/orm/reasoners.py +0 -480
- relationalai/early_access/dsl/orm/relations.py +0 -19
- relationalai/early_access/dsl/orm/relationships.py +0 -251
- relationalai/early_access/dsl/orm/types.py +0 -42
- relationalai/early_access/dsl/orm/utils.py +0 -79
- relationalai/early_access/dsl/orm/verb.py +0 -204
- relationalai/early_access/dsl/physical_metadata/tables.py +0 -133
- relationalai/early_access/dsl/relations.py +0 -170
- relationalai/early_access/dsl/rulesets.py +0 -69
- relationalai/early_access/dsl/schemas/__init__.py +0 -450
- relationalai/early_access/dsl/schemas/builder.py +0 -48
- relationalai/early_access/dsl/schemas/comp_names.py +0 -51
- relationalai/early_access/dsl/schemas/components.py +0 -203
- relationalai/early_access/dsl/schemas/contexts.py +0 -156
- relationalai/early_access/dsl/schemas/exprs.py +0 -89
- relationalai/early_access/dsl/schemas/fragments.py +0 -464
- relationalai/early_access/dsl/serialization.py +0 -79
- relationalai/early_access/dsl/serialize/exporter.py +0 -163
- relationalai/early_access/dsl/snow/api.py +0 -105
- relationalai/early_access/dsl/snow/common.py +0 -76
- relationalai/early_access/dsl/state_mgmt/__init__.py +0 -129
- relationalai/early_access/dsl/state_mgmt/state_charts.py +0 -125
- relationalai/early_access/dsl/state_mgmt/transitions.py +0 -130
- relationalai/early_access/dsl/types/__init__.py +0 -40
- relationalai/early_access/dsl/types/concepts.py +0 -12
- relationalai/early_access/dsl/types/entities.py +0 -135
- relationalai/early_access/dsl/types/values.py +0 -17
- relationalai/early_access/dsl/utils.py +0 -102
- relationalai/early_access/graphs/__init__.py +0 -13
- relationalai/early_access/lqp/__init__.py +0 -12
- relationalai/early_access/lqp/compiler/__init__.py +0 -12
- relationalai/early_access/lqp/constructors/__init__.py +0 -18
- relationalai/early_access/lqp/executor/__init__.py +0 -12
- relationalai/early_access/lqp/ir/__init__.py +0 -12
- relationalai/early_access/lqp/passes/__init__.py +0 -12
- relationalai/early_access/lqp/pragmas/__init__.py +0 -12
- relationalai/early_access/lqp/primitives/__init__.py +0 -12
- relationalai/early_access/lqp/types/__init__.py +0 -12
- relationalai/early_access/lqp/utils/__init__.py +0 -12
- relationalai/early_access/lqp/validators/__init__.py +0 -12
- relationalai/early_access/metamodel/__init__.py +0 -58
- relationalai/early_access/metamodel/builtins/__init__.py +0 -12
- relationalai/early_access/metamodel/compiler/__init__.py +0 -12
- relationalai/early_access/metamodel/dependency/__init__.py +0 -12
- relationalai/early_access/metamodel/factory/__init__.py +0 -17
- relationalai/early_access/metamodel/helpers/__init__.py +0 -12
- relationalai/early_access/metamodel/ir/__init__.py +0 -14
- relationalai/early_access/metamodel/rewrite/__init__.py +0 -7
- relationalai/early_access/metamodel/typer/__init__.py +0 -3
- relationalai/early_access/metamodel/typer/typer/__init__.py +0 -12
- relationalai/early_access/metamodel/types/__init__.py +0 -15
- relationalai/early_access/metamodel/util/__init__.py +0 -15
- relationalai/early_access/metamodel/visitor/__init__.py +0 -12
- relationalai/early_access/rel/__init__.py +0 -12
- relationalai/early_access/rel/executor/__init__.py +0 -12
- relationalai/early_access/rel/rel_utils/__init__.py +0 -12
- relationalai/early_access/rel/rewrite/__init__.py +0 -7
- relationalai/early_access/solvers/__init__.py +0 -19
- relationalai/early_access/sql/__init__.py +0 -11
- relationalai/early_access/sql/executor/__init__.py +0 -3
- relationalai/early_access/sql/rewrite/__init__.py +0 -3
- relationalai/early_access/tests/logging/__init__.py +0 -12
- relationalai/early_access/tests/test_snapshot_base/__init__.py +0 -12
- relationalai/early_access/tests/utils/__init__.py +0 -12
- relationalai/environments/__init__.py +0 -35
- relationalai/environments/base.py +0 -381
- relationalai/environments/colab.py +0 -14
- relationalai/environments/generic.py +0 -71
- relationalai/environments/ipython.py +0 -68
- relationalai/environments/jupyter.py +0 -9
- relationalai/environments/snowbook.py +0 -169
- relationalai/errors.py +0 -2496
- relationalai/experimental/SF.py +0 -38
- relationalai/experimental/inspect.py +0 -47
- relationalai/experimental/pathfinder/__init__.py +0 -158
- relationalai/experimental/pathfinder/api.py +0 -160
- relationalai/experimental/pathfinder/automaton.py +0 -584
- relationalai/experimental/pathfinder/bridge.py +0 -226
- relationalai/experimental/pathfinder/compiler.py +0 -416
- relationalai/experimental/pathfinder/datalog.py +0 -214
- relationalai/experimental/pathfinder/diagnostics.py +0 -56
- relationalai/experimental/pathfinder/filter.py +0 -236
- relationalai/experimental/pathfinder/glushkov.py +0 -439
- relationalai/experimental/pathfinder/options.py +0 -265
- relationalai/experimental/pathfinder/pathfinder-v0.7.0.rel +0 -1951
- relationalai/experimental/pathfinder/rpq.py +0 -344
- relationalai/experimental/pathfinder/transition.py +0 -200
- relationalai/experimental/pathfinder/utils.py +0 -26
- relationalai/experimental/paths/README.md +0 -107
- relationalai/experimental/paths/api.py +0 -143
- relationalai/experimental/paths/benchmarks/grid_graph.py +0 -37
- relationalai/experimental/paths/code_organization.md +0 -2
- relationalai/experimental/paths/examples/Movies.ipynb +0 -16328
- relationalai/experimental/paths/examples/basic_example.py +0 -40
- relationalai/experimental/paths/examples/minimal_engine_warmup.py +0 -3
- relationalai/experimental/paths/examples/movie_example.py +0 -77
- relationalai/experimental/paths/examples/movies_data/actedin.csv +0 -193
- relationalai/experimental/paths/examples/movies_data/directed.csv +0 -45
- relationalai/experimental/paths/examples/movies_data/follows.csv +0 -7
- relationalai/experimental/paths/examples/movies_data/movies.csv +0 -39
- relationalai/experimental/paths/examples/movies_data/person.csv +0 -134
- relationalai/experimental/paths/examples/movies_data/produced.csv +0 -16
- relationalai/experimental/paths/examples/movies_data/ratings.csv +0 -10
- relationalai/experimental/paths/examples/movies_data/wrote.csv +0 -11
- relationalai/experimental/paths/examples/paths_benchmark.py +0 -115
- relationalai/experimental/paths/examples/paths_example.py +0 -116
- relationalai/experimental/paths/examples/pattern_to_automaton.py +0 -28
- relationalai/experimental/paths/find_paths_via_automaton.py +0 -85
- relationalai/experimental/paths/graph.py +0 -185
- relationalai/experimental/paths/path_algorithms/find_paths.py +0 -280
- relationalai/experimental/paths/path_algorithms/one_sided_ball_repetition.py +0 -26
- relationalai/experimental/paths/path_algorithms/one_sided_ball_upto.py +0 -111
- relationalai/experimental/paths/path_algorithms/single.py +0 -59
- relationalai/experimental/paths/path_algorithms/two_sided_balls_repetition.py +0 -39
- relationalai/experimental/paths/path_algorithms/two_sided_balls_upto.py +0 -103
- relationalai/experimental/paths/path_algorithms/usp-old.py +0 -130
- relationalai/experimental/paths/path_algorithms/usp-tuple.py +0 -183
- relationalai/experimental/paths/path_algorithms/usp.py +0 -150
- relationalai/experimental/paths/product_graph.py +0 -93
- relationalai/experimental/paths/rpq/automaton.py +0 -584
- relationalai/experimental/paths/rpq/diagnostics.py +0 -56
- relationalai/experimental/paths/rpq/rpq.py +0 -378
- relationalai/experimental/paths/tests/tests_limit_sp_max_length.py +0 -90
- relationalai/experimental/paths/tests/tests_limit_sp_multiple.py +0 -119
- relationalai/experimental/paths/tests/tests_limit_sp_single.py +0 -104
- relationalai/experimental/paths/tests/tests_limit_walks_multiple.py +0 -113
- relationalai/experimental/paths/tests/tests_limit_walks_single.py +0 -149
- relationalai/experimental/paths/tests/tests_one_sided_ball_repetition_multiple.py +0 -70
- relationalai/experimental/paths/tests/tests_one_sided_ball_repetition_single.py +0 -64
- relationalai/experimental/paths/tests/tests_one_sided_ball_upto_multiple.py +0 -115
- relationalai/experimental/paths/tests/tests_one_sided_ball_upto_single.py +0 -75
- relationalai/experimental/paths/tests/tests_single_paths.py +0 -152
- relationalai/experimental/paths/tests/tests_single_walks.py +0 -208
- relationalai/experimental/paths/tests/tests_single_walks_undirected.py +0 -297
- relationalai/experimental/paths/tests/tests_two_sided_balls_repetition_multiple.py +0 -107
- relationalai/experimental/paths/tests/tests_two_sided_balls_repetition_single.py +0 -76
- relationalai/experimental/paths/tests/tests_two_sided_balls_upto_multiple.py +0 -76
- relationalai/experimental/paths/tests/tests_two_sided_balls_upto_single.py +0 -110
- relationalai/experimental/paths/tests/tests_usp_nsp_multiple.py +0 -229
- relationalai/experimental/paths/tests/tests_usp_nsp_single.py +0 -108
- relationalai/experimental/paths/tree_agg.py +0 -168
- relationalai/experimental/paths/utilities/iterators.py +0 -27
- relationalai/experimental/paths/utilities/prefix_sum.py +0 -91
- relationalai/experimental/solvers.py +0 -1095
- relationalai/loaders/csv.py +0 -195
- relationalai/loaders/loader.py +0 -177
- relationalai/loaders/types.py +0 -23
- relationalai/rel_emitter.py +0 -373
- relationalai/rel_utils.py +0 -185
- relationalai/semantics/designs/query_builder/identify_by.md +0 -106
- relationalai/semantics/devtools/benchmark_lqp.py +0 -535
- relationalai/semantics/devtools/compilation_manager.py +0 -294
- relationalai/semantics/devtools/extract_lqp.py +0 -110
- relationalai/semantics/internal/internal.py +0 -3785
- relationalai/semantics/internal/snowflake.py +0 -329
- relationalai/semantics/lqp/README.md +0 -34
- relationalai/semantics/lqp/algorithms.py +0 -173
- relationalai/semantics/lqp/builtins.py +0 -213
- relationalai/semantics/lqp/compiler.py +0 -22
- relationalai/semantics/lqp/constructors.py +0 -68
- relationalai/semantics/lqp/executor.py +0 -518
- relationalai/semantics/lqp/export_rewriter.py +0 -40
- relationalai/semantics/lqp/intrinsics.py +0 -24
- relationalai/semantics/lqp/ir.py +0 -150
- relationalai/semantics/lqp/model2lqp.py +0 -1056
- relationalai/semantics/lqp/passes.py +0 -38
- relationalai/semantics/lqp/primitives.py +0 -252
- relationalai/semantics/lqp/result_helpers.py +0 -266
- relationalai/semantics/lqp/rewrite/__init__.py +0 -32
- relationalai/semantics/lqp/rewrite/algorithm.py +0 -385
- relationalai/semantics/lqp/rewrite/annotate_constraints.py +0 -69
- relationalai/semantics/lqp/rewrite/cdc.py +0 -216
- relationalai/semantics/lqp/rewrite/constants_to_vars.py +0 -70
- relationalai/semantics/lqp/rewrite/deduplicate_vars.py +0 -104
- relationalai/semantics/lqp/rewrite/eliminate_data.py +0 -108
- relationalai/semantics/lqp/rewrite/extract_common.py +0 -340
- relationalai/semantics/lqp/rewrite/extract_keys.py +0 -577
- relationalai/semantics/lqp/rewrite/flatten_script.py +0 -301
- relationalai/semantics/lqp/rewrite/function_annotations.py +0 -114
- relationalai/semantics/lqp/rewrite/functional_dependencies.py +0 -348
- relationalai/semantics/lqp/rewrite/period_math.py +0 -77
- relationalai/semantics/lqp/rewrite/quantify_vars.py +0 -339
- relationalai/semantics/lqp/rewrite/splinter.py +0 -76
- relationalai/semantics/lqp/rewrite/unify_definitions.py +0 -323
- relationalai/semantics/lqp/types.py +0 -101
- relationalai/semantics/lqp/utils.py +0 -170
- relationalai/semantics/lqp/validators.py +0 -70
- relationalai/semantics/metamodel/compiler.py +0 -134
- relationalai/semantics/metamodel/dependency.py +0 -880
- relationalai/semantics/metamodel/executor.py +0 -78
- relationalai/semantics/metamodel/factory.py +0 -287
- relationalai/semantics/metamodel/helpers.py +0 -368
- relationalai/semantics/metamodel/ir.py +0 -924
- relationalai/semantics/metamodel/rewrite/__init__.py +0 -8
- relationalai/semantics/metamodel/rewrite/discharge_constraints.py +0 -39
- relationalai/semantics/metamodel/rewrite/dnf_union_splitter.py +0 -220
- relationalai/semantics/metamodel/rewrite/extract_nested_logicals.py +0 -78
- relationalai/semantics/metamodel/rewrite/flatten.py +0 -590
- relationalai/semantics/metamodel/rewrite/format_outputs.py +0 -256
- relationalai/semantics/metamodel/rewrite/handle_aggregations_and_ranks.py +0 -237
- relationalai/semantics/metamodel/typer/checker.py +0 -355
- relationalai/semantics/metamodel/typer/typer.py +0 -1396
- relationalai/semantics/metamodel/util.py +0 -506
- relationalai/semantics/metamodel/visitor.py +0 -945
- relationalai/semantics/reasoners/__init__.py +0 -10
- relationalai/semantics/reasoners/graph/README.md +0 -620
- relationalai/semantics/reasoners/graph/__init__.py +0 -37
- relationalai/semantics/reasoners/graph/core.py +0 -9019
- relationalai/semantics/reasoners/graph/design/beyond_demand_transform.md +0 -797
- relationalai/semantics/reasoners/graph/tests/README.md +0 -21
- relationalai/semantics/reasoners/optimization/__init__.py +0 -68
- relationalai/semantics/reasoners/optimization/common.py +0 -88
- relationalai/semantics/reasoners/optimization/solvers_dev.py +0 -568
- relationalai/semantics/reasoners/optimization/solvers_pb.py +0 -1407
- relationalai/semantics/rel/builtins.py +0 -40
- relationalai/semantics/rel/compiler.py +0 -994
- relationalai/semantics/rel/executor.py +0 -363
- relationalai/semantics/rel/rel.py +0 -482
- relationalai/semantics/rel/rel_utils.py +0 -276
- relationalai/semantics/snowflake/__init__.py +0 -3
- relationalai/semantics/sql/compiler.py +0 -2503
- relationalai/semantics/sql/executor/duck_db.py +0 -52
- relationalai/semantics/sql/executor/result_helpers.py +0 -64
- relationalai/semantics/sql/executor/snowflake.py +0 -149
- relationalai/semantics/sql/rewrite/denormalize.py +0 -222
- relationalai/semantics/sql/rewrite/double_negation.py +0 -49
- relationalai/semantics/sql/rewrite/recursive_union.py +0 -127
- relationalai/semantics/sql/rewrite/sort_output_query.py +0 -246
- relationalai/semantics/sql/sql.py +0 -504
- relationalai/semantics/std/pragmas.py +0 -11
- relationalai/semantics/std/std.py +0 -14
- relationalai/semantics/tests/lqp/algorithms.py +0 -345
- relationalai/semantics/tests/test_snapshot_abstract.py +0 -144
- relationalai/semantics/tests/test_snapshot_base.py +0 -9
- relationalai/semantics/tests/utils.py +0 -46
- relationalai/std/__init__.py +0 -70
- relationalai/tools/cli.py +0 -2089
- relationalai/tools/cli_controls.py +0 -1975
- relationalai/tools/cli_helpers.py +0 -802
- relationalai/tools/debugger_client.py +0 -109
- relationalai/tools/debugger_server.py +0 -302
- relationalai/tools/dev.py +0 -685
- relationalai/tools/notes +0 -7
- relationalai/tools/qb_debugger.py +0 -425
- relationalai/tools/txn_progress.py +0 -188
- relationalai/util/clean_up_databases.py +0 -95
- relationalai/util/list_databases.py +0 -9
- relationalai/util/otel_configuration.py +0 -26
- relationalai/util/otel_handler.py +0 -484
- relationalai/util/snowflake_handler.py +0 -88
- relationalai/util/span_format_test.py +0 -43
- relationalai/util/span_tracker.py +0 -207
- relationalai/util/spans_file_handler.py +0 -72
- relationalai/util/tracing_handler.py +0 -34
- relationalai-0.13.5.dist-info/METADATA +0 -74
- relationalai-0.13.5.dist-info/RECORD +0 -473
- relationalai-0.13.5.dist-info/WHEEL +0 -4
- relationalai-0.13.5.dist-info/entry_points.txt +0 -3
- relationalai-0.13.5.dist-info/licenses/LICENSE +0 -202
- relationalai_test_util/__init__.py +0 -4
- relationalai_test_util/fixtures.py +0 -233
- relationalai_test_util/snapshot.py +0 -252
- relationalai_test_util/traceback.py +0 -118
- /relationalai/{analysis → semantics/frontend}/__init__.py +0 -0
- /relationalai/{auth/__init__.py → semantics/metamodel/metamodel_compiler.py} +0 -0
- /relationalai/{early_access → shims}/__init__.py +0 -0
- {relationalai/early_access/dsl/adapters → v0/relationalai/analysis}/__init__.py +0 -0
- {relationalai → v0/relationalai}/analysis/mechanistic.py +0 -0
- {relationalai → v0/relationalai}/analysis/whynot.py +0 -0
- {relationalai/early_access/dsl/adapters/orm → v0/relationalai/auth}/__init__.py +0 -0
- {relationalai → v0/relationalai}/auth/jwt_generator.py +0 -0
- {relationalai → v0/relationalai}/auth/oauth_callback_server.py +0 -0
- {relationalai → v0/relationalai}/auth/token_handler.py +0 -0
- {relationalai → v0/relationalai}/auth/util.py +0 -0
- {relationalai/clients/resources/snowflake → v0/relationalai/clients}/cache_store.py +0 -0
- {relationalai → v0/relationalai}/compiler.py +0 -0
- {relationalai → v0/relationalai}/dependencies.py +0 -0
- {relationalai → v0/relationalai}/docutils.py +0 -0
- {relationalai/early_access/dsl/adapters/owl → v0/relationalai/early_access}/__init__.py +0 -0
- {relationalai → v0/relationalai}/early_access/dsl/__init__.py +0 -0
- {relationalai/early_access/dsl/bindings → v0/relationalai/early_access/dsl/adapters}/__init__.py +0 -0
- {relationalai/early_access/dsl/bindings/legacy → v0/relationalai/early_access/dsl/adapters/orm}/__init__.py +0 -0
- {relationalai → v0/relationalai}/early_access/dsl/adapters/orm/model.py +0 -0
- {relationalai/early_access/dsl/codegen → v0/relationalai/early_access/dsl/adapters/owl}/__init__.py +0 -0
- {relationalai → v0/relationalai}/early_access/dsl/adapters/owl/model.py +0 -0
- {relationalai/early_access/dsl/core/temporal → v0/relationalai/early_access/dsl/bindings}/__init__.py +0 -0
- {relationalai/early_access/dsl/ir → v0/relationalai/early_access/dsl/bindings/legacy}/__init__.py +0 -0
- {relationalai/early_access/dsl/ontologies → v0/relationalai/early_access/dsl/codegen}/__init__.py +0 -0
- {relationalai → v0/relationalai}/early_access/dsl/constants.py +0 -0
- {relationalai → v0/relationalai}/early_access/dsl/core/__init__.py +0 -0
- {relationalai → v0/relationalai}/early_access/dsl/core/constraints/__init__.py +0 -0
- {relationalai → v0/relationalai}/early_access/dsl/core/constraints/predicate/__init__.py +0 -0
- {relationalai → v0/relationalai}/early_access/dsl/core/stack.py +0 -0
- {relationalai/early_access/dsl/orm → v0/relationalai/early_access/dsl/core/temporal}/__init__.py +0 -0
- {relationalai → v0/relationalai}/early_access/dsl/core/utils.py +0 -0
- {relationalai/early_access/dsl/orm/measures → v0/relationalai/early_access/dsl/ir}/__init__.py +0 -0
- {relationalai/early_access/dsl/physical_metadata → v0/relationalai/early_access/dsl/ontologies}/__init__.py +0 -0
- {relationalai → v0/relationalai}/early_access/dsl/ontologies/raw_source.py +0 -0
- {relationalai/early_access/dsl/serialize → v0/relationalai/early_access/dsl/orm}/__init__.py +0 -0
- {relationalai/early_access/dsl/snow → v0/relationalai/early_access/dsl/orm/measures}/__init__.py +0 -0
- {relationalai → v0/relationalai}/early_access/dsl/orm/reasoner_errors.py +0 -0
- {relationalai/loaders → v0/relationalai/early_access/dsl/physical_metadata}/__init__.py +0 -0
- {relationalai/semantics/tests → v0/relationalai/early_access/dsl/serialize}/__init__.py +0 -0
- {relationalai → v0/relationalai}/early_access/dsl/serialize/binding_model.py +0 -0
- {relationalai → v0/relationalai}/early_access/dsl/serialize/model.py +0 -0
- {relationalai/semantics/tests/lqp → v0/relationalai/early_access/dsl/snow}/__init__.py +0 -0
- {relationalai → v0/relationalai}/early_access/tests/__init__.py +0 -0
- {relationalai → v0/relationalai}/environments/ci.py +0 -0
- {relationalai → v0/relationalai}/environments/hex.py +0 -0
- {relationalai → v0/relationalai}/environments/terminal.py +0 -0
- {relationalai → v0/relationalai}/experimental/__init__.py +0 -0
- {relationalai → v0/relationalai}/experimental/graphs.py +0 -0
- {relationalai → v0/relationalai}/experimental/paths/__init__.py +0 -0
- {relationalai → v0/relationalai}/experimental/paths/benchmarks/__init__.py +0 -0
- {relationalai → v0/relationalai}/experimental/paths/path_algorithms/__init__.py +0 -0
- {relationalai → v0/relationalai}/experimental/paths/rpq/__init__.py +0 -0
- {relationalai → v0/relationalai}/experimental/paths/rpq/filter.py +0 -0
- {relationalai → v0/relationalai}/experimental/paths/rpq/glushkov.py +0 -0
- {relationalai → v0/relationalai}/experimental/paths/rpq/transition.py +0 -0
- {relationalai → v0/relationalai}/experimental/paths/utilities/__init__.py +0 -0
- {relationalai → v0/relationalai}/experimental/paths/utilities/utilities.py +0 -0
- {relationalai/tools → v0/relationalai/loaders}/__init__.py +0 -0
- {relationalai → v0/relationalai}/metagen.py +0 -0
- {relationalai → v0/relationalai}/metamodel.py +0 -0
- {relationalai → v0/relationalai}/rel.py +0 -0
- {relationalai → v0/relationalai}/semantics/devtools/__init__.py +0 -0
- {relationalai → v0/relationalai}/semantics/internal/__init__.py +0 -0
- {relationalai → v0/relationalai}/semantics/internal/annotations.py +0 -0
- {relationalai → v0/relationalai}/semantics/lqp/__init__.py +0 -0
- {relationalai → v0/relationalai}/semantics/lqp/pragmas.py +0 -0
- {relationalai → v0/relationalai}/semantics/metamodel/dataflow.py +0 -0
- {relationalai → v0/relationalai}/semantics/metamodel/typer/__init__.py +0 -0
- {relationalai → v0/relationalai}/semantics/metamodel/types.py +0 -0
- {relationalai → v0/relationalai}/semantics/reasoners/experimental/__init__.py +0 -0
- {relationalai → v0/relationalai}/semantics/rel/__init__.py +0 -0
- {relationalai → v0/relationalai}/semantics/sql/__init__.py +0 -0
- {relationalai → v0/relationalai}/semantics/sql/executor/__init__.py +0 -0
- {relationalai → v0/relationalai}/semantics/sql/rewrite/__init__.py +0 -0
- {relationalai → v0/relationalai}/semantics/tests/logging.py +0 -0
- {relationalai → v0/relationalai}/std/aggregates.py +0 -0
- {relationalai → v0/relationalai}/std/dates.py +0 -0
- {relationalai → v0/relationalai}/std/graphs.py +0 -0
- {relationalai → v0/relationalai}/std/inspect.py +0 -0
- {relationalai → v0/relationalai}/std/math.py +0 -0
- {relationalai → v0/relationalai}/std/re.py +0 -0
- {relationalai → v0/relationalai}/std/strings.py +0 -0
- {relationalai → v0/relationalai}/tools/cleanup_snapshots.py +0 -0
- {relationalai → v0/relationalai}/tools/constants.py +0 -0
- {relationalai → v0/relationalai}/tools/query_utils.py +0 -0
- {relationalai → v0/relationalai}/tools/snapshot_viewer.py +0 -0
- {relationalai → v0/relationalai}/util/__init__.py +0 -0
- {relationalai → v0/relationalai}/util/constants.py +0 -0
- {relationalai → v0/relationalai}/util/graph.py +0 -0
- {relationalai → v0/relationalai}/util/timeout.py +0 -0
|
@@ -0,0 +1,1719 @@
|
|
|
1
|
+
"""Base frontend classes for RelationalAI semantics API.
|
|
2
|
+
"""
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import decimal
|
|
6
|
+
import math
|
|
7
|
+
import re
|
|
8
|
+
import datetime as dt
|
|
9
|
+
from typing import TYPE_CHECKING, Any, Generic, Iterable, Iterator, NoReturn, Optional, Sequence, Tuple, Type, TypeGuard, TypeVar, cast
|
|
10
|
+
import itertools
|
|
11
|
+
from more_itertools import peekable
|
|
12
|
+
from pandas import DataFrame
|
|
13
|
+
import pandas as pd
|
|
14
|
+
from enum import Enum, EnumMeta
|
|
15
|
+
|
|
16
|
+
from ...util import schema as schema_util
|
|
17
|
+
from ...util.structures import KeyedSet
|
|
18
|
+
from ...util.naming import Namer, sanitize
|
|
19
|
+
from ...util.python import pytype_to_concept_name
|
|
20
|
+
from ...util.source import SourcePos
|
|
21
|
+
from ...util.tracing import get_tracer
|
|
22
|
+
from ...util.error import err, exc, warn, source
|
|
23
|
+
from ...util.docutils import include_in_docs
|
|
24
|
+
from ..metamodel.builtins import builtins
|
|
25
|
+
from ..metamodel.metamodel import Model as mModel
|
|
26
|
+
|
|
27
|
+
tracer = get_tracer()
|
|
28
|
+
tracer.start_program()
|
|
29
|
+
|
|
30
|
+
#------------------------------------------------------
|
|
31
|
+
# Global ID Generator
|
|
32
|
+
#------------------------------------------------------
|
|
33
|
+
|
|
34
|
+
_global_id = peekable(itertools.count(0))
|
|
35
|
+
|
|
36
|
+
#------------------------------------------------------
|
|
37
|
+
# Helpers
|
|
38
|
+
#------------------------------------------------------
|
|
39
|
+
|
|
40
|
+
def _find_field(fields: list[Field], field: str|int|Concept) -> tuple[Field, int]|None:
|
|
41
|
+
resolved = None
|
|
42
|
+
if isinstance(field, int):
|
|
43
|
+
if not (0 <= field < len(fields)):
|
|
44
|
+
raise IndexError(f"Field index {field} out of range for relationship with {len(fields)} fields")
|
|
45
|
+
resolved = fields[field]
|
|
46
|
+
elif isinstance(field, str):
|
|
47
|
+
resolved = next((f for f in fields if f.name == field), None)
|
|
48
|
+
elif isinstance(field, Concept):
|
|
49
|
+
resolved = next((f for f in fields if f.type is field), None)
|
|
50
|
+
if not resolved:
|
|
51
|
+
return None
|
|
52
|
+
return resolved, fields.index(resolved)
|
|
53
|
+
|
|
54
|
+
def _find_concept(model: Model, passed_type: Any, extra_types: Sequence[Concept] = [], default:Concept|None = None) -> Concept | None:
|
|
55
|
+
if isinstance(passed_type, Concept):
|
|
56
|
+
return passed_type
|
|
57
|
+
if isinstance(passed_type, str):
|
|
58
|
+
for ext_type in extra_types:
|
|
59
|
+
if ext_type._name == passed_type:
|
|
60
|
+
return ext_type
|
|
61
|
+
name = pytype_to_concept_name.get(passed_type, passed_type)
|
|
62
|
+
if name.startswith("Number(") or name.startswith("Decimal("):
|
|
63
|
+
return cast(Any, CoreConcepts["Number"]).parse(name)
|
|
64
|
+
return model._find_concept(name)
|
|
65
|
+
#TODO: try to find a variable in the frame that is a concept with this name
|
|
66
|
+
return default
|
|
67
|
+
|
|
68
|
+
#------------------------------------------------------
|
|
69
|
+
# Primitive
|
|
70
|
+
#------------------------------------------------------
|
|
71
|
+
|
|
72
|
+
Primitive = str|int|float|bool
|
|
73
|
+
|
|
74
|
+
def is_primitive(value: Any) -> TypeGuard[str | int | float | bool]:
|
|
75
|
+
return isinstance(value, (str, int, float, bool))
|
|
76
|
+
|
|
77
|
+
#------------------------------------------------------
|
|
78
|
+
# DSLBase
|
|
79
|
+
#------------------------------------------------------
|
|
80
|
+
|
|
81
|
+
class DSLBase:
|
|
82
|
+
def __init__(self, model:Model):
|
|
83
|
+
self._model = model
|
|
84
|
+
self._id = next(_global_id)
|
|
85
|
+
self._source = SourcePos.new()
|
|
86
|
+
|
|
87
|
+
#------------------------------------------------------
|
|
88
|
+
# Hashing
|
|
89
|
+
#------------------------------------------------------
|
|
90
|
+
|
|
91
|
+
def to_dict_key(self) -> int|tuple[int, int]:
|
|
92
|
+
return self._id
|
|
93
|
+
|
|
94
|
+
def __hash__(self) -> NoReturn:
|
|
95
|
+
exc("Unhashable type", f"{self.__class__.__name__} objects are unhashable. Use `to_dict_key()` instead.", [source(self)])
|
|
96
|
+
|
|
97
|
+
#------------------------------------------------------
|
|
98
|
+
# DSLDict
|
|
99
|
+
#------------------------------------------------------
|
|
100
|
+
|
|
101
|
+
def dsl_key(obj:Any) -> Any:
|
|
102
|
+
if isinstance(obj, DSLBase):
|
|
103
|
+
return obj.to_dict_key()
|
|
104
|
+
elif isinstance(obj, tuple):
|
|
105
|
+
return tuple(dsl_key(o) for o in obj)
|
|
106
|
+
return obj
|
|
107
|
+
|
|
108
|
+
#------------------------------------------------------
|
|
109
|
+
# Variable
|
|
110
|
+
#------------------------------------------------------
|
|
111
|
+
|
|
112
|
+
class Variable(DSLBase):
|
|
113
|
+
#--------------------------------------------------
|
|
114
|
+
# Infix operator overloads
|
|
115
|
+
#--------------------------------------------------
|
|
116
|
+
|
|
117
|
+
def _bin_op(self, op, left, right) -> Expression:
|
|
118
|
+
res = CoreConcepts["Numeric"].ref()
|
|
119
|
+
return Expression(CoreRelationships[op], [left, right, res])
|
|
120
|
+
|
|
121
|
+
def __add__(self, other):
|
|
122
|
+
return self._bin_op("+", self, other)
|
|
123
|
+
def __radd__(self, other):
|
|
124
|
+
return self._bin_op("+", other, self)
|
|
125
|
+
|
|
126
|
+
def __mul__(self, other):
|
|
127
|
+
return self._bin_op("*", self, other)
|
|
128
|
+
def __rmul__(self, other):
|
|
129
|
+
return self._bin_op("*", other, self)
|
|
130
|
+
|
|
131
|
+
def __sub__(self, other):
|
|
132
|
+
return self._bin_op("-", self, other)
|
|
133
|
+
def __rsub__(self, other):
|
|
134
|
+
return self._bin_op("-", other, self)
|
|
135
|
+
|
|
136
|
+
def __truediv__(self, other):
|
|
137
|
+
return self._bin_op("/", self, other)
|
|
138
|
+
def __rtruediv__(self, other):
|
|
139
|
+
return self._bin_op("/", other, self)
|
|
140
|
+
|
|
141
|
+
def __floordiv__(self, other):
|
|
142
|
+
return self._bin_op("//", self, other)
|
|
143
|
+
def __rfloordiv__(self, other):
|
|
144
|
+
return self._bin_op("//", other, self)
|
|
145
|
+
|
|
146
|
+
def __pow__(self, other):
|
|
147
|
+
return self._bin_op("^", self, other)
|
|
148
|
+
def __rpow__(self, other):
|
|
149
|
+
return self._bin_op("^", other, self)
|
|
150
|
+
|
|
151
|
+
def __mod__(self, other):
|
|
152
|
+
return self._bin_op("%", self, other)
|
|
153
|
+
def __rmod__(self, other):
|
|
154
|
+
return self._bin_op("%", other, self)
|
|
155
|
+
|
|
156
|
+
def __neg__(self):
|
|
157
|
+
return self._bin_op("*", self, -1)
|
|
158
|
+
|
|
159
|
+
#--------------------------------------------------
|
|
160
|
+
# Filter overloads
|
|
161
|
+
#--------------------------------------------------
|
|
162
|
+
|
|
163
|
+
def _filter(self, op, left, right) -> Expression:
|
|
164
|
+
return Expression(CoreRelationships[op], [left, right])
|
|
165
|
+
|
|
166
|
+
def __gt__(self, other):
|
|
167
|
+
return self._filter(">", self, other)
|
|
168
|
+
def __ge__(self, other):
|
|
169
|
+
return self._filter(">=", self, other)
|
|
170
|
+
def __lt__(self, other):
|
|
171
|
+
return self._filter("<", self, other)
|
|
172
|
+
def __le__(self, other):
|
|
173
|
+
return self._filter("<=", self, other)
|
|
174
|
+
def __eq__(self, other) -> Any:
|
|
175
|
+
return self._filter("=", self, other)
|
|
176
|
+
def __ne__(self, other) -> Any:
|
|
177
|
+
return self._filter("!=", self, other)
|
|
178
|
+
|
|
179
|
+
#--------------------------------------------------
|
|
180
|
+
# And/Or
|
|
181
|
+
#--------------------------------------------------
|
|
182
|
+
|
|
183
|
+
def __or__(self, other) -> Match:
|
|
184
|
+
return Match(self._model, self, other)
|
|
185
|
+
|
|
186
|
+
def __and__(self, other) -> Fragment:
|
|
187
|
+
if isinstance(other, Fragment):
|
|
188
|
+
new = other.where()
|
|
189
|
+
new._where.insert(0, self)
|
|
190
|
+
return new
|
|
191
|
+
return self._model.where(self, other)
|
|
192
|
+
|
|
193
|
+
#------------------------------------------------------
|
|
194
|
+
# AsBool
|
|
195
|
+
#------------------------------------------------------
|
|
196
|
+
|
|
197
|
+
def as_bool(self) -> AsBool:
|
|
198
|
+
return AsBool(self)
|
|
199
|
+
|
|
200
|
+
#------------------------------------------------------
|
|
201
|
+
# Alias
|
|
202
|
+
#------------------------------------------------------
|
|
203
|
+
|
|
204
|
+
def alias(self, alias: str) -> Alias:
|
|
205
|
+
return Alias(self, alias)
|
|
206
|
+
|
|
207
|
+
#--------------------------------------------------
|
|
208
|
+
# in_
|
|
209
|
+
#--------------------------------------------------
|
|
210
|
+
|
|
211
|
+
def in_(self, values:Sequence[Value]|Variable):
|
|
212
|
+
if isinstance(values, Variable):
|
|
213
|
+
return self == values
|
|
214
|
+
if all(isinstance(v, (str, int, float, bool)) for v in values):
|
|
215
|
+
data_table = self._model.data([(v,) for v in values])
|
|
216
|
+
return self == data_table[0]
|
|
217
|
+
else:
|
|
218
|
+
return self == self._model.union(*values)
|
|
219
|
+
|
|
220
|
+
#--------------------------------------------------
|
|
221
|
+
# Check value
|
|
222
|
+
#--------------------------------------------------
|
|
223
|
+
|
|
224
|
+
def _check_value(self) -> bool:
|
|
225
|
+
return True
|
|
226
|
+
|
|
227
|
+
def _to_concept(self) -> Concept|None:
|
|
228
|
+
return None
|
|
229
|
+
|
|
230
|
+
#------------------------------------------------------
|
|
231
|
+
# Error handling
|
|
232
|
+
#------------------------------------------------------
|
|
233
|
+
|
|
234
|
+
def __bool__(self) -> NoReturn:
|
|
235
|
+
cur_source = self._source.block.source or ""
|
|
236
|
+
invalid = next((bool_check for bool_check in ["if ", "while ", " and ", " or "] if bool_check in cur_source), "bool check").strip()
|
|
237
|
+
mapped = {"and": "`&` or `,`", "or": "`|`", "if": "`where`"}
|
|
238
|
+
if m := mapped.get(invalid):
|
|
239
|
+
exc(f"Invalid operator", f"Cannot use python's `{invalid}` in model expressions. Use {m} instead.", [source(self)])
|
|
240
|
+
else:
|
|
241
|
+
exc(f"Invalid operator", f"Cannot use python's `{invalid}` in model expressions.", [source(self)])
|
|
242
|
+
|
|
243
|
+
if not TYPE_CHECKING:
|
|
244
|
+
def __iter__(self):
|
|
245
|
+
common_incorrect = ["sum", "min", "max"]
|
|
246
|
+
for agg in common_incorrect:
|
|
247
|
+
if agg in self._source.block.source:
|
|
248
|
+
exc(f"Invalid built-in", f"The Python built-in `{agg}()` was used instead of the RAI equivalent.", [
|
|
249
|
+
source(self),
|
|
250
|
+
f"Use [cyan]`relationalai.semantics.std.aggregates.{agg}`[/cyan] instead.",
|
|
251
|
+
])
|
|
252
|
+
exc("Invalid iteration", f"Cannot iterate over {self.__class__.__name__} objects.", [
|
|
253
|
+
source(self)
|
|
254
|
+
])
|
|
255
|
+
|
|
256
|
+
def __len__(self) -> NoReturn:
|
|
257
|
+
exc("Invalid operator", "Cannot use len() on model variables.", [source(self)])
|
|
258
|
+
|
|
259
|
+
#------------------------------------------------------
|
|
260
|
+
# Concept
|
|
261
|
+
#------------------------------------------------------
|
|
262
|
+
|
|
263
|
+
class Concept(Variable):
|
|
264
|
+
lookup_by_id: dict[int, Concept] = {}
|
|
265
|
+
|
|
266
|
+
def __init__(self, name: str, extends: list[Concept], identify_by: dict[str, Concept], model:Model):
|
|
267
|
+
super().__init__(model)
|
|
268
|
+
Concept.lookup_by_id[self._id] = self
|
|
269
|
+
self._name = name
|
|
270
|
+
clean_extends = []
|
|
271
|
+
for extend in extends:
|
|
272
|
+
if not isinstance(extend, Concept):
|
|
273
|
+
exc("Invalid extend", f"Cannot extend from non-Concept type: {extend}", [source(self)])
|
|
274
|
+
if "Number" in CoreConcepts and extend is CoreConcepts["Number"]:
|
|
275
|
+
clean_extends.append(extend.size(38, 14))
|
|
276
|
+
else:
|
|
277
|
+
clean_extends.append(extend)
|
|
278
|
+
self._extends = clean_extends
|
|
279
|
+
self._annotations: list[Expression|Relationship] = []
|
|
280
|
+
self._relationships = {}
|
|
281
|
+
self._identify_by:list[Relationship] = []
|
|
282
|
+
for id_name, id_val in identify_by.items():
|
|
283
|
+
if not isinstance(id_val, (Concept, Property)):
|
|
284
|
+
exc("Invalid identify_by", f"identify_by values must be Concepts, got: {type(id_val)}", [source(self)])
|
|
285
|
+
id_val = self._dot(id_name, with_type=id_val)
|
|
286
|
+
self._identify_by.append(id_val)
|
|
287
|
+
|
|
288
|
+
def __setattr__(self, name: str, value: Any) -> None:
|
|
289
|
+
if isinstance(value, Relationship):
|
|
290
|
+
key = name.lower()
|
|
291
|
+
if self._relationships.get(key) is not None:
|
|
292
|
+
exc("Duplicate relationship", f"Relationship '{name}' is already defined on concept '{self._name}'", [
|
|
293
|
+
source(self._relationships[key])
|
|
294
|
+
])
|
|
295
|
+
if not value._short_name:
|
|
296
|
+
value._short_name = name
|
|
297
|
+
self._relationships[key] = value
|
|
298
|
+
else:
|
|
299
|
+
super().__setattr__(name, value)
|
|
300
|
+
|
|
301
|
+
def _dot_recur(self, name: str, with_type: Any = None) -> Relationship|None:
|
|
302
|
+
if name.lower() in self._relationships:
|
|
303
|
+
return self._relationships[name.lower()]
|
|
304
|
+
for ext in self._extends:
|
|
305
|
+
rel = ext._dot_recur(name, with_type)
|
|
306
|
+
if rel is not None:
|
|
307
|
+
return rel
|
|
308
|
+
# We should create the relationship on the root type if it's not
|
|
309
|
+
# found anywhere
|
|
310
|
+
if self._model is not CoreLibrary:
|
|
311
|
+
field_type = _find_concept(self._model, with_type, default=CoreConcepts["Any"])
|
|
312
|
+
assert field_type is not None
|
|
313
|
+
rel = self._relationships[name.lower()] = Property(fields=[
|
|
314
|
+
Field(self._name.lower(), self),
|
|
315
|
+
Field(name, field_type)
|
|
316
|
+
], short_name=name, model=self._model)
|
|
317
|
+
return rel
|
|
318
|
+
return None
|
|
319
|
+
|
|
320
|
+
def _dot(self, name: str, with_type: Any = None) -> Relationship:
|
|
321
|
+
if self._model is CoreLibrary:
|
|
322
|
+
exc("Invalid relationship", f"Cannot access relationships on core concept '{self._name}'.", [source(self)])
|
|
323
|
+
if with_type is None:
|
|
324
|
+
with_type = CoreConcepts["Any"]
|
|
325
|
+
rel = self._dot_recur(name, with_type)
|
|
326
|
+
assert rel is not None
|
|
327
|
+
return rel
|
|
328
|
+
|
|
329
|
+
def __getattr__(self, item) -> Chain:
|
|
330
|
+
if item.startswith("_"):
|
|
331
|
+
return object.__getattribute__(self, item)
|
|
332
|
+
return Chain(self, self._dot(item))
|
|
333
|
+
|
|
334
|
+
def __call__(self, *args: Any, **kwargs: Any) -> Expression:
|
|
335
|
+
return Expression(self, list(args), kwargs)
|
|
336
|
+
|
|
337
|
+
def new(self, *args: StatementAndSchema, **kwargs: Any) -> New:
|
|
338
|
+
return New(self, args, kwargs)
|
|
339
|
+
|
|
340
|
+
def to_identity(self, *args: Any, **kwargs: Any) -> New:
|
|
341
|
+
return New(self, args, kwargs, identity_only=True)
|
|
342
|
+
|
|
343
|
+
def identify_by(self, *properties: Property|Chain) -> Concept:
|
|
344
|
+
for prop in properties:
|
|
345
|
+
if isinstance(prop, Chain):
|
|
346
|
+
prop = prop._next
|
|
347
|
+
if not isinstance(prop, Property) and not (isinstance(prop, Reading) and isinstance(prop._relationship, Property)):
|
|
348
|
+
exc("Invalid identify_by", f"identify_by expects Properties, got: {type(prop).__name__}", [source(self)])
|
|
349
|
+
if prop._fields[0].type is not self:
|
|
350
|
+
exc("Invalid identify_by", f"Property {prop} have the first field be {self._name}", [source(self)])
|
|
351
|
+
self._identify_by.append(prop)
|
|
352
|
+
return self
|
|
353
|
+
|
|
354
|
+
def filter_by(self, **kwargs: Any) -> Expression:
|
|
355
|
+
return FilterBy(self, kwargs)
|
|
356
|
+
|
|
357
|
+
def require(self, *items: Variable|Fragment) -> Fragment:
|
|
358
|
+
return self._model.where(self).require(*items)
|
|
359
|
+
|
|
360
|
+
def ref(self, name="") -> Ref:
|
|
361
|
+
return Ref(self, name)
|
|
362
|
+
|
|
363
|
+
def annotate(self, *annos:Expression|Relationship) -> Concept:
|
|
364
|
+
self._annotations.extend(annos)
|
|
365
|
+
return self
|
|
366
|
+
|
|
367
|
+
def _to_concept(self) -> Concept:
|
|
368
|
+
return self
|
|
369
|
+
|
|
370
|
+
def __dir__(self):
|
|
371
|
+
default = set(super().__dir__())
|
|
372
|
+
return sorted(default.union(self._relationships.keys()))
|
|
373
|
+
|
|
374
|
+
def __format__(self, format_spec: str) -> str:
|
|
375
|
+
if not format_spec:
|
|
376
|
+
return f"{{{self._name}#{self._id}}}"
|
|
377
|
+
return f"{{{self._name}#{self._id}:{format_spec.strip()}}}"
|
|
378
|
+
|
|
379
|
+
#------------------------------------------------------
|
|
380
|
+
# NumberConcept
|
|
381
|
+
#------------------------------------------------------
|
|
382
|
+
|
|
383
|
+
class NumberConcept(Concept):
|
|
384
|
+
def __init__(self, name: str, precision: int, scale: int, model: Model):
|
|
385
|
+
super().__init__(name, extends=[CoreConcepts["Numeric"]], identify_by={}, model=model)
|
|
386
|
+
self._precision = precision
|
|
387
|
+
self._scale = scale
|
|
388
|
+
|
|
389
|
+
#------------------------------------------------------
|
|
390
|
+
# Ref
|
|
391
|
+
#------------------------------------------------------
|
|
392
|
+
|
|
393
|
+
class Ref(Variable):
|
|
394
|
+
def __init__(self, concept: Concept, name: str|None = None):
|
|
395
|
+
super().__init__(concept._model)
|
|
396
|
+
self._concept = concept
|
|
397
|
+
self._name = name or ("number" if (isinstance(concept, NumberConcept) or concept is CoreConcepts["Numeric"])
|
|
398
|
+
else concept._name)
|
|
399
|
+
|
|
400
|
+
def __getattr__(self, item):
|
|
401
|
+
if item.startswith("_"):
|
|
402
|
+
return object.__getattribute__(self, item)
|
|
403
|
+
return Chain(self, self._concept._dot(item))
|
|
404
|
+
|
|
405
|
+
def _to_concept(self) -> Concept:
|
|
406
|
+
return self._concept
|
|
407
|
+
|
|
408
|
+
def FilterBy(self, **kwargs: Any) -> Expression:
|
|
409
|
+
return FilterBy(self, kwargs)
|
|
410
|
+
|
|
411
|
+
#------------------------------------------------------
|
|
412
|
+
# Table
|
|
413
|
+
#------------------------------------------------------
|
|
414
|
+
|
|
415
|
+
class Table(Concept):
|
|
416
|
+
def __init__(self, name:str, schema: dict[str, Concept], model: Model):
|
|
417
|
+
super().__init__(name, [], {}, model)
|
|
418
|
+
self._known_columns = []
|
|
419
|
+
for col_name, col_type in schema.items():
|
|
420
|
+
if isinstance(col_type, Property):
|
|
421
|
+
col_rel = col_type
|
|
422
|
+
setattr(self, col_name, col_rel)
|
|
423
|
+
else:
|
|
424
|
+
col_rel = self._dot(col_name, with_type=col_type)
|
|
425
|
+
self._known_columns.append(col_rel)
|
|
426
|
+
|
|
427
|
+
@property
|
|
428
|
+
def _columns(self) -> list[Relationship]:
|
|
429
|
+
if self._known_columns:
|
|
430
|
+
return self._known_columns
|
|
431
|
+
schema = schema_util.fetch(self._name)
|
|
432
|
+
for col_name, col_type_name in schema.items():
|
|
433
|
+
col_type = _find_concept(self._model, col_type_name, default=CoreConcepts["Any"])
|
|
434
|
+
col_rel = self._dot(col_name, with_type=col_type)
|
|
435
|
+
col_rel._short_name = col_name
|
|
436
|
+
if col_type is not None:
|
|
437
|
+
col_rel._fields[1].type = col_type
|
|
438
|
+
self._known_columns.append(col_rel)
|
|
439
|
+
return self._known_columns
|
|
440
|
+
|
|
441
|
+
def __getitem__(self, index: int|str) -> Chain:
|
|
442
|
+
if isinstance(index, int):
|
|
443
|
+
if not (0 <= index < len(self._columns)):
|
|
444
|
+
raise IndexError(f"Column index {index} out of range, there are {len(self._columns)} columns")
|
|
445
|
+
return Chain(self, self._columns[index])
|
|
446
|
+
col = next((c for c in self._columns if c._short_name == index), None)
|
|
447
|
+
if col is None:
|
|
448
|
+
raise KeyError(f"Column name '{index}' not found, columns have names {[c._short_name for c in self._columns]}")
|
|
449
|
+
return Chain(self, col)
|
|
450
|
+
|
|
451
|
+
def __iter__(self) -> Iterator[Relationship]:
|
|
452
|
+
return iter(self._columns)
|
|
453
|
+
|
|
454
|
+
def new(self, *args:StatementAndSchema, **kwargs: Any) -> NoReturn:
|
|
455
|
+
exc("Invalid new call", "Cannot create new instances of Tables.", [source(self)])
|
|
456
|
+
|
|
457
|
+
def to_identity(self, *args: Any, **kwargs: Any) -> NoReturn:
|
|
458
|
+
exc("Invalid identity call", "Cannot create identity instances of Tables.", [source(self)])
|
|
459
|
+
|
|
460
|
+
def to_schema(self, *, exclude: list[str] = []) -> TableSchema:
|
|
461
|
+
return TableSchema(self, exclude=exclude)
|
|
462
|
+
|
|
463
|
+
#------------------------------------------------------
|
|
464
|
+
# TableSchema
|
|
465
|
+
#------------------------------------------------------
|
|
466
|
+
|
|
467
|
+
class TableSchema(DSLBase):
|
|
468
|
+
def __init__(self, table: Table, exclude: list[str] = []):
|
|
469
|
+
super().__init__(table._model)
|
|
470
|
+
self._table = table
|
|
471
|
+
self.exclude = set([e.lower() for e in exclude])
|
|
472
|
+
|
|
473
|
+
def get_columns(self) -> list[Relationship]:
|
|
474
|
+
return [col for col in self._table._columns if col._short_name.lower() not in self.exclude]
|
|
475
|
+
|
|
476
|
+
#--------------------------------------------------
|
|
477
|
+
# DerivedTable
|
|
478
|
+
#--------------------------------------------------
|
|
479
|
+
|
|
480
|
+
class DerivedTable(Variable):
|
|
481
|
+
def __init__(self, model: Model):
|
|
482
|
+
super().__init__(model)
|
|
483
|
+
self.__cols = []
|
|
484
|
+
|
|
485
|
+
@property
|
|
486
|
+
def _columns(self):
|
|
487
|
+
if not self.__cols:
|
|
488
|
+
self.__cols = self._get_cols()
|
|
489
|
+
if not self.__cols:
|
|
490
|
+
raise ValueError(f"Cannot use {self.__class__.__name__} as it has no columns")
|
|
491
|
+
return self.__cols
|
|
492
|
+
|
|
493
|
+
def _check_value(self):
|
|
494
|
+
return bool(self._columns)
|
|
495
|
+
|
|
496
|
+
def __iter__(self) -> Iterator[DerivedColumn]:
|
|
497
|
+
return iter(self._columns)
|
|
498
|
+
|
|
499
|
+
def __getitem__(self, index: int|str) -> DerivedColumn:
|
|
500
|
+
if isinstance(index, int):
|
|
501
|
+
if not (0 <= index < len(self._columns)):
|
|
502
|
+
raise IndexError(f"Column index {index} out of range, there are {len(self._columns)} columns")
|
|
503
|
+
return self._columns[index]
|
|
504
|
+
col = next((c for c in self._columns if c._name == index), None)
|
|
505
|
+
if not col:
|
|
506
|
+
raise KeyError(f"Column name '{index}' not found, columns have names {[c._name for c in self._columns]}")
|
|
507
|
+
return col
|
|
508
|
+
|
|
509
|
+
def _get_cols(self):
|
|
510
|
+
raise NotImplementedError()
|
|
511
|
+
|
|
512
|
+
#--------------------------------------------------
|
|
513
|
+
# DerivedColumn
|
|
514
|
+
#--------------------------------------------------
|
|
515
|
+
|
|
516
|
+
class DerivedColumn(Variable):
|
|
517
|
+
def __init__(self, table: DerivedTable, index: int, name: str|None = None, type_: Concept|None = None):
|
|
518
|
+
super().__init__(table._model)
|
|
519
|
+
self._table = table
|
|
520
|
+
self._index = index
|
|
521
|
+
self._name = name
|
|
522
|
+
self._type = type_
|
|
523
|
+
self._relationships = {}
|
|
524
|
+
|
|
525
|
+
def _dot(self, name: str) -> Relationship:
|
|
526
|
+
if self._type is not None:
|
|
527
|
+
return self._type._dot(name)
|
|
528
|
+
if name.lower() not in self._relationships:
|
|
529
|
+
self._relationships[name.lower()] = Property(fields=[
|
|
530
|
+
Field("entity", CoreConcepts["Any"]),
|
|
531
|
+
Field(name, CoreConcepts["Any"])
|
|
532
|
+
], short_name=name, model=self._model)
|
|
533
|
+
return self._relationships[name.lower()]
|
|
534
|
+
|
|
535
|
+
def __getattr__(self, item):
|
|
536
|
+
if item.startswith("_"):
|
|
537
|
+
return object.__getattribute__(self, item)
|
|
538
|
+
return Chain(self, self._dot(item))
|
|
539
|
+
|
|
540
|
+
@staticmethod
|
|
541
|
+
def from_value(table: DerivedTable, index: int, var: Value) -> DerivedColumn:
|
|
542
|
+
name = None
|
|
543
|
+
if isinstance(var, Alias):
|
|
544
|
+
name = var._alias
|
|
545
|
+
if not isinstance(var, Variable):
|
|
546
|
+
var = Literal(var, table._model)
|
|
547
|
+
return DerivedColumn(table=table, index=index, name=name, type_=var._to_concept())
|
|
548
|
+
|
|
549
|
+
#------------------------------------------------------
|
|
550
|
+
# Field
|
|
551
|
+
#------------------------------------------------------
|
|
552
|
+
|
|
553
|
+
class Field:
|
|
554
|
+
def __init__(self, name: str, type_: Concept, is_input: bool = False, is_list: bool = False, source: SourcePos | None = None):
|
|
555
|
+
self._id = next(_global_id)
|
|
556
|
+
self.name = name
|
|
557
|
+
self.type = type_
|
|
558
|
+
self.is_input = is_input
|
|
559
|
+
self.is_list = is_list
|
|
560
|
+
self._source = source
|
|
561
|
+
|
|
562
|
+
@classmethod
|
|
563
|
+
def input(cls, name: str, concept: Concept, is_list: bool = False) -> Field:
|
|
564
|
+
return Field(name, concept, is_input=True, is_list=is_list)
|
|
565
|
+
|
|
566
|
+
def _match(self, other: Field) -> bool:
|
|
567
|
+
return self.name == other.name and self.type is other.type
|
|
568
|
+
|
|
569
|
+
#--------------------------------------------------
|
|
570
|
+
# Relationship
|
|
571
|
+
#--------------------------------------------------
|
|
572
|
+
|
|
573
|
+
class Relationship(Variable):
|
|
574
|
+
def __init__(self, model: Model, reading_str:str = "", fields: list[Field] = [], short_name: str = "", allow_no_fields: bool = False, overloads:list[list[Concept]]|None = None, is_unresolved: bool = False):
|
|
575
|
+
super().__init__(model)
|
|
576
|
+
if not reading_str and not fields and not allow_no_fields:
|
|
577
|
+
raise ValueError("Either reading_str or fields must be provided")
|
|
578
|
+
if not reading_str and fields:
|
|
579
|
+
reading_str = " and ".join([f"{{{f.type._name}#{f.type._id}:{f.name}}}" for f in fields[:-1]])
|
|
580
|
+
reading_str = f"{reading_str} has {{{fields[-1].type._name}#{fields[-1].type._id}:{fields[-1].name}}}"
|
|
581
|
+
parts = []
|
|
582
|
+
if not fields:
|
|
583
|
+
(fields, parts) = Reading.parse(model, reading_str, fields, _source=self)
|
|
584
|
+
self._fields = fields
|
|
585
|
+
if not fields and not allow_no_fields:
|
|
586
|
+
exc("Invalid Relationship", "A Relationship must have at least one field.", [source(self)])
|
|
587
|
+
self._readings = [Reading(model, self, reading_str, fields, parts)]
|
|
588
|
+
self._short_name = short_name
|
|
589
|
+
self._relationships = {}
|
|
590
|
+
self._annotations: list[Expression|Relationship] = []
|
|
591
|
+
self._overloads = overloads
|
|
592
|
+
self._is_unresolved = is_unresolved
|
|
593
|
+
for f in self._fields:
|
|
594
|
+
if f._source is None:
|
|
595
|
+
f._source = self._source
|
|
596
|
+
|
|
597
|
+
def _dot(self, name: str) -> Relationship:
|
|
598
|
+
field_type = self._fields[-1].type
|
|
599
|
+
if name.lower() in self._relationships:
|
|
600
|
+
return self._relationships[name.lower()]
|
|
601
|
+
if field_type is CoreConcepts["Any"]:
|
|
602
|
+
rel = Property(fields=[
|
|
603
|
+
Field(self._fields[-1].name, field_type),
|
|
604
|
+
Field(name, CoreConcepts["Any"])
|
|
605
|
+
], short_name=name, model=self._model, is_unresolved=True)
|
|
606
|
+
self._relationships[name.lower()] = rel
|
|
607
|
+
return rel
|
|
608
|
+
return field_type._dot(name)
|
|
609
|
+
|
|
610
|
+
def __getattr__(self, item):
|
|
611
|
+
if item.startswith("_"):
|
|
612
|
+
return object.__getattribute__(self, item)
|
|
613
|
+
op = self._fields[-1].type._dot(item)
|
|
614
|
+
return Chain(self, op)
|
|
615
|
+
|
|
616
|
+
def __call__(self, *args: Any, **kwargs: Any) -> Expression:
|
|
617
|
+
return Expression(self, list(args), kwargs, root=self)
|
|
618
|
+
|
|
619
|
+
def __getitem__(self, field: str|int|Concept) -> FieldRef:
|
|
620
|
+
resolved = _find_field(self._fields, field)
|
|
621
|
+
if not resolved:
|
|
622
|
+
raise KeyError(f"Field {field} not found in relationship with fields {[f.name for f in self._fields]}")
|
|
623
|
+
return FieldRef(self, field, *resolved)
|
|
624
|
+
|
|
625
|
+
def alt(self, reading_str: str) -> Reading:
|
|
626
|
+
reading = Reading(self._model, self, reading_str)
|
|
627
|
+
self._readings.append(reading)
|
|
628
|
+
return reading
|
|
629
|
+
|
|
630
|
+
def annotate(self, *annos:Expression|Relationship) -> Relationship:
|
|
631
|
+
self._annotations.extend(annos)
|
|
632
|
+
return self
|
|
633
|
+
|
|
634
|
+
def _to_concept(self) -> Concept|None:
|
|
635
|
+
return self._fields[-1].type
|
|
636
|
+
|
|
637
|
+
def __dir__(self):
|
|
638
|
+
default = set(super().__dir__())
|
|
639
|
+
return sorted(default.union(self._fields[-1].type.__dir__()))
|
|
640
|
+
|
|
641
|
+
def to_df(self) -> DataFrame:
|
|
642
|
+
refs = [self[ix] for ix in range(len(self._fields))]
|
|
643
|
+
return self._model.select(*refs).where(self(*refs)).to_df()
|
|
644
|
+
|
|
645
|
+
def inspect(self):
|
|
646
|
+
print(self.to_df())
|
|
647
|
+
|
|
648
|
+
#------------------------------------------------------
|
|
649
|
+
# Reading
|
|
650
|
+
#------------------------------------------------------
|
|
651
|
+
|
|
652
|
+
class Reading(Relationship):
|
|
653
|
+
def __init__(self, model: Model, relationship: Relationship, reading_str: str, reading_fields: list[Field] = [], reading_parts: list[str|int] = []):
|
|
654
|
+
Variable.__init__(self, model)
|
|
655
|
+
self._relationship = relationship
|
|
656
|
+
if not reading_fields or not reading_parts:
|
|
657
|
+
parsed_fields, parsed_parts = Reading.parse(model, reading_str, reading_fields, _source=self)
|
|
658
|
+
reading_fields = reading_fields or parsed_fields
|
|
659
|
+
reading_parts = reading_parts or parsed_parts
|
|
660
|
+
# make sure we can find all these fields in the base relationship
|
|
661
|
+
index_map = {}
|
|
662
|
+
matched = []
|
|
663
|
+
for field_i, field in enumerate(reading_fields):
|
|
664
|
+
found = next(((i, f) for i, f in enumerate(relationship._fields) if f._match(field)), None)
|
|
665
|
+
if not found:
|
|
666
|
+
raise ValueError(f"Field {field.name}:{field.type._name} not found in relationship with fields {[f'{f.name}:{f.type._name}' for f in relationship._fields]}")
|
|
667
|
+
matched.append(found[1])
|
|
668
|
+
index_map[field_i] = found[0]
|
|
669
|
+
self._fields = matched
|
|
670
|
+
self._reading = reading_str
|
|
671
|
+
self._short_name = ""
|
|
672
|
+
self._parts:list[str|int] = [index_map.get(p, p) for p in reading_parts]
|
|
673
|
+
self._relationships = {}
|
|
674
|
+
self._annotations: list[Expression|Relationship] = []
|
|
675
|
+
|
|
676
|
+
#------------------------------------------------------
|
|
677
|
+
# Parse
|
|
678
|
+
#------------------------------------------------------
|
|
679
|
+
|
|
680
|
+
@classmethod
|
|
681
|
+
def parse(cls, model: Model, reading: str, known_fields: list[Field], _source=None) -> Tuple[list[Field], list[str|int]]:
|
|
682
|
+
# match <class 'foo'> which is the serialized form of a python type mistakenly passed instead of a concept
|
|
683
|
+
class_pattern = re.compile(r'<class \'(.*?)\'>')
|
|
684
|
+
match = class_pattern.search(reading)
|
|
685
|
+
if match:
|
|
686
|
+
extra: list = [source(_source)]
|
|
687
|
+
if match.group(1) in pytype_to_concept_name:
|
|
688
|
+
concept = pytype_to_concept_name[match.group(1)]
|
|
689
|
+
extra.append(f" Did you mean to use [cyan]relationalai.semantics.{concept}[/cyan]?")
|
|
690
|
+
exc("Invalid field type", f"The type '{match.group(1)}' is not a valid Concept.", extra)
|
|
691
|
+
|
|
692
|
+
# {Type} or {name:Type}, where Type can include Number(38,14)
|
|
693
|
+
pattern = re.compile(r'\{([a-zA-Z0-9_.#]+(?:(?:\([0-9]+,[0-9]+\))(?:[#0-9]+)?)?)(?::\s*([a-zA-Z0-9_.]+(?:\([0-9]+,[0-9]+\))?))?\}')
|
|
694
|
+
|
|
695
|
+
namer = Namer()
|
|
696
|
+
fields: list[Field] = []
|
|
697
|
+
parts: list[str|int] = []
|
|
698
|
+
|
|
699
|
+
last_end = 0
|
|
700
|
+
is_old_style = True
|
|
701
|
+
for m in pattern.finditer(reading):
|
|
702
|
+
# literal chunk before this match
|
|
703
|
+
parts.append(reading[last_end:m.start()])
|
|
704
|
+
field_name, field_type_name = m.group(1), m.group(2)
|
|
705
|
+
field_type_id = None
|
|
706
|
+
if "#" in field_name:
|
|
707
|
+
temp_name = field_type_name
|
|
708
|
+
field_type_name, field_type_id = field_name.split("#")
|
|
709
|
+
field_name = temp_name or sanitize(field_type_name.lower())
|
|
710
|
+
is_old_style = False
|
|
711
|
+
|
|
712
|
+
# if we don't have a type_name, then only a type was provided
|
|
713
|
+
if not field_type_name:
|
|
714
|
+
field_type_name = field_name
|
|
715
|
+
field_name = sanitize(field_name.lower())
|
|
716
|
+
|
|
717
|
+
field_name = namer.get_name(field_name)
|
|
718
|
+
field_type = Concept.lookup_by_id.get(int(field_type_id)) \
|
|
719
|
+
if field_type_id \
|
|
720
|
+
else _find_concept(model, field_type_name, extra_types=[f.type for f in known_fields])
|
|
721
|
+
if field_type is None:
|
|
722
|
+
exc("Unknown Concept", f"The Concept '{field_type_name}' couldn't be found in the model", [
|
|
723
|
+
source(_source),
|
|
724
|
+
])
|
|
725
|
+
|
|
726
|
+
fields.append(Field(field_name, field_type))
|
|
727
|
+
parts.append(len(fields) - 1)
|
|
728
|
+
last_end = m.end()
|
|
729
|
+
|
|
730
|
+
# trailing literal after the final match
|
|
731
|
+
if(last_end < len(reading)):
|
|
732
|
+
parts.append(reading[last_end:])
|
|
733
|
+
|
|
734
|
+
if is_old_style and fields:
|
|
735
|
+
correct = []
|
|
736
|
+
for part in parts:
|
|
737
|
+
if isinstance(part, int):
|
|
738
|
+
type_name = fields[part].type._name
|
|
739
|
+
correct_type_name = type_name
|
|
740
|
+
if isinstance(fields[part].type, NumberConcept):
|
|
741
|
+
precision = fields[part].type._precision
|
|
742
|
+
scale = fields[part].type._scale
|
|
743
|
+
if scale == 0:
|
|
744
|
+
correct_type_name = f"Integer"
|
|
745
|
+
else:
|
|
746
|
+
correct_type_name = f"Number.size({precision},{scale})"
|
|
747
|
+
if type_name.lower() != fields[part].name and correct_type_name.lower() != fields[part].name:
|
|
748
|
+
correct.append(f"{{{correct_type_name}:{fields[part].name}}}")
|
|
749
|
+
else:
|
|
750
|
+
correct.append(f"{{{correct_type_name}}}")
|
|
751
|
+
else:
|
|
752
|
+
correct.append(part)
|
|
753
|
+
warn("Deprecated format", "Plain strings for Relationships/Properties is deprecated. Use an f-string instead.", [
|
|
754
|
+
source(_source),
|
|
755
|
+
f'For example: [cyan]f"{"".join(correct)}"',
|
|
756
|
+
])
|
|
757
|
+
|
|
758
|
+
return fields, parts
|
|
759
|
+
|
|
760
|
+
#------------------------------------------------------
|
|
761
|
+
# Property
|
|
762
|
+
#------------------------------------------------------
|
|
763
|
+
|
|
764
|
+
class Property(Relationship):
|
|
765
|
+
pass
|
|
766
|
+
|
|
767
|
+
#------------------------------------------------------
|
|
768
|
+
# Literal
|
|
769
|
+
#------------------------------------------------------
|
|
770
|
+
|
|
771
|
+
class Literal(Variable):
|
|
772
|
+
def __init__(self, value: Any, model:Model, type: Concept|None = None):
|
|
773
|
+
super().__init__(model)
|
|
774
|
+
self._value = value
|
|
775
|
+
self._type = type if type is not None else self._get_type(value)
|
|
776
|
+
|
|
777
|
+
def _to_concept(self) -> Concept:
|
|
778
|
+
return self._type
|
|
779
|
+
|
|
780
|
+
@staticmethod
|
|
781
|
+
def _get_type(value: Any) -> Concept:
|
|
782
|
+
if type(value) is float and (math.isnan(value) or math.isinf(value)):
|
|
783
|
+
return CoreConcepts["Float"]
|
|
784
|
+
if type(value) is float:
|
|
785
|
+
fractional_digits = 0 if math.isnan(value) else min(len(str(value).split(".")[1]), 14)
|
|
786
|
+
return CoreConcepts["Decimal"].size(38, fractional_digits) # type: ignore
|
|
787
|
+
if type(value) is decimal.Decimal:
|
|
788
|
+
str_value = format(value, 'f')
|
|
789
|
+
if '.' in str_value:
|
|
790
|
+
_, fractional_part = str_value.split('.')
|
|
791
|
+
scale = min(len(fractional_part), 14)
|
|
792
|
+
else:
|
|
793
|
+
scale = 14
|
|
794
|
+
return CoreConcepts["Decimal"].size(38, scale) # type: ignore
|
|
795
|
+
if type(value) in pytype_to_concept_name:
|
|
796
|
+
return CoreConcepts[pytype_to_concept_name[type(value)]]
|
|
797
|
+
else:
|
|
798
|
+
raise NotImplementedError(f"Literal type not implemented for value type: {type(value)}")
|
|
799
|
+
|
|
800
|
+
#------------------------------------------------------
|
|
801
|
+
# Chain
|
|
802
|
+
#------------------------------------------------------
|
|
803
|
+
|
|
804
|
+
class Chain(Variable):
|
|
805
|
+
def __init__(self, start: Chain|Concept|Relationship|DerivedColumn|Table|Ref|FieldRef|Expression, next: Relationship, is_ref = False):
|
|
806
|
+
super().__init__(start._model)
|
|
807
|
+
self._start = start
|
|
808
|
+
self._next = next
|
|
809
|
+
self._is_ref = is_ref
|
|
810
|
+
|
|
811
|
+
def __getattr__(self, item):
|
|
812
|
+
if item.startswith("_"):
|
|
813
|
+
return object.__getattribute__(self._next, item)
|
|
814
|
+
next_rel = self._next._dot(item)
|
|
815
|
+
return Chain(self, next_rel)
|
|
816
|
+
|
|
817
|
+
def __call__(self, *args: Any, **kwargs: Any) -> Expression:
|
|
818
|
+
last = self._next
|
|
819
|
+
assert not isinstance(last, Chain)
|
|
820
|
+
if len(last._fields) > len(args):
|
|
821
|
+
return Expression(last, [self._start, *args], kwargs, root=self)
|
|
822
|
+
return Expression(last, [*args], kwargs, root=self)
|
|
823
|
+
|
|
824
|
+
def to_dict_key(self) -> int|tuple:
|
|
825
|
+
if self._is_ref:
|
|
826
|
+
return self._id
|
|
827
|
+
return (self._start.to_dict_key(), self._next.to_dict_key())
|
|
828
|
+
|
|
829
|
+
def __getitem__(self, field: str|int|Concept) -> FieldRef:
|
|
830
|
+
resolved = _find_field(self._next._fields, field)
|
|
831
|
+
if not resolved:
|
|
832
|
+
raise KeyError(f"Field {field} not found in relationship with fields {[f.name for f in self._next._fields]}")
|
|
833
|
+
return FieldRef(self, field, *resolved)
|
|
834
|
+
|
|
835
|
+
def ref(self) -> Chain:
|
|
836
|
+
return Chain(self._start, self._next, is_ref=True)
|
|
837
|
+
|
|
838
|
+
def alt(self, reading_str: str) -> Reading:
|
|
839
|
+
return self._next.alt(reading_str)
|
|
840
|
+
|
|
841
|
+
def annotate(self, *annos:Expression|Relationship) -> Relationship:
|
|
842
|
+
return self._next.annotate(*annos)
|
|
843
|
+
|
|
844
|
+
def _to_concept(self) -> Concept | None:
|
|
845
|
+
return self._next._to_concept()
|
|
846
|
+
|
|
847
|
+
#------------------------------------------------------
|
|
848
|
+
# Expression
|
|
849
|
+
#------------------------------------------------------
|
|
850
|
+
|
|
851
|
+
class Expression(Variable):
|
|
852
|
+
def __init__(self, op: Relationship|Concept, args: Sequence[Value], kwargs: dict|None = None, root: Chain|Relationship|Concept|None = None):
|
|
853
|
+
super().__init__(op._model)
|
|
854
|
+
self._op = op
|
|
855
|
+
self._has_output = isinstance(op, Concept) or (any(not f.is_input for f in op._fields))
|
|
856
|
+
self._auto_filled = False
|
|
857
|
+
self._root = root
|
|
858
|
+
|
|
859
|
+
# clean args
|
|
860
|
+
clean_args = []
|
|
861
|
+
for arg in args:
|
|
862
|
+
if isinstance(arg, (ModelEnum, Field, TupleVariable)):
|
|
863
|
+
pass
|
|
864
|
+
elif isinstance(arg, TableSchema):
|
|
865
|
+
exc("Invalid argument", "Cannot use a schema as an argument to an Expression.", [source(self)])
|
|
866
|
+
elif not isinstance(arg, Variable):
|
|
867
|
+
arg = Literal(arg, self._model)
|
|
868
|
+
elif isinstance(arg, (Match, Fragment)):
|
|
869
|
+
arg._columns
|
|
870
|
+
clean_args.append(arg)
|
|
871
|
+
self._args = clean_args
|
|
872
|
+
|
|
873
|
+
self._kwargs = kwargs or {}
|
|
874
|
+
# clean kwargs
|
|
875
|
+
for k, v in self._kwargs.items():
|
|
876
|
+
if not isinstance(v, Variable):
|
|
877
|
+
self._kwargs[k] = Literal(v, self._model)
|
|
878
|
+
|
|
879
|
+
if isinstance(op, Relationship):
|
|
880
|
+
op_len = len(op._fields)
|
|
881
|
+
arg_len = len(self._args)
|
|
882
|
+
if op_len - arg_len == 1:
|
|
883
|
+
self._args.append(op._fields[-1].type.ref(name=op._fields[-1].name))
|
|
884
|
+
self._auto_filled = True
|
|
885
|
+
elif op_len != arg_len:
|
|
886
|
+
dir = "Too few" if arg_len < op_len - 1 else "Too many"
|
|
887
|
+
exc(f"{dir} args", f"{op._short_name or 'Relationship'} requires {op_len - 1}-{op_len} arguments but got {arg_len}", [
|
|
888
|
+
source(self),
|
|
889
|
+
])
|
|
890
|
+
|
|
891
|
+
def __getattr__(self, item):
|
|
892
|
+
if item.startswith("_"):
|
|
893
|
+
return object.__getattribute__(self, item)
|
|
894
|
+
rel = self._op._dot(item)
|
|
895
|
+
return Chain(self, rel)
|
|
896
|
+
|
|
897
|
+
def __getitem__(self, field: str|int|Concept) -> FieldRef:
|
|
898
|
+
if not isinstance(self._op, Relationship):
|
|
899
|
+
raise TypeError(f"Cannot index into Expression with non-Relationship: {self._op}")
|
|
900
|
+
resolved = _find_field(self._op._fields, field)
|
|
901
|
+
if not resolved:
|
|
902
|
+
raise KeyError(f"Field {field} not found in relationship with fields {[f.name for f in self._op._fields]}")
|
|
903
|
+
return FieldRef(self, field, *resolved)
|
|
904
|
+
|
|
905
|
+
def _to_concept(self) -> Concept | None:
|
|
906
|
+
return self._args[-1]._to_concept() if self._args else self._op._to_concept()
|
|
907
|
+
|
|
908
|
+
#------------------------------------------------------
|
|
909
|
+
# New
|
|
910
|
+
#------------------------------------------------------
|
|
911
|
+
|
|
912
|
+
class New(Expression):
|
|
913
|
+
def __init__(self, concept: Concept, args: Sequence[Any], kwargs: dict[str, Any], identity_only: bool = False):
|
|
914
|
+
clean_args = []
|
|
915
|
+
row_ids = []
|
|
916
|
+
for arg in args:
|
|
917
|
+
if isinstance(arg, TableSchema):
|
|
918
|
+
row_ids.append(arg._table)
|
|
919
|
+
lower_case_kwargs = {k.lower() for k, v in kwargs.items()}
|
|
920
|
+
# add any keyword args that aren't already in kwargs
|
|
921
|
+
for col in arg.get_columns():
|
|
922
|
+
if col._short_name and col._short_name.lower() not in lower_case_kwargs:
|
|
923
|
+
kwargs[col._short_name] = col(arg._table)
|
|
924
|
+
else:
|
|
925
|
+
clean_args.append(arg)
|
|
926
|
+
for k in list(kwargs.keys()):
|
|
927
|
+
concept._dot(k)
|
|
928
|
+
super().__init__(concept, clean_args, kwargs)
|
|
929
|
+
self._identity_only = identity_only
|
|
930
|
+
self._row_ids = row_ids
|
|
931
|
+
|
|
932
|
+
#------------------------------------------------------
|
|
933
|
+
# FilterBy
|
|
934
|
+
#------------------------------------------------------
|
|
935
|
+
|
|
936
|
+
class FilterBy(Expression):
|
|
937
|
+
def __init__(self, item: Concept|Ref, kwargs: dict[str, Any]):
|
|
938
|
+
concept = item._concept if isinstance(item, Ref) else item
|
|
939
|
+
super().__init__(concept, [item], kwargs)
|
|
940
|
+
|
|
941
|
+
#------------------------------------------------------
|
|
942
|
+
# FieldRef
|
|
943
|
+
#------------------------------------------------------
|
|
944
|
+
|
|
945
|
+
class FieldRef(Variable):
|
|
946
|
+
def __init__(self, root: Chain|Relationship|Expression, field: str|int|Concept, resolved: Field, resolved_ix:int):
|
|
947
|
+
super().__init__(root._model)
|
|
948
|
+
self._root = root
|
|
949
|
+
self._field = field
|
|
950
|
+
self._resolved = resolved
|
|
951
|
+
self._resolved_ix = resolved_ix
|
|
952
|
+
|
|
953
|
+
def _to_concept(self) -> Concept:
|
|
954
|
+
return self._resolved.type
|
|
955
|
+
|
|
956
|
+
def __getattr__(self, item):
|
|
957
|
+
if item.startswith("_"):
|
|
958
|
+
return object.__getattribute__(self, item)
|
|
959
|
+
return Chain(self, self._resolved.type._dot(item))
|
|
960
|
+
|
|
961
|
+
def to_dict_key(self) -> tuple:
|
|
962
|
+
return (self._root.to_dict_key(), self._resolved._id)
|
|
963
|
+
|
|
964
|
+
|
|
965
|
+
#------------------------------------------------------
|
|
966
|
+
# MetaRef
|
|
967
|
+
#------------------------------------------------------
|
|
968
|
+
|
|
969
|
+
class MetaRef(Variable):
|
|
970
|
+
def __init__(self, target: Concept|Relationship|Field):
|
|
971
|
+
model = target._model if isinstance(target, DSLBase) else target.type._model
|
|
972
|
+
super().__init__(model)
|
|
973
|
+
self._target = target
|
|
974
|
+
|
|
975
|
+
#------------------------------------------------------
|
|
976
|
+
# TupleVariable
|
|
977
|
+
#------------------------------------------------------
|
|
978
|
+
|
|
979
|
+
class TupleVariable:
|
|
980
|
+
def __init__(self, items: Sequence[Value|Distinct]):
|
|
981
|
+
self._items = list(items)
|
|
982
|
+
self._source = SourcePos.new()
|
|
983
|
+
|
|
984
|
+
#------------------------------------------------------
|
|
985
|
+
# AsBool
|
|
986
|
+
#------------------------------------------------------
|
|
987
|
+
|
|
988
|
+
class AsBool(Variable):
|
|
989
|
+
def __init__(self, item: Variable):
|
|
990
|
+
super().__init__(item._model)
|
|
991
|
+
self._item = item
|
|
992
|
+
|
|
993
|
+
def _to_concept(self) -> Concept:
|
|
994
|
+
return CoreConcepts["Boolean"]
|
|
995
|
+
|
|
996
|
+
#------------------------------------------------------
|
|
997
|
+
# Alias
|
|
998
|
+
#------------------------------------------------------
|
|
999
|
+
|
|
1000
|
+
class Alias(Variable):
|
|
1001
|
+
def __init__(self, source: Variable, alias: str):
|
|
1002
|
+
super().__init__(source._model)
|
|
1003
|
+
self._source = source
|
|
1004
|
+
self._alias = alias
|
|
1005
|
+
|
|
1006
|
+
def _to_concept(self) -> Concept|None:
|
|
1007
|
+
return self._source._to_concept()
|
|
1008
|
+
|
|
1009
|
+
def __format__(self, format_spec: str) -> str:
|
|
1010
|
+
if isinstance(self._source, Concept):
|
|
1011
|
+
if format_spec:
|
|
1012
|
+
exc("Invalid alias", f"Alias already specifies an alias for this concept, you can remove `:{format_spec}`")
|
|
1013
|
+
return self._source.__format__(self._alias)
|
|
1014
|
+
return super().__format__(format_spec)
|
|
1015
|
+
|
|
1016
|
+
#------------------------------------------------------
|
|
1017
|
+
# Match
|
|
1018
|
+
#------------------------------------------------------
|
|
1019
|
+
|
|
1020
|
+
class Match(DerivedTable):
|
|
1021
|
+
def __init__(self, model:Model, *items: Statement):
|
|
1022
|
+
super().__init__(model)
|
|
1023
|
+
t = type(self)
|
|
1024
|
+
self._items = [
|
|
1025
|
+
x
|
|
1026
|
+
for item in items
|
|
1027
|
+
for x in (item._items if (type(item) is t and isinstance(item, Match)) else (item,))
|
|
1028
|
+
] # flatten nested Matches/Unions
|
|
1029
|
+
|
|
1030
|
+
def _get_cols(self) -> list[DerivedColumn]:
|
|
1031
|
+
return [DerivedColumn(self, i) for i in range(self._arg_count())]
|
|
1032
|
+
|
|
1033
|
+
def _arg_count(self) -> int:
|
|
1034
|
+
counts = []
|
|
1035
|
+
for item in self._items:
|
|
1036
|
+
if isinstance(item, DerivedTable):
|
|
1037
|
+
try:
|
|
1038
|
+
counts.append(len(item._columns))
|
|
1039
|
+
except ValueError:
|
|
1040
|
+
counts.append(0)
|
|
1041
|
+
else:
|
|
1042
|
+
# Expressions with no output and Not are filters, do not count as returning values
|
|
1043
|
+
is_filter = isinstance(item, Not) or (isinstance(item, Expression) and not item._has_output)
|
|
1044
|
+
counts.append(0 if is_filter else 1)
|
|
1045
|
+
if not counts:
|
|
1046
|
+
return 0
|
|
1047
|
+
first = counts[0]
|
|
1048
|
+
if any(c != first for c in counts[1:]):
|
|
1049
|
+
exc("Inconsistent branches",
|
|
1050
|
+
f"All branches in a {self.__class__.__name__} must have the same number of returned values",
|
|
1051
|
+
[source(self)])
|
|
1052
|
+
return first
|
|
1053
|
+
|
|
1054
|
+
def __getattr__(self, item) -> Chain:
|
|
1055
|
+
if item.startswith("_"):
|
|
1056
|
+
return object.__getattribute__(self, item)
|
|
1057
|
+
return getattr(self._columns[-1], item)
|
|
1058
|
+
|
|
1059
|
+
#------------------------------------------------------
|
|
1060
|
+
# Union
|
|
1061
|
+
#------------------------------------------------------
|
|
1062
|
+
|
|
1063
|
+
class Union(Match):
|
|
1064
|
+
def __init__(self, model:Model, *items: Value):
|
|
1065
|
+
super().__init__(model, *items)
|
|
1066
|
+
|
|
1067
|
+
#------------------------------------------------------
|
|
1068
|
+
# Not
|
|
1069
|
+
#------------------------------------------------------
|
|
1070
|
+
|
|
1071
|
+
class Not(DSLBase):
|
|
1072
|
+
def __init__(self, *items: Value, model:Model):
|
|
1073
|
+
super().__init__(model)
|
|
1074
|
+
self._items = items
|
|
1075
|
+
|
|
1076
|
+
def __or__(self, other) -> Match:
|
|
1077
|
+
return Match(self._model, self, other)
|
|
1078
|
+
|
|
1079
|
+
def __and__(self, other) -> Fragment:
|
|
1080
|
+
if isinstance(other, Fragment):
|
|
1081
|
+
new = other.where()
|
|
1082
|
+
new._where.insert(0, self)
|
|
1083
|
+
return new
|
|
1084
|
+
return self._model.where(self, other)
|
|
1085
|
+
|
|
1086
|
+
#------------------------------------------------------
|
|
1087
|
+
# Distinct
|
|
1088
|
+
#------------------------------------------------------
|
|
1089
|
+
|
|
1090
|
+
class Distinct(DSLBase):
|
|
1091
|
+
def __init__(self, *items: Value, model:Model):
|
|
1092
|
+
super().__init__(model)
|
|
1093
|
+
self._items = items
|
|
1094
|
+
|
|
1095
|
+
#------------------------------------------------------
|
|
1096
|
+
# Aggregate
|
|
1097
|
+
#------------------------------------------------------
|
|
1098
|
+
|
|
1099
|
+
class Group(DSLBase):
|
|
1100
|
+
def __init__(self, *args: Value):
|
|
1101
|
+
model = [arg._model for arg in args if isinstance(arg, Variable)][0] if args else None
|
|
1102
|
+
super().__init__(model) # type: ignore
|
|
1103
|
+
self._args = list(args)
|
|
1104
|
+
|
|
1105
|
+
def _extend(self, args: Sequence[Value]) -> Group:
|
|
1106
|
+
new = Group(*self._args)
|
|
1107
|
+
new._args.extend(args)
|
|
1108
|
+
return new
|
|
1109
|
+
|
|
1110
|
+
def _clone(self):
|
|
1111
|
+
return Group(*self._args)
|
|
1112
|
+
|
|
1113
|
+
class Aggregate(Variable):
|
|
1114
|
+
def __init__(self, op: Relationship, *args: Value|Distinct, check_args: bool = True, distinct: bool = False):
|
|
1115
|
+
model = self._find_model(args) or op._model
|
|
1116
|
+
super().__init__(model)
|
|
1117
|
+
self._op = op
|
|
1118
|
+
self._where = Fragment(model)
|
|
1119
|
+
self._group = Group()
|
|
1120
|
+
self._args: list[Value] = []
|
|
1121
|
+
self._projection_args: list[Value] = []
|
|
1122
|
+
self._distinct = distinct
|
|
1123
|
+
if check_args:
|
|
1124
|
+
# unwrap distinct if present
|
|
1125
|
+
if any(isinstance(arg, Distinct) for arg in args):
|
|
1126
|
+
if len(args) != 1:
|
|
1127
|
+
exc("Invalid distinct", "Distinct must be applied to all arguments", [source(self)])
|
|
1128
|
+
assert isinstance(args[0], Distinct)
|
|
1129
|
+
args = args[0]._items
|
|
1130
|
+
self._distinct = True
|
|
1131
|
+
|
|
1132
|
+
args = cast(tuple[Value], args)
|
|
1133
|
+
|
|
1134
|
+
num_inputs = sum(f.is_input for f in op._fields)
|
|
1135
|
+
if len(args) < num_inputs:
|
|
1136
|
+
need = [f.name for f in op._fields if f.is_input][len(args):]
|
|
1137
|
+
exc("Missing argument",
|
|
1138
|
+
f"`{op._short_name or 'Relationship'}(..)` is missing: {', '.join(need)}",
|
|
1139
|
+
[source(self)])
|
|
1140
|
+
|
|
1141
|
+
self._projection_args = list(args[:-num_inputs] if num_inputs else args)
|
|
1142
|
+
supplied = iter(args[-num_inputs:] if num_inputs else [])
|
|
1143
|
+
|
|
1144
|
+
self._args = [
|
|
1145
|
+
(next(supplied) if f.is_input else f.type.ref(f.name))
|
|
1146
|
+
for f in op._fields
|
|
1147
|
+
]
|
|
1148
|
+
|
|
1149
|
+
def _find_model(self, args: Sequence[Value|Distinct|TupleVariable]) -> Model|None:
|
|
1150
|
+
for arg in args:
|
|
1151
|
+
if isinstance(arg, (Variable, Distinct)):
|
|
1152
|
+
return arg._model
|
|
1153
|
+
elif isinstance(arg, TupleVariable):
|
|
1154
|
+
for item in arg._items:
|
|
1155
|
+
if isinstance(item, (Variable, Distinct)):
|
|
1156
|
+
return item._model
|
|
1157
|
+
return None
|
|
1158
|
+
|
|
1159
|
+
|
|
1160
|
+
def where(self, *args: Value) -> Aggregate:
|
|
1161
|
+
new = self._clone()
|
|
1162
|
+
new._where = new._where.where(*args)
|
|
1163
|
+
return new
|
|
1164
|
+
|
|
1165
|
+
def per(self, *args: Value) -> Aggregate:
|
|
1166
|
+
new = self._clone()
|
|
1167
|
+
new._group = new._group._extend(args)
|
|
1168
|
+
return new
|
|
1169
|
+
|
|
1170
|
+
def _clone(self):
|
|
1171
|
+
agg = Aggregate(self._op, check_args=False)
|
|
1172
|
+
agg._args = self._args
|
|
1173
|
+
agg._projection_args = self._projection_args
|
|
1174
|
+
agg._where = self._where
|
|
1175
|
+
agg._group = self._group
|
|
1176
|
+
agg._distinct = self._distinct
|
|
1177
|
+
return agg
|
|
1178
|
+
|
|
1179
|
+
#------------------------------------------------------
|
|
1180
|
+
# Data
|
|
1181
|
+
#------------------------------------------------------
|
|
1182
|
+
|
|
1183
|
+
class Data(Table):
|
|
1184
|
+
def __init__(self, df:DataFrame, model:Model):
|
|
1185
|
+
schema = {}
|
|
1186
|
+
for col in df.columns:
|
|
1187
|
+
_type = df[col].dtype
|
|
1188
|
+
if pd.api.types.is_datetime64_any_dtype(_type):
|
|
1189
|
+
col_type = "DateTime"
|
|
1190
|
+
elif pd.api.types.is_object_dtype(_type) and self._is_date_column(df[col]):
|
|
1191
|
+
col_type = "Date"
|
|
1192
|
+
else:
|
|
1193
|
+
col_type = pytype_to_concept_name.get(_type, "Any")
|
|
1194
|
+
if isinstance(col, int):
|
|
1195
|
+
col = f"col{col}"
|
|
1196
|
+
schema[col] = CoreConcepts[col_type]
|
|
1197
|
+
super().__init__("Data", schema, model)
|
|
1198
|
+
self._data = df
|
|
1199
|
+
|
|
1200
|
+
def _is_date_column(self, col) -> bool:
|
|
1201
|
+
sample = col.dropna()
|
|
1202
|
+
if sample.empty:
|
|
1203
|
+
return False
|
|
1204
|
+
sample_value = sample.iloc[0]
|
|
1205
|
+
return isinstance(sample_value, dt.date) and not isinstance(sample_value, dt.datetime)
|
|
1206
|
+
|
|
1207
|
+
@staticmethod
|
|
1208
|
+
def raw_to_df(data: DataFrame | list[tuple] | list[dict], columns:list[str]|None) -> DataFrame:
|
|
1209
|
+
if isinstance(data, DataFrame):
|
|
1210
|
+
return data
|
|
1211
|
+
if not data:
|
|
1212
|
+
return DataFrame()
|
|
1213
|
+
if isinstance(data, list):
|
|
1214
|
+
if isinstance(data[0], tuple):
|
|
1215
|
+
# Named tuple check
|
|
1216
|
+
if hasattr(data[0], '_fields'):
|
|
1217
|
+
return DataFrame([t._asdict() for t in data]) #type: ignore
|
|
1218
|
+
return DataFrame(data, columns=columns)
|
|
1219
|
+
elif isinstance(data[0], dict):
|
|
1220
|
+
return DataFrame(data)
|
|
1221
|
+
raise TypeError(f"Cannot convert {type(data)} to DataFrame. Use DataFrame, list of tuples, or list of dicts.")
|
|
1222
|
+
|
|
1223
|
+
#------------------------------------------------------
|
|
1224
|
+
# Enum
|
|
1225
|
+
#------------------------------------------------------
|
|
1226
|
+
|
|
1227
|
+
class ModelEnumMeta(EnumMeta):
|
|
1228
|
+
_concept: Concept
|
|
1229
|
+
def __setattr__(self, name: str, value: Any) -> None:
|
|
1230
|
+
if name.startswith("_") or isinstance(value, self):
|
|
1231
|
+
super().__setattr__(name, value)
|
|
1232
|
+
elif isinstance(value, (Relationship, Reading)):
|
|
1233
|
+
setattr(self._concept, name, value)
|
|
1234
|
+
else:
|
|
1235
|
+
raise AttributeError(f"Cannot set attribute {name} on {type(self).__name__}")
|
|
1236
|
+
|
|
1237
|
+
def __format__(self, format_spec: str) -> str:
|
|
1238
|
+
return format(self._concept, format_spec)
|
|
1239
|
+
|
|
1240
|
+
class ModelEnum(Enum, metaclass=ModelEnumMeta):
|
|
1241
|
+
_model:Model
|
|
1242
|
+
|
|
1243
|
+
def __init__(self, *args) -> None:
|
|
1244
|
+
super().__init__(*args)
|
|
1245
|
+
self._source = SourcePos.new()
|
|
1246
|
+
|
|
1247
|
+
def _compile_lookup(self):
|
|
1248
|
+
concept = getattr(self.__class__, "_concept")
|
|
1249
|
+
return concept.to_identity(name=self.name)
|
|
1250
|
+
|
|
1251
|
+
@classmethod
|
|
1252
|
+
def lookup(cls, value:Variable|str):
|
|
1253
|
+
concept = cls._concept
|
|
1254
|
+
return concept.to_identity(name=value)
|
|
1255
|
+
|
|
1256
|
+
# Python 3.10 doesn't correctly populate __members__ by the time it calls
|
|
1257
|
+
# __init_subclass__, so we need to initialize the members lazily when we
|
|
1258
|
+
# encounter the enum for the first time.
|
|
1259
|
+
@classmethod
|
|
1260
|
+
def _init_members(cls):
|
|
1261
|
+
if cls._has_inited_members:
|
|
1262
|
+
return
|
|
1263
|
+
c = cls._concept
|
|
1264
|
+
# Add the name and value attributes to the hashes we create for the enum
|
|
1265
|
+
members = [
|
|
1266
|
+
c.new(name=name, value=value.value)
|
|
1267
|
+
for name, value in cls.__members__.items()
|
|
1268
|
+
]
|
|
1269
|
+
cls._model.define(*members)
|
|
1270
|
+
cls._has_inited_members = True
|
|
1271
|
+
|
|
1272
|
+
def __format__(self, format_spec: str) -> str:
|
|
1273
|
+
return format(self._concept, format_spec)
|
|
1274
|
+
|
|
1275
|
+
def create_enum_class(model: Model):
|
|
1276
|
+
class AttachedModelEnum(ModelEnum):
|
|
1277
|
+
def __init_subclass__(cls, **kwargs):
|
|
1278
|
+
super().__init_subclass__(**kwargs)
|
|
1279
|
+
# this is voodoo black magic that is doing meta meta programming where
|
|
1280
|
+
# we are plugging into anytime a new subtype of this class is created
|
|
1281
|
+
# and then creating a concept to represent the enum. This happens both
|
|
1282
|
+
# when you do `class Foo(Enum)` and when you do `Enum("Foo", [a, b, c])`
|
|
1283
|
+
cls._model = model
|
|
1284
|
+
c = model.Concept(
|
|
1285
|
+
cls.__name__,
|
|
1286
|
+
extends=[CoreConcepts["Enum"]],
|
|
1287
|
+
identify_by={"name": CoreConcepts["String"]}
|
|
1288
|
+
)
|
|
1289
|
+
model.enums.append(cls)
|
|
1290
|
+
model.enums_index[cls.__name__] = cls
|
|
1291
|
+
cls._has_inited_members = False
|
|
1292
|
+
cls._concept = c
|
|
1293
|
+
|
|
1294
|
+
return AttachedModelEnum
|
|
1295
|
+
|
|
1296
|
+
#------------------------------------------------------
|
|
1297
|
+
# Value
|
|
1298
|
+
#------------------------------------------------------
|
|
1299
|
+
|
|
1300
|
+
Value = Variable|Primitive|Field|TupleVariable
|
|
1301
|
+
|
|
1302
|
+
#------------------------------------------------------
|
|
1303
|
+
# Statement
|
|
1304
|
+
#------------------------------------------------------
|
|
1305
|
+
|
|
1306
|
+
Statement = Value | Group | Not | Distinct | Aggregate
|
|
1307
|
+
"""Union of statement types."""
|
|
1308
|
+
|
|
1309
|
+
StatementAndSchema = Statement | TableSchema
|
|
1310
|
+
|
|
1311
|
+
#------------------------------------------------------
|
|
1312
|
+
# Fragment
|
|
1313
|
+
#------------------------------------------------------
|
|
1314
|
+
|
|
1315
|
+
@include_in_docs
|
|
1316
|
+
class Fragment(DerivedTable):
|
|
1317
|
+
"""Composable chunk of a query with select/where/define state.
|
|
1318
|
+
|
|
1319
|
+
Parameters
|
|
1320
|
+
----------
|
|
1321
|
+
model : Model
|
|
1322
|
+
The semantic model that provides type information and stores any
|
|
1323
|
+
definitions produced by this fragment.
|
|
1324
|
+
parent : Fragment, optional
|
|
1325
|
+
An existing fragment to inherit selection and filter state from when
|
|
1326
|
+
building chained queries.
|
|
1327
|
+
"""
|
|
1328
|
+
|
|
1329
|
+
def __init__(self, model:Model, parent:Fragment|None=None):
|
|
1330
|
+
super().__init__(model)
|
|
1331
|
+
self._id = next(_global_id)
|
|
1332
|
+
self._select = []
|
|
1333
|
+
self._where = []
|
|
1334
|
+
self._require = []
|
|
1335
|
+
self._define = []
|
|
1336
|
+
self._order_by = []
|
|
1337
|
+
self._limit = 0
|
|
1338
|
+
self._model = model
|
|
1339
|
+
self._into:Optional[Table] = None
|
|
1340
|
+
self._is_into_update = False
|
|
1341
|
+
self._has_executed = False
|
|
1342
|
+
assert self._model, "Fragment must have a model"
|
|
1343
|
+
|
|
1344
|
+
self._parent = parent
|
|
1345
|
+
# self._source = runtime_env.get_source_pos()
|
|
1346
|
+
self._meta = {}
|
|
1347
|
+
self._annotations = []
|
|
1348
|
+
if parent is not None:
|
|
1349
|
+
self._select.extend(parent._select)
|
|
1350
|
+
self._where.extend(parent._where)
|
|
1351
|
+
self._require.extend(parent._require)
|
|
1352
|
+
self._define.extend(parent._define)
|
|
1353
|
+
self._order_by.extend(parent._order_by)
|
|
1354
|
+
self._limit = parent._limit
|
|
1355
|
+
|
|
1356
|
+
|
|
1357
|
+
def _add_items(self, items:Sequence[Statement], to_attr:list[Statement]):
|
|
1358
|
+
# TODO: ensure that you are _either_ a select, require, or then
|
|
1359
|
+
# not a mix of them
|
|
1360
|
+
model = self._model
|
|
1361
|
+
|
|
1362
|
+
# remove any existing rules that this consumes
|
|
1363
|
+
for item in itertools.chain(items, [self._parent]):
|
|
1364
|
+
if isinstance(item, Fragment) and item._is_effect():
|
|
1365
|
+
model._remove_rule(item)
|
|
1366
|
+
|
|
1367
|
+
to_attr.extend(items)
|
|
1368
|
+
if self._is_effect():
|
|
1369
|
+
model._add_rule(self)
|
|
1370
|
+
|
|
1371
|
+
return self
|
|
1372
|
+
|
|
1373
|
+
#------------------------------------------------------
|
|
1374
|
+
# Select arg handling
|
|
1375
|
+
#------------------------------------------------------
|
|
1376
|
+
|
|
1377
|
+
def _check_select_args(self, args:Sequence[StatementAndSchema]) -> Sequence[Statement]:
|
|
1378
|
+
clean_args = []
|
|
1379
|
+
for arg in args:
|
|
1380
|
+
# If you select x > y, treat that as AsBool(x > y)
|
|
1381
|
+
if isinstance(arg, Expression) and not arg._has_output:
|
|
1382
|
+
clean_args.append(AsBool(arg))
|
|
1383
|
+
elif isinstance(arg, TableSchema):
|
|
1384
|
+
for col in arg.get_columns():
|
|
1385
|
+
clean_args.append(col(arg._table))
|
|
1386
|
+
else:
|
|
1387
|
+
clean_args.append(arg)
|
|
1388
|
+
return clean_args
|
|
1389
|
+
|
|
1390
|
+
#------------------------------------------------------
|
|
1391
|
+
# Core API
|
|
1392
|
+
#------------------------------------------------------
|
|
1393
|
+
|
|
1394
|
+
def where(self, *args: Statement) -> Fragment:
|
|
1395
|
+
f = Fragment(self._model, parent=self)
|
|
1396
|
+
return f._add_items(args, f._where)
|
|
1397
|
+
|
|
1398
|
+
def select(self, *args: StatementAndSchema) -> Fragment:
|
|
1399
|
+
f = Fragment(self._model, parent=self)
|
|
1400
|
+
return f._add_items(self._check_select_args(args), f._select)
|
|
1401
|
+
|
|
1402
|
+
def require(self, *args: Statement) -> Fragment:
|
|
1403
|
+
f = Fragment(self._model, parent=self)
|
|
1404
|
+
return f._add_items(args, f._require)
|
|
1405
|
+
|
|
1406
|
+
def define(self, *args: Statement) -> Fragment:
|
|
1407
|
+
f = Fragment(self._model, parent=self)
|
|
1408
|
+
return f._add_items(args, f._define)
|
|
1409
|
+
|
|
1410
|
+
if not TYPE_CHECKING:
|
|
1411
|
+
def order_by(self, *args: Any) -> Fragment:
|
|
1412
|
+
exc("Feature unavailable", "The 'order_by' method is not yet available when querying RAI directly.", [
|
|
1413
|
+
source(self),
|
|
1414
|
+
"You can use [cyan]`relationalai.semantics.std.aggregates.rank`[/cyan] and select it as the first column as a temporary substitute.",
|
|
1415
|
+
])
|
|
1416
|
+
f = Fragment(self._model, parent=self)
|
|
1417
|
+
return f._add_items(args, f._order_by)
|
|
1418
|
+
|
|
1419
|
+
def limit(self, n:int) -> Fragment:
|
|
1420
|
+
exc("Feature unavailable", "The 'order_by' method is not yet available when querying RAI directly.", [
|
|
1421
|
+
source(self),
|
|
1422
|
+
"You can use [cyan]`relationalai.semantics.std.aggregates.limit`[/cyan] in a where clause as a temporary substitute.",
|
|
1423
|
+
])
|
|
1424
|
+
f = Fragment(self._model, parent=self)
|
|
1425
|
+
f._limit = n
|
|
1426
|
+
return f
|
|
1427
|
+
|
|
1428
|
+
# def meta(self, **kwargs: Any) -> Fragment:
|
|
1429
|
+
# self._meta.update(kwargs)
|
|
1430
|
+
# return self
|
|
1431
|
+
|
|
1432
|
+
def annotate(self, *annos:Expression|Relationship) -> Fragment:
|
|
1433
|
+
self._annotations.extend(annos)
|
|
1434
|
+
return self
|
|
1435
|
+
|
|
1436
|
+
#------------------------------------------------------
|
|
1437
|
+
# into
|
|
1438
|
+
#------------------------------------------------------
|
|
1439
|
+
|
|
1440
|
+
def into(self, table: Table, update=False) -> Fragment:
|
|
1441
|
+
f = Fragment(self._model, parent=self)
|
|
1442
|
+
f._into = table
|
|
1443
|
+
f._is_into_update = update
|
|
1444
|
+
self._model.exports.add(f)
|
|
1445
|
+
return f
|
|
1446
|
+
|
|
1447
|
+
#------------------------------------------------------
|
|
1448
|
+
# Execution
|
|
1449
|
+
#------------------------------------------------------
|
|
1450
|
+
|
|
1451
|
+
def exec(self):
|
|
1452
|
+
if self._has_executed:
|
|
1453
|
+
return
|
|
1454
|
+
if self._into is None:
|
|
1455
|
+
exc("Cannot execute", "Query must have an 'into' table specified to execute.", [source(self)])
|
|
1456
|
+
from relationalai.shims.executor import execute
|
|
1457
|
+
self._has_executed = True
|
|
1458
|
+
return execute(self, self._model, export_to=self._into._name, update=self._is_into_update)
|
|
1459
|
+
|
|
1460
|
+
def to_df(self):
|
|
1461
|
+
from relationalai.shims.executor import execute
|
|
1462
|
+
return execute(self, self._model)
|
|
1463
|
+
|
|
1464
|
+
def inspect(self):
|
|
1465
|
+
print(self.to_df())
|
|
1466
|
+
|
|
1467
|
+
#------------------------------------------------------
|
|
1468
|
+
# helpers
|
|
1469
|
+
#------------------------------------------------------
|
|
1470
|
+
|
|
1471
|
+
def _is_effect(self) -> bool:
|
|
1472
|
+
return bool(self._define or self._require)
|
|
1473
|
+
|
|
1474
|
+
def _is_where_only(self) -> bool:
|
|
1475
|
+
return not self._select and not self._define and not self._require and not self._order_by
|
|
1476
|
+
|
|
1477
|
+
#------------------------------------------------------
|
|
1478
|
+
# And/Or
|
|
1479
|
+
#------------------------------------------------------
|
|
1480
|
+
|
|
1481
|
+
def __or__(self, other) -> Match:
|
|
1482
|
+
return Match(self._model, self, other)
|
|
1483
|
+
|
|
1484
|
+
def __and__(self, other) -> Fragment:
|
|
1485
|
+
if not isinstance(other, Fragment):
|
|
1486
|
+
other = Fragment(self._model).where(other)
|
|
1487
|
+
if self._is_where_only() and other._is_where_only():
|
|
1488
|
+
return self.where(*other._where)
|
|
1489
|
+
elif self._is_where_only():
|
|
1490
|
+
return other.where(*self._where)
|
|
1491
|
+
elif other._is_where_only():
|
|
1492
|
+
return self.where(*other._where)
|
|
1493
|
+
else:
|
|
1494
|
+
raise Exception("Cannot AND two non-where-only fragments")
|
|
1495
|
+
|
|
1496
|
+
#------------------------------------------------------
|
|
1497
|
+
# DerivedTable cols
|
|
1498
|
+
#------------------------------------------------------
|
|
1499
|
+
|
|
1500
|
+
def _get_cols(self):
|
|
1501
|
+
return [DerivedColumn.from_value(self, i, col) for i, col in enumerate(self._select)]
|
|
1502
|
+
|
|
1503
|
+
#------------------------------------------------------
|
|
1504
|
+
# Marterialize
|
|
1505
|
+
#------------------------------------------------------
|
|
1506
|
+
|
|
1507
|
+
def to_metamodel(self):
|
|
1508
|
+
m = self._model
|
|
1509
|
+
return m._compiler.compile(self)
|
|
1510
|
+
|
|
1511
|
+
#------------------------------------------------------
|
|
1512
|
+
# Model
|
|
1513
|
+
#------------------------------------------------------
|
|
1514
|
+
|
|
1515
|
+
@include_in_docs
|
|
1516
|
+
class Model:
|
|
1517
|
+
"""Class representing a semantic model.
|
|
1518
|
+
|
|
1519
|
+
Parameters
|
|
1520
|
+
----------
|
|
1521
|
+
name : str, optional
|
|
1522
|
+
Name of the model.
|
|
1523
|
+
exclude_core : bool, optional
|
|
1524
|
+
If True, the core library will not be included by default.
|
|
1525
|
+
is_library : bool, optional
|
|
1526
|
+
If True, this model is a library and will not be added to the global
|
|
1527
|
+
list of models.
|
|
1528
|
+
|
|
1529
|
+
Examples
|
|
1530
|
+
--------
|
|
1531
|
+
Create a model object:
|
|
1532
|
+
|
|
1533
|
+
>>> from relationalai.semantics import Model
|
|
1534
|
+
>>> model = Model(name="MyModel")
|
|
1535
|
+
"""
|
|
1536
|
+
all_models:list[Model] = []
|
|
1537
|
+
|
|
1538
|
+
def __init__(self, name: str = "", exclude_core: bool = False, is_library: bool = False):
|
|
1539
|
+
self.name = name
|
|
1540
|
+
self.defines:KeyedSet[Fragment] = KeyedSet(dsl_key)
|
|
1541
|
+
self.requires:KeyedSet[Fragment] = KeyedSet(dsl_key)
|
|
1542
|
+
self.exports:KeyedSet[Fragment] = KeyedSet(dsl_key)
|
|
1543
|
+
self.libraries = []
|
|
1544
|
+
self.concepts: list[Concept] = []
|
|
1545
|
+
self.tables: list[Table] = []
|
|
1546
|
+
self.relationships: list[Relationship] = []
|
|
1547
|
+
self.enums: list[Type[ModelEnum]] = []
|
|
1548
|
+
self.concept_index: dict[str, Concept] = {}
|
|
1549
|
+
self.table_index: dict[str, Table] = {}
|
|
1550
|
+
self.relationship_index: dict[str, Relationship] = {}
|
|
1551
|
+
self.enums_index: dict[str, Type[ModelEnum]] = {}
|
|
1552
|
+
self._source = SourcePos.new()
|
|
1553
|
+
|
|
1554
|
+
self.Enum = create_enum_class(self)
|
|
1555
|
+
|
|
1556
|
+
if not is_library:
|
|
1557
|
+
Model.all_models.append(self)
|
|
1558
|
+
|
|
1559
|
+
self.__compiler = None
|
|
1560
|
+
if not exclude_core:
|
|
1561
|
+
self.libraries.append(CoreLibrary)
|
|
1562
|
+
|
|
1563
|
+
#------------------------------------------------------
|
|
1564
|
+
# Internal
|
|
1565
|
+
#------------------------------------------------------
|
|
1566
|
+
|
|
1567
|
+
@property
|
|
1568
|
+
def _compiler(self):
|
|
1569
|
+
if not self.__compiler:
|
|
1570
|
+
from .front_compiler import FrontCompiler
|
|
1571
|
+
self.__compiler = FrontCompiler(self)
|
|
1572
|
+
return self.__compiler
|
|
1573
|
+
|
|
1574
|
+
def _find_concept(self, name: str) -> Concept|None:
|
|
1575
|
+
if name in self.concept_index:
|
|
1576
|
+
return self.concept_index[name]
|
|
1577
|
+
if name in self.table_index:
|
|
1578
|
+
return self.table_index[name]
|
|
1579
|
+
for lib in self.libraries:
|
|
1580
|
+
found = lib._find_concept(name)
|
|
1581
|
+
if found is not None:
|
|
1582
|
+
return found
|
|
1583
|
+
|
|
1584
|
+
def _remove_rule(self, fragment: Fragment) -> None:
|
|
1585
|
+
if fragment._define and fragment in self.defines:
|
|
1586
|
+
self.defines.remove(fragment)
|
|
1587
|
+
elif fragment._require and fragment in self.requires:
|
|
1588
|
+
self.requires.remove(fragment)
|
|
1589
|
+
else:
|
|
1590
|
+
raise ValueError("Fragment must have either define or require clauses to be removed as a rule")
|
|
1591
|
+
|
|
1592
|
+
def _add_rule(self, fragment: Fragment) -> None:
|
|
1593
|
+
if fragment._define and fragment not in self.defines:
|
|
1594
|
+
self.defines.add(fragment)
|
|
1595
|
+
elif fragment._require and fragment not in self.requires:
|
|
1596
|
+
self.requires.add(fragment)
|
|
1597
|
+
else:
|
|
1598
|
+
raise ValueError("Fragment must have either define or require clauses to be added as a rule")
|
|
1599
|
+
|
|
1600
|
+
#------------------------------------------------------
|
|
1601
|
+
# Primary API
|
|
1602
|
+
#------------------------------------------------------
|
|
1603
|
+
|
|
1604
|
+
def Concept(self, name: str, extends: list[Concept] = [], identify_by: dict[str, Property|Concept] = {}) -> Concept:
|
|
1605
|
+
c = Concept(name, extends=extends, identify_by=identify_by, model=self)
|
|
1606
|
+
self.concepts.append(c)
|
|
1607
|
+
self.concept_index[name] = c
|
|
1608
|
+
return c
|
|
1609
|
+
|
|
1610
|
+
def Table(self, path: str, schema: dict[str, Concept] = {}) -> Table:
|
|
1611
|
+
t = Table(path, schema=schema, model=self)
|
|
1612
|
+
self.tables.append(t)
|
|
1613
|
+
self.table_index[path] = t
|
|
1614
|
+
return t
|
|
1615
|
+
|
|
1616
|
+
def Relationship(self, reading: str = "", fields: list[Field] = [], short_name: str = "") -> Relationship:
|
|
1617
|
+
r = Relationship(self, reading, fields, short_name)
|
|
1618
|
+
self.relationships.append(r)
|
|
1619
|
+
self.relationship_index[short_name] = r
|
|
1620
|
+
return r
|
|
1621
|
+
|
|
1622
|
+
def Property(self, reading: str = "", fields: list[Field] = [], short_name: str = "") -> Property:
|
|
1623
|
+
p = Property(self, reading, fields, short_name)
|
|
1624
|
+
self.relationships.append(p)
|
|
1625
|
+
self.relationship_index[short_name] = p
|
|
1626
|
+
return p
|
|
1627
|
+
|
|
1628
|
+
def select(self, *args: StatementAndSchema) -> Fragment:
|
|
1629
|
+
f = Fragment(self)
|
|
1630
|
+
return f.select(*args)
|
|
1631
|
+
|
|
1632
|
+
def where(self, *args: Statement) -> Fragment:
|
|
1633
|
+
f = Fragment(self)
|
|
1634
|
+
return f.where(*args)
|
|
1635
|
+
|
|
1636
|
+
def require(self, *args: Statement) -> Fragment:
|
|
1637
|
+
f = Fragment(self)
|
|
1638
|
+
return f.require(*args)
|
|
1639
|
+
|
|
1640
|
+
def define(self, *args: Statement) -> Fragment:
|
|
1641
|
+
f = Fragment(self)
|
|
1642
|
+
return f.define(*args)
|
|
1643
|
+
|
|
1644
|
+
def union(self, *items: Value) -> Union:
|
|
1645
|
+
return Union(self, *items)
|
|
1646
|
+
|
|
1647
|
+
def data(self, data: DataFrame | list[tuple] | list[dict], columns: list[str]|None = None) -> Data:
|
|
1648
|
+
df = Data.raw_to_df(data, columns)
|
|
1649
|
+
return Data(df, self)
|
|
1650
|
+
|
|
1651
|
+
def not_(self, *items: Value) -> Not:
|
|
1652
|
+
return Not(*items, model=self)
|
|
1653
|
+
|
|
1654
|
+
def distinct(self, *items: Value) -> Distinct:
|
|
1655
|
+
return Distinct(*items, model=self)
|
|
1656
|
+
|
|
1657
|
+
#------------------------------------------------------
|
|
1658
|
+
# Meta
|
|
1659
|
+
#------------------------------------------------------
|
|
1660
|
+
|
|
1661
|
+
def to_metamodel(self) -> mModel:
|
|
1662
|
+
return self._compiler.compile_model(self)
|
|
1663
|
+
|
|
1664
|
+
#------------------------------------------------------
|
|
1665
|
+
# Library
|
|
1666
|
+
#------------------------------------------------------
|
|
1667
|
+
|
|
1668
|
+
class Library(Model):
|
|
1669
|
+
def __init__(self, name: str, exclude_core: bool = False):
|
|
1670
|
+
super().__init__(name=name, exclude_core=exclude_core, is_library=True)
|
|
1671
|
+
|
|
1672
|
+
def Type(self, name: str, super_types: list[Concept] = []) -> Concept:
|
|
1673
|
+
c = self.Concept(name, extends=super_types)
|
|
1674
|
+
self._register_builtin(c)
|
|
1675
|
+
return c
|
|
1676
|
+
|
|
1677
|
+
def Relation(self, name: str, fields: list[Field], overloads: list[list[Concept]]|None = None, annotations: list[Any]|None = None) -> Relationship:
|
|
1678
|
+
r = Relationship(self, "", fields, name, allow_no_fields=True, overloads=overloads)
|
|
1679
|
+
self.relationships.append(r)
|
|
1680
|
+
self.relationship_index[name] = r
|
|
1681
|
+
self._register_builtin(r)
|
|
1682
|
+
# TODO - deal with annotations
|
|
1683
|
+
return r
|
|
1684
|
+
|
|
1685
|
+
def _register_builtin(self, builtin: Concept|Relationship) -> None:
|
|
1686
|
+
""" Register a builtin concept or relationship in the global builtins registry. """
|
|
1687
|
+
builtins.register(self.name, self._compiler.to_relation(builtin))
|
|
1688
|
+
|
|
1689
|
+
|
|
1690
|
+
#------------------------------------------------------
|
|
1691
|
+
# Core Library
|
|
1692
|
+
#------------------------------------------------------
|
|
1693
|
+
|
|
1694
|
+
# Library is populated when relationalai.semantics.frontend.core is imported
|
|
1695
|
+
CoreLibrary: Library = Library("core", exclude_core=True)
|
|
1696
|
+
CoreRelationships = CoreLibrary.relationship_index
|
|
1697
|
+
CoreConcepts = CoreLibrary.concept_index
|
|
1698
|
+
|
|
1699
|
+
|
|
1700
|
+
#------------------------------------------------------
|
|
1701
|
+
# todo
|
|
1702
|
+
#------------------------------------------------------
|
|
1703
|
+
|
|
1704
|
+
"""
|
|
1705
|
+
x new/to_identity/filter_by
|
|
1706
|
+
x capture all defines/requires
|
|
1707
|
+
x identify_by
|
|
1708
|
+
x figure out Table/DerivedTable
|
|
1709
|
+
x data node
|
|
1710
|
+
x in_
|
|
1711
|
+
x error on invalid iteration/sum/min/max
|
|
1712
|
+
x Aggregates
|
|
1713
|
+
x fill in std.agg functions when Thiago's lib stuff lands
|
|
1714
|
+
- Error?
|
|
1715
|
+
x rank stuff
|
|
1716
|
+
- validation checks for identify_by
|
|
1717
|
+
- unique
|
|
1718
|
+
- Enum?
|
|
1719
|
+
"""
|