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,311 @@
|
|
|
1
|
+
"""EDN type helpers and common keyword constants.
|
|
2
|
+
|
|
3
|
+
This module provides explicit type constructors for EDN values and
|
|
4
|
+
pre-defined constants for frequently-used Datahike keywords.
|
|
5
|
+
|
|
6
|
+
Use these when you need fine-grained control over EDN conversion or
|
|
7
|
+
want to avoid string prefixes.
|
|
8
|
+
|
|
9
|
+
Example:
|
|
10
|
+
>>> from datahike import edn, kw
|
|
11
|
+
>>>
|
|
12
|
+
>>> # Explicit types
|
|
13
|
+
>>> schema = [{
|
|
14
|
+
... "db/ident": edn.keyword("person/name"),
|
|
15
|
+
... "db/valueType": kw.STRING,
|
|
16
|
+
... "db/cardinality": kw.ONE,
|
|
17
|
+
... "db/doc": edn.string(":literal-colon-in-doc")
|
|
18
|
+
... }]
|
|
19
|
+
>>>
|
|
20
|
+
>>> db.transact(schema)
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from typing import Optional
|
|
24
|
+
from .database import EDNType
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# =============================================================================
|
|
28
|
+
# Explicit EDN Types
|
|
29
|
+
# =============================================================================
|
|
30
|
+
|
|
31
|
+
class Keyword(EDNType):
|
|
32
|
+
"""Explicit EDN keyword.
|
|
33
|
+
|
|
34
|
+
Use when you need to construct keywords programmatically or force
|
|
35
|
+
a value to be a keyword regardless of the string prefix rules.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
name: Keyword name
|
|
39
|
+
namespace: Optional namespace
|
|
40
|
+
|
|
41
|
+
Examples:
|
|
42
|
+
>>> edn.keyword("name")
|
|
43
|
+
# → :name
|
|
44
|
+
|
|
45
|
+
>>> edn.keyword("name", "person")
|
|
46
|
+
# → :person/name
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
def __init__(self, name: str, namespace: Optional[str] = None):
|
|
50
|
+
if namespace:
|
|
51
|
+
self.value = f"{namespace}/{name}"
|
|
52
|
+
else:
|
|
53
|
+
self.value = name
|
|
54
|
+
|
|
55
|
+
def to_edn(self) -> str:
|
|
56
|
+
return f":{self.value}"
|
|
57
|
+
|
|
58
|
+
def __repr__(self):
|
|
59
|
+
return f"Keyword('{self.value}')"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class Symbol(EDNType):
|
|
63
|
+
"""Explicit EDN symbol.
|
|
64
|
+
|
|
65
|
+
Symbols are used for function names and special forms in Clojure.
|
|
66
|
+
Rarely needed in data transactions.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
name: Symbol name
|
|
70
|
+
namespace: Optional namespace
|
|
71
|
+
|
|
72
|
+
Examples:
|
|
73
|
+
>>> edn.symbol("my-fn")
|
|
74
|
+
# → my-fn
|
|
75
|
+
|
|
76
|
+
>>> edn.symbol("assoc", "clojure.core")
|
|
77
|
+
# → clojure.core/assoc
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
def __init__(self, name: str, namespace: Optional[str] = None):
|
|
81
|
+
if namespace:
|
|
82
|
+
self.value = f"{namespace}/{name}"
|
|
83
|
+
else:
|
|
84
|
+
self.value = name
|
|
85
|
+
|
|
86
|
+
def to_edn(self) -> str:
|
|
87
|
+
return self.value
|
|
88
|
+
|
|
89
|
+
def __repr__(self):
|
|
90
|
+
return f"Symbol('{self.value}')"
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class UUID(EDNType):
|
|
94
|
+
"""Explicit EDN UUID.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
value: UUID string (with or without #uuid prefix)
|
|
98
|
+
|
|
99
|
+
Examples:
|
|
100
|
+
>>> edn.uuid("550e8400-e29b-41d4-a716-446655440000")
|
|
101
|
+
# → #uuid "550e8400-e29b-41d4-a716-446655440000"
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
def __init__(self, value: str):
|
|
105
|
+
# Strip #uuid prefix if present
|
|
106
|
+
if value.startswith('#uuid'):
|
|
107
|
+
value = value[5:].strip().strip('"')
|
|
108
|
+
self.value = value
|
|
109
|
+
|
|
110
|
+
def to_edn(self) -> str:
|
|
111
|
+
return f'#uuid "{self.value}"'
|
|
112
|
+
|
|
113
|
+
def __repr__(self):
|
|
114
|
+
return f"UUID('{self.value}')"
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class Inst(EDNType):
|
|
118
|
+
"""Explicit EDN instant (timestamp).
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
value: ISO 8601 timestamp string
|
|
122
|
+
|
|
123
|
+
Examples:
|
|
124
|
+
>>> edn.inst("2024-01-01T00:00:00Z")
|
|
125
|
+
# → #inst "2024-01-01T00:00:00Z"
|
|
126
|
+
"""
|
|
127
|
+
|
|
128
|
+
def __init__(self, value: str):
|
|
129
|
+
# Strip #inst prefix if present
|
|
130
|
+
if value.startswith('#inst'):
|
|
131
|
+
value = value[5:].strip().strip('"')
|
|
132
|
+
self.value = value
|
|
133
|
+
|
|
134
|
+
def to_edn(self) -> str:
|
|
135
|
+
return f'#inst "{self.value}"'
|
|
136
|
+
|
|
137
|
+
def __repr__(self):
|
|
138
|
+
return f"Inst('{self.value}')"
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
# =============================================================================
|
|
142
|
+
# Convenience Functions
|
|
143
|
+
# =============================================================================
|
|
144
|
+
|
|
145
|
+
def keyword(name: str, namespace: Optional[str] = None) -> Keyword:
|
|
146
|
+
"""Create an EDN keyword.
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
name: Keyword name
|
|
150
|
+
namespace: Optional namespace
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
Keyword instance
|
|
154
|
+
|
|
155
|
+
Examples:
|
|
156
|
+
>>> keyword("name")
|
|
157
|
+
# → :name
|
|
158
|
+
|
|
159
|
+
>>> keyword("name", "person")
|
|
160
|
+
# → :person/name
|
|
161
|
+
"""
|
|
162
|
+
return Keyword(name, namespace)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def symbol(name: str, namespace: Optional[str] = None) -> Symbol:
|
|
166
|
+
"""Create an EDN symbol.
|
|
167
|
+
|
|
168
|
+
Args:
|
|
169
|
+
name: Symbol name
|
|
170
|
+
namespace: Optional namespace
|
|
171
|
+
|
|
172
|
+
Returns:
|
|
173
|
+
Symbol instance
|
|
174
|
+
"""
|
|
175
|
+
return Symbol(name, namespace)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def uuid(value: str) -> UUID:
|
|
179
|
+
"""Create an EDN UUID.
|
|
180
|
+
|
|
181
|
+
Args:
|
|
182
|
+
value: UUID string
|
|
183
|
+
|
|
184
|
+
Returns:
|
|
185
|
+
UUID instance
|
|
186
|
+
"""
|
|
187
|
+
return UUID(value)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def inst(value: str) -> Inst:
|
|
191
|
+
"""Create an EDN instant.
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
value: ISO 8601 timestamp string
|
|
195
|
+
|
|
196
|
+
Returns:
|
|
197
|
+
Inst instance
|
|
198
|
+
"""
|
|
199
|
+
return Inst(value)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def string(value: str) -> str:
|
|
203
|
+
"""Force a value to be treated as a string.
|
|
204
|
+
|
|
205
|
+
Use this to create strings that start with ':' without them
|
|
206
|
+
becoming keywords.
|
|
207
|
+
|
|
208
|
+
Args:
|
|
209
|
+
value: String value (can start with :)
|
|
210
|
+
|
|
211
|
+
Returns:
|
|
212
|
+
Marked string that will be quoted in EDN
|
|
213
|
+
|
|
214
|
+
Examples:
|
|
215
|
+
>>> edn.string(":literal-colon")
|
|
216
|
+
# → ":literal-colon" (not :literal-colon keyword)
|
|
217
|
+
"""
|
|
218
|
+
# Use special marker that database.py recognizes
|
|
219
|
+
return f"<STRING>{value}"
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
# =============================================================================
|
|
223
|
+
# Common Keyword Constants
|
|
224
|
+
# =============================================================================
|
|
225
|
+
|
|
226
|
+
class Keywords:
|
|
227
|
+
"""Pre-defined Datahike keyword constants.
|
|
228
|
+
|
|
229
|
+
Use these for common Datahike schema and transaction attributes
|
|
230
|
+
to avoid typos and get IDE autocompletion.
|
|
231
|
+
|
|
232
|
+
Examples:
|
|
233
|
+
>>> from datahike import kw
|
|
234
|
+
>>>
|
|
235
|
+
>>> schema = [{
|
|
236
|
+
... kw.DB_IDENT: edn.keyword("person/name"),
|
|
237
|
+
... kw.DB_VALUE_TYPE: kw.STRING,
|
|
238
|
+
... kw.DB_CARDINALITY: kw.ONE
|
|
239
|
+
... }]
|
|
240
|
+
"""
|
|
241
|
+
|
|
242
|
+
# Entity attributes
|
|
243
|
+
DB_ID = ":db/id"
|
|
244
|
+
DB_IDENT = ":db/ident"
|
|
245
|
+
|
|
246
|
+
# Schema attributes
|
|
247
|
+
DB_VALUE_TYPE = ":db/valueType"
|
|
248
|
+
DB_CARDINALITY = ":db/cardinality"
|
|
249
|
+
DB_DOC = ":db/doc"
|
|
250
|
+
DB_UNIQUE = ":db/unique"
|
|
251
|
+
DB_IS_COMPONENT = ":db/isComponent"
|
|
252
|
+
DB_NO_HISTORY = ":db/noHistory"
|
|
253
|
+
DB_INDEX = ":db/index"
|
|
254
|
+
DB_FULLTEXT = ":db/fulltext"
|
|
255
|
+
|
|
256
|
+
# Value types
|
|
257
|
+
STRING = ":db.type/string"
|
|
258
|
+
BOOLEAN = ":db.type/boolean"
|
|
259
|
+
LONG = ":db.type/long"
|
|
260
|
+
BIGINT = ":db.type/bigint"
|
|
261
|
+
FLOAT = ":db.type/float"
|
|
262
|
+
DOUBLE = ":db.type/double"
|
|
263
|
+
BIGDEC = ":db.type/bigdec"
|
|
264
|
+
INSTANT = ":db.type/instant"
|
|
265
|
+
UUID_TYPE = ":db.type/uuid"
|
|
266
|
+
KEYWORD_TYPE = ":db.type/keyword"
|
|
267
|
+
SYMBOL_TYPE = ":db.type/symbol"
|
|
268
|
+
REF = ":db.type/ref"
|
|
269
|
+
BYTES = ":db.type/bytes"
|
|
270
|
+
|
|
271
|
+
# Cardinality
|
|
272
|
+
ONE = ":db.cardinality/one"
|
|
273
|
+
MANY = ":db.cardinality/many"
|
|
274
|
+
|
|
275
|
+
# Uniqueness
|
|
276
|
+
UNIQUE_VALUE = ":db.unique/value"
|
|
277
|
+
UNIQUE_IDENTITY = ":db.unique/identity"
|
|
278
|
+
|
|
279
|
+
# Transaction metadata
|
|
280
|
+
TX = ":db/tx"
|
|
281
|
+
TX_INSTANT = ":db/txInstant"
|
|
282
|
+
|
|
283
|
+
# Schema flexibility
|
|
284
|
+
SCHEMA_READ = ":read"
|
|
285
|
+
SCHEMA_WRITE = ":write"
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
# Export singleton instance
|
|
289
|
+
kw = Keywords()
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
# =============================================================================
|
|
293
|
+
# Public API
|
|
294
|
+
# =============================================================================
|
|
295
|
+
|
|
296
|
+
__all__ = [
|
|
297
|
+
# Types
|
|
298
|
+
'Keyword',
|
|
299
|
+
'Symbol',
|
|
300
|
+
'UUID',
|
|
301
|
+
'Inst',
|
|
302
|
+
# Functions
|
|
303
|
+
'keyword',
|
|
304
|
+
'symbol',
|
|
305
|
+
'uuid',
|
|
306
|
+
'inst',
|
|
307
|
+
'string',
|
|
308
|
+
# Constants
|
|
309
|
+
'Keywords',
|
|
310
|
+
'kw',
|
|
311
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Pytest configuration and shared fixtures."""
|
|
2
|
+
import pytest
|
|
3
|
+
import uuid
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@pytest.fixture
|
|
7
|
+
def mem_config():
|
|
8
|
+
"""Generate a unique in-memory database config."""
|
|
9
|
+
db_id = str(uuid.uuid4())
|
|
10
|
+
return f'{{:store {{:backend :memory :id #uuid "{db_id}"}}}}'
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@pytest.fixture
|
|
14
|
+
def mem_config_flexible():
|
|
15
|
+
"""Generate a unique in-memory database config with schema flexibility."""
|
|
16
|
+
db_id = str(uuid.uuid4())
|
|
17
|
+
return f'{{:store {{:backend :memory :id #uuid "{db_id}"}} :schema-flexibility :read}}'
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
"""Basic tests for Datahike Python bindings."""
|
|
2
|
+
import json
|
|
3
|
+
import uuid
|
|
4
|
+
import sys
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
# Add src to path for development
|
|
8
|
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src'))
|
|
9
|
+
|
|
10
|
+
from datahike import (
|
|
11
|
+
create_database,
|
|
12
|
+
delete_database,
|
|
13
|
+
database_exists,
|
|
14
|
+
transact,
|
|
15
|
+
q,
|
|
16
|
+
pull,
|
|
17
|
+
schema,
|
|
18
|
+
DatahikeException,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_database_lifecycle():
|
|
23
|
+
"""Test create, exists, delete database."""
|
|
24
|
+
db_id = str(uuid.uuid4())
|
|
25
|
+
config = f'{{:store {{:backend :memory :id #uuid "{db_id}"}}}}'
|
|
26
|
+
|
|
27
|
+
print(f"Using config: {config}")
|
|
28
|
+
|
|
29
|
+
# Create
|
|
30
|
+
print("Creating database...")
|
|
31
|
+
create_database(config)
|
|
32
|
+
|
|
33
|
+
# Check exists
|
|
34
|
+
print("Checking if database exists...")
|
|
35
|
+
exists = database_exists(config)
|
|
36
|
+
print(f"Database exists: {exists}")
|
|
37
|
+
assert exists == True
|
|
38
|
+
|
|
39
|
+
# Delete
|
|
40
|
+
print("Deleting database...")
|
|
41
|
+
delete_database(config)
|
|
42
|
+
|
|
43
|
+
# Check deleted
|
|
44
|
+
exists = database_exists(config)
|
|
45
|
+
print(f"Database exists after delete: {exists}")
|
|
46
|
+
assert exists == False
|
|
47
|
+
|
|
48
|
+
print("SUCCESS: Database lifecycle test passed!")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def test_transact_and_query_with_schema_flexibility():
|
|
52
|
+
"""Test transact and query with schema-flexibility :read (flexible schema)."""
|
|
53
|
+
db_id = str(uuid.uuid4())
|
|
54
|
+
config = f'{{:store {{:backend :memory :id #uuid "{db_id}"}} :schema-flexibility :read}}'
|
|
55
|
+
|
|
56
|
+
print(f"\nUsing config: {config}")
|
|
57
|
+
|
|
58
|
+
try:
|
|
59
|
+
# Create database
|
|
60
|
+
print("Creating database...")
|
|
61
|
+
create_database(config)
|
|
62
|
+
|
|
63
|
+
# Transact data using JSON format
|
|
64
|
+
print("Transacting data...")
|
|
65
|
+
tx_data = json.dumps([
|
|
66
|
+
{"name": "Alice", "age": 30},
|
|
67
|
+
{"name": "Bob", "age": 25}
|
|
68
|
+
])
|
|
69
|
+
result = transact(config, tx_data, input_format='json')
|
|
70
|
+
print(f"Transaction result: {result}")
|
|
71
|
+
|
|
72
|
+
# Query for Alice
|
|
73
|
+
print("Querying for Alice...")
|
|
74
|
+
query_result = q(
|
|
75
|
+
'[:find ?e ?name :where [?e :name ?name] [?e :name "Alice"]]',
|
|
76
|
+
[('db', config)],
|
|
77
|
+
output_format='json'
|
|
78
|
+
)
|
|
79
|
+
print(f"Query result: {query_result}")
|
|
80
|
+
assert len(query_result) > 0, "Should find Alice"
|
|
81
|
+
|
|
82
|
+
# Pull entity
|
|
83
|
+
print("Pulling entity...")
|
|
84
|
+
# Query result is ['!set', [[eid, name], ...]]
|
|
85
|
+
actual_results = query_result[1] if query_result[0] == '!set' else query_result
|
|
86
|
+
entity_id = actual_results[0][0]
|
|
87
|
+
entity = pull(config, '[*]', entity_id, output_format='json')
|
|
88
|
+
print(f"Pulled entity: {entity}")
|
|
89
|
+
# In JSON mode, keywords don't have : prefix
|
|
90
|
+
assert entity.get('name') == 'Alice' or entity.get(':name') == 'Alice'
|
|
91
|
+
|
|
92
|
+
print("SUCCESS: Transact and query test passed!")
|
|
93
|
+
|
|
94
|
+
finally:
|
|
95
|
+
# Cleanup
|
|
96
|
+
print("Cleaning up...")
|
|
97
|
+
delete_database(config)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def test_transact_with_explicit_schema():
|
|
101
|
+
"""Test transact and query with explicit schema."""
|
|
102
|
+
db_id = str(uuid.uuid4())
|
|
103
|
+
config = f'{{:store {{:backend :memory :id #uuid "{db_id}"}}}}'
|
|
104
|
+
|
|
105
|
+
print(f"\nUsing config: {config}")
|
|
106
|
+
|
|
107
|
+
try:
|
|
108
|
+
# Create database
|
|
109
|
+
print("Creating database...")
|
|
110
|
+
create_database(config)
|
|
111
|
+
|
|
112
|
+
# First transact schema using EDN format (keywords need proper representation)
|
|
113
|
+
print("Transacting schema...")
|
|
114
|
+
schema_tx = '[{:db/ident :name :db/valueType :db.type/string :db/cardinality :db.cardinality/one} {:db/ident :age :db/valueType :db.type/long :db/cardinality :db.cardinality/one}]'
|
|
115
|
+
transact(config, schema_tx, input_format='edn')
|
|
116
|
+
|
|
117
|
+
# Check schema was added
|
|
118
|
+
print("Checking schema...")
|
|
119
|
+
db_schema = schema(config, output_format='json')
|
|
120
|
+
print(f"Schema: {db_schema}")
|
|
121
|
+
|
|
122
|
+
# Transact data using JSON - schema validates types
|
|
123
|
+
print("Transacting data...")
|
|
124
|
+
tx_data = json.dumps([
|
|
125
|
+
{"name": "Charlie", "age": 35},
|
|
126
|
+
{"name": "Diana", "age": 28}
|
|
127
|
+
])
|
|
128
|
+
transact(config, tx_data, input_format='json')
|
|
129
|
+
|
|
130
|
+
# Query
|
|
131
|
+
print("Querying...")
|
|
132
|
+
query_result = q(
|
|
133
|
+
'[:find ?e ?name ?age :where [?e :name ?name] [?e :age ?age]]',
|
|
134
|
+
[('db', config)],
|
|
135
|
+
output_format='json'
|
|
136
|
+
)
|
|
137
|
+
print(f"Query result: {query_result}")
|
|
138
|
+
actual_results = query_result[1] if query_result[0] == '!set' else query_result
|
|
139
|
+
assert len(actual_results) == 2, f"Expected 2 results, got {len(actual_results)}"
|
|
140
|
+
|
|
141
|
+
print("SUCCESS: Explicit schema test passed!")
|
|
142
|
+
|
|
143
|
+
finally:
|
|
144
|
+
# Cleanup
|
|
145
|
+
print("Cleaning up...")
|
|
146
|
+
delete_database(config)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def test_exception_handling():
|
|
150
|
+
"""Test that exceptions are properly raised."""
|
|
151
|
+
db_id = str(uuid.uuid4())
|
|
152
|
+
config = f'{{:store {{:backend :memory :id #uuid "{db_id}"}}}}'
|
|
153
|
+
|
|
154
|
+
print("\nTesting exception handling...")
|
|
155
|
+
|
|
156
|
+
try:
|
|
157
|
+
# Try to query non-existent database
|
|
158
|
+
q('[:find ?e :where [?e :name "test"]]', [('db', config)])
|
|
159
|
+
assert False, "Should have raised exception"
|
|
160
|
+
except DatahikeException as e:
|
|
161
|
+
print(f"Caught expected exception: {e}")
|
|
162
|
+
print("SUCCESS: Exception handling test passed!")
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
if __name__ == '__main__':
|
|
166
|
+
test_database_lifecycle()
|
|
167
|
+
test_transact_and_query_with_schema_flexibility()
|
|
168
|
+
test_transact_with_explicit_schema()
|
|
169
|
+
test_exception_handling()
|
|
170
|
+
print("\n=== ALL TESTS PASSED ===")
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""Tests for database lifecycle operations."""
|
|
2
|
+
import pytest
|
|
3
|
+
from datahike import (
|
|
4
|
+
create_database,
|
|
5
|
+
delete_database,
|
|
6
|
+
database_exists,
|
|
7
|
+
database,
|
|
8
|
+
DatahikeException,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_create_and_delete(mem_config):
|
|
13
|
+
"""Test create and delete database."""
|
|
14
|
+
# Create
|
|
15
|
+
create_database(mem_config)
|
|
16
|
+
|
|
17
|
+
# Check exists
|
|
18
|
+
assert database_exists(mem_config) is True
|
|
19
|
+
|
|
20
|
+
# Delete
|
|
21
|
+
delete_database(mem_config)
|
|
22
|
+
|
|
23
|
+
# Check deleted
|
|
24
|
+
assert database_exists(mem_config) is False
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_database_context_manager(mem_config):
|
|
28
|
+
"""Test database context manager for automatic cleanup."""
|
|
29
|
+
# Database should not exist before
|
|
30
|
+
assert database_exists(mem_config) is False
|
|
31
|
+
|
|
32
|
+
# Use context manager
|
|
33
|
+
with database(mem_config) as cfg:
|
|
34
|
+
# Should exist inside context
|
|
35
|
+
assert database_exists(cfg) is True
|
|
36
|
+
assert cfg == mem_config
|
|
37
|
+
|
|
38
|
+
# Should be deleted after context
|
|
39
|
+
assert database_exists(mem_config) is False
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def test_exception_on_nonexistent_database(mem_config):
|
|
43
|
+
"""Test that operations on nonexistent database raise exception."""
|
|
44
|
+
from datahike import q
|
|
45
|
+
|
|
46
|
+
# Database doesn't exist
|
|
47
|
+
assert database_exists(mem_config) is False
|
|
48
|
+
|
|
49
|
+
# Try to query should raise exception
|
|
50
|
+
with pytest.raises(DatahikeException):
|
|
51
|
+
q('[:find ?e :where [?e :name "test"]]', [('db', mem_config)])
|