Coding active
Coding active

Reputation: 1680

Datomic - working with OR clause

I'm currently working on migrating my clojure app(with korma) to Datomic framework and been in a loop while I was translating the queries. I realise the queries are not completely flexible(compared to korma), for example i would like to evaluate conditional clauses around different variables.

Considering a korma query,

(select users
   (where (or (and {:first_name [= "user"]}
                   {:last_name [= "sample"]})
              {:email [= "user.sample@email.com"]})))

this can be converted to Datomic, with something like this?

 [:find ?e
  :where (or (and [?u :user/first-name "user"]
                  [?u :user/last-name "sample"])
             [?u :user/email "user.sample@email.com"])

but this is not the recommended way of querying(according to Datomic docs), as all clauses used in an or clause must use the same set of variables. How do I set an OR clause around different sets of variables?

Upvotes: 6

Views: 3129

Answers (2)

Anthony R.
Anthony R.

Reputation: 695

Your query should work. All of your clauses do use the same variable: ?u

(d/q '[:find ?u
       :where (or (and [?u :user/first-name "user"]
                       [?u :user/last-name "sample"])
                  [?u :user/email "user.sample@email.com"])]
  [[1 :user/first-name "user"]
   [1 :user/last-name "sample"]
   [2 :user/email "user.sample@email.com"]
   [3 :user/first-name "user"]
   [3 :user/last-name "not sample"]])

=> #{[1] [2]}

If you need them to use different variables, you can use or-join to explicitly list them:

(d/q '[:find ?u
       :in $ ?first-name ?last-name ?email
       :where (or-join [?u ?first-name ?last-name ?email]
                (and [?u :user/first-name ?first-name]
                     [?u :user/last-name ?last-name])
                [?u :user/email ?email])]
     [[1 :user/first-name "user"]
      [1 :user/last-name "sample"]
      [2 :user/email "user.sample@email.com"]
      [3 :user/first-name "user"]
      [3 :user/last-name "not sample"]]
     "user"
     "sample"
     "user.sample@email.com")

=> #{[1] [2]}

Upvotes: 8

adamneilson
adamneilson

Reputation: 758

This is very similar to this question:

SQL LIKE operator in datomic

You need to check out query rules.

http://docs.datomic.com/query.html

Your query would look something like this (untested!)

(let [rules '[[(find-user ?user ?fname ?lname ?email) 
               [?user :user/first-name ?fname]
               [?user :user/last-name ?lname]]
              [(find-user ?user ?fname ?lname ?email) 
               [?user :user/email ?email]]]]

  (:find ?user
         :in $ % ?fname ?lname ?email
         :where 
         (find-user ?user ?fname ?lname ?email)
         conn rules "user" "sample" "user.sample@email.com"))

Upvotes: 0

Related Questions