Allow sets of attributes in fulltext expressions. Fixes #54. r=nalexander
This commit is contained in:
parent
32cd08ba13
commit
1296b8090f
3 changed files with 129 additions and 16 deletions
|
@ -97,12 +97,36 @@
|
||||||
;; [(fulltext $ _ "foo") [[?x]]]
|
;; [(fulltext $ _ "foo") [[?x]]]
|
||||||
;;
|
;;
|
||||||
;; so we instead have a placeholder attribute. Sigh.
|
;; so we instead have a placeholder attribute. Sigh.
|
||||||
attr-constant (or
|
;; We also support sets of attributes, so you can write
|
||||||
(and (instance? Constant attr)
|
;;
|
||||||
(not (= :any (:value attr)))
|
;; [(fulltext $ #{:foo/bar :foo/baz} "Noo") [[?x]]]
|
||||||
(source/attribute-in-source (:source cc) (:value attr)))
|
;;
|
||||||
(and (instance? Variable attr)
|
;; which involves some tomfoolery here.
|
||||||
(cc/binding-for-symbol-or-throw cc (:symbol attr))))
|
;;
|
||||||
|
;; TODO: exclude any non-fulltext attributes. If the set shrinks to nothing,
|
||||||
|
;; fail the entire pattern.
|
||||||
|
;; https://github.com/mozilla/datomish/issues/56
|
||||||
|
attr-constants (or
|
||||||
|
(and (instance? Constant attr)
|
||||||
|
(let [attr (:value attr)
|
||||||
|
intern (partial source/attribute-in-source (:source cc))]
|
||||||
|
(when-not (= :any attr)
|
||||||
|
(cond
|
||||||
|
(set? attr)
|
||||||
|
(map intern attr)
|
||||||
|
|
||||||
|
(or (keyword? attr)
|
||||||
|
(integer? attr))
|
||||||
|
(list (intern attr))
|
||||||
|
|
||||||
|
:else
|
||||||
|
(raise-str "Unknown fulltext attribute " attr {:attr attr})))))
|
||||||
|
|
||||||
|
(and (instance? Variable attr)
|
||||||
|
(cc/binding-for-symbol-or-throw cc (:symbol attr)))
|
||||||
|
|
||||||
|
;; nil, so it's seqable.
|
||||||
|
nil)
|
||||||
|
|
||||||
;; Pull out the symbols for the binding array.
|
;; Pull out the symbols for the binding array.
|
||||||
[entity value tx score]
|
[entity value tx score]
|
||||||
|
@ -127,16 +151,19 @@
|
||||||
wheres (concat
|
wheres (concat
|
||||||
[[:match match-column match-value] ; The FTS match.
|
[[:match match-column match-value] ; The FTS match.
|
||||||
|
|
||||||
;; The fulltext rowid-to-datom correspondence.
|
;; The fulltext rowid-to-datom correspondence.
|
||||||
[:=
|
[:=
|
||||||
(sql/qualify datom-alias :v)
|
(sql/qualify datom-alias :v)
|
||||||
(sql/qualify fulltext-alias :rowid)]]
|
(sql/qualify fulltext-alias :rowid)]]
|
||||||
|
|
||||||
(when attr-constant
|
;; If known, the attribute itself must match.
|
||||||
;; If known, the attribute itself must match.
|
(when (seq attr-constants)
|
||||||
[[:=
|
(let [a (sql/qualify datom-alias :a)
|
||||||
(sql/qualify datom-alias :a)
|
fragments (map (fn [v] [:= a v])
|
||||||
attr-constant]]))
|
attr-constants)]
|
||||||
|
(if (seq (rest fragments))
|
||||||
|
[(cons :or fragments)]
|
||||||
|
fragments))))
|
||||||
|
|
||||||
;; Now compose any bindings for entity, value, tx, and score.
|
;; Now compose any bindings for entity, value, tx, and score.
|
||||||
;; TODO: do we need to examine existing bindings to capture
|
;; TODO: do we need to examine existing bindings to capture
|
||||||
|
|
|
@ -109,6 +109,26 @@
|
||||||
:db/valueType :db.type/instant
|
:db/valueType :db.type/instant
|
||||||
:db/cardinality :db.cardinality/many}])
|
:db/cardinality :db.cardinality/many}])
|
||||||
|
|
||||||
|
(def save-schema
|
||||||
|
[{:db/id (d/id-literal :db.part/user)
|
||||||
|
:db.install/_attribute :db.part/db
|
||||||
|
:db/cardinality :db.cardinality/one
|
||||||
|
:db/valueType :db.type/string
|
||||||
|
:db/fulltext true
|
||||||
|
:db/ident :save/title}
|
||||||
|
{:db/id (d/id-literal :db.part/user)
|
||||||
|
:db.install/_attribute :db.part/db
|
||||||
|
:db/cardinality :db.cardinality/one
|
||||||
|
:db/valueType :db.type/string
|
||||||
|
:db/fulltext true
|
||||||
|
:db/ident :save/excerpt}
|
||||||
|
{:db/id (d/id-literal :db.part/user)
|
||||||
|
:db.install/_attribute :db.part/db
|
||||||
|
:db/cardinality :db.cardinality/one
|
||||||
|
:db/valueType :db.type/string
|
||||||
|
:db/fulltext true
|
||||||
|
:db/ident :save/content}])
|
||||||
|
|
||||||
(def schema-with-page
|
(def schema-with-page
|
||||||
(concat
|
(concat
|
||||||
simple-schema
|
simple-schema
|
||||||
|
@ -567,3 +587,40 @@
|
||||||
(query/options-into-context context 10 [[:date :asc]]))
|
(query/options-into-context context 10 [[:date :asc]]))
|
||||||
[:order-by :limit]
|
[:order-by :limit]
|
||||||
)))))
|
)))))
|
||||||
|
|
||||||
|
(deftest-db test-parsing-fulltext conn
|
||||||
|
(let [attrs (<? (<initialize-with-schema conn save-schema))]
|
||||||
|
(is (= {:select (list [:datoms1.e :save]),
|
||||||
|
:modifiers [:distinct],
|
||||||
|
:from (list [:fulltext_values 'fulltext_values0]
|
||||||
|
[:datoms 'datoms1]),
|
||||||
|
:where (list :and
|
||||||
|
[:match :fulltext_values0.fulltext_values "something"]
|
||||||
|
[:= :datoms1.v :fulltext_values0.rowid]
|
||||||
|
[:= :datoms1.a (:save/title attrs)])}
|
||||||
|
(expand {:find '[?save]
|
||||||
|
:in '[$]
|
||||||
|
:where [[(list 'fulltext
|
||||||
|
'$
|
||||||
|
:save/title
|
||||||
|
"something")
|
||||||
|
'[[?save]]]]}
|
||||||
|
conn)))
|
||||||
|
(is (= {:select (list [:datoms1.e :save]),
|
||||||
|
:modifiers [:distinct],
|
||||||
|
:from (list [:fulltext_values 'fulltext_values0]
|
||||||
|
[:datoms 'datoms1]),
|
||||||
|
:where (list :and
|
||||||
|
[:match :fulltext_values0.fulltext_values "something"]
|
||||||
|
[:= :datoms1.v :fulltext_values0.rowid]
|
||||||
|
(list :or
|
||||||
|
[:= :datoms1.a (:save/title attrs)]
|
||||||
|
[:= :datoms1.a (:save/excerpt attrs)]))}
|
||||||
|
(expand {:find '[?save]
|
||||||
|
:in '[$]
|
||||||
|
:where [[(list 'fulltext
|
||||||
|
'$
|
||||||
|
#{:save/title :save/excerpt}
|
||||||
|
"something")
|
||||||
|
'[[?save]]]]}
|
||||||
|
conn)))))
|
||||||
|
|
|
@ -127,7 +127,13 @@
|
||||||
:db/cardinality :db.cardinality/one
|
:db/cardinality :db.cardinality/one
|
||||||
:db/valueType :db.type/string
|
:db/valueType :db.type/string
|
||||||
:db/fulltext true
|
:db/fulltext true
|
||||||
:db/ident :save/content}])
|
:db/ident :save/content}
|
||||||
|
{:db/id (d/id-literal :db.part/user)
|
||||||
|
:db.install/_attribute :db.part/db
|
||||||
|
:db/cardinality :db.cardinality/many
|
||||||
|
:db/valueType :db.type/string
|
||||||
|
:db/fulltext false
|
||||||
|
:db/ident :save/unindexed}])
|
||||||
|
|
||||||
(def tofino-schema (concat page-schema visit-schema session-schema save-schema))
|
(def tofino-schema (concat page-schema visit-schema session-schema save-schema))
|
||||||
|
|
||||||
|
@ -460,3 +466,26 @@
|
||||||
(is (= apricot-url "http://example.com/apricots/1"))
|
(is (= apricot-url "http://example.com/apricots/1"))
|
||||||
(is (= apricot-title "A page about apricots."))
|
(is (= apricot-title "A page about apricots."))
|
||||||
(is (= apricot-excerpt "")))))))
|
(is (= apricot-excerpt "")))))))
|
||||||
|
|
||||||
|
(deftest-db test-fulltext-set-attribute conn
|
||||||
|
(<? (d/<transact! conn tofino-schema))
|
||||||
|
(<? (d/<transact! conn
|
||||||
|
[{:db/id 999
|
||||||
|
:save/title "Whenever you want something"}
|
||||||
|
{:db/id 998
|
||||||
|
:save/excerpt "If there is something…"}
|
||||||
|
{:db/id 997
|
||||||
|
:save/unindexed "What something means…"}
|
||||||
|
{:db/id 996
|
||||||
|
:save/title "This is anything but."}
|
||||||
|
{:db/id 995
|
||||||
|
:save/content "There's something here that would match."}]))
|
||||||
|
(let [results
|
||||||
|
(<?
|
||||||
|
(d/<q (d/db conn)
|
||||||
|
{:find '[?save]
|
||||||
|
:in '[$]
|
||||||
|
:where [[(list 'fulltext '$ #{:save/title :save/excerpt} "something")
|
||||||
|
'[[?save]]]]}))]
|
||||||
|
(is (= (set (map first results))
|
||||||
|
#{999 998}))))
|
||||||
|
|
Loading…
Reference in a new issue