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.
Files changed (324) hide show
  1. package/.circleci/config.yml +405 -0
  2. package/.circleci/scripts/gen_ci.clj +194 -0
  3. package/.cirrus.yml +60 -0
  4. package/.clj-kondo/babashka/sci/config.edn +1 -0
  5. package/.clj-kondo/babashka/sci/sci/core.clj +9 -0
  6. package/.clj-kondo/config.edn +95 -0
  7. package/.dir-locals.el +2 -0
  8. package/.github/FUNDING.yml +3 -0
  9. package/.github/ISSUE_TEMPLATE/1-bug-report.yml +68 -0
  10. package/.github/ISSUE_TEMPLATE/2-feature-request.yml +28 -0
  11. package/.github/ISSUE_TEMPLATE/config.yml +6 -0
  12. package/.github/pull_request_template.md +24 -0
  13. package/.github/workflows/native-image.yml +84 -0
  14. package/LICENSE +203 -0
  15. package/README.md +273 -0
  16. package/bb/deps.edn +9 -0
  17. package/bb/resources/github-fingerprints +3 -0
  18. package/bb/resources/native-image-tests/run-bb-pod-tests.clj +162 -0
  19. package/bb/resources/native-image-tests/run-libdatahike-tests +12 -0
  20. package/bb/resources/native-image-tests/run-native-image-tests +74 -0
  21. package/bb/resources/native-image-tests/run-python-tests +22 -0
  22. package/bb/resources/native-image-tests/testconfig.attr-refs.edn +6 -0
  23. package/bb/resources/native-image-tests/testconfig.edn +5 -0
  24. package/bb/resources/template/.settings/org.eclipse.jdt.apt.core.prefs +2 -0
  25. package/bb/resources/template/.settings/org.eclipse.jdt.core.prefs +9 -0
  26. package/bb/resources/template/.settings/org.eclipse.m2e.core.prefs +4 -0
  27. package/bb/resources/template/pom.xml +22 -0
  28. package/bb/src/tools/build.clj +132 -0
  29. package/bb/src/tools/clj_kondo.clj +32 -0
  30. package/bb/src/tools/deploy.clj +26 -0
  31. package/bb/src/tools/examples.clj +19 -0
  32. package/bb/src/tools/npm.clj +100 -0
  33. package/bb/src/tools/python.clj +14 -0
  34. package/bb/src/tools/release.clj +94 -0
  35. package/bb/src/tools/test.clj +148 -0
  36. package/bb/src/tools/version.clj +47 -0
  37. package/bb.edn +269 -0
  38. package/benchmark/src/benchmark/cli.clj +195 -0
  39. package/benchmark/src/benchmark/compare.clj +157 -0
  40. package/benchmark/src/benchmark/config.clj +316 -0
  41. package/benchmark/src/benchmark/measure.clj +187 -0
  42. package/benchmark/src/benchmark/store.clj +190 -0
  43. package/benchmark/test/benchmark/measure_test.clj +156 -0
  44. package/build.clj +30 -0
  45. package/config.edn +49 -0
  46. package/deps.edn +138 -0
  47. package/dev/sandbox.clj +82 -0
  48. package/dev/sandbox.cljs +127 -0
  49. package/dev/sandbox_benchmarks.clj +27 -0
  50. package/dev/sandbox_client.clj +87 -0
  51. package/dev/sandbox_transact_bench.clj +109 -0
  52. package/dev/user.clj +79 -0
  53. package/doc/README.md +96 -0
  54. package/doc/adl/README.md +6 -0
  55. package/doc/adl/adr-000-adr.org +28 -0
  56. package/doc/adl/adr-001-attribute-references.org +15 -0
  57. package/doc/adl/adr-002-build-tooling.org +54 -0
  58. package/doc/adl/adr-003-db-meta-data.md +52 -0
  59. package/doc/adl/adr-004-github-flow.md +40 -0
  60. package/doc/adl/adr-XYZ-template.md +30 -0
  61. package/doc/adl/index.org +3 -0
  62. package/doc/assets/datahike-logo.svg +3 -0
  63. package/doc/assets/datahiking-invoice.org +85 -0
  64. package/doc/assets/hhtree2.png +0 -0
  65. package/doc/assets/network_topology.svg +624 -0
  66. package/doc/assets/perf.png +0 -0
  67. package/doc/assets/schema_mindmap.mm +132 -0
  68. package/doc/assets/schema_mindmap.svg +970 -0
  69. package/doc/assets/temporal_index.mm +74 -0
  70. package/doc/backend-development.md +78 -0
  71. package/doc/bb-pod.md +89 -0
  72. package/doc/benchmarking.md +360 -0
  73. package/doc/bindings/edn-conversion.md +383 -0
  74. package/doc/cli.md +162 -0
  75. package/doc/cljdoc.edn +27 -0
  76. package/doc/cljs-support.md +133 -0
  77. package/doc/config.md +406 -0
  78. package/doc/contributing.md +114 -0
  79. package/doc/datalog-vs-sql.md +210 -0
  80. package/doc/datomic_differences.md +109 -0
  81. package/doc/development/pull-api-ns.md +186 -0
  82. package/doc/development/pull-frame-state-diagram.jpg +0 -0
  83. package/doc/distributed.md +566 -0
  84. package/doc/entity_spec.md +92 -0
  85. package/doc/gc.md +273 -0
  86. package/doc/java-api.md +808 -0
  87. package/doc/javascript-api.md +421 -0
  88. package/doc/libdatahike.md +86 -0
  89. package/doc/logging_and_error_handling.md +43 -0
  90. package/doc/norms.md +66 -0
  91. package/doc/schema-migration.md +85 -0
  92. package/doc/schema.md +287 -0
  93. package/doc/storage-backends.md +363 -0
  94. package/doc/store-id-refactoring.md +596 -0
  95. package/doc/time_variance.md +325 -0
  96. package/doc/unstructured.md +167 -0
  97. package/doc/versioning.md +261 -0
  98. package/examples/basic/README.md +19 -0
  99. package/examples/basic/deps.edn +6 -0
  100. package/examples/basic/docker-compose.yml +13 -0
  101. package/examples/basic/src/examples/core.clj +60 -0
  102. package/examples/basic/src/examples/schema.clj +155 -0
  103. package/examples/basic/src/examples/store.clj +60 -0
  104. package/examples/basic/src/examples/time_travel.clj +185 -0
  105. package/examples/java/.settings/org.eclipse.core.resources.prefs +3 -0
  106. package/examples/java/.settings/org.eclipse.jdt.apt.core.prefs +2 -0
  107. package/examples/java/.settings/org.eclipse.jdt.core.prefs +9 -0
  108. package/examples/java/.settings/org.eclipse.m2e.core.prefs +4 -0
  109. package/examples/java/README.md +162 -0
  110. package/examples/java/pom.xml +62 -0
  111. package/examples/java/src/main/java/examples/QuickStart.java +115 -0
  112. package/examples/java/src/main/java/examples/SchemaExample.java +148 -0
  113. package/examples/java/src/main/java/examples/TimeTravelExample.java +121 -0
  114. package/flake.lock +27 -0
  115. package/flake.nix +27 -0
  116. package/http-server/datahike/http/middleware.clj +75 -0
  117. package/http-server/datahike/http/server.clj +269 -0
  118. package/java/src/datahike/java/Database.java +274 -0
  119. package/java/src/datahike/java/Datahike.java +281 -0
  120. package/java/src/datahike/java/DatahikeGeneratedTest.java +349 -0
  121. package/java/src/datahike/java/DatahikeTest.java +370 -0
  122. package/java/src/datahike/java/EDN.java +170 -0
  123. package/java/src/datahike/java/IEntity.java +11 -0
  124. package/java/src/datahike/java/Keywords.java +161 -0
  125. package/java/src/datahike/java/SchemaFlexibility.java +52 -0
  126. package/java/src/datahike/java/Util.java +219 -0
  127. package/karma.conf.js +19 -0
  128. package/libdatahike/compile-cpp +7 -0
  129. package/libdatahike/src/datahike/impl/LibDatahikeBase.java +203 -0
  130. package/libdatahike/src/datahike/impl/libdatahike.clj +59 -0
  131. package/libdatahike/src/test_cpp.cpp +61 -0
  132. package/npm-package/PUBLISHING.md +140 -0
  133. package/npm-package/README.md +226 -0
  134. package/npm-package/package.template.json +34 -0
  135. package/npm-package/test-isomorphic.ts +281 -0
  136. package/npm-package/test.js +557 -0
  137. package/npm-package/typescript-test.ts +70 -0
  138. package/package.json +16 -0
  139. package/pydatahike/README.md +569 -0
  140. package/pydatahike/pyproject.toml +91 -0
  141. package/pydatahike/setup.py +42 -0
  142. package/pydatahike/src/datahike/__init__.py +134 -0
  143. package/pydatahike/src/datahike/_native.py +250 -0
  144. package/pydatahike/src/datahike/_version.py +2 -0
  145. package/pydatahike/src/datahike/database.py +722 -0
  146. package/pydatahike/src/datahike/edn.py +311 -0
  147. package/pydatahike/src/datahike/py.typed +0 -0
  148. package/pydatahike/tests/conftest.py +17 -0
  149. package/pydatahike/tests/test_basic.py +170 -0
  150. package/pydatahike/tests/test_database.py +51 -0
  151. package/pydatahike/tests/test_edn_conversion.py +299 -0
  152. package/pydatahike/tests/test_query.py +99 -0
  153. package/pydatahike/tests/test_schema.py +55 -0
  154. package/resources/clj-kondo.exports/io.replikativ/datahike/config.edn +5 -0
  155. package/resources/example_server.edn +4 -0
  156. package/shadow-cljs.edn +56 -0
  157. package/src/data_readers.clj +7 -0
  158. package/src/datahike/api/impl.cljc +176 -0
  159. package/src/datahike/api/specification.cljc +633 -0
  160. package/src/datahike/api/types.cljc +261 -0
  161. package/src/datahike/api.cljc +41 -0
  162. package/src/datahike/array.cljc +99 -0
  163. package/src/datahike/cli.clj +166 -0
  164. package/src/datahike/cljs.cljs +6 -0
  165. package/src/datahike/codegen/cli.clj +406 -0
  166. package/src/datahike/codegen/clj_kondo.clj +291 -0
  167. package/src/datahike/codegen/java.clj +403 -0
  168. package/src/datahike/codegen/naming.cljc +33 -0
  169. package/src/datahike/codegen/native.clj +559 -0
  170. package/src/datahike/codegen/pod.clj +488 -0
  171. package/src/datahike/codegen/python.clj +838 -0
  172. package/src/datahike/codegen/report.clj +55 -0
  173. package/src/datahike/codegen/typescript.clj +262 -0
  174. package/src/datahike/codegen/validation.clj +145 -0
  175. package/src/datahike/config.cljc +294 -0
  176. package/src/datahike/connections.cljc +16 -0
  177. package/src/datahike/connector.cljc +265 -0
  178. package/src/datahike/constants.cljc +142 -0
  179. package/src/datahike/core.cljc +297 -0
  180. package/src/datahike/datom.cljc +459 -0
  181. package/src/datahike/db/interface.cljc +119 -0
  182. package/src/datahike/db/search.cljc +305 -0
  183. package/src/datahike/db/transaction.cljc +937 -0
  184. package/src/datahike/db/utils.cljc +338 -0
  185. package/src/datahike/db.cljc +956 -0
  186. package/src/datahike/experimental/unstructured.cljc +126 -0
  187. package/src/datahike/experimental/versioning.cljc +172 -0
  188. package/src/datahike/externs.js +31 -0
  189. package/src/datahike/gc.cljc +69 -0
  190. package/src/datahike/http/client.clj +188 -0
  191. package/src/datahike/http/writer.clj +79 -0
  192. package/src/datahike/impl/entity.cljc +218 -0
  193. package/src/datahike/index/interface.cljc +93 -0
  194. package/src/datahike/index/persistent_set.cljc +469 -0
  195. package/src/datahike/index/utils.cljc +44 -0
  196. package/src/datahike/index.cljc +32 -0
  197. package/src/datahike/js/api.cljs +172 -0
  198. package/src/datahike/js/api_macros.clj +22 -0
  199. package/src/datahike/js.cljs +163 -0
  200. package/src/datahike/json.cljc +209 -0
  201. package/src/datahike/lru.cljc +146 -0
  202. package/src/datahike/migrate.clj +39 -0
  203. package/src/datahike/norm/norm.clj +245 -0
  204. package/src/datahike/online_gc.cljc +252 -0
  205. package/src/datahike/pod.clj +155 -0
  206. package/src/datahike/pull_api.cljc +325 -0
  207. package/src/datahike/query.cljc +1945 -0
  208. package/src/datahike/query_stats.cljc +88 -0
  209. package/src/datahike/readers.cljc +62 -0
  210. package/src/datahike/remote.cljc +218 -0
  211. package/src/datahike/schema.cljc +228 -0
  212. package/src/datahike/schema_cache.cljc +42 -0
  213. package/src/datahike/spec.cljc +101 -0
  214. package/src/datahike/store.cljc +80 -0
  215. package/src/datahike/tools.cljc +308 -0
  216. package/src/datahike/transit.cljc +80 -0
  217. package/src/datahike/writer.cljc +239 -0
  218. package/src/datahike/writing.cljc +362 -0
  219. package/src/deps.cljs +1 -0
  220. package/src-hitchhiker-tree/datahike/index/hitchhiker_tree/insert.cljc +76 -0
  221. package/src-hitchhiker-tree/datahike/index/hitchhiker_tree/upsert.cljc +128 -0
  222. package/src-hitchhiker-tree/datahike/index/hitchhiker_tree.cljc +213 -0
  223. package/test/datahike/backward_compatibility_test/src/backward_test.clj +37 -0
  224. package/test/datahike/integration_test/config_record_file_test.clj +14 -0
  225. package/test/datahike/integration_test/config_record_test.clj +14 -0
  226. package/test/datahike/integration_test/depr_config_uri_test.clj +15 -0
  227. package/test/datahike/integration_test/return_map_test.clj +62 -0
  228. package/test/datahike/integration_test.cljc +67 -0
  229. package/test/datahike/norm/norm_test.clj +124 -0
  230. package/test/datahike/norm/resources/naming-and-sorting-test/001-a1-example.edn +5 -0
  231. package/test/datahike/norm/resources/naming-and-sorting-test/002-a2-example.edn +5 -0
  232. package/test/datahike/norm/resources/naming-and-sorting-test/003-tx-fn-test.edn +1 -0
  233. package/test/datahike/norm/resources/naming-and-sorting-test/004-tx-data-and-tx-fn-test.edn +5 -0
  234. package/test/datahike/norm/resources/naming-and-sorting-test/01-transact-basic-characters.edn +2 -0
  235. package/test/datahike/norm/resources/naming-and-sorting-test/02 add occupation.edn +5 -0
  236. package/test/datahike/norm/resources/naming-and-sorting-test/checksums.edn +12 -0
  237. package/test/datahike/norm/resources/simple-test/001-a1-example.edn +5 -0
  238. package/test/datahike/norm/resources/simple-test/002-a2-example.edn +5 -0
  239. package/test/datahike/norm/resources/simple-test/checksums.edn +4 -0
  240. package/test/datahike/norm/resources/tx-data-and-tx-fn-test/first/001-a1-example.edn +5 -0
  241. package/test/datahike/norm/resources/tx-data-and-tx-fn-test/first/002-a2-example.edn +5 -0
  242. package/test/datahike/norm/resources/tx-data-and-tx-fn-test/first/003-tx-fn-test.edn +1 -0
  243. package/test/datahike/norm/resources/tx-data-and-tx-fn-test/first/checksums.edn +6 -0
  244. package/test/datahike/norm/resources/tx-data-and-tx-fn-test/second/004-tx-data-and-tx-fn-test.edn +5 -0
  245. package/test/datahike/norm/resources/tx-data-and-tx-fn-test/second/checksums.edn +2 -0
  246. package/test/datahike/norm/resources/tx-fn-test/first/001-a1-example.edn +5 -0
  247. package/test/datahike/norm/resources/tx-fn-test/first/002-a2-example.edn +5 -0
  248. package/test/datahike/norm/resources/tx-fn-test/first/checksums.edn +4 -0
  249. package/test/datahike/norm/resources/tx-fn-test/second/003-tx-fn-test.edn +1 -0
  250. package/test/datahike/norm/resources/tx-fn-test/second/checksums.edn +2 -0
  251. package/test/datahike/test/api_test.cljc +895 -0
  252. package/test/datahike/test/array_test.cljc +40 -0
  253. package/test/datahike/test/attribute_refs/datoms_test.cljc +140 -0
  254. package/test/datahike/test/attribute_refs/db_test.cljc +42 -0
  255. package/test/datahike/test/attribute_refs/differences_test.cljc +515 -0
  256. package/test/datahike/test/attribute_refs/entity_test.cljc +89 -0
  257. package/test/datahike/test/attribute_refs/pull_api_test.cljc +320 -0
  258. package/test/datahike/test/attribute_refs/query_find_specs_test.cljc +59 -0
  259. package/test/datahike/test/attribute_refs/query_fns_test.cljc +130 -0
  260. package/test/datahike/test/attribute_refs/query_interop_test.cljc +47 -0
  261. package/test/datahike/test/attribute_refs/query_not_test.cljc +193 -0
  262. package/test/datahike/test/attribute_refs/query_or_test.cljc +137 -0
  263. package/test/datahike/test/attribute_refs/query_pull_test.cljc +156 -0
  264. package/test/datahike/test/attribute_refs/query_rules_test.cljc +176 -0
  265. package/test/datahike/test/attribute_refs/query_test.cljc +241 -0
  266. package/test/datahike/test/attribute_refs/temporal_search.cljc +22 -0
  267. package/test/datahike/test/attribute_refs/transact_test.cljc +220 -0
  268. package/test/datahike/test/attribute_refs/utils.cljc +128 -0
  269. package/test/datahike/test/cache_test.cljc +38 -0
  270. package/test/datahike/test/components_test.cljc +92 -0
  271. package/test/datahike/test/config_test.cljc +158 -0
  272. package/test/datahike/test/core_test.cljc +105 -0
  273. package/test/datahike/test/datom_test.cljc +44 -0
  274. package/test/datahike/test/db_test.cljc +54 -0
  275. package/test/datahike/test/entity_spec_test.cljc +159 -0
  276. package/test/datahike/test/entity_test.cljc +103 -0
  277. package/test/datahike/test/explode_test.cljc +143 -0
  278. package/test/datahike/test/filter_test.cljc +75 -0
  279. package/test/datahike/test/gc_test.cljc +159 -0
  280. package/test/datahike/test/http/server_test.clj +192 -0
  281. package/test/datahike/test/http/writer_test.clj +86 -0
  282. package/test/datahike/test/ident_test.cljc +32 -0
  283. package/test/datahike/test/index_test.cljc +345 -0
  284. package/test/datahike/test/insert.cljc +125 -0
  285. package/test/datahike/test/java_bindings_test.clj +6 -0
  286. package/test/datahike/test/listen_test.cljc +41 -0
  287. package/test/datahike/test/lookup_refs_test.cljc +266 -0
  288. package/test/datahike/test/lru_test.cljc +27 -0
  289. package/test/datahike/test/migrate_test.clj +297 -0
  290. package/test/datahike/test/model/core.cljc +376 -0
  291. package/test/datahike/test/model/invariant.cljc +142 -0
  292. package/test/datahike/test/model/rng.cljc +82 -0
  293. package/test/datahike/test/model_test.clj +217 -0
  294. package/test/datahike/test/nodejs_test.cljs +262 -0
  295. package/test/datahike/test/online_gc_test.cljc +475 -0
  296. package/test/datahike/test/pod_test.clj +369 -0
  297. package/test/datahike/test/pull_api_test.cljc +474 -0
  298. package/test/datahike/test/purge_test.cljc +144 -0
  299. package/test/datahike/test/query_aggregates_test.cljc +101 -0
  300. package/test/datahike/test/query_find_specs_test.cljc +52 -0
  301. package/test/datahike/test/query_fns_test.cljc +523 -0
  302. package/test/datahike/test/query_interop_test.cljc +47 -0
  303. package/test/datahike/test/query_not_test.cljc +189 -0
  304. package/test/datahike/test/query_or_test.cljc +158 -0
  305. package/test/datahike/test/query_pull_test.cljc +147 -0
  306. package/test/datahike/test/query_rules_test.cljc +248 -0
  307. package/test/datahike/test/query_stats_test.cljc +218 -0
  308. package/test/datahike/test/query_test.cljc +984 -0
  309. package/test/datahike/test/schema_test.cljc +424 -0
  310. package/test/datahike/test/specification_test.cljc +30 -0
  311. package/test/datahike/test/store_test.cljc +78 -0
  312. package/test/datahike/test/stress_test.cljc +57 -0
  313. package/test/datahike/test/time_variance_test.cljc +518 -0
  314. package/test/datahike/test/tools_test.clj +134 -0
  315. package/test/datahike/test/transact_test.cljc +518 -0
  316. package/test/datahike/test/tuples_test.cljc +564 -0
  317. package/test/datahike/test/unstructured_test.cljc +291 -0
  318. package/test/datahike/test/upsert_impl_test.cljc +205 -0
  319. package/test/datahike/test/upsert_test.cljc +363 -0
  320. package/test/datahike/test/utils.cljc +110 -0
  321. package/test/datahike/test/validation_test.cljc +48 -0
  322. package/test/datahike/test/versioning_test.cljc +56 -0
  323. package/test/datahike/test.cljc +66 -0
  324. package/tests.edn +24 -0
