Clarifying how to query and pull with tuples

A little late to this feature, but I’m writing some queries using tuples, and am getting surprised by the way some aspects work, specifically around nested lookup refs.

I’ve included a scratch namespace with 4 examples toward the bottom, with 4 different attempts to formulate queries using datomic.api/q and datomic.api/pull, 2 were successful and 2 were not (comments indicate which is which).

It would help me to understand the limitations, if any, of the query syntax when it comes to tuples (especially around “nested” lookup refs as in the final d/pull example below). It’s possible the documentation could be improved to include query examples and/or a discussion of limitations (if any), because while the system allows transaction data to include lookup refs, a developer trying to do what they think is a symmetric operation with datomic.api/pull might be surprised at what happens. Alternatively, I am just doing it completely wrong and would be grateful to improve my understanding around tuples.

Thank you very much!

;; Tested with Datomic Pro (on-prem) version 1.0.6269

(ns scratch
  (:require [datomic.api :as d]))

(d/delete-database "datomic:mem://test")

(d/create-database "datomic:mem://test")

(def conn
  (d/connect "datomic:mem://test"))

(def schema
  [;;; Partner

   ;; Partner domain name (unique)
   {:db/ident :partner/domain
    :db/valueType :db.type/string
    :db/cardinality :db.cardinality/one
    :db/unique :db.unique/identity}

   ;;; Referral code

   ;; Ref -> Partner
   {:db/ident :referral-code/partner
    :db/valueType :db.type/ref
    :db/cardinality :db.cardinality/one}
   ;; String code
   {:db/ident :referral-code/code
    :db/valueType :db.type/string
    :db/cardinality :db.cardinality/one}

   ;; Composite
   {:db/ident :referral-code/partner+code
    :db/valueType :db.type/tuple
    :db/tupleAttrs [:referral-code/partner :referral-code/code]
    :db/cardinality :db.cardinality/one
    :db/unique :db.unique/identity}])

(d/transact conn schema)

(def partners
  [{:partner/domain "google.com"}
   {:partner/domain "bing.com"}])

(d/transact conn partners)

(def codes
  [{:referral-code/partner [:partner/domain "google.com"]
    :referral-code/code "1E100"}
   {:referral-code/partner [:partner/domain "google.com"]
    :referral-code/code "GOOGOL"}])

(d/transact conn codes)

(def db
  (d/db conn))

;; This returns the result I expect.
(d/q '{:find [[(pull ?e [{:referral-code/partner [:partner/domain]} :referral-code/code]) ...]]
       :where [[?p :partner/domain "google.com"]
               [(tuple ?p "1E100") ?tup]
               [?e :referral-code/partner+code ?tup]]}
     db)

;; => [{:referral-code/partner {:partner/domain "google.com"} :referral-code/code "1E100"}]]

;; This does not return the result I expected (though more understandable).
(d/q '{:find [[(pull ?e [*]) ...]]
       :where [[?p :partner/domain "google.com"]
               [?e :referral-code/partner+code [?p "1E100"]]]}
     db)

;; => []]

;; This returns the result I expect, but is awkward.
(let [partner-eid (:db/id (d/entity db [:partner/domain "google.com"]))]
  (d/pull db
          [{:referral-code/partner [:partner/domain]} :referral-code/code]
          [:referral-code/partner+code [partner-eid "1E100"]]))

;; => [{:referral-code/partner {:partner/domain "google.com"} :referral-code/code "1E100"}]]

;; This does not return the result I expect, and was the first thing I reached for.
(d/pull db '[*] [:referral-code/partner+code [[:partner/domain "google.com"] "1E100"]])

;; => {:db/id nil}
2 Likes

Hi, @gws,

I’m also interested in this. I wonder if https://ask.datomic.com is a more active/better place to ask?

I’m new here, and new with Datomic :slight_smile: