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,469 @@
1
+ (ns ^:no-doc datahike.index.persistent-set
2
+ (:require [clojure.string]
3
+ [org.replikativ.persistent-sorted-set :as psset]
4
+ #?(:cljs [org.replikativ.persistent-sorted-set.btset :refer [BTSet]])
5
+ #?(:cljs [org.replikativ.persistent-sorted-set.branch :refer [Branch]])
6
+ #?(:cljs [org.replikativ.persistent-sorted-set.leaf :refer [Leaf]])
7
+ #?(:cljs [org.replikativ.persistent-sorted-set.impl.storage :refer [IStorage]])
8
+ [org.replikativ.persistent-sorted-set.arrays :as arrays]
9
+ #?@(:clj [[clojure.core.cache :as cache]
10
+ [clojure.core.cache.wrapped :as wrapped]]
11
+ :cljs [[cljs.cache :as cache]
12
+ [cljs.cache.wrapped :as wrapped]])
13
+ [datahike.datom :as dd :refer [index-type->cmp-quick]]
14
+ [datahike.constants :refer [tx0 txmax]]
15
+ [datahike.index.interface :as di :refer [IIndex]]
16
+ [datahike.tools :as dt]
17
+ [konserve.core :as k]
18
+ [konserve.serializers :refer [fressian-serializer]]
19
+ #?(:cljs [fress.api :as fress])
20
+ [hasch.core :refer [uuid squuid]]
21
+ [taoensso.timbre :refer [trace]])
22
+ #?(:cljs (:require-macros [datahike.index.persistent-set :refer [generate-slice-comparator-constructor]]))
23
+ #?(:clj (:import [datahike.datom Datom]
24
+ [org.fressian.handlers WriteHandler ReadHandler]
25
+ [org.replikativ.persistent_sorted_set PersistentSortedSet IStorage Leaf Branch ANode Settings]
26
+ [java.util List])))
27
+
28
+ (def index-type->kwseq
29
+ {:eavt [:e :a :v :tx :added]
30
+ :aevt [:a :e :v :tx :added]
31
+ :avet [:a :v :e :tx :added]})
32
+
33
+ (defn slice-from-to-tree
34
+ "This function generates code for deciding which datom elements that need to be compared based on which elements in the slice bounds are nil, as well as the index. Once all datom elements have been considered, `leaf-fn` is called with a vector containing the keywords of the actual elements to compare."
35
+ [from-sym to-sym index-spec acc leaf-fn]
36
+ (if (empty? index-spec)
37
+
38
+ ;; When there is nothing left to compare,
39
+ ;; return the correct comparator.
40
+ (leaf-fn acc)
41
+
42
+ (let [[findex & index-spec] index-spec]
43
+ `(if (and (nil? (~findex ~from-sym))
44
+ (nil? (~findex ~to-sym)))
45
+
46
+ ;; Whenever both slice bounds are nil, there is nothing more to compare
47
+ ;; and we know what comparator to return.
48
+ ~(leaf-fn acc)
49
+
50
+ ;; Otherwise, if at least one slie bound is non-nil, we need a comparator for
51
+ ;; the remaining datom elements.
52
+ ~(slice-from-to-tree from-sym to-sym index-spec (conj acc findex) leaf-fn)))))
53
+
54
+ (defn cmp-for-kwseq-sub
55
+ "This function generates the actual body of the comparator"
56
+ [datom0 datom1 kwseq]
57
+ (let [result (gensym)]
58
+ (if (empty? kwseq)
59
+ 0
60
+ (let [[k & kwseq] kwseq]
61
+ `(let [;; Compare the datoms at the element with key `k`
62
+ ~result ~(dd/cmp-val-expr k datom0 datom1)]
63
+ (cond
64
+ ;; If it is nil, typically return 0
65
+ (nil? ~result) 0
66
+
67
+ ;; If it is zero, we need to proceed with the next datom element to compare.
68
+ (zero? ~result) ~(cmp-for-kwseq-sub datom0 datom1 kwseq)
69
+
70
+ ;; If it is non-zero, it means that this is the result of the comparison.
71
+ :else ~result))))))
72
+
73
+ (defn cmp-for-kwseq
74
+ "Given a sequence of keywords for datom elements to compare, generate the code for a function that performs the comparison."
75
+ [kwseq]
76
+ (let [datom0 (dd/type-hint-datom (gensym))
77
+ datom1 (dd/type-hint-datom (gensym))]
78
+ `(fn [~datom0 ~datom1] ~(cmp-for-kwseq-sub datom0 datom1 kwseq))))
79
+
80
+ (defmacro generate-slice-comparator-constructor []
81
+ (let [index-sym (gensym)
82
+ from-sym (gensym)
83
+ to-sym (gensym)
84
+
85
+ ;; List keyword sequences referring to datom elements for
86
+ ;; all combinations of indexes and leftmost slices of the
87
+ ;; corresponding datom elements.
88
+ all-kwseqs (set (for [[_ kwseq] index-type->kwseq
89
+ limit (range 6)]
90
+ (vec (take limit kwseq))))
91
+ kwseq-sym-map (zipmap all-kwseqs (repeatedly gensym))]
92
+ `(let [;; Pre-build comparators for every sequence
93
+ ;; of keywords referring to datom elements.
94
+ ;; A comparator is a function taking two datoms
95
+ ;; as arguments.
96
+ ~@(mapcat (fn [[kwseq sym]]
97
+ [sym (cmp-for-kwseq kwseq)])
98
+ kwseq-sym-map)]
99
+
100
+ ;; This is the function generated by this macro
101
+ ;; and it is called by the `-slice` method.
102
+ (fn [~index-sym ~from-sym ~to-sym]
103
+
104
+ ;; First branch based on which index to use ...
105
+ (case ~index-sym
106
+ ~@(mapcat
107
+ (fn [[index-key index-spec]]
108
+ [index-key
109
+
110
+ ;; ... then branch based on what elements
111
+ ;; are non-nil in the slice bound datoms ...
112
+ (slice-from-to-tree
113
+ from-sym to-sym
114
+ index-spec
115
+ []
116
+
117
+ (fn [acc]
118
+ {:post [(symbol? %)]}
119
+
120
+ ;; ... and eventually return a precomputed comparator
121
+ ;; that will be used by `psset/slice`. The generated
122
+ ;; code is the symbol that is bound to a comparator.
123
+ (get kwseq-sym-map acc)))])
124
+ index-type->kwseq))))))
125
+
126
+ (def slice-comparator-constructor (generate-slice-comparator-constructor))
127
+
128
+ (defn remove-datom [pset ^Datom datom index-type]
129
+ (psset/disj pset datom (index-type->cmp-quick index-type false)))
130
+
131
+ (defn insert [pset ^Datom datom index-type]
132
+ ;; Use lookup with prefix comparator - O(log n) with zero allocations
133
+ ;; Prefix comparator checks only (e,a,v) to find if ANY datom exists with same triple
134
+ (if #?(:clj (.lookup ^PersistentSortedSet pset datom (dd/index-type->cmp-prefix index-type))
135
+ :cljs (psset/lookup pset datom (dd/index-type->cmp-prefix index-type)))
136
+ pset
137
+ (psset/conj pset datom (index-type->cmp-quick index-type))))
138
+
139
+ (defn temporal-insert [pset ^Datom datom index-type]
140
+ (psset/conj pset datom (index-type->cmp-quick index-type false)))
141
+
142
+ (defn upsert [pset ^Datom datom index-type old-datom]
143
+ (if old-datom
144
+ (if (= index-type :avet)
145
+ (-> pset
146
+ (psset/disj old-datom (index-type->cmp-quick index-type))
147
+ (psset/conj datom (index-type->cmp-quick index-type)))
148
+ #?(:clj (.replace ^PersistentSortedSet pset old-datom datom (dd/index-type->cmp-replace index-type))
149
+ :cljs (psset/replace pset old-datom datom (dd/index-type->cmp-replace index-type))))
150
+ (psset/conj pset datom (index-type->cmp-quick index-type))))
151
+
152
+ (defn temporal-upsert [pset ^Datom datom index-type {old-val :v}]
153
+ (let [{:keys [e a v tx added]} datom]
154
+ (if added
155
+ (if old-val
156
+ (if (= v old-val)
157
+ pset
158
+ (-> pset
159
+ (psset/conj (dd/datom e a old-val tx false)
160
+ (index-type->cmp-quick index-type false))
161
+ (psset/conj datom
162
+ (index-type->cmp-quick index-type false))))
163
+ (psset/conj pset datom (index-type->cmp-quick index-type false)))
164
+ (if old-val
165
+ (psset/conj pset
166
+ (dd/datom e a old-val tx false)
167
+ (index-type->cmp-quick index-type false))
168
+ pset))))
169
+
170
+ (defn mark [^PersistentSortedSet pset]
171
+ (when-not (.-_address pset)
172
+ (throw (ex-info "Index needs to be properly flushed before marking."
173
+ {:type :flush-before-marking})))
174
+ (let [addresses (atom #{})]
175
+ (psset/walk-addresses pset (fn [address] (swap! addresses conj address)))
176
+ @addresses))
177
+
178
+ (extend-type #?(:clj PersistentSortedSet :cljs BTSet)
179
+ IIndex
180
+ (-slice [^PersistentSortedSet pset from to index-type]
181
+ (psset/slice pset from to (slice-comparator-constructor index-type from to)))
182
+ (-all [pset]
183
+ (identity pset))
184
+ (-seq [^PersistentSortedSet pset]
185
+ (seq pset))
186
+ (-count [^PersistentSortedSet pset]
187
+ (count pset))
188
+ (-insert [^PersistentSortedSet pset datom index-type _op-count]
189
+ (insert pset datom index-type))
190
+ (-temporal-insert [^PersistentSortedSet pset datom index-type _op-count]
191
+ (psset/conj pset datom (index-type->cmp-quick index-type)))
192
+ (-upsert [^PersistentSortedSet pset datom index-type _op-count old-datom]
193
+ (upsert pset datom index-type old-datom))
194
+ (-temporal-upsert [^PersistentSortedSet pset datom index-type _op-count old-val]
195
+ (temporal-upsert pset datom index-type old-val))
196
+ (-remove [^PersistentSortedSet pset datom index-type _op-count]
197
+ (remove-datom pset datom index-type))
198
+ (-flush [^PersistentSortedSet pset _]
199
+ (psset/store pset)
200
+ pset)
201
+ (-transient [^PersistentSortedSet pset]
202
+ (transient pset))
203
+ (-persistent! [^PersistentSortedSet pset]
204
+ (persistent! pset))
205
+ (-mark [^PersistentSortedSet pset]
206
+ (mark pset)))
207
+
208
+ (defn- gen-address [^ANode node crypto-hash?]
209
+ (if crypto-hash?
210
+ (if (instance? Branch node)
211
+ (uuid (vec (.addresses ^Branch node)))
212
+ (uuid (mapv (comp vec seq) (.keys node))))
213
+ (squuid))) ;; Sequential UUID for better index locality
214
+
215
+ (defn- freelist-pop!
216
+ "Atomically pop an address from the freelist. Returns nil if empty."
217
+ [freelist-atom]
218
+ (loop []
219
+ (let [current @freelist-atom]
220
+ (if (empty? current)
221
+ nil
222
+ (let [addr (peek current)
223
+ new-list (pop current)]
224
+ (if (compare-and-set! freelist-atom current new-list)
225
+ addr
226
+ (recur)))))))
227
+
228
+ (defrecord CachedStorage [store config cache stats pending-writes freed-addresses freed-set freelist cost-center-fn]
229
+ IStorage
230
+ (store [_ node #?(:cljs opts)]
231
+ (@cost-center-fn :store)
232
+ (swap! stats update :writes inc)
233
+ (let [;; Only reuse addresses when not using crypto-hash (content-addressed storage
234
+ ;; requires the address to match the content)
235
+ reused (when-not (:crypto-hash? config)
236
+ (freelist-pop! freelist))
237
+ address (or reused (gen-address node (:crypto-hash? config)))
238
+ _ (trace "writing storage: " address " reused: " (boolean reused) " crypto: " (:crypto-hash? config))]
239
+ ;; Evict old cached value when reusing an address
240
+ (when reused
241
+ (wrapped/evict cache address))
242
+ (swap! pending-writes conj [address node])
243
+ (wrapped/miss cache address node)
244
+ address))
245
+ (accessed [_ address]
246
+ (@cost-center-fn :accessed)
247
+ (trace "accessing storage: " address)
248
+ (swap! stats update :accessed inc)
249
+ (wrapped/hit cache address)
250
+ nil)
251
+ (restore [_ address #?(:cljs opts)]
252
+ (@cost-center-fn :restore)
253
+ (trace "reading: " address)
254
+ (if-let [cached (wrapped/lookup cache address)]
255
+ cached
256
+ (let [node (k/get store address nil {:sync? true})]
257
+ (when (nil? node)
258
+ (dt/raise "Node not found in storage." {:type :node-not-found
259
+ :address address
260
+ :store store}))
261
+ (swap! stats update :reads inc)
262
+ (wrapped/miss cache address node)
263
+ node)))
264
+ (markFreed [_ address]
265
+ (when address
266
+ (let [now #?(:clj (java.util.Date.) :cljs (js/Date.))]
267
+ (trace "marking address as freed: " address)
268
+ (swap! freed-set conj address)
269
+ (swap! freed-addresses conj [address now]))))
270
+ (isFreed [_ address]
271
+ (contains? @freed-set address))
272
+ (freedInfo [_ address]
273
+ (when (contains? @freed-set address)
274
+ "Address has been marked as freed")))
275
+
276
+ (def init-stats {:writes 0
277
+ :reads 0
278
+ :accessed 0})
279
+
280
+ (defn create-storage [store config]
281
+ (CachedStorage. store config
282
+ (atom (cache/lru-cache-factory {} :threshold (:store-cache-size config)))
283
+ (atom init-stats)
284
+ (atom [])
285
+ (atom []) ;; freed-addresses: vector of [address timestamp] pairs
286
+ (atom #{}) ;; freed-set: HashSet for O(1) isFreed lookups
287
+ (atom []) ;; freelist: vector of reusable addresses (used as stack via peek/pop)
288
+ (atom (fn [_] nil))))
289
+
290
+ (def ^:const DEFAULT_BRANCHING_FACTOR 512)
291
+
292
+ (defmethod di/empty-index :datahike.index/persistent-set [_index-name store index-type _]
293
+ (let [^PersistentSortedSet pset (psset/sorted-set* {:cmp (index-type->cmp-quick index-type false)
294
+ :storage (:storage store)
295
+ :branching-factor DEFAULT_BRANCHING_FACTOR})]
296
+ (with-meta pset
297
+ {:index-type index-type})))
298
+
299
+ (defmethod di/init-index :datahike.index/persistent-set [_index-name store datoms index-type _ {:keys [indexed]}]
300
+ (let [arr (if (= index-type :avet)
301
+ (->> datoms
302
+ (filter #(contains? indexed (.-a ^Datom %)))
303
+ to-array)
304
+ (cond-> datoms
305
+ (not (arrays/array? datoms))
306
+ (arrays/into-array)))
307
+ _ (arrays/asort arr (index-type->cmp-quick index-type false))
308
+ ^PersistentSortedSet pset (psset/from-sorted-array (index-type->cmp-quick index-type false)
309
+ arr
310
+ (arrays/alength arr)
311
+ {:branching-factor DEFAULT_BRANCHING_FACTOR})]
312
+ (set! (.-_storage pset) (:storage store))
313
+ (with-meta pset
314
+ {:index-type index-type})))
315
+
316
+ ;; temporary import from psset until public
317
+ (defn- map->settings ^Settings [m]
318
+ #?(:cljs m
319
+ :clj (Settings.
320
+ (int (or (:branching-factor m) 0))
321
+ nil ;; weak ref default
322
+ )))
323
+
324
+ (defmethod di/add-konserve-handlers :datahike.index/persistent-set [config store]
325
+ ;; Check if store has pre-configured handlers (e.g., LMDB with buffer encoder).
326
+ ;; If so, the store will have :storage-atom that handlers close over.
327
+ (if-let [storage-atom (:storage-atom store)]
328
+ ;; Non-fressian store - handlers already configured, just create storage
329
+ (let [storage (or (:storage store)
330
+ (create-storage store config))]
331
+ (reset! storage-atom storage)
332
+ (assoc store :storage storage))
333
+
334
+ ;; Standard fressian store - set up serializers
335
+ ;; deal with circular reference between storage and store
336
+ (let [settings (map->settings {:branching-factor DEFAULT_BRANCHING_FACTOR})
337
+ storage (atom nil)
338
+ store
339
+ (k/assoc-serializers
340
+ store
341
+ {:FressianSerializer (fressian-serializer
342
+
343
+ ;; read handlers
344
+ {"datahike.index.PersistentSortedSet"
345
+ #?(:clj
346
+ (reify ReadHandler
347
+ (read [_ reader _tag _component-count]
348
+ (let [{:keys [meta address count]} (.readObject reader)
349
+ cmp (index-type->cmp-quick (:index-type meta) false)]
350
+ ;; The following fields are reset as they cannot be accessed from outside:
351
+ ;; - 'edit' is set to false, i.e. the set is assumed to be persistent, not transient
352
+ ;; - 'version' is set back to 0
353
+ (PersistentSortedSet. meta cmp address @storage nil count settings 0))))
354
+ :cljs
355
+ (fn [reader _tag _component-count]
356
+ (let [{:keys [meta address count]} (fress/read-object reader)
357
+ cmp (index-type->cmp-quick (:index-type meta) false)]
358
+ ;; CLJS BTSet deftype: [root cnt comparator meta _hash storage address settings]
359
+ (BTSet. nil count cmp meta nil @storage address settings))))
360
+ "datahike.index.PersistentSortedSet.Leaf"
361
+ #?(:clj
362
+ (reify ReadHandler
363
+ (read [_ reader _tag _component-count]
364
+ (let [{:keys [keys _level]} (.readObject reader)]
365
+ (Leaf. ^List keys settings))))
366
+ :cljs
367
+ (fn [reader _tag _component-count]
368
+ (let [{:keys [keys _level]} (fress/read-object reader)]
369
+ ;; CLJS Leaf deftype: [keys settings _measure]
370
+ (Leaf. (clj->js keys) settings nil))))
371
+ "datahike.index.PersistentSortedSet.Branch"
372
+ #?(:clj
373
+ (reify ReadHandler
374
+ (read [_ reader _tag _component-count]
375
+ (let [{:keys [keys level addresses subtree-count]} (.readObject reader)]
376
+ (Branch. (int level) (count keys) (into-array Object keys) (into-array Object (seq addresses)) nil (long (or subtree-count -1)) settings))))
377
+ :cljs
378
+ (fn [reader _tag _component-count]
379
+ (let [{:keys [keys level addresses subtree-count]} (fress/read-object reader)]
380
+ ;; CLJS Branch deftype: [level keys children addresses subtree-count _measure settings]
381
+ (Branch. (int level) (clj->js keys) nil (clj->js addresses) (or subtree-count -1) nil settings))))
382
+ "datahike.datom.Datom"
383
+ #?(:clj
384
+ (reify ReadHandler
385
+ (read [_ reader _tag _component-count]
386
+ (dd/datom-from-reader (.readObject reader))))
387
+ :cljs
388
+ (fn [reader _tag _component-count]
389
+ (dd/datom-from-reader (fress/read-object reader))))}
390
+
391
+ ;; write handlers
392
+ ;; CLJ format: nested {Type {"tag" handler}} for clojure.data.fressian
393
+ ;; CLJS format: flat {Type handler-fn} for fress library
394
+ #?(:clj
395
+ {org.replikativ.persistent_sorted_set.PersistentSortedSet
396
+ {"datahike.index.PersistentSortedSet"
397
+ (reify WriteHandler
398
+ (write [_ writer pset]
399
+ (when (nil? (.-_address ^PersistentSortedSet pset))
400
+ (dt/raise "Must be flushed." {:type :must-be-flushed
401
+ :pset pset}))
402
+ (.writeTag writer "datahike.index.PersistentSortedSet" 1)
403
+ (.writeObject writer {:meta (meta pset)
404
+ :address (.-_address ^PersistentSortedSet pset)
405
+ :count (count pset)})))}
406
+
407
+ org.replikativ.persistent_sorted_set.Leaf
408
+ {"datahike.index.PersistentSortedSet.Leaf"
409
+ (reify WriteHandler
410
+ (write [_ writer leaf]
411
+ (.writeTag writer "datahike.index.PersistentSortedSet.Leaf" 1)
412
+ (.writeObject writer {:level (.level ^Leaf leaf)
413
+ :keys (.keys ^Leaf leaf)})))}
414
+
415
+ org.replikativ.persistent_sorted_set.Branch
416
+ {"datahike.index.PersistentSortedSet.Branch"
417
+ (reify WriteHandler
418
+ (write [_ writer node]
419
+ (.writeTag writer "datahike.index.PersistentSortedSet.Branch" 1)
420
+ (.writeObject writer {:level (.level ^Branch node)
421
+ :keys (.keys ^Branch node)
422
+ :addresses (.addresses ^Branch node)
423
+ :subtree-count (.subtreeCount ^Branch node)})))}
424
+
425
+ datahike.datom.Datom
426
+ {"datahike.datom.Datom"
427
+ (reify WriteHandler
428
+ (write [_ writer datom]
429
+ (.writeTag writer "datahike.datom.Datom" 1)
430
+ (.writeObject writer (vec (seq ^Datom datom)))))}}
431
+
432
+ :cljs
433
+ {BTSet
434
+ (fn [writer pset]
435
+ (when (nil? (.-address ^BTSet pset))
436
+ (dt/raise "Must be flushed." {:type :must-be-flushed
437
+ :pset pset}))
438
+ (fress/write-tag writer "datahike.index.PersistentSortedSet" 1)
439
+ (fress/write-object writer {:meta (meta pset)
440
+ :address (.-address ^BTSet pset)
441
+ :count (count pset)}))
442
+
443
+ Leaf
444
+ (fn [writer leaf]
445
+ (fress/write-tag writer "datahike.index.PersistentSortedSet.Leaf" 1)
446
+ (fress/write-object writer {:level 0 ;; not supported in cljs
447
+ :keys (vec (.-keys ^Leaf leaf))}))
448
+
449
+ Branch
450
+ (fn [writer node]
451
+ (fress/write-tag writer "datahike.index.PersistentSortedSet.Branch" 1)
452
+ (fress/write-object writer {:level (.-level ^Branch node)
453
+ :keys (vec (.-keys ^Branch node))
454
+ :addresses (vec (.-addresses ^Branch node))
455
+ :subtree-count (.-subtree-count ^Branch node)}))
456
+
457
+ datahike.datom.Datom
458
+ (fn [writer datom]
459
+ (fress/write-tag writer "datahike.datom.Datom" 1)
460
+ (fress/write-object writer (vec (seq ^Datom datom))))}))})]
461
+ (reset! storage (or (:storage store)
462
+ (create-storage store config)))
463
+ (assoc store :storage @storage))))
464
+
465
+ (defmethod di/konserve-backend :datahike.index/persistent-set [_index-name store]
466
+ store)
467
+
468
+ (defmethod di/default-index-config :datahike.index/persistent-set [_index-name]
469
+ {})
@@ -0,0 +1,44 @@
1
+ (ns datahike.index.utils
2
+ (:require [datahike.constants :refer [e0 tx0 emax txmax]]
3
+ [datahike.datom :as dd])
4
+ #?(:clj (:import [datahike.datom Datom])))
5
+
6
+ (defn datom-to-vec [^Datom datom index-type start?]
7
+ (let [e (fn [^Datom datom] (when-not (or (and start? (= e0 (.-e datom)))
8
+ (and (not start?) (= emax (.-e datom))))
9
+ (.-e datom)))
10
+ tx (fn [^Datom datom] (when-not (or (and start? (= tx0 (.-tx datom)))
11
+ (and (not start?) (= txmax (.-tx datom))))
12
+ (.-tx datom)))
13
+ datom-seq (case index-type
14
+ :aevt (list (.-a datom) (e datom) (.-v datom) (tx datom))
15
+ :avet (list (.-a datom) (.-v datom) (e datom) (tx datom))
16
+ (list (e datom) (.-a datom) (.-v datom) (tx datom)))]
17
+ (->> datom-seq
18
+ (take-while some?)
19
+ vec)))
20
+
21
+ (defn- slice-datom-compare [cmp]
22
+ (fn [v1 v2] (-> (filter #(not (= % 0)) (map cmp v1 v2))
23
+ first
24
+ (or 0))))
25
+
26
+ (defn prefix-scan [cmp [e f g h]]
27
+ (let [datom-vec-compare (slice-datom-compare cmp)]
28
+ (fn [[i j k l]]
29
+ (< (cond (and e f g h) (datom-vec-compare [i j k l] [e f g h])
30
+ (and e f g) (datom-vec-compare [i j k] [e f g])
31
+ (and e f) (datom-vec-compare [i j] [e f])
32
+ e (cmp i e)
33
+ :else 0)
34
+ 1))))
35
+
36
+ (defn equals-on-indices?
37
+ "Returns true if 'k1' and 'k2' have the same value at positions indicated by 'indices'"
38
+ [k1, k2, indices]
39
+ (reduce (fn [_ i]
40
+ (if (= (nth k1 i) (nth k2 i))
41
+ true
42
+ (reduced false)))
43
+ true
44
+ indices))
@@ -0,0 +1,32 @@
1
+ (ns ^:no-doc datahike.index
2
+ (:refer-clojure :exclude [-persistent! -flush -count -seq])
3
+ (:require [datahike.index.interface :as di]
4
+ [datahike.index.persistent-set]))
5
+
6
+ ;; Aliases for protocol functions
7
+
8
+ (def -all di/-all)
9
+ (def -seq di/-seq)
10
+ (def -count di/-count)
11
+ (def -insert di/-insert)
12
+ (def -temporal-insert di/-temporal-insert)
13
+ (def -upsert di/-upsert)
14
+ (def -temporal-upsert di/-temporal-upsert)
15
+ (def -remove di/-remove)
16
+ (def -slice di/-slice)
17
+ (def -flush di/-flush)
18
+ (def -transient di/-transient)
19
+ (def -persistent! di/-persistent!)
20
+ (def -mark di/-mark)
21
+
22
+ ;; Aliases for multimethods
23
+
24
+ (def empty-index di/empty-index)
25
+ (def init-index di/init-index)
26
+ (def add-konserve-handlers di/add-konserve-handlers)
27
+ (def konserve-backend di/konserve-backend)
28
+ (def default-index-config di/default-index-config)
29
+
30
+ ;; Other functions
31
+
32
+ (def index-types (keys (methods empty-index)))