datahike-browser-tests 1.0.0
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.
- package/.circleci/config.yml +405 -0
- package/.circleci/scripts/gen_ci.clj +194 -0
- package/.cirrus.yml +60 -0
- package/.clj-kondo/babashka/sci/config.edn +1 -0
- package/.clj-kondo/babashka/sci/sci/core.clj +9 -0
- package/.clj-kondo/config.edn +95 -0
- package/.dir-locals.el +2 -0
- package/.github/FUNDING.yml +3 -0
- package/.github/ISSUE_TEMPLATE/1-bug-report.yml +68 -0
- package/.github/ISSUE_TEMPLATE/2-feature-request.yml +28 -0
- package/.github/ISSUE_TEMPLATE/config.yml +6 -0
- package/.github/pull_request_template.md +24 -0
- package/.github/workflows/native-image.yml +84 -0
- package/LICENSE +203 -0
- package/README.md +273 -0
- package/bb/deps.edn +9 -0
- package/bb/resources/github-fingerprints +3 -0
- package/bb/resources/native-image-tests/run-bb-pod-tests.clj +162 -0
- package/bb/resources/native-image-tests/run-libdatahike-tests +12 -0
- package/bb/resources/native-image-tests/run-native-image-tests +74 -0
- package/bb/resources/native-image-tests/run-python-tests +22 -0
- package/bb/resources/native-image-tests/testconfig.attr-refs.edn +6 -0
- package/bb/resources/native-image-tests/testconfig.edn +5 -0
- package/bb/resources/template/.settings/org.eclipse.jdt.apt.core.prefs +2 -0
- package/bb/resources/template/.settings/org.eclipse.jdt.core.prefs +9 -0
- package/bb/resources/template/.settings/org.eclipse.m2e.core.prefs +4 -0
- package/bb/resources/template/pom.xml +22 -0
- package/bb/src/tools/build.clj +132 -0
- package/bb/src/tools/clj_kondo.clj +32 -0
- package/bb/src/tools/deploy.clj +26 -0
- package/bb/src/tools/examples.clj +19 -0
- package/bb/src/tools/npm.clj +100 -0
- package/bb/src/tools/python.clj +14 -0
- package/bb/src/tools/release.clj +94 -0
- package/bb/src/tools/test.clj +148 -0
- package/bb/src/tools/version.clj +47 -0
- package/bb.edn +269 -0
- package/benchmark/src/benchmark/cli.clj +195 -0
- package/benchmark/src/benchmark/compare.clj +157 -0
- package/benchmark/src/benchmark/config.clj +316 -0
- package/benchmark/src/benchmark/measure.clj +187 -0
- package/benchmark/src/benchmark/store.clj +190 -0
- package/benchmark/test/benchmark/measure_test.clj +156 -0
- package/build.clj +30 -0
- package/config.edn +49 -0
- package/deps.edn +138 -0
- package/dev/sandbox.clj +82 -0
- package/dev/sandbox.cljs +127 -0
- package/dev/sandbox_benchmarks.clj +27 -0
- package/dev/sandbox_client.clj +87 -0
- package/dev/sandbox_transact_bench.clj +109 -0
- package/dev/user.clj +79 -0
- package/doc/README.md +96 -0
- package/doc/adl/README.md +6 -0
- package/doc/adl/adr-000-adr.org +28 -0
- package/doc/adl/adr-001-attribute-references.org +15 -0
- package/doc/adl/adr-002-build-tooling.org +54 -0
- package/doc/adl/adr-003-db-meta-data.md +52 -0
- package/doc/adl/adr-004-github-flow.md +40 -0
- package/doc/adl/adr-XYZ-template.md +30 -0
- package/doc/adl/index.org +3 -0
- package/doc/assets/datahike-logo.svg +3 -0
- package/doc/assets/datahiking-invoice.org +85 -0
- package/doc/assets/hhtree2.png +0 -0
- package/doc/assets/network_topology.svg +624 -0
- package/doc/assets/perf.png +0 -0
- package/doc/assets/schema_mindmap.mm +132 -0
- package/doc/assets/schema_mindmap.svg +970 -0
- package/doc/assets/temporal_index.mm +74 -0
- package/doc/backend-development.md +78 -0
- package/doc/bb-pod.md +89 -0
- package/doc/benchmarking.md +360 -0
- package/doc/bindings/edn-conversion.md +383 -0
- package/doc/cli.md +162 -0
- package/doc/cljdoc.edn +27 -0
- package/doc/cljs-support.md +133 -0
- package/doc/config.md +406 -0
- package/doc/contributing.md +114 -0
- package/doc/datalog-vs-sql.md +210 -0
- package/doc/datomic_differences.md +109 -0
- package/doc/development/pull-api-ns.md +186 -0
- package/doc/development/pull-frame-state-diagram.jpg +0 -0
- package/doc/distributed.md +566 -0
- package/doc/entity_spec.md +92 -0
- package/doc/gc.md +273 -0
- package/doc/java-api.md +808 -0
- package/doc/javascript-api.md +421 -0
- package/doc/libdatahike.md +86 -0
- package/doc/logging_and_error_handling.md +43 -0
- package/doc/norms.md +66 -0
- package/doc/schema-migration.md +85 -0
- package/doc/schema.md +287 -0
- package/doc/storage-backends.md +363 -0
- package/doc/store-id-refactoring.md +596 -0
- package/doc/time_variance.md +325 -0
- package/doc/unstructured.md +167 -0
- package/doc/versioning.md +261 -0
- package/examples/basic/README.md +19 -0
- package/examples/basic/deps.edn +6 -0
- package/examples/basic/docker-compose.yml +13 -0
- package/examples/basic/src/examples/core.clj +60 -0
- package/examples/basic/src/examples/schema.clj +155 -0
- package/examples/basic/src/examples/store.clj +60 -0
- package/examples/basic/src/examples/time_travel.clj +185 -0
- package/examples/java/.settings/org.eclipse.core.resources.prefs +3 -0
- package/examples/java/.settings/org.eclipse.jdt.apt.core.prefs +2 -0
- package/examples/java/.settings/org.eclipse.jdt.core.prefs +9 -0
- package/examples/java/.settings/org.eclipse.m2e.core.prefs +4 -0
- package/examples/java/README.md +162 -0
- package/examples/java/pom.xml +62 -0
- package/examples/java/src/main/java/examples/QuickStart.java +115 -0
- package/examples/java/src/main/java/examples/SchemaExample.java +148 -0
- package/examples/java/src/main/java/examples/TimeTravelExample.java +121 -0
- package/flake.lock +27 -0
- package/flake.nix +27 -0
- package/http-server/datahike/http/middleware.clj +75 -0
- package/http-server/datahike/http/server.clj +269 -0
- package/java/src/datahike/java/Database.java +274 -0
- package/java/src/datahike/java/Datahike.java +281 -0
- package/java/src/datahike/java/DatahikeGeneratedTest.java +349 -0
- package/java/src/datahike/java/DatahikeTest.java +370 -0
- package/java/src/datahike/java/EDN.java +170 -0
- package/java/src/datahike/java/IEntity.java +11 -0
- package/java/src/datahike/java/Keywords.java +161 -0
- package/java/src/datahike/java/SchemaFlexibility.java +52 -0
- package/java/src/datahike/java/Util.java +219 -0
- package/karma.conf.js +19 -0
- package/libdatahike/compile-cpp +7 -0
- package/libdatahike/src/datahike/impl/LibDatahikeBase.java +203 -0
- package/libdatahike/src/datahike/impl/libdatahike.clj +59 -0
- package/libdatahike/src/test_cpp.cpp +61 -0
- package/npm-package/PUBLISHING.md +140 -0
- package/npm-package/README.md +226 -0
- package/npm-package/package.template.json +34 -0
- package/npm-package/test-isomorphic.ts +281 -0
- package/npm-package/test.js +557 -0
- package/npm-package/typescript-test.ts +70 -0
- package/package.json +16 -0
- package/pydatahike/README.md +569 -0
- package/pydatahike/pyproject.toml +91 -0
- package/pydatahike/setup.py +42 -0
- package/pydatahike/src/datahike/__init__.py +134 -0
- package/pydatahike/src/datahike/_native.py +250 -0
- package/pydatahike/src/datahike/_version.py +2 -0
- package/pydatahike/src/datahike/database.py +722 -0
- package/pydatahike/src/datahike/edn.py +311 -0
- package/pydatahike/src/datahike/py.typed +0 -0
- package/pydatahike/tests/conftest.py +17 -0
- package/pydatahike/tests/test_basic.py +170 -0
- package/pydatahike/tests/test_database.py +51 -0
- package/pydatahike/tests/test_edn_conversion.py +299 -0
- package/pydatahike/tests/test_query.py +99 -0
- package/pydatahike/tests/test_schema.py +55 -0
- package/resources/clj-kondo.exports/io.replikativ/datahike/config.edn +5 -0
- package/resources/example_server.edn +4 -0
- package/shadow-cljs.edn +56 -0
- package/src/data_readers.clj +7 -0
- package/src/datahike/api/impl.cljc +176 -0
- package/src/datahike/api/specification.cljc +633 -0
- package/src/datahike/api/types.cljc +261 -0
- package/src/datahike/api.cljc +41 -0
- package/src/datahike/array.cljc +99 -0
- package/src/datahike/cli.clj +166 -0
- package/src/datahike/cljs.cljs +6 -0
- package/src/datahike/codegen/cli.clj +406 -0
- package/src/datahike/codegen/clj_kondo.clj +291 -0
- package/src/datahike/codegen/java.clj +403 -0
- package/src/datahike/codegen/naming.cljc +33 -0
- package/src/datahike/codegen/native.clj +559 -0
- package/src/datahike/codegen/pod.clj +488 -0
- package/src/datahike/codegen/python.clj +838 -0
- package/src/datahike/codegen/report.clj +55 -0
- package/src/datahike/codegen/typescript.clj +262 -0
- package/src/datahike/codegen/validation.clj +145 -0
- package/src/datahike/config.cljc +294 -0
- package/src/datahike/connections.cljc +16 -0
- package/src/datahike/connector.cljc +265 -0
- package/src/datahike/constants.cljc +142 -0
- package/src/datahike/core.cljc +297 -0
- package/src/datahike/datom.cljc +459 -0
- package/src/datahike/db/interface.cljc +119 -0
- package/src/datahike/db/search.cljc +305 -0
- package/src/datahike/db/transaction.cljc +937 -0
- package/src/datahike/db/utils.cljc +338 -0
- package/src/datahike/db.cljc +956 -0
- package/src/datahike/experimental/unstructured.cljc +126 -0
- package/src/datahike/experimental/versioning.cljc +172 -0
- package/src/datahike/externs.js +31 -0
- package/src/datahike/gc.cljc +69 -0
- package/src/datahike/http/client.clj +188 -0
- package/src/datahike/http/writer.clj +79 -0
- package/src/datahike/impl/entity.cljc +218 -0
- package/src/datahike/index/interface.cljc +93 -0
- package/src/datahike/index/persistent_set.cljc +469 -0
- package/src/datahike/index/utils.cljc +44 -0
- package/src/datahike/index.cljc +32 -0
- package/src/datahike/js/api.cljs +172 -0
- package/src/datahike/js/api_macros.clj +22 -0
- package/src/datahike/js.cljs +163 -0
- package/src/datahike/json.cljc +209 -0
- package/src/datahike/lru.cljc +146 -0
- package/src/datahike/migrate.clj +39 -0
- package/src/datahike/norm/norm.clj +245 -0
- package/src/datahike/online_gc.cljc +252 -0
- package/src/datahike/pod.clj +155 -0
- package/src/datahike/pull_api.cljc +325 -0
- package/src/datahike/query.cljc +1945 -0
- package/src/datahike/query_stats.cljc +88 -0
- package/src/datahike/readers.cljc +62 -0
- package/src/datahike/remote.cljc +218 -0
- package/src/datahike/schema.cljc +228 -0
- package/src/datahike/schema_cache.cljc +42 -0
- package/src/datahike/spec.cljc +101 -0
- package/src/datahike/store.cljc +80 -0
- package/src/datahike/tools.cljc +308 -0
- package/src/datahike/transit.cljc +80 -0
- package/src/datahike/writer.cljc +239 -0
- package/src/datahike/writing.cljc +362 -0
- package/src/deps.cljs +1 -0
- package/src-hitchhiker-tree/datahike/index/hitchhiker_tree/insert.cljc +76 -0
- package/src-hitchhiker-tree/datahike/index/hitchhiker_tree/upsert.cljc +128 -0
- package/src-hitchhiker-tree/datahike/index/hitchhiker_tree.cljc +213 -0
- package/test/datahike/backward_compatibility_test/src/backward_test.clj +37 -0
- package/test/datahike/integration_test/config_record_file_test.clj +14 -0
- package/test/datahike/integration_test/config_record_test.clj +14 -0
- package/test/datahike/integration_test/depr_config_uri_test.clj +15 -0
- package/test/datahike/integration_test/return_map_test.clj +62 -0
- package/test/datahike/integration_test.cljc +67 -0
- package/test/datahike/norm/norm_test.clj +124 -0
- package/test/datahike/norm/resources/naming-and-sorting-test/001-a1-example.edn +5 -0
- package/test/datahike/norm/resources/naming-and-sorting-test/002-a2-example.edn +5 -0
- package/test/datahike/norm/resources/naming-and-sorting-test/003-tx-fn-test.edn +1 -0
- package/test/datahike/norm/resources/naming-and-sorting-test/004-tx-data-and-tx-fn-test.edn +5 -0
- package/test/datahike/norm/resources/naming-and-sorting-test/01-transact-basic-characters.edn +2 -0
- package/test/datahike/norm/resources/naming-and-sorting-test/02 add occupation.edn +5 -0
- package/test/datahike/norm/resources/naming-and-sorting-test/checksums.edn +12 -0
- package/test/datahike/norm/resources/simple-test/001-a1-example.edn +5 -0
- package/test/datahike/norm/resources/simple-test/002-a2-example.edn +5 -0
- package/test/datahike/norm/resources/simple-test/checksums.edn +4 -0
- package/test/datahike/norm/resources/tx-data-and-tx-fn-test/first/001-a1-example.edn +5 -0
- package/test/datahike/norm/resources/tx-data-and-tx-fn-test/first/002-a2-example.edn +5 -0
- package/test/datahike/norm/resources/tx-data-and-tx-fn-test/first/003-tx-fn-test.edn +1 -0
- package/test/datahike/norm/resources/tx-data-and-tx-fn-test/first/checksums.edn +6 -0
- package/test/datahike/norm/resources/tx-data-and-tx-fn-test/second/004-tx-data-and-tx-fn-test.edn +5 -0
- package/test/datahike/norm/resources/tx-data-and-tx-fn-test/second/checksums.edn +2 -0
- package/test/datahike/norm/resources/tx-fn-test/first/001-a1-example.edn +5 -0
- package/test/datahike/norm/resources/tx-fn-test/first/002-a2-example.edn +5 -0
- package/test/datahike/norm/resources/tx-fn-test/first/checksums.edn +4 -0
- package/test/datahike/norm/resources/tx-fn-test/second/003-tx-fn-test.edn +1 -0
- package/test/datahike/norm/resources/tx-fn-test/second/checksums.edn +2 -0
- package/test/datahike/test/api_test.cljc +895 -0
- package/test/datahike/test/array_test.cljc +40 -0
- package/test/datahike/test/attribute_refs/datoms_test.cljc +140 -0
- package/test/datahike/test/attribute_refs/db_test.cljc +42 -0
- package/test/datahike/test/attribute_refs/differences_test.cljc +515 -0
- package/test/datahike/test/attribute_refs/entity_test.cljc +89 -0
- package/test/datahike/test/attribute_refs/pull_api_test.cljc +320 -0
- package/test/datahike/test/attribute_refs/query_find_specs_test.cljc +59 -0
- package/test/datahike/test/attribute_refs/query_fns_test.cljc +130 -0
- package/test/datahike/test/attribute_refs/query_interop_test.cljc +47 -0
- package/test/datahike/test/attribute_refs/query_not_test.cljc +193 -0
- package/test/datahike/test/attribute_refs/query_or_test.cljc +137 -0
- package/test/datahike/test/attribute_refs/query_pull_test.cljc +156 -0
- package/test/datahike/test/attribute_refs/query_rules_test.cljc +176 -0
- package/test/datahike/test/attribute_refs/query_test.cljc +241 -0
- package/test/datahike/test/attribute_refs/temporal_search.cljc +22 -0
- package/test/datahike/test/attribute_refs/transact_test.cljc +220 -0
- package/test/datahike/test/attribute_refs/utils.cljc +128 -0
- package/test/datahike/test/cache_test.cljc +38 -0
- package/test/datahike/test/components_test.cljc +92 -0
- package/test/datahike/test/config_test.cljc +158 -0
- package/test/datahike/test/core_test.cljc +105 -0
- package/test/datahike/test/datom_test.cljc +44 -0
- package/test/datahike/test/db_test.cljc +54 -0
- package/test/datahike/test/entity_spec_test.cljc +159 -0
- package/test/datahike/test/entity_test.cljc +103 -0
- package/test/datahike/test/explode_test.cljc +143 -0
- package/test/datahike/test/filter_test.cljc +75 -0
- package/test/datahike/test/gc_test.cljc +159 -0
- package/test/datahike/test/http/server_test.clj +192 -0
- package/test/datahike/test/http/writer_test.clj +86 -0
- package/test/datahike/test/ident_test.cljc +32 -0
- package/test/datahike/test/index_test.cljc +345 -0
- package/test/datahike/test/insert.cljc +125 -0
- package/test/datahike/test/java_bindings_test.clj +6 -0
- package/test/datahike/test/listen_test.cljc +41 -0
- package/test/datahike/test/lookup_refs_test.cljc +266 -0
- package/test/datahike/test/lru_test.cljc +27 -0
- package/test/datahike/test/migrate_test.clj +297 -0
- package/test/datahike/test/model/core.cljc +376 -0
- package/test/datahike/test/model/invariant.cljc +142 -0
- package/test/datahike/test/model/rng.cljc +82 -0
- package/test/datahike/test/model_test.clj +217 -0
- package/test/datahike/test/nodejs_test.cljs +262 -0
- package/test/datahike/test/online_gc_test.cljc +475 -0
- package/test/datahike/test/pod_test.clj +369 -0
- package/test/datahike/test/pull_api_test.cljc +474 -0
- package/test/datahike/test/purge_test.cljc +144 -0
- package/test/datahike/test/query_aggregates_test.cljc +101 -0
- package/test/datahike/test/query_find_specs_test.cljc +52 -0
- package/test/datahike/test/query_fns_test.cljc +523 -0
- package/test/datahike/test/query_interop_test.cljc +47 -0
- package/test/datahike/test/query_not_test.cljc +189 -0
- package/test/datahike/test/query_or_test.cljc +158 -0
- package/test/datahike/test/query_pull_test.cljc +147 -0
- package/test/datahike/test/query_rules_test.cljc +248 -0
- package/test/datahike/test/query_stats_test.cljc +218 -0
- package/test/datahike/test/query_test.cljc +984 -0
- package/test/datahike/test/schema_test.cljc +424 -0
- package/test/datahike/test/specification_test.cljc +30 -0
- package/test/datahike/test/store_test.cljc +78 -0
- package/test/datahike/test/stress_test.cljc +57 -0
- package/test/datahike/test/time_variance_test.cljc +518 -0
- package/test/datahike/test/tools_test.clj +134 -0
- package/test/datahike/test/transact_test.cljc +518 -0
- package/test/datahike/test/tuples_test.cljc +564 -0
- package/test/datahike/test/unstructured_test.cljc +291 -0
- package/test/datahike/test/upsert_impl_test.cljc +205 -0
- package/test/datahike/test/upsert_test.cljc +363 -0
- package/test/datahike/test/utils.cljc +110 -0
- package/test/datahike/test/validation_test.cljc +48 -0
- package/test/datahike/test/versioning_test.cljc +56 -0
- package/test/datahike/test.cljc +66 -0
- package/tests.edn +24 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# Why Datalog?
|
|
2
|
+
|
|
3
|
+
Datalog is a declarative query language that excels at expressing complex relationships in graph-structured data. While SQL handles tabular data well, modern applications increasingly need to model interconnected entities—social networks, organizational hierarchies, supply chains, knowledge graphs.
|
|
4
|
+
|
|
5
|
+
## When SQL Falls Short
|
|
6
|
+
|
|
7
|
+
**The problem**: As applications evolve, relational schemas accumulate join complexity. What starts as simple tables becomes a maze of foreign keys, junction tables, and nested subqueries. Developers spend more time managing joins than expressing business logic.
|
|
8
|
+
|
|
9
|
+
**Common pattern**: Start with SQL for simplicity → encounter complex relationships → add more joins → build ad-hoc graph features → end up with fragile, hard-to-maintain queries.
|
|
10
|
+
|
|
11
|
+
**Datalog's approach**: Pattern matching over relationships. Instead of explicitly specifying how to join tables, you describe what patterns you're looking for. The query engine handles traversal.
|
|
12
|
+
|
|
13
|
+
## Query Comparison
|
|
14
|
+
|
|
15
|
+
Let's find all projects where someone manages a team member who contributed to that project.
|
|
16
|
+
|
|
17
|
+
### SQL Approach
|
|
18
|
+
|
|
19
|
+
```sql
|
|
20
|
+
SELECT DISTINCT
|
|
21
|
+
m.name AS manager_name,
|
|
22
|
+
e.name AS employee_name,
|
|
23
|
+
p.name AS project_name
|
|
24
|
+
FROM employees m
|
|
25
|
+
JOIN manager_relationships mr ON m.id = mr.manager_id
|
|
26
|
+
JOIN employees e ON mr.employee_id = e.id
|
|
27
|
+
JOIN project_contributors pc ON e.id = pc.employee_id
|
|
28
|
+
JOIN projects p ON pc.project_id = p.id
|
|
29
|
+
WHERE m.id != e.id;
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
This requires:
|
|
33
|
+
- Understanding the table structure (4 tables, 3 join conditions)
|
|
34
|
+
- Explicit join order management
|
|
35
|
+
- Mental model of how data flows through joins
|
|
36
|
+
|
|
37
|
+
### Datalog Approach
|
|
38
|
+
|
|
39
|
+
```clojure
|
|
40
|
+
(d/q '[:find ?manager-name ?employee-name ?project-name
|
|
41
|
+
:where
|
|
42
|
+
[?manager :employee/manages ?employee]
|
|
43
|
+
[?employee :project/contributed-to ?project]
|
|
44
|
+
[?manager :employee/name ?manager-name]
|
|
45
|
+
[?employee :employee/name ?employee-name]
|
|
46
|
+
[?project :project/name ?project-name]]
|
|
47
|
+
db)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Pattern matching expresses intent:
|
|
51
|
+
- "Find managers who manage employees"
|
|
52
|
+
- "Find employees who contributed to projects"
|
|
53
|
+
- "Get their names"
|
|
54
|
+
|
|
55
|
+
No explicit joins. No table knowledge required. The relationships are first-class.
|
|
56
|
+
|
|
57
|
+
## More Complex: Transitive Relationships
|
|
58
|
+
|
|
59
|
+
Find all skills accessible through management chain (managers inherit team skills).
|
|
60
|
+
|
|
61
|
+
### SQL
|
|
62
|
+
|
|
63
|
+
```sql
|
|
64
|
+
WITH RECURSIVE management_chain AS (
|
|
65
|
+
-- Base case: direct reports
|
|
66
|
+
SELECT manager_id, employee_id, 1 as depth
|
|
67
|
+
FROM manager_relationships
|
|
68
|
+
|
|
69
|
+
UNION ALL
|
|
70
|
+
|
|
71
|
+
-- Recursive case: indirect reports
|
|
72
|
+
SELECT mc.manager_id, mr.employee_id, mc.depth + 1
|
|
73
|
+
FROM management_chain mc
|
|
74
|
+
JOIN manager_relationships mr ON mc.employee_id = mr.manager_id
|
|
75
|
+
WHERE mc.depth < 10 -- Prevent infinite recursion
|
|
76
|
+
)
|
|
77
|
+
SELECT DISTINCT
|
|
78
|
+
m.name AS manager_name,
|
|
79
|
+
s.name AS accessible_skill
|
|
80
|
+
FROM employees m
|
|
81
|
+
JOIN management_chain mc ON m.id = mc.manager_id
|
|
82
|
+
JOIN employee_skills es ON mc.employee_id = es.employee_id
|
|
83
|
+
JOIN skills s ON es.skill_id = s.id;
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Requires:
|
|
87
|
+
- Understanding recursive CTEs
|
|
88
|
+
- Managing recursion depth manually
|
|
89
|
+
- Multiple joins to assemble result
|
|
90
|
+
|
|
91
|
+
### Datalog with Rules
|
|
92
|
+
|
|
93
|
+
```clojure
|
|
94
|
+
;; Define a rule for transitive management
|
|
95
|
+
(d/q '[:find ?manager-name ?skill-name
|
|
96
|
+
:in $ %
|
|
97
|
+
:where
|
|
98
|
+
(manages-recursively ?manager ?employee)
|
|
99
|
+
[?employee :employee/has-skill ?skill]
|
|
100
|
+
[?manager :employee/name ?manager-name]
|
|
101
|
+
[?skill :skill/name ?skill-name]]
|
|
102
|
+
db
|
|
103
|
+
'[;; Rule definition
|
|
104
|
+
[(manages-recursively ?m ?e)
|
|
105
|
+
[?m :employee/manages ?e]]
|
|
106
|
+
[(manages-recursively ?m ?e)
|
|
107
|
+
[?m :employee/manages ?x]
|
|
108
|
+
(manages-recursively ?x ?e)]])
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**Rules encapsulate logic**: `manages-recursively` handles the transitive relationship. Reusable across queries.
|
|
112
|
+
|
|
113
|
+
## Graph Traversal: Friend Recommendations
|
|
114
|
+
|
|
115
|
+
Find friends-of-friends who share interests, excluding existing friends.
|
|
116
|
+
|
|
117
|
+
### SQL
|
|
118
|
+
|
|
119
|
+
```sql
|
|
120
|
+
SELECT DISTINCT
|
|
121
|
+
p1.name AS person_name,
|
|
122
|
+
p3.name AS recommendation_name,
|
|
123
|
+
i.name AS shared_interest
|
|
124
|
+
FROM people p1
|
|
125
|
+
JOIN friendships f1 ON p1.id = f1.person_id
|
|
126
|
+
JOIN people p2 ON f1.friend_id = p2.id
|
|
127
|
+
JOIN friendships f2 ON p2.id = f2.person_id
|
|
128
|
+
JOIN people p3 ON f2.friend_id = p3.id
|
|
129
|
+
JOIN person_interests pi1 ON p1.id = pi1.person_id
|
|
130
|
+
JOIN person_interests pi3 ON p3.id = pi3.person_id
|
|
131
|
+
JOIN interests i ON pi1.interest_id = i.id AND pi3.interest_id = i.id
|
|
132
|
+
LEFT JOIN friendships f_check ON (p1.id = f_check.person_id AND p3.id = f_check.friend_id)
|
|
133
|
+
WHERE p1.id != p3.id
|
|
134
|
+
AND f_check.person_id IS NULL; -- Not already friends
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
8 joins, complex exclusion logic, hard to maintain.
|
|
138
|
+
|
|
139
|
+
### Datalog
|
|
140
|
+
|
|
141
|
+
```clojure
|
|
142
|
+
(d/q '[:find ?person-name ?recommendation-name ?interest-name
|
|
143
|
+
:where
|
|
144
|
+
[?person :person/friend ?friend]
|
|
145
|
+
[?friend :person/friend ?recommendation]
|
|
146
|
+
[?person :person/interest ?interest]
|
|
147
|
+
[?recommendation :person/interest ?interest]
|
|
148
|
+
[(not= ?person ?recommendation)]
|
|
149
|
+
(not [?person :person/friend ?recommendation])
|
|
150
|
+
[?person :person/name ?person-name]
|
|
151
|
+
[?recommendation :person/name ?recommendation-name]
|
|
152
|
+
[?interest :interest/name ?interest-name]]
|
|
153
|
+
db)
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
**Natural expression**: Each line describes one relationship pattern. Negation uses `not` clause rather than LEFT JOIN tricks.
|
|
157
|
+
|
|
158
|
+
## Multi-Database Queries
|
|
159
|
+
|
|
160
|
+
Datalog can query across multiple databases in a single query—useful for data federation or comparing environments.
|
|
161
|
+
|
|
162
|
+
```clojure
|
|
163
|
+
;; Compare production and staging: Find entities in staging not in production
|
|
164
|
+
(d/q '[:find ?name ?value
|
|
165
|
+
:in $staging $prod
|
|
166
|
+
:where
|
|
167
|
+
[$staging ?e :entity/name ?name]
|
|
168
|
+
[$staging ?e :entity/value ?value]
|
|
169
|
+
(not [$prod ?e2 :entity/name ?name])]
|
|
170
|
+
staging-db prod-db)
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
SQL would require UNION queries, temporary tables, or separate queries with application-level comparison.
|
|
174
|
+
|
|
175
|
+
## When to Use Datalog
|
|
176
|
+
|
|
177
|
+
**Choose Datalog when:**
|
|
178
|
+
- Modeling interconnected entities (social graphs, org charts, supply chains)
|
|
179
|
+
- Need recursive or transitive queries
|
|
180
|
+
- Schema evolves frequently (schema-less or flexible schema)
|
|
181
|
+
- Want to express logic through rules
|
|
182
|
+
- Query across multiple databases or time points
|
|
183
|
+
- Building knowledge graphs or semantic systems
|
|
184
|
+
|
|
185
|
+
**SQL still wins for:**
|
|
186
|
+
- Pure tabular data with minimal relationships
|
|
187
|
+
- Standard BI tool integration
|
|
188
|
+
- Window functions and complex aggregations over large sorted sets
|
|
189
|
+
- Existing SQL infrastructure and expertise
|
|
190
|
+
|
|
191
|
+
## Performance Note
|
|
192
|
+
|
|
193
|
+
Datalog query engines (including Datahike) use sophisticated join optimization. Performance is comparable to well-tuned SQL for most queries. For specific workloads, see our [benchmarks](https://gitlab.com/arbetsformedlingen/taxonomy-dev/backend/experimental/datahike-benchmark/).
|
|
194
|
+
|
|
195
|
+
## Learning Datalog
|
|
196
|
+
|
|
197
|
+
Datalog syntax is minimal:
|
|
198
|
+
- Pattern matching: `[?entity :attribute ?value]`
|
|
199
|
+
- Rules: Define reusable logic patterns
|
|
200
|
+
- Negation: `(not ...)`
|
|
201
|
+
- Functions: `[(function arg) result]`
|
|
202
|
+
|
|
203
|
+
Most developers become productive within hours. The conceptual shift from imperative joins to declarative patterns is the main learning curve, not syntax complexity.
|
|
204
|
+
|
|
205
|
+
## Further Reading
|
|
206
|
+
|
|
207
|
+
- [Datalog Wikipedia](https://en.wikipedia.org/wiki/Datalog) - Academic background
|
|
208
|
+
- [Learn Datalog Today](http://www.learndatalogtoday.org/) - Interactive tutorial
|
|
209
|
+
- [Datomic Query Documentation](https://docs.datomic.com/query/query.html) - Datahike uses compatible query syntax
|
|
210
|
+
- [Datahike Query Guide](https://cljdoc.org/d/org.replikativ/datahike/CURRENT/doc/query) - Full query capabilities
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Differences to Datomic Client API
|
|
2
|
+
|
|
3
|
+
Although Datahike supports a part of [Datomic's
|
|
4
|
+
API](https://docs.datomic.com/client-api/datomic.client.api.html), some
|
|
5
|
+
behavior is different using the different functions. Datahike supports part of the `datomic.client.api`:
|
|
6
|
+
|
|
7
|
+
[](https://cljdoc.org/d/org.replikativ/datahike)
|
|
8
|
+
|
|
9
|
+
Without differences in the signature you may use the following like in Datomic:
|
|
10
|
+
|
|
11
|
+
- as-of
|
|
12
|
+
- datoms
|
|
13
|
+
- [db](#db)
|
|
14
|
+
- history
|
|
15
|
+
- pull
|
|
16
|
+
- q
|
|
17
|
+
- since
|
|
18
|
+
- [transact](#transact)
|
|
19
|
+
|
|
20
|
+
Please be aware that `q` returns sets instead of vectors. Only when using aggregates or `:with`, `q` returns a
|
|
21
|
+
vector. That is the behavior of Datascript.
|
|
22
|
+
|
|
23
|
+
The following functions from the `datahike.api` namespace are different from Datomic's client API:
|
|
24
|
+
|
|
25
|
+
- [connect](#connect)
|
|
26
|
+
- [create-database](#create-database)
|
|
27
|
+
- [database-exists?](#database-exists)
|
|
28
|
+
- [db-with](https://cljdoc.org/d/org.replikativ/datahike/0.3.6/api/datahike.api#db-with)
|
|
29
|
+
- [delete-database](#delete-database)
|
|
30
|
+
- [entity](https://cljdoc.org/d/org.replikativ/datahike/0.3.6/api/datahike.api#entity)
|
|
31
|
+
- [entity-db](https://cljdoc.org/d/org.replikativ/datahike/0.3.6/api/datahike.api#entity-db)
|
|
32
|
+
- [filter](https://cljdoc.org/d/org.replikativ/datahike/0.3.6/api/datahike.api#filter)
|
|
33
|
+
- [is-filtered](https://cljdoc.org/d/org.replikativ/datahike/0.3.6/api/datahike.api#is-filtered)
|
|
34
|
+
- [load-entities](https://cljdoc.org/d/org.replikativ/datahike/0.3.6/api/datahike.api#load-entities)
|
|
35
|
+
- [pull-many](https://cljdoc.org/d/org.replikativ/datahike/0.3.6/api/datahike.api#pull-many)
|
|
36
|
+
- [release](https://cljdoc.org/d/org.replikativ/datahike/0.3.6/api/datahike.api#release)
|
|
37
|
+
- [seek-datoms](https://cljdoc.org/d/org.replikativ/datahike/0.3.6/api/datahike.api#seek-datoms)
|
|
38
|
+
- [tempid](https://cljdoc.org/d/org.replikativ/datahike/0.3.6/api/datahike.api#tempid)
|
|
39
|
+
- [with](#with)
|
|
40
|
+
|
|
41
|
+
Additionally, datahike supports most functions from [datascript](https://github.com/tonsky/datascript)
|
|
42
|
+
in the `datahike.api` namespace.
|
|
43
|
+
|
|
44
|
+
These functions of Datomic are not yet implemented but considered candidates for future development:
|
|
45
|
+
- tx-range
|
|
46
|
+
- index-pull
|
|
47
|
+
- with-db
|
|
48
|
+
|
|
49
|
+
These functions are part of Datomics' distributed implementation and are currently not part of the
|
|
50
|
+
Datahike API:
|
|
51
|
+
- client
|
|
52
|
+
- administer-system
|
|
53
|
+
- db-stats
|
|
54
|
+
- list-databases
|
|
55
|
+
- sync
|
|
56
|
+
|
|
57
|
+
Async support is on our roadmap as well as running distributed Datahike.
|
|
58
|
+
|
|
59
|
+
## connect
|
|
60
|
+
|
|
61
|
+
Connects to an existing database given the configuration hash-map where Datomic
|
|
62
|
+
takes a client as argument. The specification for the configuration can be found
|
|
63
|
+
[here](./config.md).
|
|
64
|
+
|
|
65
|
+
[](https://cljdoc.org/d/org.replikativ/datahike)
|
|
66
|
+
|
|
67
|
+
## create-database
|
|
68
|
+
|
|
69
|
+
Creates a new database with the given configuration hash-map where Datomic takes a client and an
|
|
70
|
+
arg-map as arguments. Additional optional parameters are `schema-flexibility`, `keep-history?`
|
|
71
|
+
and `initial-tx`. Have a look at the [configuration documentation](./config.md) for details.
|
|
72
|
+
|
|
73
|
+
[](https://cljdoc.org/d/org.replikativ/datahike)
|
|
74
|
+
|
|
75
|
+
## delete-database
|
|
76
|
+
|
|
77
|
+
Deletes a database with the configuration hash-map as argument.
|
|
78
|
+
|
|
79
|
+
## database-exists?
|
|
80
|
+
|
|
81
|
+
Checks if a database exists with the configuration hash-map as argument.
|
|
82
|
+
|
|
83
|
+
## db
|
|
84
|
+
|
|
85
|
+
Since the database can be just de-referenced from the connection this function is
|
|
86
|
+
just a small wrapper for Datomic compliance.
|
|
87
|
+
|
|
88
|
+
```clojure
|
|
89
|
+
(db conn)
|
|
90
|
+
```
|
|
91
|
+
equivalent to:
|
|
92
|
+
```
|
|
93
|
+
@conn
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## transact
|
|
97
|
+
|
|
98
|
+
Returns a hash map different from Datomics' as a report. The `:tx-meta` is not part
|
|
99
|
+
of Datomics' transaction report but apart from that the same keys are present. The
|
|
100
|
+
values are different records though.
|
|
101
|
+
|
|
102
|
+
[](https://cljdoc.org/d/org.replikativ/datahike)
|
|
103
|
+
|
|
104
|
+
## with
|
|
105
|
+
|
|
106
|
+
Applies transactions to an immutable database value and returns a new database
|
|
107
|
+
snapshot. It does not change the mutable database inside the connection unlike
|
|
108
|
+
[transact](#transact). It works quite the same as Datomics' `with` but does
|
|
109
|
+
not need a `with-db` function to work.
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# Pull-API Namespace
|
|
2
|
+
|
|
3
|
+
This document is aimed at Datahike core developers that want to debug or extend the Pull-API.
|
|
4
|
+
Hopefully, this will help people getting a grip of the existing code and work flow of the Pull-API.
|
|
5
|
+
|
|
6
|
+
## Terminology
|
|
7
|
+
|
|
8
|
+
### Selector
|
|
9
|
+
|
|
10
|
+
A quoted vector holding one or several of the following expressions:
|
|
11
|
+
1) wildcard
|
|
12
|
+
- pulls all datoms for entity id
|
|
13
|
+
- e.g. `*` -> `{:db/id 1 :name "Petr" :child [{:db/id 2}]}`
|
|
14
|
+
2) keyword for
|
|
15
|
+
- attributes to get
|
|
16
|
+
- e.g. `:child` (= has child) -> `{:child [#:db{:id 10}]}`
|
|
17
|
+
- reverse attributes to get (datom where requested entity is referenced)
|
|
18
|
+
- signaled by underscore
|
|
19
|
+
- e.g. `:_child` (= child of) -> `{:_child [#:db{:id 1}]}`
|
|
20
|
+
3) map of keywords for (reverse) attributes to get and
|
|
21
|
+
- vectors of attributes
|
|
22
|
+
- attribute should be reference
|
|
23
|
+
- e.g. `{:child [:name :age]}` -> `{:child {:name "Thomas" :age 10}}`
|
|
24
|
+
- e.g. `{:_child [:name :age]}` -> `{:_child {:name "Charles" :age 10}}`
|
|
25
|
+
- three dots
|
|
26
|
+
- for the infinite recursion on an attribute
|
|
27
|
+
- number
|
|
28
|
+
- for the finite recursion on an attribute
|
|
29
|
+
4) function call form:
|
|
30
|
+
- limit:
|
|
31
|
+
- restricts the amount of pulled datoms to the given number
|
|
32
|
+
- e.g. `(limit :child 2)`
|
|
33
|
+
- defaults:
|
|
34
|
+
- sets a default value if search returns nil
|
|
35
|
+
- e.g. `(default :name "unknown")`
|
|
36
|
+
5) vectors of 3
|
|
37
|
+
- functions of 4) in vector form
|
|
38
|
+
- attribute, function name as keyword, parameter
|
|
39
|
+
- e.g. `[:child :limit 2]` or `[:name :default "unknown"]`
|
|
40
|
+
- renaming of attributes with :as
|
|
41
|
+
- attribute, :as, new name
|
|
42
|
+
- e.g. `[:aka :as :alias]` or `[:name :as "Name"]`
|
|
43
|
+
|
|
44
|
+
### Pattern = (Pull-)Spec
|
|
45
|
+
|
|
46
|
+
Record with keys
|
|
47
|
+
- `:wildcard?` = boolean value indicating if wildcard expansion has to be done
|
|
48
|
+
- `:attrs` = map of attribute names as in selector and map with keys
|
|
49
|
+
- `:attr` = attribute name as in database; only different from key when key describes reverse attribute
|
|
50
|
+
- `:subpattern` (optional) = spec of a subpattern applied to an attribute
|
|
51
|
+
- `:recursion` = always nil, only checks if key exists, if it does: recursion is applied on attribute
|
|
52
|
+
- `:limit`= amount of maximum number of results returned
|
|
53
|
+
- `:default` = default value if nothing found
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
### Frame
|
|
57
|
+
|
|
58
|
+
Map with keys
|
|
59
|
+
- `:multi?` = true if multiple entities requested
|
|
60
|
+
- `:eids` = vector of eids to pull pattern for
|
|
61
|
+
- `:state` = pattern/expand/expand-rev/recursion/done
|
|
62
|
+
- `:pattern` = (Pull-)Spec
|
|
63
|
+
- `:recursion` = map with keys :depth (depth per attribute) and :seen (eids)
|
|
64
|
+
- `:specs` = attrs from spec
|
|
65
|
+
- `:wildcard?` = wildcard from spec
|
|
66
|
+
- `:kvps` = final result map; in pull-pattern transfered to next frame when current frame is done
|
|
67
|
+
- `:results` = result map for current frame, i.e. for current attribute
|
|
68
|
+
- `:attr` = optional; current attribute (pull-attr-datoms) or enum, e.g. recursion
|
|
69
|
+
- `:datoms` = optional; datoms pulled from database; e.g. added by wildcard-expand
|
|
70
|
+
- `:expand-kvps` = optional; added by expand-frame, used by expand-rev-frame
|
|
71
|
+
|
|
72
|
+
#### Frame Creation and State Changes
|
|
73
|
+
|
|
74
|
+
- only 1 frame created for basic input pattern
|
|
75
|
+
- additional frames added on processing of
|
|
76
|
+
- recursion: 2 frames per depth
|
|
77
|
+
- wildcard: 1 frame
|
|
78
|
+
|
|
79
|
+

