Exact unordered match of multi-valued string field


#1

Given documents:

{:myid 100
 :v [1 2 3 4]}
{:myid 200
 :v [3 4 2 1]}
{:myid 300
 :v [1 2]}
{:myid 400
 :v [1 2 3 4 5 6]}

How would I query for the myid values from any documents that have an exact match for a certain value of v, with elements in any order?

For example, let’s say I want to find myid from all documents where v is any permutation of [1 2 3 4]. i.e. what query can I write that would return [100 200]? Actually, I’d want the entities, not the value of myid, but it’s easier to explain this way.

At the moment if I had to do this, I’d reach for something like:

'[:find [?myid ...]
  :in $ [?vMatch ...]
  :where
  [?e :myid ?myid]
  [?e :v ?vMatch]
  [(count :v) (count ?vMatch)]]

#2

The issue is you need set equality on the actual set

(d/q '[:in $ ?needle
       :find (pull ?e [:community/name :community/category])
       :where
       [?e :community/category]
       [(datomic.api/entity $ ?e) ?fat]
       [(get ?fat :community/category) ?cs]
       [(= ?cs ?needle)]]
     $ #{"events" "news"})
=>
[[#:community{:name "belltown", :category ["events" "news"]}]
 [#:community{:name "Greenwood Blog", :category ["events" "news"]}]]

You can also define your own peer functions (if peer):

(defn f [$ e needle]
  (let [en (->> e (d/entity $) :community/category)]
    #_(println needle en)
    (= needle en)))

(->> (d/q '[:in $ ?needle
            :find (pull ?e [:community/name :community/category])
            :where
            [?e :community/category]
            [(user/f $ ?e ?needle)]]
          $ #{"events" "news"}))
=> #'user/f
=>
[[#:community{:name "belltown", :category ["events" "news"]}]
 [#:community{:name "Greenwood Blog", :category ["events" "news"]}]]

#3

Taking it home with a peer function for set matching, someone pls yell out a better name

(defn matches [$ e a v] (-> (datomic.api/entity $ e) (get a) (= v)))

(d/q '[:in $ ?needle
       :find (pull ?e [:community/name :community/category])
       :where
       [?e :community/category] 
       [(user/matches $ ?e :community/category ?needle)]]       ; basically mongo.findOne
     $ #{"events" "news"})
=> #'user/matches
=>
[[#:community{:name "belltown", :category ["events" "news"]}]
 [#:community{:name "Greenwood Blog", :category ["events" "news"]}]]