@@ -0,0 +1,325 @@
1
+ # Time Variance
2
+
3
+ As a [temporal database](https://en.wikipedia.org/wiki/Temporal_database), Datahike tracks transaction time for every change, enabling auditing, analytics, and time-travel queries. Each transaction records `:db/txInstant`, allowing you to view data at different points in time.
4
+
5
+ ## Time Travel Views
6
+
7
+ | View | Function | Returns | Use Case |
8
+ |------|----------|---------|----------|
9
+ | **Current** | `@conn` or `(d/db conn)` | Latest state | Normal queries |
10
+ | **As-of** | `(d/as-of db date)` | State at specific time | "What did the data look like on July 1st?" |
11
+ | **History** | `(d/history db)` | All versions (current + historical) | Audit trails, change analysis |
12
+ | **Since** | `(d/since db date)` | Changes after specific time | "What changed since yesterday?" |
13
+
14
+ **Related:** For git-like branching and merging of database snapshots, see [Versioning](versioning.md). For removing old historical data from storage, see [Garbage Collection](gc.md).
15
+
16
+ ## Disabling History
17
+
18
+ If you don't need time-travel queries, disable history tracking to save storage:
19
+
20
+ ```clojure
21
+ (require '[datahike.api :as d])
22
+
23
+ (d/create-database {:store {:backend :memory :id #uuid "550e8400-e29b-41d4-a716-446655440000"} :keep-history? false})
24
+ ```
25
+
26
+ **Trade-off:** Saves storage and improves write performance, but removes as-of/history/since queries and purging capabilities.
27
+
28
+ ## Setup for Examples
29
+
30
+ All examples below use this shared setup:
31
+
32
+ ```clojure
33
+ (require '[datahike.api :as d])
34
+
35
+ ;; Simple schema for person data
36
+ (def schema [{:db/ident :name
37
+ :db/valueType :db.type/string
38
+ :db/unique :db.unique/identity
39
+ :db/index true
40
+ :db/cardinality :db.cardinality/one}
41
+ {:db/ident :age
42
+ :db/valueType :db.type/long
43
+ :db/cardinality :db.cardinality/one}])
44
+
45
+ (def cfg {:store {:backend :memory :id #uuid "550e8400-e29b-41d4-a716-446655440001"} :initial-tx schema})
46
+
47
+ (d/create-database cfg)
48
+ (def conn (d/connect cfg))
49
+
50
+ ;; Query to find names and ages
51
+ (def query '[:find ?n ?a :where [?e :name ?n] [?e :age ?a]])
52
+ ```
53
+
54
+ ## DB (Current State)
55
+
56
+ `@conn` or `(d/db conn)` returns the current state - the most common view for queries:
57
+
58
+ ```clojure
59
+
60
+ ;; add first data
61
+ (d/transact conn {:tx-data [{:name "Alice" :age 25}]})
62
+
63
+ ;; define simple query for name and age
64
+ (def query '[:find ?n ?a :where [?e :name ?n] [?e :age ?a]])
65
+
66
+ (d/q query @conn)
67
+ ;; => #{["Alice" 25]}
68
+
69
+ ;; update the entity
70
+ (d/transact conn {:tx-data [{:db/id [:name "Alice"] :age 30}]})
71
+
72
+ ;; `db` reflects the latest state of the database
73
+ (d/q query @conn)
74
+ ;; => #{["Alice" 30]}
75
+ ```
76
+
77
+ ## As-Of
78
+
79
+ You can query the database at a specific point in time using `as-of`:
80
+
81
+ ```clojure
82
+ (require '[datahike.api :as d])
83
+
84
+ ;; define simple schema
85
+ (def schema [{:db/ident :name
86
+ :db/valueType :db.type/string
87
+ :db/unique :db.unique/identity
88
+ :db/index true
89
+ :db/cardinality :db.cardinality/one}
90
+ {:db/ident :age
91
+ :db/valueType :db.type/long
92
+ :db/cardinality :db.cardinality/one}])
93
+
94
+ ;; create our temporal database
95
+ (def cfg {:store {:backend :memory :id #uuid "550e8400-e29b-41d4-a716-446655440003"} :initial-tx schema})
96
+
97
+ (d/create-database cfg)
98
+
99
+ (def conn (d/connect cfg))
100
+
101
+
102
+ ;; add first data
103
+ (d/transact conn {:tx-data [{:name "Alice" :age 25}]})
104
+
105
+ (def first-date (java.util.Date.))
106
+
107
+ ;; define simple query for name and age
108
+ (def query '[:find ?n ?a :where [?e :name ?n] [?e :age ?a]])
109
+
110
+ (d/q query @conn)
111
+ ;; => #{["Alice" 25]}
112
+
113
+ ;; update the entity
114
+ (d/transact conn {:tx-data [{:db/id [:name "Alice"] :age 30}]})
115
+
116
+ ;; let's compare the current and the as-of value:
117
+ (d/q query @conn)
118
+ ;; => #{["Alice" 30]}
119
+
120
+ (d/q query (d/as-of @conn first-date))
121
+ ;; => #{["Alice" 25]}
122
+ ```
123
+
124
+ ## History
125
+
126
+ For querying all data over the whole time span you may use `history` which joins
127
+ current and all historical data:
128
+
129
+ ```clojure
130
+ (require '[datahike.api :as d])
131
+
132
+ ;; define simple schema
133
+ (def schema [{:db/ident :name
134
+ :db/valueType :db.type/string
135
+ :db/unique :db.unique/identity
136
+ :db/index true
137
+ :db/cardinality :db.cardinality/one}
138
+ {:db/ident :age
139
+ :db/valueType :db.type/long
140
+ :db/cardinality :db.cardinality/one}])
141
+
142
+ ;; create our temporal database
143
+ (def cfg {:store {:backend :memory :id #uuid "550e8400-e29b-41d4-a716-446655440004"} :initial-tx schema})
144
+
145
+ (d/create-database cfg)
146
+
147
+ (def conn (d/connect cfg))
148
+
149
+ ;; add first data
150
+ (d/transact conn {:tx-data [{:name "Alice" :age 25}]})
151
+
152
+ ;; define simple query for name and age
153
+ (def query '[:find ?n ?a :where [?e :name ?n] [?e :age ?a]])
154
+
155
+ ;; history should have only one entry
156
+ (d/q query (d/history @conn))
157
+ ;; => #{["Alice" 25]}
158
+
159
+ ;; update the entity
160
+ (d/transact conn {:tx-data [{:db/id [:name "Alice"] :age 30}]})
161
+
162
+ ;; both entries are present
163
+ (d/q query (d/history @conn))
164
+ ;; => #{["Alice" 30] ["Alice" 25]}
165
+ ```
166
+
167
+ ## Since
168
+
169
+ Changes since a specific point in time can be searched by using the `since`
170
+ database:
171
+
172
+ ```clojure
173
+ (require '[datahike.api :as d])
174
+
175
+ ;; define simple schema
176
+ (def schema [{:db/ident :name
177
+ :db/valueType :db.type/string
178
+ :db/unique :db.unique/identity
179
+ :db/index true
180
+ :db/cardinality :db.cardinality/one}
181
+ {:db/ident :age
182
+ :db/valueType :db.type/long
183
+ :db/cardinality :db.cardinality/one}])
184
+
185
+ ;; create our temporal database
186
+ (def cfg {:store {:backend :memory :id #uuid "550e8400-e29b-41d4-a716-446655440005"} :initial-tx schema})
187
+
188
+ (d/create-database cfg)
189
+
190
+ (def conn (d/connect cfg))
191
+
192
+
193
+ ;; add first data
194
+ (d/transact conn {:tx-data [{:name "Alice" :age 25}]})
195
+
196
+ (def first-date (java.util.Date.))
197
+
198
+ ;; define simple query for name and age
199
+ (def query '[:find ?n ?a :where [?e :name ?n] [?e :age ?a]])
200
+
201
+ (d/q query @conn)
202
+ ;; => #{["Alice" 25]}
203
+
204
+ ;; update the entity
205
+ (d/transact conn {:tx-data [{:db/id [:name "Alice"] :age 30}]})
206
+
207
+ ;; let's compare the current and the as-of value:
208
+ (d/q query @conn)
209
+ ;; => #{["Alice" 30]}
210
+
211
+ ;; now we want to know any additions after a specific time
212
+ (d/q query (d/since @conn first-date))
213
+ ;; => {}, because :name was transacted before the first date
214
+
215
+ ;; let's build a query where we use the latest db to find the name and the since db to find out who's age changed
216
+ (d/q '[:find ?n ?a
217
+ :in $ $since
218
+ :where
219
+ [$ ?e :name ?n]
220
+ [$since ?e :age ?a]]
221
+ @conn
222
+ (d/since @conn first-date))
223
+ ```
224
+
225
+ ## Meta Entity
226
+
227
+ With each transaction a meta entity is added to the index that stores the
228
+ current point in time in the `:db/txInstant` attribute.
229
+
230
+ With this data present in the current index, you can search and analyze them for
231
+ your purposes.
232
+
233
+ ```clojure
234
+ (require '[datahike.api :as d])
235
+
236
+ ;; define simple schema
237
+ (def schema [{:db/ident :name
238
+ :db/valueType :db.type/string
239
+ :db/unique :db.unique/identity
240
+ :db/index true
241
+ :db/cardinality :db.cardinality/one}
242
+ {:db/ident :age
243
+ :db/valueType :db.type/long
244
+ :db/cardinality :db.cardinality/one}])
245
+
246
+ ;; create our temporal database
247
+ (def cfg {:store {:backend :memory :id #uuid "550e8400-e29b-41d4-a716-446655440006"} :initial-tx schema})
248
+
249
+ (d/create-database cfg)
250
+
251
+ (def conn (d/connect cfg))
252
+
253
+ ;; add first data
254
+ (d/transact conn {:tx-data [{:name "Alice" :age 25}]})
255
+
256
+ ;; let's find all transaction dates, should be two: one for the schema and one
257
+ ;; for the first data
258
+ (d/q '[:find ?t :where [_ :db/txInstant ?t]] @conn)
259
+ ;; => #{[#inst "2019-08-16T11:40:28.794-00:00"] [#inst "2019-08-16T11:40:26.587-00:00"]}
260
+
261
+ ;; you might join over the tx id to get the date of any transaction
262
+ (d/q '[:find ?n ?t :where [_ :name ?n ?tx] [?tx :db/txInstant ?t]] @conn)
263
+ ;; => #{["Alice" #inst "2019-08-16T11:40:28.794-00:00"]}
264
+ ```
265
+
266
+ ## Data Purging
267
+
268
+ **Retraction vs Purging:** Normal retractions preserve data in history. Purging permanently deletes data from both current and historical indices.
269
+
270
+ **When to purge:** Privacy regulations (GDPR, HIPAA, CCPA) requiring "right to deletion", sensitive data removal, or retention policy enforcement.
271
+
272
+ **⚠️ Warning:** Purging is permanent and irreversible. Use only when legally required or explicitly needed.
273
+
274
+ ### Purge Operations
275
+
276
+ - **`:db/purge`** - Remove specific datom (entity, attribute, value)
277
+ - **`:db.purge/attribute`** - Remove all values for an attribute on an entity
278
+ - **`:db.purge/entity`** - Remove entire entity and all its attributes
279
+ - **`:db.history.purge/before`** - Remove all historical data before a date (retention policy cleanup)
280
+
281
+ ```clojure
282
+ (require '[datahike.api :as d])
283
+
284
+ ;; define simple schema
285
+ (def schema [{:db/ident :name
286
+ :db/valueType :db.type/string
287
+ :db/unique :db.unique/identity
288
+ :db/index true
289
+ :db/cardinality :db.cardinality/one}
290
+ {:db/ident :age
291
+ :db/valueType :db.type/long
292
+ :db/cardinality :db.cardinality/one}])
293
+
294
+ ;; create our temporal database
295
+ (def cfg {:store {:backend :memory :id #uuid "550e8400-e29b-41d4-a716-446655440007"} :initial-tx schema})
296
+
297
+ (d/create-database cfg)
298
+
299
+ (def conn (d/connect cfg))
300
+
301
+
302
+ ;; add data
303
+ (d/transact conn {:tx-data [{:name "Alice" :age 25}]})
304
+
305
+ ;; define simple query for name and age
306
+ (def query '[:find ?n ?a :where [?e :name ?n] [?e :age ?a]])
307
+
308
+ (d/q query @conn)
309
+ ;; => #{["Alice" 25]}
310
+
311
+ (d/transact conn {:tx-data [[:db.purge/entity [:name "Alice"]]]})
312
+
313
+ ;; data was removed from current database view
314
+ (d/q query @conn)
315
+ ;; => #{}
316
+
317
+ ;; data was also removed from history
318
+ (d/q query (d/history @conn))
319
+ ;; => #{}
320
+ ```
321
+
322
+ **Requirements for purging:**
323
+ - History must be enabled (`:keep-history? true`)
324
+ - Cannot purge schema attributes
325
+ - Use retractions for normal data lifecycle - reserve purging for compliance requirements
@@ -0,0 +1,167 @@
1
+ # Unstructured Input Support
2
+
3
+ This experimental feature enables automatic schema inference from unstructured data (like JSON/EDN), allowing you to store richly nested data in Datahike with minimal setup.
4
+
5
+ ## Overview
6
+
7
+ The unstructured input feature automatically:
8
+
9
+ - Infers schema definitions from your data structure
10
+ - Handles nested objects, collections, and primitive values
11
+ - Converts complex data structures to Datahike's entity model
12
+ - Works with both schema-on-read and schema-on-write configurations
13
+
14
+ ## Basic Usage
15
+
16
+ ```clojure
17
+ (require '[datahike.api :as d])
18
+ (require '[datahike.experimental.unstructured :as du])
19
+
20
+ ;; Initialize database (schema-on-read is best for unstructured data)
21
+ (def cfg {:store {:backend :memory :id #uuid "550e8400-e29b-41d4-a716-446655440000"}
22
+ :schema-flexibility :read})
23
+ (d/create-database cfg)
24
+ (def conn (d/connect cfg))
25
+
26
+ ;; Insert complex unstructured data with a single function call
27
+ (du/transact-unstructured conn
28
+ {:name "Alice"
29
+ :age 42
30
+ :addresses [{:city "New York" :primary true}
31
+ {:city "London" :primary false}]
32
+ :skills ["programming" "design"]})
33
+ ```
34
+
35
+ ## Type and Cardinality Mapping
36
+
37
+ The feature maps runtime values to Datahike types according to this conversion:
38
+
39
+ | Input Type | Datahike Type | Cardinality |
40
+ |------------|---------------|-------------|
41
+ | Integer | `:db.type/long` | `:db.cardinality/one` |
42
+ | Float | `:db.type/float` | `:db.cardinality/one` |
43
+ | Double | `:db.type/double` | `:db.cardinality/one` |
44
+ | String | `:db.type/string` | `:db.cardinality/one` |
45
+ | Boolean | `:db.type/boolean` | `:db.cardinality/one` |
46
+ | Keyword | `:db.type/keyword` | `:db.cardinality/one` |
47
+ | Symbol | `:db.type/symbol` | `:db.cardinality/one` |
48
+ | UUID | `:db.type/uuid` | `:db.cardinality/one` |
49
+ | Date | `:db.type/instant` | `:db.cardinality/one` |
50
+ | Map | `:db.type/ref` | `:db.cardinality/one` |
51
+ | Vector/List (with values) | Based on first value | `:db.cardinality/many` |
52
+ | Empty Vector/List | No schema generated until values exist | - |
53
+
54
+ ### Array Handling and Cardinality-Many
55
+
56
+ When you provide an array/vector value, the system automatically:
57
+
58
+ 1. Infers the element type from the first item in the collection
59
+ 2. Sets the attribute's cardinality to `:db.cardinality/many`
60
+ 3. Stores each value in the array as a separate datom
61
+
62
+ This means you can model one-to-many relationships naturally:
63
+
64
+ ```clojure
65
+ ;; This will create tags with cardinality/many
66
+ (du/transact-unstructured conn
67
+ {:name "Alice"
68
+ :tags ["clojure" "database" "datalog"]})
69
+
70
+ ;; Later you can add more tags without losing existing ones
71
+ (d/transact conn [{:db/id [:name "Alice"]
72
+ :tags ["awesome"]}])
73
+
74
+ ;; All tags will be preserved
75
+ ;; => ["clojure" "database" "datalog" "awesome"]
76
+ ```
77
+
78
+ ## Advanced Usage
79
+
80
+ ### Working with the Schema Directly
81
+
82
+ If you need more control, you can process the data first to examine the schema:
83
+
84
+ ```clojure
85
+ ;; Process data to get both schema and transaction data
86
+ (def result (du/process-unstructured-data
87
+ {:user {:name "Bob"
88
+ :roles ["admin" "user"]}}))
89
+
90
+ ;; Examine the inferred schema
91
+ (println (:schema result))
92
+ ;; => [{:db/ident :user, :db/valueType :db.type/ref, :db/cardinality :db.cardinality/one}
93
+ ;; {:db/ident :name, :db/valueType :db.type/string, :db/cardinality :db.cardinality/one}
94
+ ;; {:db/ident :roles, :db/valueType :db.type/string, :db/cardinality :db.cardinality/many}]
95
+
96
+ ;; Manually transact the schema first, if needed
97
+ (d/transact conn (:schema result))
98
+
99
+ ;; Then transact the data
100
+ (d/transact conn (:tx-data result))
101
+ ```
102
+
103
+ ### Schema-on-Write Databases
104
+
105
+ For schema-on-write databases (the default), the feature automatically:
106
+
107
+ 1. Infers schema definitions from your data
108
+ 2. Adds any missing schema attributes in the same transaction
109
+ 3. Validates compatibility with existing schema
110
+ 4. Throws helpful errors if there's a conflict
111
+
112
+ This makes the API particularly powerful for schema-on-write - you get the benefits of schema validation without the manual work of defining schemas upfront.
113
+
114
+ ```clojure
115
+ ;; With a schema-on-write database
116
+ (def cfg {:store {:backend :memory :id #uuid "550e8400-e29b-41d4-a716-446655440001"}})
117
+ (d/create-database cfg)
118
+ (def conn (d/connect cfg))
119
+
120
+ ;; This will add schema and data in a single transaction
121
+ (du/transact-unstructured conn {:name "Charlie" :age 35})
122
+
123
+ ;; Later, you can add entities with new attributes
124
+ ;; The schema will automatically be extended
125
+ (du/transact-unstructured conn {:name "Diana"
126
+ :age 28
127
+ :email "diana@example.com"}) ;; New attribute!
128
+ ```
129
+
130
+ The feature supports incremental schema evolution - as your data model grows, the schema grows with it.
131
+
132
+ ### Schema Conflicts and Error Handling
133
+
134
+ When using `transact-unstructured` with schema-on-write databases, the feature automatically detects schema conflicts. For example, if you've defined `:score` as a number but try to insert it as a string:
135
+
136
+ ```clojure
137
+ ;; First, establish schema for score as a number
138
+ (d/transact conn [{:db/ident :score
139
+ :db/valueType :db.type/long
140
+ :db/cardinality :db.cardinality/one}])
141
+
142
+ ;; Add a valid score
143
+ (d/transact conn [{:score 100}])
144
+
145
+ ;; This will fail with a schema conflict error:
146
+ (du/transact-unstructured conn {:name "Bob", :score "High"})
147
+ ;; => ExceptionInfo: Schema conflict detected with existing database schema
148
+ ;; {:conflicts [{:attr :score, :conflict :value-type,
149
+ ;; :existing :db.type/long, :inferred :db.type/string}]}
150
+ ```
151
+
152
+ The error messages provide details about the specific conflicts, making it easy to diagnose and fix issues.
153
+
154
+ ## Future Directions
155
+
156
+ This experimental feature will likely be enhanced with:
157
+
158
+ - Better handling of schema conflicts
159
+ - Schema upgrading for schema-on-write databases
160
+ - Performance optimizations for large datasets
161
+ - Special handling for additional data types
162
+
163
+ ## Limitations
164
+
165
+ - Complex querying across nested entities requires knowledge of how the data was transformed
166
+ - For schema-on-write, existing schema attributes must be compatible
167
+ - Very large nested data structures might require chunking (not yet implemented)