|
|
80
|
+
|
|
81
|
+
## Pull API Usage
|
|
82
|
+
|
|
83
|
+
See also the official [Datahike documentation](https://cljdoc.org/d/org.replikativ/datahike/0.3.2/api/datahike.api#pull).
|
|
84
|
+
|
|
85
|
+
Setup for examples:
|
|
86
|
+
|
|
87
|
+
```clojure
|
|
88
|
+
(require '(datahike [core :as d]))
|
|
89
|
+
|
|
90
|
+
(def schema
|
|
91
|
+
{:aka {:db/cardinality :db.cardinality/many}
|
|
92
|
+
:child {:db/cardinality :db.cardinality/many
|
|
93
|
+
:db/valueType :db.type/ref}
|
|
94
|
+
:friend {:db/cardinality :db.cardinality/many
|
|
95
|
+
:db/valueType :db.type/ref}
|
|
96
|
+
:father {:db/valueType :db.type/ref}
|
|
97
|
+
:spec {:db/valueType :db.type/ref
|
|
98
|
+
:db/isComponent true
|
|
99
|
+
:db/cardinality :db.cardinality/one}})
|
|
100
|
+
|
|
101
|
+
(def datoms
|
|
102
|
+
(->>
|
|
103
|
+
[[1 :name "Petr"]
|
|
104
|
+
[1 :aka "Devil"]
|
|
105
|
+
[1 :aka "Tupen"]
|
|
106
|
+
[1 :aka "P"]
|
|
107
|
+
[2 :name "David"]
|
|
108
|
+
[3 :name "Thomas"]
|
|
109
|
+
[4 :name "Lucy"]
|
|
110
|
+
[5 :name "Elizabeth"]
|
|
111
|
+
[6 :name "Matthew"]
|
|
112
|
+
[7 :name "Eunan"]
|
|
113
|
+
[8 :name "Kerri"]
|
|
114
|
+
[9 :name "Rebecca"]
|
|
115
|
+
[1 :child 2]
|
|
116
|
+
[1 :child 3]
|
|
117
|
+
[2 :father 1]
|
|
118
|
+
[3 :father 1]
|
|
119
|
+
[6 :father 3]
|
|
120
|
+
[4 :friend 5]
|
|
121
|
+
[5 :friend 6]]
|
|
122
|
+
(map (fn [[e a v]] (d/datom e a v tx0)))))
|
|
123
|
+
|
|
124
|
+
(def example-db (d/init-db datoms schema))
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Examples
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
Simple Pull
|
|
131
|
+
```clojure
|
|
132
|
+
(d/pull example-db '[:name] 1)
|
|
133
|
+
|
|
134
|
+
;; => {:name "Petr"}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Pull reverse attribute
|
|
138
|
+
```clojure
|
|
139
|
+
(d/pull example-db '[:_child] 2)
|
|
140
|
+
|
|
141
|
+
;; => {:_child [#:db{:id 1}]}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Pull db/id
|
|
145
|
+
|
|
146
|
+
```clojure
|
|
147
|
+
(d/pull example-db '[:name :db/id] 6)
|
|
148
|
+
|
|
149
|
+
;; => {:name "Matthew", :db/id 6}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Pull wildcard
|
|
153
|
+
```clojure
|
|
154
|
+
(d/pull example-db '[*] 2)
|
|
155
|
+
|
|
156
|
+
;; => {:db/id 2, :father #:db{:id 1}, :name "David"}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Pull recursion
|
|
160
|
+
```clojure
|
|
161
|
+
(d/pull example-db '[:db/id :name {:friend ...}] 4)
|
|
162
|
+
|
|
163
|
+
;; => {:db/id 4, :name "Lucy", :friend [{:db/id 5, :name "Elizabeth", :friend [{:db/id 6, :name "Matthew"}]}]}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Pull with default
|
|
167
|
+
```clojure
|
|
168
|
+
(d/pull example-db '[(default :foo "bar")] 1)
|
|
169
|
+
|
|
170
|
+
;; => {:foo "bar"}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Pull with limit
|
|
174
|
+
```clojure
|
|
175
|
+
(d/pull example-db '[(limit :aka 2)] 1)
|
|
176
|
+
|
|
177
|
+
;; => {:aka ["Devil" "Tupen"]}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Pull with subpattern
|
|
181
|
+
```clojure
|
|
182
|
+
(d/pull example-db '[{:father [:name]}] 6)
|
|
183
|
+
|
|
184
|
+
;; => {:father {:name "Thomas"}}
|
|
185
|
+
```
|
|
186
|
+
|
|
Binary file
|