Rework query tests to use a live DB. Fixes #35.
This commit is contained in:
parent
4d2079380c
commit
15df2d9eac
1 changed files with 369 additions and 257 deletions
|
@ -1,19 +1,32 @@
|
|||
(ns datomish.test.query
|
||||
#?(:cljs
|
||||
(:require-macros
|
||||
[datomish.pair-chan :refer [go-pair <?]]
|
||||
[datomish.node-tempfile-macros :refer [with-tempfile]]
|
||||
[cljs.core.async.macros :as a :refer [go]]))
|
||||
(:require
|
||||
[datomish.query.cc :as cc]
|
||||
[datomish.query.context :as context]
|
||||
[datomish.query.source :as source]
|
||||
[datomish.query.transforms :as transforms]
|
||||
[datomish.query :as query]
|
||||
[datomish.db :as db]
|
||||
[datomish.schema :as schema]
|
||||
[datomish.transact :as transact]
|
||||
[datomish.api :as d]
|
||||
#?@(:clj
|
||||
[
|
||||
[honeysql.core :as sql :refer [param]]
|
||||
[clojure.test :as t :refer [is are deftest testing]]])
|
||||
[[datomish.pair-chan :refer [go-pair <?]]
|
||||
[datomish.jdbc-sqlite]
|
||||
[datomish.test-macros :refer [deftest-db]]
|
||||
[honeysql.core :as sql :refer [param]]
|
||||
[tempfile.core :refer [tempfile with-tempfile]]
|
||||
[clojure.test :as t :refer [is are deftest testing]]])
|
||||
#?@(:cljs
|
||||
[
|
||||
[honeysql.core :as sql :refer-macros [param]]
|
||||
[cljs.test :as t :refer-macros [is are deftest testing]]])
|
||||
)
|
||||
[[datomish.promise-sqlite]
|
||||
[datomish.test-macros :refer-macros [deftest-db]]
|
||||
[honeysql.core :as sql :refer-macros [param]]
|
||||
[datomish.node-tempfile :refer [tempfile]]
|
||||
[cljs.test :as t :refer-macros [is are deftest testing]]]))
|
||||
#?(:clj
|
||||
(:import [clojure.lang ExceptionInfo])))
|
||||
|
||||
|
@ -29,305 +42,404 @@
|
|||
(fgensym s (dec (swap! counter inc)))))))
|
||||
|
||||
(def simple-schema
|
||||
{:db/txInstant {:db/ident :db/txInstant
|
||||
:db/valueType :long
|
||||
:db/cardinality :db.cardinality/one}
|
||||
:foo/int {:db/ident :foo/int
|
||||
:db/valueType :db.type/integer
|
||||
:db/cardinality :db.cardinality/one}
|
||||
:foo/str {:db/ident :foo/str
|
||||
:db/valueType :db.type/string
|
||||
:db/cardinality :db.cardinality/many}})
|
||||
[{:db/id (d/id-literal :db.part/user)
|
||||
:db.install/_attribute :db.part/db
|
||||
:db/ident :db/txInstant
|
||||
:db/valueType :db.type/long
|
||||
:db/cardinality :db.cardinality/one}
|
||||
{:db/id (d/id-literal :db.part/user)
|
||||
:db.install/_attribute :db.part/db
|
||||
:db/ident :foo/bar
|
||||
:db/valueType :db.type/string
|
||||
:db/cardinality :db.cardinality/many}
|
||||
{:db/id (d/id-literal :db.part/user)
|
||||
:db.install/_attribute :db.part/db
|
||||
:db/ident :foo/int
|
||||
:db/valueType :db.type/long
|
||||
:db/cardinality :db.cardinality/one}
|
||||
{:db/id (d/id-literal :db.part/user)
|
||||
:db.install/_attribute :db.part/db
|
||||
:db/ident :foo/str
|
||||
:db/valueType :db.type/string
|
||||
:db/cardinality :db.cardinality/many}])
|
||||
|
||||
(defn mock-source [db schema]
|
||||
(source/map->DatomsSource
|
||||
{:table :datoms
|
||||
:fulltext-table :fulltext_values
|
||||
:fulltext-view :all_datoms
|
||||
:columns [:e :a :v :tx :added]
|
||||
:attribute-transform transforms/attribute-transform-string
|
||||
:constant-transform transforms/constant-transform-default
|
||||
:table-alias (comp (make-predictable-gensym) name)
|
||||
:schema (schema/map->Schema
|
||||
{:schema schema
|
||||
:rschema nil})
|
||||
:make-constraints nil}))
|
||||
(def page-schema
|
||||
[{:db/id (d/id-literal :db.part/user)
|
||||
:db.install/_attribute :db.part/db
|
||||
:db/ident :page/loves
|
||||
:db/valueType :db.type/ref
|
||||
:db/cardinality :db.cardinality/many}
|
||||
{:db/id (d/id-literal :db.part/user)
|
||||
:db.install/_attribute :db.part/db
|
||||
:db/ident :page/likes
|
||||
:db/valueType :db.type/ref
|
||||
:db/cardinality :db.cardinality/many}
|
||||
{:db/id (d/id-literal :db.part/user)
|
||||
:db.install/_attribute :db.part/db
|
||||
:db/ident :page/url
|
||||
:db/valueType :db.type/string
|
||||
:db/unique :db.unique/identity
|
||||
:db/cardinality :db.cardinality/one}
|
||||
{:db/id (d/id-literal :db.part/user)
|
||||
:db.install/_attribute :db.part/db
|
||||
:db/ident :page/title
|
||||
:db/valueType :db.type/string
|
||||
:db/cardinality :db.cardinality/one}
|
||||
{:db/id (d/id-literal :db.part/user)
|
||||
:db.install/_attribute :db.part/db
|
||||
:db/ident :page/starred
|
||||
:db/valueType :db.type/boolean
|
||||
:db/cardinality :db.cardinality/one}])
|
||||
|
||||
(defn- expand [find schema]
|
||||
(let [context (context/->Context (mock-source nil schema) nil nil)
|
||||
(def schema-with-page
|
||||
(concat
|
||||
simple-schema
|
||||
page-schema))
|
||||
|
||||
(defn mock-source [db]
|
||||
(assoc (datomish.db/datoms-source db)
|
||||
:table-alias (comp (make-predictable-gensym) name)))
|
||||
|
||||
(defn conn->context [conn]
|
||||
(context/->Context (mock-source (d/db conn)) nil nil))
|
||||
|
||||
(defn- expand [find conn]
|
||||
(let [context (conn->context conn)
|
||||
parsed (query/parse find)]
|
||||
(query/find->sql-clause context parsed)))
|
||||
|
||||
(defn- populate [find schema]
|
||||
(let [context (context/->Context (mock-source nil schema) nil nil)
|
||||
(defn- populate [find conn]
|
||||
(let [context (conn->context conn)
|
||||
parsed (query/parse find)]
|
||||
(query/find-into-context context parsed)))
|
||||
|
||||
(deftest test-type-extraction
|
||||
(defn <initialize-with-schema [conn schema]
|
||||
(go-pair
|
||||
(let [tx (<? (d/<transact! conn schema))]
|
||||
(let [idents (map :db/ident schema)
|
||||
db (d/db conn)]
|
||||
(into {}
|
||||
(map (fn [ident]
|
||||
[ident (d/entid db ident)])
|
||||
idents))))))
|
||||
|
||||
(deftest-db test-type-extraction conn
|
||||
;; We expect to be able to look up the default types.
|
||||
(is (integer? (d/entid (d/db conn) :db.type/ref)))
|
||||
(is (integer? (d/entid (d/db conn) :db.type/long)))
|
||||
|
||||
;; Add our own schema.
|
||||
(<? (<initialize-with-schema conn simple-schema))
|
||||
(testing "Variable entity."
|
||||
(is (= (:known-types (:cc (populate '[:find ?e ?v :in $ :where [?e :foo/int ?v]] simple-schema)))
|
||||
{'?v :db.type/integer
|
||||
(is (= (->
|
||||
(populate '[:find ?e ?v :in $ :where [?e :foo/int ?v]] conn)
|
||||
:cc :known-types)
|
||||
{'?v :db.type/long
|
||||
'?e :db.type/ref})))
|
||||
|
||||
(testing "Numeric entid."
|
||||
(is (= (:known-types (:cc (populate '[:find ?v :in $ :where [6 :foo/int ?v]] simple-schema)))
|
||||
{'?v :db.type/integer})))
|
||||
(is (= (->
|
||||
(populate '[:find ?v :in $ :where [6 :foo/int ?v]] conn)
|
||||
:cc :known-types)
|
||||
{'?v :db.type/long})))
|
||||
|
||||
(testing "Keyword entity."
|
||||
(is (= (:known-types (:cc (populate '[:find ?v :in $ :where [:my/thing :foo/int ?v]] simple-schema)))
|
||||
{'?v :db.type/integer}))))
|
||||
(is (= (->
|
||||
(populate '[:find ?v :in $ :where [:my/thing :foo/int ?v]] conn)
|
||||
:cc :known-types)
|
||||
{'?v :db.type/long}))))
|
||||
|
||||
(deftest test-value-constant-constraint-descends-into-not-and-or
|
||||
(testing "Elision of types inside a join."
|
||||
(is (= '{:select ([:datoms0.e :e]
|
||||
[:datoms0.v :v]),
|
||||
:modifiers [:distinct],
|
||||
:from [[:datoms datoms0]],
|
||||
:where (:and
|
||||
[:= :datoms0.a "foo/int"]
|
||||
[:not
|
||||
[:exists
|
||||
{:select [1],
|
||||
:from [[:all_datoms all_datoms1]],
|
||||
:where (:and
|
||||
[:= :all_datoms1.e 15]
|
||||
[:= :datoms0.v :all_datoms1.v])}]])}
|
||||
(expand
|
||||
'[:find ?e ?v :in $ :where
|
||||
[?e :foo/int ?v]
|
||||
(not [15 ?a ?v])]
|
||||
simple-schema))))
|
||||
(deftest-db test-value-constant-constraint-descends-into-not-and-or conn
|
||||
(let [attrs (<? (<initialize-with-schema conn simple-schema))]
|
||||
(testing "Elision of types inside a join."
|
||||
(is (= {:select '([:datoms0.e :e]
|
||||
[:datoms0.v :v]),
|
||||
:modifiers [:distinct],
|
||||
:from [[:datoms 'datoms0]],
|
||||
:where (list :and
|
||||
[:= :datoms0.a (:foo/int attrs)]
|
||||
[:not
|
||||
[:exists
|
||||
{:select [1],
|
||||
:from [[:all_datoms 'all_datoms1]],
|
||||
:where (list :and
|
||||
[:= :all_datoms1.e 15]
|
||||
[:= :datoms0.v :all_datoms1.v])}]])}
|
||||
(expand
|
||||
'[:find ?e ?v :in $ :where
|
||||
[?e :foo/int ?v]
|
||||
(not [15 ?a ?v])]
|
||||
conn))))
|
||||
|
||||
(testing "Type collisions inside :not."
|
||||
(is (thrown-with-msg?
|
||||
ExceptionInfo #"\?v already has type :db\.type\/integer"
|
||||
(expand
|
||||
'[:find ?e ?v :in $ :where
|
||||
[?e :foo/int ?v]
|
||||
(not [15 :foo/str ?v])]
|
||||
simple-schema))))
|
||||
(testing "Type collisions inside :or"
|
||||
(is (thrown-with-msg?
|
||||
ExceptionInfo #"\?v already has type :db\.type\/integer"
|
||||
(expand
|
||||
'[:find ?e ?v :in $ :where
|
||||
[?e :foo/int ?v]
|
||||
(or
|
||||
[15 :foo/str ?v]
|
||||
[10 :foo/int ?v])]
|
||||
simple-schema)))))
|
||||
(testing "Type collisions inside :not."
|
||||
(is (thrown-with-msg?
|
||||
ExceptionInfo #"v already has type :db.type.long"
|
||||
(expand
|
||||
'[:find ?e ?v :in $ :where
|
||||
[?e :foo/int ?v]
|
||||
(not [15 :foo/str ?v])]
|
||||
conn))))
|
||||
|
||||
(deftest test-type-collision
|
||||
(testing "Type collisions inside :or"
|
||||
(is (thrown-with-msg?
|
||||
ExceptionInfo #"v already has type :db.type.long"
|
||||
(expand
|
||||
'[:find ?e ?v :in $ :where
|
||||
[?e :foo/int ?v]
|
||||
(or
|
||||
[15 :foo/str ?v]
|
||||
[10 :foo/int ?v])]
|
||||
conn))))))
|
||||
|
||||
(deftest-db test-type-collision conn
|
||||
(<? (<initialize-with-schema conn simple-schema))
|
||||
(let [find '[:find ?e ?v :in $
|
||||
:where
|
||||
[?e :foo/int ?v]
|
||||
[?x :foo/str ?v]]]
|
||||
(is (thrown-with-msg?
|
||||
ExceptionInfo #"\?v already has type :db\.type\/integer"
|
||||
(populate find simple-schema)))))
|
||||
ExceptionInfo #"v already has type :db.type.long"
|
||||
(populate find conn)))))
|
||||
|
||||
(deftest test-value-constant-constraint
|
||||
(deftest-db test-value-constant-constraint conn
|
||||
(<? (<initialize-with-schema conn simple-schema))
|
||||
(is (= {:select '([:all_datoms0.e :foo]),
|
||||
:modifiers [:distinct],
|
||||
:from '[[:all_datoms all_datoms0]],
|
||||
:where (list :and
|
||||
(list :or
|
||||
[:= :all_datoms0.value_type_tag 0]
|
||||
;; In CLJS, this can also be an `instant`.
|
||||
#?@(:cljs [[:= :all_datoms0.value_type_tag 4]])
|
||||
[:= :all_datoms0.value_type_tag 5])
|
||||
[:= :all_datoms0.v 99])}
|
||||
(expand
|
||||
'[:find ?foo :in $ :where
|
||||
[?foo _ 99]]
|
||||
simple-schema))))
|
||||
conn))))
|
||||
|
||||
(deftest test-value-constant-constraint-elided-using-schema
|
||||
(testing "There's no need to produce value_type_tag constraints when the attribute is specified."
|
||||
(is
|
||||
(= '{:select ([:datoms1.v :timestampMicros] [:datoms0.e :page]),
|
||||
:modifiers [:distinct],
|
||||
:from [[:datoms datoms0]
|
||||
[:datoms datoms1]],
|
||||
:where (:and
|
||||
;; We don't need a type check on the range of page/starred...
|
||||
[:= :datoms0.a "page/starred"]
|
||||
[:= :datoms0.v 1]
|
||||
[:= :datoms1.a "db/txInstant"]
|
||||
[:not
|
||||
[:exists
|
||||
{:select [1],
|
||||
:from [[:datoms datoms2]],
|
||||
:where (:and
|
||||
[:= :datoms2.a "foo/bar"]
|
||||
[:= :datoms0.e :datoms2.e])}]]
|
||||
[:= :datoms0.tx :datoms1.e])}
|
||||
(expand
|
||||
'[:find ?timestampMicros ?page :in $ ?latest :where
|
||||
[?page :page/starred true ?t]
|
||||
[?t :db/txInstant ?timestampMicros]
|
||||
(not [?page :foo/bar _])]
|
||||
(deftest-db test-value-constant-constraint-elided-using-schema conn
|
||||
(let [attrs (<? (<initialize-with-schema conn schema-with-page))]
|
||||
(testing "Our attributes were interned."
|
||||
(is (integer? (d/entid (d/db conn) :foo/str)))
|
||||
(is (integer? (d/entid (d/db conn) :page/starred))))
|
||||
|
||||
(merge
|
||||
simple-schema
|
||||
{:page/starred {:db/valueType :db.type/boolean
|
||||
:db/ident :page/starred
|
||||
:db/cardinality :db.cardinality/one}}))))))
|
||||
(testing "There's no need to produce value_type_tag constraints when the attribute is specified."
|
||||
(is
|
||||
(= {:select '([:datoms1.v :timestampMicros] [:datoms0.e :page]),
|
||||
:modifiers [:distinct],
|
||||
:from [[:datoms 'datoms0]
|
||||
[:datoms 'datoms1]],
|
||||
:where (list :and
|
||||
;; We don't need a type check on the range of page/starred...
|
||||
[:= :datoms0.a (:page/starred attrs)]
|
||||
[:= :datoms0.v 1]
|
||||
[:= :datoms1.a (:db/txInstant attrs)]
|
||||
[:not
|
||||
[:exists
|
||||
{:select [1],
|
||||
:from [[:datoms 'datoms2]],
|
||||
:where (list :and
|
||||
[:= :datoms2.a (:foo/bar attrs)]
|
||||
[:= :datoms0.e :datoms2.e])}]]
|
||||
[:= :datoms0.tx :datoms1.e])}
|
||||
(expand
|
||||
'[:find ?timestampMicros ?page :in $ ?latest :where
|
||||
[?page :page/starred true ?t]
|
||||
[?t :db/txInstant ?timestampMicros]
|
||||
(not [?page :foo/bar _])]
|
||||
|
||||
(deftest test-basic-join
|
||||
(is (= {:select '([:datoms1.v :timestampMicros] [:datoms0.e :page]),
|
||||
:modifiers [:distinct],
|
||||
:from '[[:datoms datoms0]
|
||||
[:datoms datoms1]],
|
||||
:where (list
|
||||
:and
|
||||
[:= :datoms0.a "page/starred"]
|
||||
[:= :datoms0.value_type_tag 1] ; boolean
|
||||
[:= :datoms0.v 1]
|
||||
[:= :datoms1.a "db/txInstant"]
|
||||
[:not
|
||||
(list :and (list :> :datoms0.tx (sql/param :latest)))]
|
||||
[:= :datoms0.tx :datoms1.e])}
|
||||
(expand
|
||||
'[:find ?timestampMicros ?page :in $ ?latest :where
|
||||
[?page :page/starred true ?t]
|
||||
[?t :db/txInstant ?timestampMicros]
|
||||
(not [(> ?t ?latest)])]
|
||||
simple-schema))))
|
||||
conn))))))
|
||||
|
||||
(deftest test-pattern-not-join
|
||||
(is (= '{:select ([:datoms1.v :timestampMicros] [:datoms0.e :page]),
|
||||
:modifiers [:distinct],
|
||||
:from [[:datoms datoms0]
|
||||
[:datoms datoms1]],
|
||||
:where (:and
|
||||
[:= :datoms0.a "page/starred"]
|
||||
(deftest-db test-basic-join conn
|
||||
;; Note that we use a schema without :page/starred, so we
|
||||
;; don't know what type it is.
|
||||
(let [attrs (<? (<initialize-with-schema conn simple-schema))]
|
||||
(is (= {:select '([:datoms1.v :timestampMicros] [:datoms0.e :page]),
|
||||
:modifiers [:distinct],
|
||||
:from '([:datoms datoms0]
|
||||
[:datoms datoms1]),
|
||||
:where (list
|
||||
:and
|
||||
;; Note that :page/starred is literal, because
|
||||
;; it's not present in the interned schema.
|
||||
[:= :datoms0.a :page/starred]
|
||||
[:= :datoms0.value_type_tag 1] ; boolean
|
||||
[:= :datoms0.v 1]
|
||||
[:= :datoms1.a "db/txInstant"]
|
||||
[:= :datoms1.a (:db/txInstant attrs)]
|
||||
[:not
|
||||
(list :and (list :> :datoms0.tx (sql/param :latest)))]
|
||||
[:= :datoms0.tx :datoms1.e])}
|
||||
(expand
|
||||
'[:find ?timestampMicros ?page :in $ ?latest :where
|
||||
[?page :page/starred true ?t]
|
||||
[?t :db/txInstant ?timestampMicros]
|
||||
(not [(> ?t ?latest)])]
|
||||
conn)))))
|
||||
|
||||
(deftest-db test-pattern-not-join conn
|
||||
(let [attrs (<? (<initialize-with-schema conn simple-schema))]
|
||||
(is (= {:select '([:datoms1.v :timestampMicros] [:datoms0.e :page]),
|
||||
:modifiers [:distinct],
|
||||
:from [[:datoms 'datoms0]
|
||||
[:datoms 'datoms1]],
|
||||
:where (list
|
||||
:and
|
||||
;; Note that :page/starred is literal, because
|
||||
;; it's not present in the interned schema.
|
||||
[:= :datoms0.a :page/starred]
|
||||
[:= :datoms0.value_type_tag 1] ; boolean
|
||||
[:= :datoms0.v 1]
|
||||
[:= :datoms1.a (:db/txInstant attrs)]
|
||||
[:not
|
||||
[:exists
|
||||
{:select [1],
|
||||
:from [[:datoms datoms2]],
|
||||
:where (:and
|
||||
[:= :datoms2.a "foo/bar"]
|
||||
:from [[:datoms 'datoms2]],
|
||||
:where (list :and
|
||||
[:= :datoms2.a (:foo/bar attrs)]
|
||||
[:= :datoms0.e :datoms2.e])}]]
|
||||
[:= :datoms0.tx :datoms1.e])}
|
||||
(expand
|
||||
'[:find ?timestampMicros ?page :in $ ?latest :where
|
||||
[?page :page/starred true ?t]
|
||||
[?t :db/txInstant ?timestampMicros]
|
||||
(not [?page :foo/bar _])]
|
||||
simple-schema))))
|
||||
(expand
|
||||
'[:find ?timestampMicros ?page :in $ ?latest :where
|
||||
[?page :page/starred true ?t]
|
||||
[?t :db/txInstant ?timestampMicros]
|
||||
(not [?page :foo/bar _])]
|
||||
conn)))))
|
||||
|
||||
;; Note that clause ordering is not directly correlated to the output: cross-bindings end up
|
||||
;; at the front. The SQL engine will do its own analysis. See `clauses/expand-where-from-bindings`.
|
||||
(deftest test-not-clause-ordering-preserved
|
||||
(is (= {:select '([:datoms1.v :timestampMicros] [:datoms0.e :page]),
|
||||
:modifiers [:distinct],
|
||||
:from '[[:datoms datoms0]
|
||||
[:datoms datoms1]],
|
||||
:where (list
|
||||
:and
|
||||
[:= :datoms0.a "page/starred"]
|
||||
[:= :datoms0.value_type_tag 1] ; boolean
|
||||
[:= :datoms0.v 1]
|
||||
[:not
|
||||
(list :and (list :> :datoms0.tx (sql/param :latest)))]
|
||||
[:= :datoms1.a "db/txInstant"]
|
||||
[:= :datoms0.tx :datoms1.e]
|
||||
)}
|
||||
(expand
|
||||
'[:find ?timestampMicros ?page :in $ ?latest :where
|
||||
[?page :page/starred true ?t]
|
||||
(not [(> ?t ?latest)])
|
||||
[?t :db/txInstant ?timestampMicros]]
|
||||
simple-schema))))
|
||||
|
||||
(deftest test-pattern-not-join-ordering-preserved
|
||||
(is (= '{:select ([:datoms2.v :timestampMicros] [:datoms0.e :page]),
|
||||
:modifiers [:distinct],
|
||||
:from [[:datoms datoms0]
|
||||
[:datoms datoms2]],
|
||||
:where (:and
|
||||
[:= :datoms0.a "page/starred"]
|
||||
[:= :datoms0.value_type_tag 1] ; boolean
|
||||
(deftest-db test-not-clause-ordering-preserved conn
|
||||
(let [attrs (<? (<initialize-with-schema conn schema-with-page))]
|
||||
(is (= {:select '([:datoms1.v :timestampMicros] [:datoms0.e :page]),
|
||||
:modifiers [:distinct],
|
||||
:from '[[:datoms datoms0]
|
||||
[:datoms datoms1]],
|
||||
:where (list
|
||||
:and
|
||||
;; We don't need a value tag constraint -- we know the range of the attribute.
|
||||
[:= :datoms0.a (:page/starred attrs)]
|
||||
[:= :datoms0.v 1]
|
||||
[:not
|
||||
[:exists
|
||||
{:select [1],
|
||||
:from [[:datoms datoms1]],
|
||||
:where (:and
|
||||
[:= :datoms1.a "foo/bar"]
|
||||
[:= :datoms0.e :datoms1.e])}]]
|
||||
[:= :datoms2.a "db/txInstant"]
|
||||
[:= :datoms0.tx :datoms2.e]
|
||||
(list :and (list :> :datoms0.tx (sql/param :latest)))]
|
||||
[:= :datoms1.a (:db/txInstant attrs)]
|
||||
[:= :datoms0.tx :datoms1.e]
|
||||
)}
|
||||
(expand
|
||||
'[:find ?timestampMicros ?page :in $ ?latest :where
|
||||
[?page :page/starred true ?t]
|
||||
(not [?page :foo/bar _])
|
||||
[?t :db/txInstant ?timestampMicros]]
|
||||
simple-schema))))
|
||||
(expand
|
||||
'[:find ?timestampMicros ?page :in $ ?latest :where
|
||||
[?page :page/starred true ?t]
|
||||
(not [(> ?t ?latest)])
|
||||
[?t :db/txInstant ?timestampMicros]]
|
||||
conn)))))
|
||||
|
||||
(deftest test-single-or
|
||||
(is (= '{:select ([:datoms0.e :page]),
|
||||
:modifiers [:distinct],
|
||||
:from ([:datoms datoms0] [:datoms datoms1] [:datoms datoms2]),
|
||||
:where (:and
|
||||
[:= :datoms0.a "page/url"]
|
||||
[:= :datoms0.value_type_tag 10]
|
||||
[:= :datoms0.v "http://example.com/"]
|
||||
[:= :datoms1.a "page/title"]
|
||||
[:= :datoms2.a "page/loves"]
|
||||
[:= :datoms0.e :datoms1.e]
|
||||
[:= :datoms0.e :datoms2.v])}
|
||||
(expand
|
||||
'[:find ?page :in $ ?latest :where
|
||||
[?page :page/url "http://example.com/"]
|
||||
[?page :page/title ?title]
|
||||
(or
|
||||
[?entity :page/loves ?page])]
|
||||
simple-schema))))
|
||||
(deftest-db test-pattern-not-join-ordering-preserved conn
|
||||
(let [attrs (<? (<initialize-with-schema conn schema-with-page))]
|
||||
(is (= {:select '([:datoms2.v :timestampMicros] [:datoms0.e :page]),
|
||||
:modifiers [:distinct],
|
||||
:from [[:datoms 'datoms0]
|
||||
[:datoms 'datoms2]],
|
||||
:where (list :and
|
||||
;; We don't need a value tag constraint -- we know the range of the attribute.
|
||||
[:= :datoms0.a (:page/starred attrs)]
|
||||
[:= :datoms0.v 1]
|
||||
[:not
|
||||
[:exists
|
||||
{:select [1],
|
||||
:from [[:datoms 'datoms1]],
|
||||
:where (list :and
|
||||
[:= :datoms1.a (:foo/bar attrs)]
|
||||
[:= :datoms0.e :datoms1.e])}]]
|
||||
[:= :datoms2.a (:db/txInstant attrs)]
|
||||
[:= :datoms0.tx :datoms2.e])}
|
||||
(expand
|
||||
'[:find ?timestampMicros ?page :in $ ?latest :where
|
||||
[?page :page/starred true ?t]
|
||||
(not [?page :foo/bar _])
|
||||
[?t :db/txInstant ?timestampMicros]]
|
||||
conn)))))
|
||||
|
||||
(deftest test-simple-or
|
||||
(is (= '{:select ([:datoms0.e :page]),
|
||||
:modifiers [:distinct],
|
||||
:from ([:datoms datoms0] [:datoms datoms1] [:datoms datoms2]),
|
||||
:where (:and
|
||||
[:= :datoms0.a "page/url"]
|
||||
[:= :datoms0.value_type_tag 10]
|
||||
[:= :datoms0.v "http://example.com/"]
|
||||
[:= :datoms1.a "page/title"]
|
||||
(:or
|
||||
[:= :datoms2.a "page/likes"]
|
||||
[:= :datoms2.a "page/loves"])
|
||||
[:= :datoms0.e :datoms1.e]
|
||||
[:= :datoms0.e :datoms2.v])}
|
||||
(expand
|
||||
'[:find ?page :in $ ?latest :where
|
||||
[?page :page/url "http://example.com/"]
|
||||
[?page :page/title ?title]
|
||||
(or
|
||||
[?entity :page/likes ?page]
|
||||
[?entity :page/loves ?page])]
|
||||
simple-schema))))
|
||||
(deftest-db test-single-or conn
|
||||
(let [attrs (<? (<initialize-with-schema conn schema-with-page))]
|
||||
(is (= {:select '([:datoms0.e :page]),
|
||||
:modifiers [:distinct],
|
||||
:from '([:datoms datoms0] [:datoms datoms1] [:datoms datoms2]),
|
||||
:where (list :and
|
||||
[:= :datoms0.a (:page/url attrs)]
|
||||
[:= :datoms0.v "http://example.com/"]
|
||||
[:= :datoms1.a (:page/title attrs)]
|
||||
[:= :datoms2.a (:page/loves attrs)]
|
||||
[:= :datoms0.e :datoms1.e]
|
||||
[:= :datoms0.e :datoms2.v])}
|
||||
(expand
|
||||
'[:find ?page :in $ ?latest :where
|
||||
[?page :page/url "http://example.com/"]
|
||||
[?page :page/title ?title]
|
||||
(or
|
||||
[?entity :page/loves ?page])]
|
||||
conn)))))
|
||||
|
||||
(deftest test-tag-projection
|
||||
(is (= '{:select ([:datoms0.e :page]
|
||||
[:datoms1.v :thing]
|
||||
[:datoms1.value_type_tag :_thing_type_tag]),
|
||||
:modifiers [:distinct],
|
||||
:from ([:datoms datoms0]
|
||||
[:datoms datoms1]),
|
||||
:where (:and
|
||||
[:= :datoms0.a "page/url"]
|
||||
[:= :datoms0.value_type_tag 10]
|
||||
[:= :datoms0.v "http://example.com/"]
|
||||
(:or
|
||||
[:= :datoms1.a "page/likes"]
|
||||
[:= :datoms1.a "page/loves"])
|
||||
[:= :datoms0.e :datoms1.e])}
|
||||
(expand
|
||||
'[:find ?page ?thing :in $ ?latest :where
|
||||
[?page :page/url "http://example.com/"]
|
||||
(or
|
||||
[?page :page/likes ?thing]
|
||||
[?page :page/loves ?thing])]
|
||||
simple-schema))))
|
||||
(deftest-db test-simple-or conn
|
||||
(let [attrs (<? (<initialize-with-schema conn schema-with-page))]
|
||||
(is (= {:select '([:datoms0.e :page]),
|
||||
:modifiers [:distinct],
|
||||
:from '([:datoms datoms0] [:datoms datoms1] [:datoms datoms2]),
|
||||
:where (list :and
|
||||
[:= :datoms0.a (:page/url attrs)]
|
||||
[:= :datoms0.v "http://example.com/"]
|
||||
[:= :datoms1.a (:page/title attrs)]
|
||||
(list :or
|
||||
[:= :datoms2.a (:page/likes attrs)]
|
||||
[:= :datoms2.a (:page/loves attrs)])
|
||||
[:= :datoms0.e :datoms1.e]
|
||||
[:= :datoms0.e :datoms2.v])}
|
||||
(expand
|
||||
'[:find ?page :in $ ?latest :where
|
||||
[?page :page/url "http://example.com/"]
|
||||
[?page :page/title ?title]
|
||||
(or
|
||||
[?entity :page/likes ?page]
|
||||
[?entity :page/loves ?page])]
|
||||
conn)))))
|
||||
|
||||
(defn tag-clauses [column input]
|
||||
(let [codes (cc/->tag-codes input)]
|
||||
(if (= 1 (count codes))
|
||||
[:= column (first codes)]
|
||||
(cons :or (map (fn [tag]
|
||||
[:= column tag])
|
||||
codes)))))
|
||||
|
||||
(deftest-db test-url-tag conn
|
||||
(let [attrs (<? (<initialize-with-schema conn schema-with-page))]
|
||||
(is (= {:select '([:all_datoms0.e :page]
|
||||
[:datoms1.v :thing]),
|
||||
:modifiers [:distinct],
|
||||
:from '([:all_datoms all_datoms0]
|
||||
[:datoms datoms1]),
|
||||
:where (list
|
||||
:and
|
||||
(tag-clauses :all_datoms0.value_type_tag "http://example.com/")
|
||||
[:= :all_datoms0.v "http://example.com/"]
|
||||
(list
|
||||
:or
|
||||
[:= :datoms1.a (:page/likes attrs)]
|
||||
[:= :datoms1.a (:page/loves attrs)])
|
||||
[:= :all_datoms0.e :datoms1.e])}
|
||||
(expand
|
||||
'[:find ?page ?thing :in $ ?latest :where
|
||||
[?page _ "http://example.com/"]
|
||||
(or
|
||||
[?page :page/likes ?thing]
|
||||
[?page :page/loves ?thing])]
|
||||
conn)))))
|
||||
|
||||
(deftest-db test-tag-projection conn
|
||||
(let [attrs (<? (<initialize-with-schema conn schema-with-page))]
|
||||
(is (= {:select '([:all_datoms0.e :page]
|
||||
[:all_datoms0.v :thing]
|
||||
[:all_datoms0.value_type_tag :_thing_type_tag]),
|
||||
:modifiers [:distinct],
|
||||
:from '([:all_datoms all_datoms0])}
|
||||
(expand
|
||||
'[:find ?page ?thing :in $ :where
|
||||
[?page _ ?thing]]
|
||||
conn)))))
|
||||
|
|
Loading…
Reference in a new issue