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