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,895 @@
1
+ (ns datahike.test.api-test
2
+ (:require
3
+ ;; Load hitchhiker-tree FIRST before anything that loads datahike.index
4
+ #?(:clj [datahike.index.hitchhiker-tree])
5
+ #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]]
6
+ :clj [clojure.test :as t :refer [is are deftest testing]])
7
+ [datahike.test.utils :as utils]
8
+ [datahike.api :as d]
9
+ [datahike.db :as db]
10
+ [datahike.query :as dq]
11
+ [datahike.db.interface :as dbi]
12
+ [datahike.index.interface :as di]
13
+ [datahike.constants :refer [tx0]]))
14
+
15
+ (deftest test-transact-docs
16
+ (let [cfg {:store {:backend :memory
17
+ :id #uuid "ba5b1000-0000-0000-0000-000000000001"}
18
+ :keep-history? false
19
+ :schema-flexibility :read}
20
+ conn (utils/setup-db cfg)
21
+ dvec #(vector (:e %) (:a %) (:v %))]
22
+ ;; add a single datom to an existing entity (1)
23
+ (is (= [[1 :name "Ivan"]]
24
+ (map dvec (:tx-data (d/transact conn {:tx-data [[:db/add 1 :name "Ivan"]]})))))
25
+
26
+ ;; retract a single datom
27
+ (is (= [[1 :name "Ivan"]]
28
+ (map dvec (:tx-data (d/transact conn {:tx-data [[:db/retract 1 :name "Ivan"]]})))))
29
+
30
+ ;; retract single entity attribute
31
+ (is (= []
32
+ (map dvec (:tx-data (d/transact conn {:tx-data [[:db.fn/retractAttribute 1 :name]]})))))
33
+
34
+ ;; retract all entity attributes (effectively deletes entity)
35
+ (is (= []
36
+ (map dvec (:tx-data (d/transact conn {:tx-data [[:db.fn/retractEntity 1]]})))))
37
+
38
+ ;; create a new entity (`-1`, as any other negative value, is a tempid
39
+ ;; that will be replaced with DataScript to a next unused eid)
40
+ (is (= '([2 :name "Ivan"])
41
+ (map dvec (:tx-data (d/transact conn {:tx-data [[:db/add -1 :name "Ivan"]]})))))
42
+
43
+ ;; check assigned id (here `*1` is a result returned from previous `transact` call)
44
+ (is (= {-1 3, :db/current-tx 536870918}
45
+ (:tempids (d/transact conn {:tx-data [[:db/add -1 :name "Ivan"]]}))))
46
+
47
+ ;; check actual datoms inserted
48
+ (is (= '([4 :name "Ivan"])
49
+ (map dvec (:tx-data (d/transact conn {:tx-data [[:db/add -1 :name "Ivan"]]}))))) ; => [#datahike/Datom [296 :name "Ivan"]]
50
+
51
+ ;; tempid can also be a string
52
+ (is (= {"ivan" 5, :db/current-tx 536870920}
53
+ (:tempids (d/transact conn {:tx-data [[:db/add "ivan" :name "Ivan"]]}))))
54
+
55
+ ;; reference another entity (must exist)
56
+ (is (= '([6 :friend 296])
57
+ (map dvec (:tx-data (d/transact conn {:tx-data [[:db/add -1 :friend 296]]})))))
58
+
59
+ ;; create an entity and set multiple attributes (in a single transaction
60
+ ;; equal tempids will be replaced with the same unused yet entid)
61
+ (is (= '([7 :name "Ivan"] [7 :likes "fries"] [7 :likes "pizza"] [7 :friend 296])
62
+ (map dvec (:tx-data (d/transact conn {:tx-data [[:db/add -1 :name "Ivan"]
63
+ [:db/add -1 :likes "fries"]
64
+ [:db/add -1 :likes "pizza"]
65
+ [:db/add -1 :friend 296]]})))))
66
+
67
+ ;; create an entity and set multiple attributes (alternative map form)
68
+ (is (= '([8 :name "Ivan"] [8 :likes ["fries" "pizza"]] [8 :friend 296])
69
+ (map dvec (:tx-data (d/transact conn {:tx-data [{:db/id -1
70
+ :name "Ivan"
71
+ :likes ["fries" "pizza"]
72
+ :friend 296}]})))))
73
+
74
+ ;; update an entity (alternative map form). Can’t retract attributes in
75
+ ;; map form. For cardinality many attrs, value (fish in this example)
76
+ ;; will be added to the list of existing values
77
+ (is (= '([296 :name "Oleg"] [296 :likes ["fish"]])
78
+ (map dvec (:tx-data (d/transact conn {:tx-data [{:db/id 296
79
+ :name "Oleg"
80
+ :likes ["fish"]}]})))))
81
+
82
+ ;; ref attributes can be specified as nested map, that will create netsed entity as well
83
+ (is (= '([297 :name "Oleg"] [297 :friend {:db/id -2, :name "Sergey"}])
84
+ (map dvec (:tx-data (d/transact conn {:tx-data [{:db/id -1
85
+ :name "Oleg"
86
+ :friend {:db/id -2
87
+ :name "Sergey"}}]})))))
88
+
89
+ ;; schema is needed for using a reverse attribute
90
+ (is (= '([298 :db/valueType :db.type/ref] [298 :db/cardinality :db.cardinality/one] [298 :db/ident :friend])
91
+ (map dvec (:tx-data (d/transact conn {:tx-data [{:db/valueType :db.type/ref
92
+ :db/cardinality :db.cardinality/one
93
+ :db/ident :friend}]})))))
94
+
95
+ ;; reverse attribute name can be used if you want created entity to become
96
+ ;; a value in another entity reference
97
+ (is (= '([299 :name "Oleg"] [296 :friend 299])
98
+ (map dvec (:tx-data (d/transact conn {:tx-data [{:db/id -1
99
+ :name "Oleg"
100
+ :_friend 296}]})))))
101
+
102
+ ;; equivalent to
103
+ (is (= '([300 :name "Oleg"] [296 :friend 300])
104
+ (map dvec (:tx-data (d/transact conn {:tx-data [{:db/id -1, :name "Oleg"}
105
+ {:db/id 296, :friend -1}]})))))
106
+
107
+ ;; deprecated api
108
+ (is (= '([301 :name "Oleg"] [301 :likes "pie"] [301 :likes "dates"] [301 :friend 297])
109
+ (map dvec (:tx-data (d/transact conn [[:db/add -1 :name "Oleg"]
110
+ [:db/add -1 :likes "pie"]
111
+ [:db/add -1 :likes "dates"]
112
+ [:db/add -1 :friend 297]])))))
113
+
114
+ ;; lazy sequence
115
+ (is (= '([302 :name "Oleg"] [302 :likes "pie"] [302 :likes "dates"] [302 :friend 297])
116
+ (map dvec (:tx-data (d/transact conn (take 4 [[:db/add -1 :name "Oleg"]
117
+ [:db/add -1 :likes "pie"]
118
+ [:db/add -1 :likes "dates"]
119
+ [:db/add -1 :friend 297]]))))))
120
+
121
+ ;; incorrect arguments
122
+ (is (thrown? clojure.lang.ExceptionInfo (d/transact conn nil)))
123
+ (is (thrown? clojure.lang.ExceptionInfo (d/transact conn :foo)))
124
+ (is (thrown? clojure.lang.ExceptionInfo (d/transact conn 1)))
125
+ (is (thrown? clojure.lang.ExceptionInfo (d/transact conn {:foo "bar"})))
126
+ (d/release conn)))
127
+
128
+ (deftest test-transact!-docs
129
+ (let [cfg {:store {:backend :memory
130
+ :id #uuid "ba5b1000-0000-0000-0000-000000000001"}
131
+ :keep-history? false
132
+ :schema-flexibility :read}
133
+ conn (utils/setup-db cfg)]
134
+ ;; add a single datom to an existing entity (1)
135
+ (is (d/transact! conn [[:db/add 1 :name "Ivan"]]))
136
+ (d/release conn)))
137
+
138
+ ;; retract a single datom
139
+
140
+ (deftest test-pull-docs
141
+ (let [cfg {:store {:backend :memory
142
+ :id #uuid "0011aaaa-0000-0000-0000-000000000001"}
143
+ :initial-tx [{:db/ident :likes
144
+ :db/cardinality :db.cardinality/many}
145
+ {:db/ident :friends
146
+ :db/cardinality :db.cardinality/many}]
147
+ :keep-history? false
148
+ :schema-flexibility :read}
149
+ conn (utils/setup-db cfg)
150
+ dvec #(vector (:e %) (:a %) (:v %))]
151
+ (is (d/transact conn [{:db/id 1
152
+ :name "Ivan"
153
+ :likes :pizza
154
+ :friends 2}
155
+ {:db/id 2
156
+ :name "Oleg"}]))
157
+
158
+ (is (= {:db/id 1,
159
+ :name "Ivan"
160
+ :likes [:pizza]
161
+ :friends [{:db/id 2, :name "Oleg"}]}
162
+ (d/pull @conn '{:selector [:db/id :name :likes {:friends [:db/id :name]}] :eid 1})))
163
+
164
+ (is (= {:db/id 1,
165
+ :name "Ivan"
166
+ :likes [:pizza]
167
+ :friends [{:db/id 2, :name "Oleg"}]}
168
+ (d/pull @conn '[:db/id :name :likes {:friends [:db/id :name]}] 1)))
169
+ (d/release conn)))
170
+
171
+ (deftest test-pull-many-docs
172
+ (let [cfg {:store {:backend :memory
173
+ :id #uuid "ba5b1000-0000-0000-0000-000000000001"}
174
+ :initial-tx [[:db/add 1 :name "Ivan"]
175
+ [:db/add 2 :name "Oleg"]]
176
+ :keep-history? false
177
+ :schema-flexibility :read}
178
+ conn (utils/setup-db cfg)]
179
+ (is (= (d/pull-many @conn [:db/id :name] [1 2])
180
+ [{:db/id 1, :name "Ivan"}
181
+ {:db/id 2, :name "Oleg"}]))
182
+ (d/release conn)))
183
+
184
+ (deftest test-q-docs
185
+ (let [cfg {:store {:backend :memory
186
+ :id #uuid "00000000-0000-0000-0000-00000000000a"}
187
+ :initial-tx [[:db/add -1 :name "Ivan"]
188
+ [:db/add -1 :likes "fries"]
189
+ [:db/add -1 :likes "pizza"]
190
+ [:db/add -1 :friend 296]]
191
+ :keep-history? false
192
+ :schema-flexibility :read}
193
+ conn (utils/setup-db cfg)]
194
+ (is (= #{["fries"] ["candy"] ["pie"] ["pizza"]}
195
+ (d/q '[:find ?value :where [_ :likes ?value]]
196
+ #{[1 :likes "fries"]
197
+ [2 :likes "candy"]
198
+ [3 :likes "pie"]
199
+ [4 :likes "pizza"]})))
200
+
201
+ (is (= #{["fries"] ["candy"] ["pie"] ["pizza"]}
202
+ (d/q {:query '[:find ?value :where [_ :likes ?value]]
203
+ :args [#{[1 :likes "fries"]
204
+ [2 :likes "candy"]
205
+ [3 :likes "pie"]
206
+ [4 :likes "pizza"]}]})))
207
+
208
+ (is (= #{["fries"]}
209
+ (d/q {:query '[:find ?value :where [_ :likes ?value]]
210
+ :offset 2
211
+ :limit 1
212
+ :args [#{[1 :likes "fries"]
213
+ [2 :likes "candy"]
214
+ [3 :likes "pie"]
215
+ [4 :likes "pizza"]}]})))
216
+
217
+ (is (= #{["fries"] ["pie"] ["candy"] ["pizza"]}
218
+ (d/q {:query '[:find ?value :where [_ :likes ?value]]
219
+ :offset 0
220
+ :timeout 50
221
+ :args [#{[1 :likes "fries"]
222
+ [2 :likes "candy"]
223
+ [3 :likes "pie"]
224
+ [4 :likes "pizza"]}]})))
225
+
226
+ (is (= #{["candy"] ["fries"]}
227
+ (d/q {:query '[:find ?value :where [_ :likes ?value]]
228
+ :offset 2
229
+ :timeout 50
230
+ :args [#{[1 :likes "fries"]
231
+ [2 :likes "candy"]
232
+ [3 :likes "pie"]
233
+ [4 :likes "pizza"]}]})))
234
+
235
+ (is (= #{["fries"] ["candy"] ["pie"] ["pizza"]}
236
+ (d/q '{:find [?value] :where [[_ :likes ?value]]}
237
+ #{[1 :likes "fries"]
238
+ [2 :likes "candy"]
239
+ [3 :likes "pie"]
240
+ [4 :likes "pizza"]})))
241
+
242
+ (is (= #{["fries"] ["candy"] ["pie"] ["pizza"]}
243
+ (d/q {:query '{:find [?value] :where [[_ :likes ?value]]}
244
+ :args [#{[1 :likes "fries"]
245
+ [2 :likes "candy"]
246
+ [3 :likes "pie"]
247
+ [4 :likes "pizza"]}]})))
248
+
249
+ (is (= #{["fries"] ["candy"] ["pie"] ["pizza"]}
250
+ (d/q {:query "[:find ?value :where [_ :likes ?value]]"
251
+ :args [#{[1 :likes "fries"]
252
+ [2 :likes "candy"]
253
+ [3 :likes "pie"]
254
+ [4 :likes "pizza"]}]})))
255
+
256
+ ;; TODO better testing
257
+ (is (= [{:db/id 1, :friend 296, :likes "pizza", :name "Ivan"}]
258
+ (d/q '[:find [(pull ?e [*]) ...]
259
+ :where [?e ?a ?v]]
260
+ @conn)))
261
+ (d/release conn)))
262
+
263
+ (deftest test-with-docs
264
+ (let [cfg {:store {:backend :memory
265
+ :id #uuid "00000111-7bbb-0000-0000-000000000001"}
266
+ :keep-history? false
267
+ :schema-flexibility :read}
268
+ conn (utils/setup-db cfg)
269
+ dvec #(vector (:e %) (:a %) (:v %))]
270
+ ;; add a single datom to an existing entity (1)
271
+ (let [res (d/with @conn {:tx-data [[:db/add 1 :name "Ivan"]]})]
272
+ (is (= #{:db/txInstant}
273
+ (set (keys (:tx-meta res)))))
274
+ (is (= '([1 :name "Ivan"])
275
+ (map dvec (:tx-data res)))))
276
+ (let [res (d/with @conn {:tx-data [[:db/add 1 :name "Ivan"]]
277
+ :tx-meta {:foo :bar}})]
278
+ (is (= {:foo :bar}
279
+ (dissoc (:tx-meta res) :db/txInstant)))
280
+ (is (= '([1 :name "Ivan"])
281
+ (map dvec (:tx-data res)))))
282
+ (d/release conn)))
283
+
284
+ ;; TODO testing properly on what?
285
+ (deftest test-db-docs
286
+ (let [cfg {:store {:backend :memory
287
+ :id #uuid "00000dbb-0000-0000-0000-000000000001"}
288
+ :keep-history? false
289
+ :schema-flexibility :read}
290
+ conn (utils/setup-db cfg)]
291
+ (is (= datahike.db.DB
292
+ (type (d/db conn))))
293
+ (is (= datahike.db.DB
294
+ (type @conn)))
295
+ (d/release conn)))
296
+
297
+ (deftest test-history-docs
298
+ (let [cfg {:store {:backend :memory
299
+ :id #uuid "b15700ee-0000-0000-0000-000000000001"}
300
+ :initial-tx [{:db/ident :name
301
+ :db/valueType :db.type/string
302
+ :db/unique :db.unique/identity
303
+ :db/index true
304
+ :db/cardinality :db.cardinality/one}
305
+ {:db/ident :age
306
+ :db/valueType :db.type/long
307
+ :db/cardinality :db.cardinality/one}]
308
+ :keep-history? true
309
+ :schema-flexibility :read}
310
+ conn (utils/setup-db cfg)]
311
+
312
+ (d/transact conn {:tx-data [{:name "Alice" :age 25} {:name "Bob" :age 30}]})
313
+
314
+ (is (= #{["Alice" 25] ["Bob" 30]}
315
+ (d/q {:query '[:find ?n ?a :where [?e :name ?n] [?e :age ?a]]
316
+ :args [(d/history (d/db conn))]})))
317
+
318
+ (d/transact conn {:tx-data [{:db/id [:name "Alice"] :age 35}]})
319
+
320
+ (is (= #{["Alice" 35] ["Bob" 30]}
321
+ (d/q {:query '[:find ?n ?a :where [?e :name ?n] [?e :age ?a]]
322
+ :args [(d/db conn)]})))
323
+
324
+ (is (= #{["Alice" 25] ["Alice" 35] ["Bob" 30]}
325
+ (d/q {:query '[:find ?n ?a :where [?e :name ?n] [?e :age ?a]]
326
+ :args [(d/history (d/db conn))]})))
327
+ (d/release conn)))
328
+
329
+ (deftest test-as-of-docs
330
+ (let [cfg {:store {:backend :memory
331
+ :id #uuid "a500ffff-0000-0000-0000-000000000001"}
332
+ :initial-tx [{:db/ident :name
333
+ :db/valueType :db.type/string
334
+ :db/unique :db.unique/identity
335
+ :db/index true
336
+ :db/cardinality :db.cardinality/one}
337
+ {:db/ident :age
338
+ :db/valueType :db.type/long
339
+ :db/cardinality :db.cardinality/one}]
340
+ :keep-history? true
341
+ :schema-flexibility :read}
342
+ conn (utils/setup-db cfg)]
343
+
344
+ (d/transact conn {:tx-data [{:name "Alice" :age 25} {:name "Bob" :age 30}]})
345
+
346
+ (Thread/sleep 100)
347
+
348
+ (def date (java.util.Date.))
349
+
350
+ (Thread/sleep 100)
351
+
352
+ (d/transact conn {:tx-data [{:db/id [:name "Alice"] :age 35}]})
353
+
354
+ (is (= #{["Alice" 25] ["Bob" 30]}
355
+ (d/q {:query '[:find ?n ?a :where [?e :name ?n] [?e :age ?a]]
356
+ :args [(d/as-of (d/db conn) date)]})))
357
+
358
+ (is (= #{["Alice" 35] ["Bob" 30]}
359
+ (d/q {:query '[:find ?n ?a :where [?e :name ?n] [?e :age ?a]]
360
+ :args [(d/db conn)]})))
361
+ (d/release conn)))
362
+
363
+ (deftest test-since-docs
364
+ (let [cfg {:store {:backend :memory
365
+ :id #uuid "051ceeee-0000-0000-0000-000000000001"}
366
+ :initial-tx [{:db/ident :name
367
+ :db/valueType :db.type/string
368
+ :db/unique :db.unique/identity
369
+ :db/index true
370
+ :db/cardinality :db.cardinality/one}
371
+ {:db/ident :age
372
+ :db/valueType :db.type/long
373
+ :db/cardinality :db.cardinality/one}]
374
+ :keep-history? true
375
+ :schema-flexibility :read}
376
+ conn (utils/setup-db cfg)]
377
+ (d/transact conn {:tx-data [{:name "Alice" :age 25} {:name "Bob" :age 30}]})
378
+
379
+ (Thread/sleep 100)
380
+
381
+ (def date (java.util.Date.))
382
+
383
+ (Thread/sleep 100)
384
+
385
+ (d/transact conn [{:db/id [:name "Alice"] :age 30}])
386
+
387
+ (is (= #{["Alice" 30]}
388
+ (d/q '[:find ?n ?a
389
+ :in $ $since
390
+ :where
391
+ [$ ?e :name ?n]
392
+ [$since ?e :age ?a]]
393
+ @conn
394
+ (d/since @conn date))))
395
+
396
+ (is (= #{["Alice" 30] ["Bob" 30]}
397
+ (d/q {:query '[:find ?n ?a :where [?e :name ?n] [?e :age ?a]]
398
+ :args [(d/db conn)]})))
399
+ (d/release conn)))
400
+
401
+ (deftest test-datoms-docs
402
+ (let [cfg {:store {:backend :memory
403
+ :id #uuid "da7000aa-0000-0000-0000-000000000001"}
404
+ :initial-tx [{:db/ident :name
405
+ :db/type :db.type/string
406
+ :db/cardinality :db.cardinality/one}
407
+ {:db/ident :likes
408
+ :db/type :db.type/string
409
+ :db/index true
410
+ :db/cardinality :db.cardinality/many}
411
+ {:db/ident :friends
412
+ :db/type :db.type/ref
413
+ :db/cardinality :db.cardinality/many}]
414
+ :keep-history? false
415
+ :schema-flexibility :read}
416
+ db (utils/setup-db cfg)
417
+ _ (d/transact db [{:db/id 4 :name "Ivan"}
418
+ {:db/id 4 :likes "fries"}
419
+ {:db/id 4 :likes "pizza"}
420
+ {:db/id 4 :friends 5}])
421
+ _ (d/transact db [{:db/id 5 :name "Oleg"}
422
+ {:db/id 5 :likes "candy"}
423
+ {:db/id 5 :likes "pie"}
424
+ {:db/id 5 :likes "pizza"}])
425
+ dvec #(vector (:e %) (:a %) (:v %))]
426
+
427
+ ;; find all datoms for entity id == 1 (any attrs and values)
428
+ ;; sort by attribute, then value
429
+ (is (= '([4 :friends 5]
430
+ [4 :likes "fries"]
431
+ [4 :likes "pizza"]
432
+ [4 :name "Ivan"])
433
+ (map dvec (d/datoms @db {:index :eavt :components [4]}))))
434
+ ;; => (#datahike/Datom [1 :friends 2]
435
+ ;; #datahike/Datom [1 :likes \"fries\"]
436
+ ;; #datahike/Datom [1 :likes \"pizza\"]
437
+ ;; #datahike/Datom [1 :name \"Ivan\"])
438
+
439
+ ;; find all datoms for entity id == 1 and attribute == :likes (any values)
440
+ ;; sorted by value
441
+ (is (= '([4 :likes "fries"]
442
+ [4 :likes "pizza"])
443
+ (map dvec (d/datoms @db {:index :eavt :components [4 :likes]}))))
444
+ ;; => (#datahike/Datom [1 :likes \"fries\"]
445
+ ;; #datahike/Datom [1 :likes \"pizza\"])
446
+
447
+ ;; find all datoms for entity id == 1, attribute == :likes and value == \"pizza\"
448
+ (is (= '([4 :likes "pizza"])
449
+ (map dvec (d/datoms @db {:index :eavt :components [4 :likes "pizza"]}))))
450
+ ;; => (#datahike/Datom [1 :likes \"pizza\"])
451
+
452
+ ;; find all datoms for attribute == :likes (any entity ids and values)
453
+ ;; sorted by entity id, then value
454
+ (is (= '([4 :likes "fries"]
455
+ [4 :likes "pizza"]
456
+ [5 :likes "candy"]
457
+ [5 :likes "pie"]
458
+ [5 :likes "pizza"])
459
+ (map dvec (d/datoms @db {:index :aevt :components [:likes]}))))
460
+ ;; => (#datahike/Datom [1 :likes \"fries\"]
461
+ ;; #datahike/Datom [1 :likes \"pizza\"]
462
+ ;; #datahike/Datom [2 :likes \"candy\"]
463
+ ;; #datahike/Datom [2 :likes \"pie\"]
464
+ ;; #datahike/Datom [2 :likes \"pizza\"])
465
+
466
+ ;; find all datoms that have attribute == `:likes` and value == `\"pizza\"` (any entity id)
467
+ ;; `:likes` must be a unique attr, reference or marked as `:db/index true`
468
+ (is (= '([4 :likes "pizza"]
469
+ [5 :likes "pizza"])
470
+ (map dvec (d/datoms @db {:index :avet :components [:likes "pizza"]}))))
471
+ ;; => (#datahike/Datom [1 :likes \"pizza\"]
472
+ ;; #datahike/Datom [2 :likes \"pizza\"])
473
+
474
+ ;; find all datoms sorted by entity id, then attribute, then value
475
+ (is (= '([1 :db/cardinality :db.cardinality/one] [1 :db/ident :name] [1 :db/type :db.type/string] [2 :db/cardinality :db.cardinality/many] [2 :db/ident :likes] [2 :db/index true] [2 :db/type :db.type/string] [3 :db/cardinality :db.cardinality/many] [3 :db/ident :friends] [3 :db/type :db.type/ref] [4 :friends 5] [4 :likes "fries"] [4 :likes "pizza"] [4 :name "Ivan"] [5 :likes "candy"] [5 :likes "pie"] [5 :likes "pizza"] [5 :name "Oleg"])
476
+ (map dvec (d/datoms @db {:index :eavt})))) ; => (...)))
477
+
478
+ ;; get all values of :db.cardinality/many attribute
479
+ (is (= '("fries" "pizza")
480
+ (->> (d/datoms @db {:index :eavt :components [4 :likes]})
481
+ (map :v))))
482
+
483
+ ;; lookup entity ids by attribute value
484
+ (is (= '(4 5)
485
+ (->> (d/datoms @db {:index :avet :components [:likes "pizza"]})
486
+ (map :e))))
487
+
488
+ ;; find all entities with a specific attribute
489
+ (is (= '(4 5)
490
+ (->> (d/datoms @db {:index :aevt :components [:name]})
491
+ (map :e))))
492
+
493
+ ;; find “singleton” entity by its attr
494
+ (is (= 4
495
+ (->> (d/datoms @db {:index :aevt :components [:name]})
496
+ first :e)))
497
+
498
+ ;; find N entities with lowest attr value (e.g. 10 earliest posts)
499
+ #_(is (= "fail"
500
+ (->> (d/datoms @db {:index :avet :components [:name]})
501
+ (take 2))))
502
+
503
+ ;; find N entities with highest attr value (e.g. 10 latest posts)
504
+ #_(is (= "fail"
505
+ (->> (d/datoms @db {:index :avet :components [:name]})
506
+ (reverse)
507
+ (take 2))
508
+ (map dvec (d/datoms @db {:index :eavt})))) ; => (...)))
509
+
510
+ ;; get all values of :db.cardinality/many attribute
511
+ (is (= '("fries" "pizza")
512
+ (->> (d/datoms @db {:index :eavt :components [4 :likes]})
513
+ (map :v))))
514
+
515
+ ;; lookup entity ids by attribute value
516
+ (is (= '(4 5)
517
+ (->> (d/datoms @db {:index :avet :components [:likes "pizza"]})
518
+ (map :e))))
519
+
520
+ ;; find all entities with a specific attribute
521
+ (is (= '(4 5)
522
+ (->> (d/datoms @db {:index :aevt :components [:name]})
523
+ (map :e))))
524
+
525
+ ;; find “singleton” entity by its attr
526
+ (is (= 4
527
+ (->> (d/datoms @db {:index :aevt :components [:name]})
528
+ first :e)))
529
+
530
+ ;; find N entities with lowest attr value (e.g. 10 earliest posts)
531
+ #_(is (= "fail"
532
+ (->> (d/datoms @db {:index :avet :components [:name]})
533
+ (take 2))))
534
+
535
+ ;; find N entities with highest attr value (e.g. 10 latest posts)
536
+ #_(is (= "fail"
537
+ (->> (d/datoms @db {:index :avet :components [:name]})
538
+ (reverse)
539
+ (take 2))))
540
+ (d/release db)))
541
+
542
+ (deftest test-seek-datoms-doc
543
+ (let [cfg {:store {:backend :memory
544
+ :id #uuid "5eeeeeee-da70-00aa-0000-000000000001"}
545
+ :initial-tx [{:db/ident :name
546
+ :db/type :db.type/string
547
+ :db/index true
548
+ :db/cardinality :db.cardinality/one}
549
+ {:db/ident :likes
550
+ :db/type :db.type/string
551
+ :db/index true
552
+ :db/cardinality :db.cardinality/many}
553
+ {:db/ident :friends
554
+ :db/type :db.type/ref
555
+ :db/index true
556
+ :db/cardinality :db.cardinality/many}]
557
+ :keep-history? false
558
+ :schema-flexibility :read}
559
+ db (utils/setup-db cfg)
560
+ dvec #(vector (:e %) (:a %) (:v %))
561
+ _ (d/transact db {:tx-data [{:db/id 4 :name "Ivan"}
562
+ {:db/id 4 :likes "fries"}
563
+ {:db/id 4 :likes "pizza"}
564
+ {:db/id 4 :friends 5}
565
+ {:db/id 5 :likes "candy"}
566
+ {:db/id 5 :likes "pie"}
567
+ {:db/id 5 :likes "pizza"}]})]
568
+
569
+ (is (= '([4 :friends 5]
570
+ [4 :likes "fries"]
571
+ [4 :likes "pizza"]
572
+ [4 :name "Ivan"]
573
+ [5 :likes "candy"]
574
+ [5 :likes "pie"]
575
+ [5 :likes "pizza"])
576
+ (map dvec (d/seek-datoms @db {:index :eavt :components [4]}))))
577
+
578
+ (is (= '([4 :name "Ivan"]
579
+ [5 :likes "candy"]
580
+ [5 :likes "pie"]
581
+ [5 :likes "pizza"])
582
+ (map dvec (d/seek-datoms @db {:index :eavt :components [4 :name]}))))
583
+
584
+ (is (= '([5 :likes "candy"]
585
+ [5 :likes "pie"]
586
+ [5 :likes "pizza"])
587
+ (map dvec (d/seek-datoms @db {:index :eavt :components [5]}))))
588
+
589
+ (is (= '([5 :likes "pie"]
590
+ [5 :likes "pizza"])
591
+ (map dvec (d/seek-datoms @db {:index :eavt :components [5 :likes "fish"]}))))
592
+ (d/release db)))
593
+
594
+ (deftest test-index-range-doc
595
+ (let [cfg {:store {:backend :memory
596
+ :id #uuid "5eeeeeee-da70-00aa-0000-000000000001"}
597
+ :initial-tx [{:db/ident :name
598
+ :db/type :db.type/string
599
+ :db/cardinality :db.cardinality/one}
600
+ {:db/ident :likes
601
+ :db/index true
602
+ :db/type :db.type/string
603
+ :db/cardinality :db.cardinality/many}
604
+ {:db/ident :age
605
+ :db/unique :db.unique/identity
606
+ :db/type :db.type/ref
607
+ :db/cardinality :db.cardinality/many}]
608
+ :keep-history? false
609
+ :schema-flexibility :read}
610
+ db (utils/setup-db cfg)
611
+ dvec #(vector (:e %) (:a %) (:v %))
612
+ _ (d/transact db {:tx-data [{:name "Ivan"}
613
+ {:likes "fries"}
614
+ {:likes "pizza"}
615
+ {:age 19}
616
+ {:likes "candy"}
617
+ {:likes "pie"}
618
+ {:likes "pizza"}]})]
619
+ (is (= '([8 :likes "candy"] [5 :likes "fries"] [9 :likes "pie"] [6 :likes "pizza"] [10 :likes "pizza"])
620
+ (map dvec (d/index-range @db {:attrid :likes :start "a" :end "zzzzzzzzz"}))))
621
+
622
+ (is (= '([5 :likes "fries"] [9 :likes "pie"])
623
+ (map dvec (d/index-range @db {:attrid :likes :start "egg" :end "pineapple"}))))
624
+ (d/release db)))
625
+
626
+ (deftest test-database-hash
627
+ (testing "Hashing without history"
628
+ (let [cfg {:store {:backend :memory
629
+ :id #uuid "ba5b1000-0000-0000-0000-000000000001"}
630
+ :keep-history? false
631
+ :schema-flexibility :read}
632
+ conn (utils/setup-db cfg false)
633
+ hash-0 0]
634
+ (testing "first hash equals zero"
635
+ (is (= hash-0 (hash @conn))))
636
+ (testing "hash remains 0 after reconnecting"
637
+ (is (= hash-0 (-> (d/connect cfg) deref hash))))
638
+ (testing "add entity to database"
639
+ (let [_ (d/transact conn [{:db/id 1 :name "Max Mustermann"}])
640
+ hash-1 (hash @conn)]
641
+ (is (= hash-1 (-> (d/connect cfg) deref hash)))
642
+ (testing "remove entity again"
643
+ (let [_ (d/transact conn [[:db/retractEntity 1]])
644
+ hash-2 (hash @conn)]
645
+ (is (not= hash-2 hash-1))
646
+ (is (= hash-0 hash-2))))))
647
+ (d/release conn)))
648
+ (testing "Hashing with history"
649
+ (let [cfg {:store {:backend :memory
650
+ :id #uuid "ba5b1001-0000-0000-0000-000000000001"}
651
+ :keep-history? true
652
+ :schema-flexibility :read}
653
+ conn (utils/setup-db cfg false)
654
+ hash-0 (hash @conn)]
655
+ (testing "first hash equals zero"
656
+ (is (= hash-0 (hash @conn))))
657
+ (testing "hash remains 0 after reconnecting"
658
+ (is (= hash-0 (-> (d/connect cfg) deref hash))))
659
+ (testing "add entity to database"
660
+ (let [_ (d/transact conn [{:db/id 1 :name "Max Mustermann"}])
661
+ hash-1 (hash @conn)]
662
+ (is (= hash-1 (-> (d/connect cfg) deref hash)))
663
+ (testing "retract entity again"
664
+ (let [_ (d/transact conn [[:db/retractEntity 1]])
665
+ hash-2 (hash @conn)]
666
+ (is (not= hash-1 hash-2))
667
+ (is (not= hash-0 hash-2))))))
668
+ (d/release conn))))
669
+
670
+ (deftest test-database-schema
671
+ (letfn [(test-schema [cfg]
672
+ (let [conn (utils/setup-db cfg)
673
+ name-schema {:db/ident :name
674
+ :db/valueType :db.type/string
675
+ :db/cardinality :db.cardinality/one
676
+ :db/unique :db.unique/identity}
677
+ related-to-schema {:db/ident :related-to
678
+ :db/valueType :db.type/ref
679
+ :db/cardinality :db.cardinality/many}
680
+ age-schema {:db/ident :age
681
+ :db/valueType :db.type/long
682
+ :db/cardinality :db.cardinality/one
683
+ :db/noHistory true}
684
+ coerced-schema (fn [db]
685
+ (reduce-kv
686
+ (fn [m k v]
687
+ (assoc m k (dissoc v :db/id)))
688
+ {}
689
+ (d/schema db)))
690
+ name-reverse-schema {:db/ident #{:name}
691
+ :db/index #{:name}
692
+ :db.unique/identity #{:name}
693
+ :db/unique #{:name}}
694
+ age-reverse-schema (-> name-reverse-schema
695
+ (update :db/ident conj :age)
696
+ (assoc :db/noHistory #{:age}))
697
+ related-to-reverse-schema (-> age-reverse-schema
698
+ (update :db/ident conj :related-to)
699
+ (update :db/index conj :related-to)
700
+ (assoc :db.cardinality/many #{:related-to})
701
+ (assoc :db.type/ref #{:related-to}))]
702
+ (d/transact conn {:tx-data [name-schema]})
703
+ (is (= {:name name-schema}
704
+ (coerced-schema @conn)))
705
+ (is (= name-reverse-schema (d/reverse-schema @conn)))
706
+
707
+ (d/transact conn {:tx-data [age-schema]})
708
+ (is (= {:name name-schema
709
+ :age age-schema}
710
+ (coerced-schema @conn)))
711
+ (is (= age-reverse-schema
712
+ (d/reverse-schema @conn)))
713
+
714
+ (d/transact conn {:tx-data [related-to-schema]})
715
+ (is (= {:name name-schema
716
+ :age age-schema
717
+ :related-to related-to-schema}
718
+ (coerced-schema @conn)))
719
+ (is (= related-to-reverse-schema
720
+ (d/reverse-schema @conn)))
721
+ (d/release conn)))]
722
+ (let [base-cfg {:store {:backend :memory
723
+ :id #uuid "a0100000-0000-0000-0000-000000000001"}
724
+ :keep-history? false
725
+ :attribute-refs? false
726
+ :schema-flexibility :write}]
727
+ (testing "Empty database without any schema"
728
+ (let [conn (do
729
+ (d/delete-database base-cfg)
730
+ (d/create-database base-cfg)
731
+ (d/connect base-cfg))]
732
+ (is (= {}
733
+ (d/schema @conn)))
734
+ (is (= {}
735
+ (d/reverse-schema @conn)))
736
+ (d/release conn)))
737
+ (testing "Empty database with write flexibility and no attribute refs"
738
+ (test-schema base-cfg))
739
+ (testing "Empty database with write flexibility and attribute refs"
740
+ (test-schema (assoc base-cfg :attribute-refs? true)))
741
+ (testing "Empty database with read flexibility and no attribute refs"
742
+ (test-schema (assoc base-cfg :schema-flexibility :read))))))
743
+
744
+ (deftest test-db-meta
745
+ (let [cfg {:store {:backend :memory
746
+ :id #uuid "a0100000-0000-0000-0000-000000000002"}
747
+ :keep-history? false
748
+ :attribute-refs? false
749
+ :schema-flexibility :write}
750
+ conn (utils/setup-db cfg)]
751
+ (is (= #{:datahike/version :datahike/id :datahike/created-at :konserve/version :hitchhiker.tree/version :persistent.set/version :datahike/commit-id}
752
+ (-> @conn :meta keys set)))
753
+ (d/release conn)))
754
+
755
+ (def ^:private metrics-base-cfg {:store {:backend :memory}
756
+ :index :datahike.index/persistent-set
757
+ :keep-history? true
758
+ :schema-flexibility :write
759
+ :attribute-refs? false})
760
+
761
+ (defn test-metrics [cfg]
762
+ (let [schema [{:db/ident :name
763
+ :db/cardinality :db.cardinality/one
764
+ :db/index true
765
+ :db/unique :db.unique/identity
766
+ :db/valueType :db.type/string}
767
+ {:db/ident :parents
768
+ :db/cardinality :db.cardinality/many
769
+ :db/valueType :db.type/ref}
770
+ {:db/ident :age
771
+ :db/cardinality :db.cardinality/one
772
+ :db/valueType :db.type/long}]
773
+ conn (utils/setup-db cfg)
774
+ schema-on-write? (= (:schema-flexibility (.-config @conn)) :write)
775
+ attribute-refs? (:attribute-refs? (:config @conn))
776
+ schema-count 11 ;; amount of user schema datoms in temporal eavt index
777
+ temporal-count 10 ;; amount of user data datoms in temporal eavt index when using schema-on-write
778
+ temporal-avet-count 9 ;; amount of user data datoms in temporal avet index when using schema-on write
779
+ sys-attr-count 69 ;; amount of system schema datoms in temporal eavt index when using attribute refs
780
+ sys-attr-avet-count 48 ;; amount of system schema datoms in temporal avet index when using attribute refs
781
+ update-for-schema-on-write
782
+ (fn [metrics]
783
+ (-> (update metrics :count #(+ % 11))
784
+ (update :avet-count #(+ % 6))
785
+ ((fn [m] (merge-with merge m {:per-attr-counts {:db/ident 3
786
+ :db/cardinality 3
787
+ :db/index 1
788
+ :db/unique 1
789
+ :db/valueType 3}
790
+ :per-entity-counts {1 5
791
+ 2 3
792
+ 3 3
793
+ 5 2
794
+ 6 3}})))))
795
+ update-for-history
796
+ (fn [metrics schema-on-write? attribute-refs?]
797
+ (->> (update metrics :count #(+ % 4))
798
+ (merge-with merge {:per-attr-counts {:db/txInstant 4}
799
+ :per-entity-counts {(+ tx0 1) 1
800
+ (+ tx0 2) 1
801
+ (+ tx0 3) 1
802
+ (+ tx0 4) 1}
803
+ ; 10 == 11 minus 1 parent datom that wouldn't get added unless retracted
804
+ :temporal-count (+ schema-count
805
+ (if schema-on-write?
806
+ (if attribute-refs?
807
+ (+ temporal-count sys-attr-count)
808
+ temporal-count)
809
+ (if attribute-refs?
810
+ (+ temporal-count sys-attr-count)
811
+ 0)))
812
+ :temporal-avet-count (if schema-on-write?
813
+ (if attribute-refs?
814
+ sys-attr-avet-count
815
+ temporal-avet-count)
816
+ (if attribute-refs?
817
+ sys-attr-avet-count
818
+ 0))})))
819
+ update-for-attr-refs
820
+ (fn [metrics]
821
+ (let [update-counts (fn [coll] (reduce (fn [m counted] (update m counted #(if % (inc %) 1)))
822
+ {}
823
+ coll))
824
+ ref-attrs (map #(.-ident-for @conn (.-a %)) db/ref-datoms)
825
+ ref-datom-per-attr-counts (update-counts ref-attrs)
826
+ indexed? (fn [a] (-> (:db/index (.-rschema @conn))
827
+ (contains? a)))]
828
+ (-> (update metrics :per-attr-counts #(merge-with + % ref-datom-per-attr-counts))
829
+ (assoc :per-entity-counts (update-counts (map :e (d/datoms @conn :eavt))))
830
+ (update :count #(+ % (count db/ref-datoms)))
831
+ (update :avet-count #(+ % 4 (count (filter indexed? ref-attrs)))))))
832
+ compare-vals
833
+ (fn [metrics expected]
834
+ (doseq [[metric val] metrics]
835
+ (testing (str (name metric) " is correct")
836
+ (is (= (metric expected) val)))))]
837
+ (when schema-on-write?
838
+ (d/transact conn {:tx-data schema}))
839
+ (d/transact conn {:tx-data [{:name "Donald" :age 35}
840
+ {:name "Daisy" :age 35}]})
841
+ (if schema-on-write?
842
+ (do
843
+ (d/transact conn {:tx-data [{:name "Dinky"
844
+ :age 5
845
+ :parents [[:name "Donald"] [:name "Daisy"]]}]})
846
+ (d/transact conn {:tx-data [[:db/retractEntity [:name "Donald"]]]}))
847
+ (do
848
+ (d/transact conn {:tx-data [{:name "Dinky"
849
+ :age 5}]})
850
+ (d/transact conn {:tx-data [[:db/add 3 :parents 1]
851
+ [:db/add 3 :parents 2]]})
852
+ (d/transact conn {:tx-data [[:db/retractEntity 1]]})))
853
+ (compare-vals (d/metrics @conn)
854
+ (cond-> {:count 5
855
+ :per-attr-counts {:name 2
856
+ :age 2
857
+ :parents 1}
858
+ :per-entity-counts {2 2
859
+ 3 3}
860
+ :avet-count 0}
861
+ schema-on-write? update-for-schema-on-write
862
+ (dbi/-keep-history? @conn) (update-for-history schema-on-write? attribute-refs?)
863
+ (:attribute-refs? (.-config @conn)) update-for-attr-refs))
864
+ (d/release conn)))
865
+
866
+ (deftest test-metrics-hht
867
+ (test-metrics (assoc metrics-base-cfg :index :datahike.index/hitchhiker-tree
868
+ :index-config (di/default-index-config :datahike.index/hitchhiker-tree))))
869
+
870
+ (deftest test-metrics-pset
871
+ (test-metrics metrics-base-cfg))
872
+
873
+ (deftest test-metrics-history
874
+ (test-metrics (assoc metrics-base-cfg :keep-history? false)))
875
+
876
+ (deftest test-metrics-schema-read
877
+ (test-metrics (assoc metrics-base-cfg :schema-flexibility :read)))
878
+
879
+ (deftest test-metrics-attr-refs
880
+ (test-metrics (assoc metrics-base-cfg :attribute-refs? true)))
881
+
882
+ (deftest test-no-history-datoms-in-empty-db
883
+ (testing "When attribute-refs? is false, there are no initial datoms"
884
+ (utils/with-connect [conn (-> {:store {:backend :memory
885
+ :id #uuid "ba5b1000-0000-0000-0000-000000000001"}
886
+ :keep-history? true
887
+ :schema-flexibility :write}
888
+ utils/provide-unique-id
889
+ utils/recreate-database)]
890
+ (is (-> @conn
891
+ d/history
892
+ (d/datoms {:index :aevt
893
+ :components []
894
+ :limit -1})
895
+ empty?)))))