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,101 @@
1
+ (ns datahike.test.query-aggregates-test
2
+ (:require
3
+ #?(:cljs [cljs.test :as t :refer-macros [is deftest testing]]
4
+ :clj [clojure.test :as t :refer [is deftest testing]])
5
+ [datahike.api :as d]))
6
+
7
+ (defn sort-reverse [xs]
8
+ (reverse (sort xs)))
9
+
10
+ (deftest test-aggregates
11
+ (let [monsters [["Cerberus" 3]
12
+ ["Medusa" 1]
13
+ ["Cyclops" 1]
14
+ ["Chimera" 1]]]
15
+ (testing "with"
16
+ (is (= (d/q '[:find ?heads
17
+ :with ?monster
18
+ :in [[?monster ?heads]]]
19
+ [["Medusa" 1]
20
+ ["Cyclops" 1]
21
+ ["Chimera" 1]])
22
+ [[1] [1] [1]])))
23
+
24
+ (testing "Wrong grouping without :with"
25
+ (is (= (d/q '[:find (sum ?heads)
26
+ :in [[?monster ?heads]]]
27
+ monsters)
28
+ [[4]])))
29
+
30
+ (testing "Multiple aggregates, correct grouping with :with"
31
+ (is (= (d/q '[:find (sum ?heads) (min ?heads) (max ?heads) (count ?heads) (count-distinct ?heads)
32
+ :with ?monster
33
+ :in [[?monster ?heads]]]
34
+ monsters)
35
+ [[6 1 3 4 2]])))
36
+
37
+ (testing "Min and max are using comparator instead of default compare"
38
+ ;; Wrong: using js '<' operator
39
+ ;; (apply min [:a/b :a-/b :a/c]) => :a-/b
40
+ ;; (apply max [:a/b :a-/b :a/c]) => :a/c
41
+ ;; Correct: use IComparable interface
42
+ ;; (sort compare [:a/b :a-/b :a/c]) => (:a/b :a/c :a-/b)
43
+ (is (= (d/q '[:find (min ?x) (max ?x)
44
+ :in [?x ...]]
45
+ [:a-/b :a/b])
46
+ [[:a/b :a-/b]]))
47
+
48
+ (is (= (d/q '[:find (min 2 ?x) (max 2 ?x)
49
+ :in [?x ...]]
50
+ [:a/b :a-/b :a/c])
51
+ [[[:a/b :a/c] [:a/c :a-/b]]])))
52
+
53
+ (testing "Grouping and parameter passing"
54
+ (is (= (set (d/q '[:find ?color (max ?amount ?x) (min ?amount ?x)
55
+ :in [[?color ?x]] ?amount]
56
+ [[:red 1] [:red 2] [:red 3] [:red 4] [:red 5]
57
+ [:blue 7] [:blue 8]]
58
+ 3))
59
+ #{[:red [3 4 5] [1 2 3]]
60
+ [:blue [7 8] [7 8]]})))
61
+
62
+ (testing "avg aggregate"
63
+ (is (= (ffirst (d/q '[:find (avg ?x)
64
+ :in [?x ...]]
65
+ [10 15 20 35 75]))
66
+ 31)))
67
+
68
+ (testing "median aggregate"
69
+ (is (= (ffirst (d/q '[:find (median ?x)
70
+ :in [?x ...]]
71
+ [10 15 20 35 75]))
72
+ 20)))
73
+
74
+ (testing "variance aggregate"
75
+ (is (= (ffirst (d/q '[:find (variance ?x)
76
+ :in [?x ...]]
77
+ [10 15 20 35 75]))
78
+ 554)))
79
+
80
+ (testing "stddev aggregate"
81
+ (is (= (ffirst (d/q '[:find (stddev ?x)
82
+ :in [?x ...]]
83
+ [10 15 20 35 75]))
84
+ 23.53720459187964)))
85
+
86
+ (testing "Custom aggregates"
87
+ (let [data [[:red 1] [:red 2] [:red 3] [:red 4] [:red 5]
88
+ [:blue 7] [:blue 8]]
89
+ result #{[:red [5 4 3 2 1]] [:blue [8 7]]}]
90
+
91
+ (is (= (set (d/q '[:find ?color (aggregate ?agg ?x)
92
+ :in [[?color ?x]] ?agg]
93
+ data
94
+ sort-reverse))
95
+ result))
96
+
97
+ #?(:clj
98
+ (is (= (set (d/q '[:find ?color (datahike.test.query-aggregates-test/sort-reverse ?x)
99
+ :in [[?color ?x]]]
100
+ data))
101
+ result)))))))
@@ -0,0 +1,52 @@
1
+ (ns datahike.test.query-find-specs-test
2
+ (:require
3
+ #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]]
4
+ :clj [clojure.test :as t :refer [is are deftest testing]])
5
+ [datahike.api :as d]
6
+ [datahike.db :as db]
7
+ [datahike.test.core-test :as tdc]))
8
+
9
+ (def test-db (d/db-with
10
+ (db/empty-db)
11
+ [[:db/add 1 :name "Petr"]
12
+ [:db/add 1 :age 44]
13
+ [:db/add 2 :name "Ivan"]
14
+ [:db/add 2 :age 25]
15
+ [:db/add 3 :name "Sergey"]
16
+ [:db/add 3 :age 11]]))
17
+
18
+ (deftest test-find-specs
19
+ (is (= (set (d/q '[:find [?name ...]
20
+ :where [_ :name ?name]] test-db))
21
+ #{"Ivan" "Petr" "Sergey"}))
22
+ (is (= (d/q '[:find [?name ?age]
23
+ :where [1 :name ?name]
24
+ [1 :age ?age]] test-db)
25
+ ["Petr" 44]))
26
+ (is (= (d/q '[:find ?name .
27
+ :where [1 :name ?name]] test-db)
28
+ "Petr"))
29
+
30
+ (testing "Multiple results get cut"
31
+ (is (contains?
32
+ #{["Petr" 44] ["Ivan" 25] ["Sergey" 11]}
33
+ (d/q '[:find [?name ?age]
34
+ :where [?e :name ?name]
35
+ [?e :age ?age]] test-db)))
36
+ (is (contains?
37
+ #{"Ivan" "Petr" "Sergey"}
38
+ (d/q '[:find ?name .
39
+ :where [_ :name ?name]] test-db))))
40
+
41
+ (testing "Aggregates work with find specs"
42
+ (is (= (d/q '[:find [(count ?name) ...]
43
+ :where [_ :name ?name]] test-db)
44
+ [3]))
45
+ (is (= (d/q '[:find [(count ?name)]
46
+ :where [_ :name ?name]] test-db)
47
+ [3]))
48
+ (is (= (d/q '[:find (count ?name) .
49
+ :where [_ :name ?name]] test-db)
50
+ 3))))
51
+
52
+ #_(t/test-ns 'datahike.test.query-find-specs)
@@ -0,0 +1,523 @@
1
+ (ns datahike.test.query-fns-test
2
+ (:require
3
+ #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]]
4
+ :clj [clojure.test :as t :refer [is are deftest testing]])
5
+ [datahike.api :as d]
6
+ [datahike.db :as db])
7
+ (:import [java.util UUID Date])
8
+ #?(:clj
9
+ (:import [clojure.lang ExceptionInfo])))
10
+
11
+ (deftest test-query-fns
12
+ (testing "predicate without free variables"
13
+ (is (= (d/q '[:find ?x
14
+ :in [?x ...]
15
+ :where [(> 2 1)]] [:a :b :c])
16
+ #{[:a] [:b] [:c]})))
17
+
18
+ (let [db (-> (db/empty-db {:parent {:db/valueType :db.type/ref}})
19
+ (d/db-with [{:db/id 1, :name "Ivan", :age 15}
20
+ {:db/id 2, :name "Petr", :age 22, :height 240, :parent 1}
21
+ {:db/id 3, :name "Slava", :age 37, :parent 2}
22
+ {:db/id 4, :name "Ivan", :age 22}]))]
23
+
24
+ (testing "ground"
25
+ (is (= (d/q '[:find ?vowel
26
+ :where [(ground [:a :e :i :o :u]) [?vowel ...]]])
27
+ #{[:a] [:e] [:i] [:o] [:u]})))
28
+
29
+ (testing "get-else"
30
+ (is (= #{[1 15 300] [2 22 240] [3 37 300] [4 22 300]}
31
+ (d/q '[:find ?e ?age ?height
32
+ :where [?e :age ?age]
33
+ [(get-else $ ?e :height 300) ?height]] db)))
34
+ (is (= #{[1 15 300] [2 22 240] [3 37 300] [4 22 300]}
35
+ (d/q '[:find ?e ?age ?height
36
+ :where
37
+ [(get-else $ ?e :height 300) ?height]
38
+ [?e :age ?age]] db)))
39
+
40
+ (is (thrown-with-msg? ExceptionInfo #"get-else: nil default value is not supported"
41
+ (d/q '[:find ?e ?height
42
+ :where [?e :age]
43
+ [(get-else $ ?e :height nil) ?height]] db))))
44
+
45
+ (testing "get-some"
46
+ (is (= #{[1 :age 15]
47
+ [2 :height 240]
48
+ [3 :age 37]
49
+ [4 :age 22]}
50
+ (d/q '[:find ?e ?a ?v
51
+ :where [?e :name _]
52
+ [(get-some $ ?e :height :age) [?a ?v]]] db))))
53
+
54
+ (testing "q"
55
+ (is (= #{[1 "Ivan"]}
56
+ (d/q '[:find ?e ?n
57
+ :where
58
+ [(q '[:find (min ?age) :where [_ :age ?age]] $) [[?a]]]
59
+ [?e :age ?a]
60
+ [?e :name ?n]]
61
+ db))))
62
+
63
+ (testing "q with rules"
64
+ (is (thrown-with-msg? ExceptionInfo #"Missing rules var '%' in :in"
65
+ (d/q '[:find ?n ?a
66
+ :in $ %
67
+ :where
68
+ [(q [:find ?name (min ?age)
69
+ :where (has-age ?name ?age)])
70
+ [[?n ?a]]]
71
+ (has-age ?n ?a)]
72
+ db
73
+ '[[(has-age ?n ?a) [?e :age ?a] [?e :name ?n]]])))
74
+ (is (= #{}
75
+ (d/q '[:find ?n ?a
76
+ :in $ %
77
+ :where
78
+ [(q [:find ?name (min ?age)
79
+ :in $ %
80
+ :where (has-age ?name ?age)])
81
+ [[?n ?a]]]
82
+ (has-age ?n ?a)]
83
+ db
84
+ '[[(has-age ?n ?a) [?e :age ?a] [?e :name ?n]]])))
85
+ (is (= #{["Petr" 22] ["Ivan" 15] ["Slava" 37]}
86
+ (d/q '[:find ?n ?a
87
+ :in $ %
88
+ :where
89
+ [(q [:find ?name (min ?age)
90
+ :in $ %
91
+ :where (has-age ?name ?age)]
92
+ $
93
+ [[(has-age ?n ?a) [?e :age ?a] [?e :name ?n]]])
94
+ [[?n ?a]]]
95
+ (has-age ?n ?a)]
96
+ db
97
+ '[[(has-age ?n ?a) [?e :age ?a] [?e :name ?n]]]))))
98
+
99
+ (testing "q without quotes"
100
+ (is (= #{[1 "Ivan"]}
101
+ (d/q '[:find ?e ?n
102
+ :where
103
+ [(q [:find (min ?age) :where [_ :age ?age]] $) [[?a]]]
104
+ [?e :age ?a]
105
+ [?e :name ?n]]
106
+ db))))
107
+
108
+ (testing "missing?"
109
+ (is (= #{[1 15] [3 37] [4 22]}
110
+ (d/q '[:find ?e ?age
111
+ :in $
112
+ :where [?e :age ?age]
113
+ [(missing? $ ?e :height)]] db))))
114
+
115
+ (testing "missing? back-ref"
116
+ (is (= #{[3] [4]}
117
+ (d/q '[:find ?e
118
+ :in $
119
+ :where [?e :age ?age]
120
+ [(missing? $ ?e :_parent)]] db))))
121
+
122
+ (testing "Built-ins"
123
+ (is (= #{[1 2] [1 3] [1 4]}
124
+ (d/q '[:find ?e1 ?e2
125
+ :where [?e1 :age ?a1]
126
+ [?e2 :age ?a2]
127
+ [(< ?a1 18 ?a2)]] db)))
128
+
129
+ (is (= (d/q '[:find ?x ?c
130
+ :in [?x ...]
131
+ :where [(count ?x) ?c]]
132
+ ["a" "abc"])
133
+ #{["a" 1] ["abc" 3]})))
134
+
135
+ (testing "Clojure core built-ins"
136
+ (is (= #{[:age] [:parent] [:height]}
137
+ (d/q '[:find ?attr
138
+ :where
139
+ [_ ?attr ?a]
140
+ [(int? ?a)]]
141
+ db)))
142
+ (is (= #{[:Ivan] [:Petr] [:Slava]}
143
+ (d/q '[:find ?k
144
+ :where
145
+ [_ :name ?n]
146
+ [(keyword ?n) ?k]]
147
+ db)))
148
+
149
+ (is (= #{[16] [23] [38]}
150
+ (d/q '[:find ?na
151
+ :where
152
+ [_ :age ?a]
153
+ [(inc ?a) ?na]]
154
+ db))))
155
+
156
+ (testing "Function binding filtered by input argument"
157
+ (is (= (d/q '[:find ?x
158
+ :in [?x ...] ?c
159
+ :where [(count ?x) ?c]]
160
+ ["a" "abc"]
161
+ 3)
162
+ #{["abc"]})))
163
+
164
+ (testing "Built-in vector, hashmap"
165
+ (is (= (d/q '[:find [?tx-data ...]
166
+ :where
167
+ [(ground :db/add) ?op]
168
+ [(vector ?op -1 :attr 12) ?tx-data]])
169
+ [[:db/add -1 :attr 12]]))
170
+
171
+ (is (= (d/q '[:find [?tx-data ...]
172
+ :where
173
+ [(hash-map :db/id -1 :age 92 :name "Aaron") ?tx-data]])
174
+ [{:db/id -1 :age 92 :name "Aaron"}])))
175
+
176
+ (testing "Passing predicate as source"
177
+ (is (= #{[2] [3] [4]}
178
+ (d/q '[:find ?e
179
+ :in $ ?adult
180
+ :where [?e :age ?a]
181
+ [(?adult ?a)]]
182
+ db
183
+ #(> % 18)))))
184
+
185
+ (testing "Calling a function"
186
+ (is (= #{[1 2 3] [2 1 3] [1 4 3] [4 1 3]}
187
+ (d/q '[:find ?e1 ?e2 ?e3
188
+ :where [?e1 :age ?a1]
189
+ [?e2 :age ?a2]
190
+ [?e3 :age ?a3]
191
+ [(+ ?a1 ?a2) ?a12]
192
+ [(= ?a12 ?a3)]]
193
+ db))))
194
+
195
+ (testing "Two conflicting function values for one binding."
196
+ (is (= (d/q '[:find ?n
197
+ :where [(identity 1) ?n]
198
+ [(identity 2) ?n]]
199
+ db)
200
+ #{})))
201
+
202
+ (testing "Destructured conflicting function values for two bindings."
203
+ (is (= (d/q '[:find ?n ?x
204
+ :where [(identity [3 4]) [?n ?x]]
205
+ [(identity [1 2]) [?n ?x]]]
206
+ db)
207
+ #{})))
208
+
209
+ (testing "Rule bindings interacting with function binding. (fn, rule)"
210
+ (is (= (d/q '[:find ?n
211
+ :in $ %
212
+ :where [(identity 2) ?n]
213
+ (my-vals ?n)]
214
+ db
215
+ '[[(my-vals ?x)
216
+ [(identity 1) ?x]]
217
+ [(my-vals ?x)
218
+ [(identity 2) ?x]]
219
+ [(my-vals ?x)
220
+ [(identity 3) ?x]]])
221
+ #{[2]})))
222
+
223
+ (testing "Rule bindings interacting with function binding. (rule, fn)"
224
+ (is (= (d/q '[:find ?n
225
+ :in $ %
226
+ :where (my-vals ?n)
227
+ [(identity 2) ?n]]
228
+ db
229
+ '[[(my-vals ?x)
230
+ [(identity 1) ?x]]
231
+ [(my-vals ?x)
232
+ [(identity 2) ?x]]
233
+ [(my-vals ?x)
234
+ [(identity 3) ?x]]])
235
+ #{[2]})))
236
+
237
+ (testing "Conflicting relational bindings with function binding. (rel, fn)"
238
+ (is (= (d/q '[:find ?age
239
+ :where [_ :age ?age]
240
+ [(identity 100) ?age]]
241
+ db)
242
+ #{})))
243
+
244
+ (testing "Conflicting relational bindings with function binding. (fn, rel)"
245
+ (is (= (d/q '[:find ?age
246
+ :where [(identity 100) ?age]
247
+ [_ :age ?age]]
248
+ db)
249
+ #{})))
250
+
251
+ (testing "Function on empty rel"
252
+ (is (= (d/q '[:find ?e ?y
253
+ :where [?e :salary ?x]
254
+ [(+ ?x 100) ?y]]
255
+ [[0 :age 15] [1 :age 35]])
256
+ #{})))
257
+
258
+ (testing "Returning nil from function filters out tuple from result"
259
+ (is (= (d/q '[:find ?x
260
+ :in [?in ...] ?f
261
+ :where [(?f ?in) ?x]]
262
+ [1 2 3 4]
263
+ #(when (even? %) %))
264
+ #{[2] [4]})))
265
+
266
+ (testing "Result bindings"
267
+ (is (= (d/q '[:find ?a ?c
268
+ :in ?in
269
+ :where [(ground ?in) [?a _ ?c]]]
270
+ [:a :b :c])
271
+ #{[:a :c]}))
272
+
273
+ (is (= (d/q '[:find ?in
274
+ :in ?in
275
+ :where [(ground ?in) _]]
276
+ :a)
277
+ #{[:a]}))
278
+
279
+ (is (= (d/q '[:find ?x ?z
280
+ :in ?in
281
+ :where [(ground ?in) [[?x _ ?z] ...]]]
282
+ [[:a :b :c] [:d :e :f]])
283
+ #{[:a :c] [:d :f]}))
284
+
285
+ (is (= (d/q '[:find ?in
286
+ :in [?in ...]
287
+ :where [(ground ?in) _]]
288
+ [])
289
+ #{})))))
290
+
291
+ (deftest test-predicates
292
+ (let [entities [{:db/id 1 :name "Ivan" :age 10}
293
+ {:db/id 2 :name "Ivan" :age 20}
294
+ {:db/id 3 :name "Oleg" :age 10}
295
+ {:db/id 4 :name "Oleg" :age 20}]
296
+ db (d/db-with (db/empty-db) entities)]
297
+ (are [q res] (= (d/q (quote q) db) res)
298
+ ;; plain predicate
299
+ [:find ?e ?a
300
+ :where [?e :age ?a]
301
+ [(> ?a 10)]]
302
+ #{[2 20] [4 20]}
303
+
304
+ ;; join in predicate
305
+ [:find ?e ?e2
306
+ :where [?e :name]
307
+ [?e2 :name]
308
+ [(< ?e ?e2)]]
309
+ #{[1 2] [1 3] [1 4] [2 3] [2 4] [3 4]}
310
+
311
+ ;; join with extra symbols
312
+ [:find ?e ?e2
313
+ :where [?e :age ?a]
314
+ [?e2 :age ?a2]
315
+ [(< ?e ?e2)]]
316
+ #{[1 2] [1 3] [1 4] [2 3] [2 4] [3 4]}
317
+
318
+ ;; empty result
319
+ [:find ?e ?e2
320
+ :where [?e :name "Ivan"]
321
+ [?e2 :name "Oleg"]
322
+ [(= ?e ?e2)]]
323
+ #{}
324
+
325
+ ;; pred over const, true
326
+ [:find ?e
327
+ :where [?e :name "Ivan"]
328
+ [?e :age 20]
329
+ [(= ?e 2)]]
330
+ #{[2]}
331
+
332
+ ;; pred over const, false
333
+ [:find ?e
334
+ :where [?e :name "Ivan"]
335
+ [?e :age 20]
336
+ [(= ?e 1)]]
337
+ #{})
338
+ (let [pred (fn [db e a]
339
+ (= a (:age (d/entity db e))))]
340
+ (is (= (d/q '[:find ?e
341
+ :in $ ?pred
342
+ :where [?e :age ?a]
343
+ [(?pred $ ?e 10)]]
344
+ db pred)
345
+ #{[1] [3]})))))
346
+
347
+ (deftest test-exceptions
348
+ (is (thrown-with-msg? ExceptionInfo #"Unknown predicate 'fun in \[\(fun \?e\)\]"
349
+ (d/q '[:find ?e
350
+ :in [?e ...]
351
+ :where [(fun ?e)]]
352
+ [1])))
353
+
354
+ (is (thrown-with-msg? ExceptionInfo #"Unknown function 'fun in \[\(fun \?e\) \?x\]"
355
+ (d/q '[:find ?e ?x
356
+ :in [?e ...]
357
+ :where [(fun ?e) ?x]]
358
+ [1]))))
359
+
360
+ (deftest test-issue-180
361
+ (is (= #{}
362
+ (d/q '[:find ?e ?a
363
+ :where [_ :pred ?pred]
364
+ [?e :age ?a]
365
+ [(?pred ?a)]]
366
+ (d/db-with (db/empty-db) [[:db/add 1 :age 20]])))))
367
+
368
+ (defn sample-query-fn [] 42)
369
+
370
+ #?(:clj
371
+ (deftest test-symbol-resolution
372
+ (is (= 42 (d/q '[:find ?x .
373
+ :where [(datahike.test.query-fns-test/sample-query-fn) ?x]])))))
374
+
375
+ (deftest test-built-in-predicates-types
376
+ (let [uuids (sort (repeatedly 3 #(UUID/randomUUID)))
377
+ dates (map #(-> (Date.) .getTime (+ (* % 86400 1000)) Date.) (range 3))
378
+ entities [{:db/id 1 :uuid (nth uuids 0) :birthday (nth dates 0) :symbol 'a :name "Alek" :age 20
379
+ :group :a :collegues [1 2] :married? false}
380
+ {:db/id 2 :uuid (nth uuids 1) :birthday (nth dates 1) :symbol 'i :name "Ivan" :age 30
381
+ :group :b :collegues [2 3] :married? true}
382
+ {:db/id 3 :uuid (nth uuids 2) :birthday (nth dates 2) :symbol 'o :name "Oleg" :age 40
383
+ :group :c :collegues [3 4] :married? true}
384
+ {:db/id 4 :uuid (nth uuids 2) :birthday (nth dates 2) :symbol 'o :name "Oleg" :age 40
385
+ :group :c :collegues [3 4] :married? true}]
386
+ db (d/db-with (db/empty-db) entities)]
387
+
388
+ (testing "lesser"
389
+ (are [attr] (= #{[1 2] [1 3] [1 4] [2 3] [2 4]}
390
+ (d/q (into '[:find ?e1 ?e2 :where]
391
+ [['?e1 attr '?a1]
392
+ ['?e2 attr '?a2]
393
+ ['(< ?a1 ?a2)]])
394
+ db))
395
+ :uuid :birthday :symbol :name :age :group :collegues)
396
+ (is (= #{[1 2] [1 3] [1 4]}
397
+ (d/q (into '[:find ?e1 ?e2 :where]
398
+ [['?e1 :married? '?a1]
399
+ ['?e2 :married? '?a2]
400
+ ['(< ?a1 ?a2)]])
401
+ db)))
402
+ (are [attr] (= #{[1 2 3] [1 2 4]}
403
+ (d/q (into '[:find ?e1 ?e2 ?e3 :where]
404
+ [['?e1 attr '?a1]
405
+ ['?e2 attr '?a2]
406
+ ['?e3 attr '?a3]
407
+ ['(< ?a1 ?a2 ?a3)]])
408
+
409
+ db))
410
+ :uuid :birthday :symbol :name :age :group :collegues)
411
+ (is (= #{}
412
+ (d/q (into '[:find ?e1 ?e2 ?e3 :where]
413
+ [['?e1 :married? '?a1]
414
+ ['?e2 :married? '?a2]
415
+ ['?e3 :married? '?a3]
416
+ ['(< ?a1 ?a2 ?a3)]])
417
+
418
+ db)))
419
+
420
+ (testing "greater"
421
+ (are [attr] (= #{[4 1] [3 1] [2 1] [4 2] [3 2]}
422
+ (d/q (into '[:find ?e1 ?e2 :where]
423
+ [['?e1 attr '?a1]
424
+ ['?e2 attr '?a2]
425
+ ['(> ?a1 ?a2)]])
426
+ db))
427
+ :uuid :birthday :symbol :name :age :group :collegues)
428
+ (is (= #{[4 1] [3 1] [2 1]}
429
+ (d/q (into '[:find ?e1 ?e2 :where]
430
+ [['?e1 :married? '?a1]
431
+ ['?e2 :married? '?a2]
432
+ ['(> ?a1 ?a2)]])
433
+ db)))
434
+ (are [attr] (= #{[3 2 1] [4 2 1]}
435
+ (d/q (into '[:find ?e1 ?e2 ?e3 :where]
436
+ [['?e1 attr '?a1]
437
+ ['?e2 attr '?a2]
438
+ ['?e3 attr '?a3]
439
+ ['(> ?a1 ?a2 ?a3)]])
440
+ db))
441
+ :uuid :birthday :symbol :name :age :group :collegues)
442
+ (is (= #{}
443
+ (d/q (into '[:find ?e1 ?e2 ?e3 :where]
444
+ [['?e1 :married? '?a1]
445
+ ['?e2 :married? '?a2]
446
+ ['?e3 :married? '?a3]
447
+ ['(> ?a1 ?a2 ?a3)]])
448
+ db))))
449
+
450
+ (testing "lesser-equal"
451
+ (are [attr] (= #{[1 2] [1 3] [1 4] [2 3] [2 4] [1 1] [2 2] [3 3] [3 4] [4 3] [4 4]}
452
+ (d/q (into '[:find ?e1 ?e2 :where]
453
+ [['?e1 attr '?a1]
454
+ ['?e2 attr '?a2]
455
+ ['(<= ?a1 ?a2)]])
456
+ db))
457
+ :uuid :birthday :symbol :name :age :group :collegues)
458
+ (is (= #{[4 3] [2 2] [2 3] [3 3] [1 1] [3 4] [4 2] [1 4] [1 3] [2 4] [4 4] [1 2] [3 2]}
459
+ (d/q (into '[:find ?e1 ?e2 :where]
460
+ [['?e1 :married? '?a1]
461
+ ['?e2 :married? '?a2]
462
+ ['(<= ?a1 ?a2)]])
463
+ db)))
464
+ (are [attr] (= #{[1 1 1] [1 1 2] [1 1 3] [1 1 4] [1 2 2] [1 2 3] [1 2 4] [1 3 3]
465
+ [1 3 4] [1 4 3] [1 4 4] [2 2 2] [2 2 3] [2 2 4] [2 3 3] [2 3 4]
466
+ [2 4 3] [2 4 4] [3 3 3] [3 3 4] [3 4 3] [3 4 4] [4 3 3] [4 3 4]
467
+ [4 4 3] [4 4 4]}
468
+ (d/q (into '[:find ?e1 ?e2 ?e3 :where]
469
+ [['?e1 attr '?a1]
470
+ ['?e2 attr '?a2]
471
+ ['?e3 attr '?a3]
472
+ ['(<= ?a1 ?a2 ?a3)]])
473
+ db))
474
+ :uuid :birthday :symbol :name :age :group :collegues)
475
+ (is (= #{[2 4 4] [3 2 4] [4 4 2] [1 4 3] [2 4 2] [2 2 4] [1 3 2] [4 3 2]
476
+ [3 4 3] [4 4 3] [4 2 4] [3 4 2] [1 1 3] [1 3 4] [1 4 4] [2 3 2]
477
+ [1 1 1] [2 2 2] [3 2 2] [3 3 3] [1 3 3] [3 2 3] [2 3 4] [4 3 4]
478
+ [2 2 3] [4 3 3] [4 4 4] [1 2 3] [1 1 4] [2 3 3] [3 4 4] [1 1 2]
479
+ [2 4 3] [3 3 4] [1 2 2] [1 2 4] [4 2 2] [4 2 3] [1 4 2] [3 3 2]}
480
+ (d/q (into '[:find ?e1 ?e2 ?e3 :where]
481
+ [['?e1 :married? '?a1]
482
+ ['?e2 :married? '?a2]
483
+ ['?e3 :married? '?a3]
484
+ ['(<= ?a1 ?a2 ?a3)]])
485
+ db))))
486
+
487
+ (testing "greater-equal"
488
+ (are [attr] (= #{[4 1] [3 1] [2 1] [4 2] [3 2] [1 1] [2 2] [3 3] [3 4] [4 3] [4 4]}
489
+ (d/q (into '[:find ?e1 ?e2 :where]
490
+ [['?e1 attr '?a1]
491
+ ['?e2 attr '?a2]
492
+ ['(>= ?a1 ?a2)]])
493
+ db))
494
+ :uuid :birthday :symbol :name :age :group :collegues)
495
+ (is (= #{[4 3] [2 2] [2 3] [3 3] [1 1] [3 4] [4 2] [4 1] [2 4] [3 1] [2 1] [4 4] [3 2]}
496
+ (d/q (into '[:find ?e1 ?e2 :where]
497
+ [['?e1 :married? '?a1]
498
+ ['?e2 :married? '?a2]
499
+ ['(>= ?a1 ?a2)]])
500
+ db)))
501
+ (are [attr] (= #{[3 2 1] [4 2 1] [4 3 1] [4 3 2] [1 1 1] [2 1 1] [2 2 1] [2 2 2]
502
+ [3 1 1] [3 2 2] [3 3 1] [3 3 2] [3 3 3] [3 3 4] [3 4 1] [3 4 2]
503
+ [3 4 3] [3 4 4] [4 1 1] [4 2 2] [4 3 3] [4 3 4] [4 4 1] [4 4 2]
504
+ [4 4 3] [4 4 4]}
505
+ (d/q (into '[:find ?e1 ?e2 ?e3 :where]
506
+ [['?e1 attr '?a1]
507
+ ['?e2 attr '?a2]
508
+ ['?e3 attr '?a3]
509
+ ['(>= ?a1 ?a2 ?a3)]])
510
+ db))
511
+ :uuid :birthday :symbol :name :age :group :collegues)
512
+ (is (= #{[2 4 4] [2 3 1] [2 4 1] [3 2 4] [4 4 2] [3 4 1] [2 4 2] [2 2 4]
513
+ [3 1 1] [4 3 2] [3 4 3] [4 4 3] [4 2 4] [3 4 2] [2 3 2] [4 4 1]
514
+ [1 1 1] [2 2 2] [3 2 2] [3 3 3] [3 2 3] [4 2 1] [2 2 1] [4 3 1]
515
+ [2 3 4] [4 3 4] [2 2 3] [3 3 1] [4 3 3] [4 4 4] [2 1 1] [4 1 1]
516
+ [2 3 3] [3 4 4] [3 2 1] [2 4 3] [3 3 4] [4 2 2] [4 2 3] [3 3 2]}
517
+ (d/q (into '[:find ?e1 ?e2 ?e3 :where]
518
+ [['?e1 :married? '?a1]
519
+ ['?e2 :married? '?a2]
520
+ ['?e3 :married? '?a3]
521
+ ['(>= ?a1 ?a2 ?a3)]])
522
+ db)))))))
523
+