Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for Heterogenous tuples with :db/tupleTypes supporting :db.type/refs #218

Closed
garrett-hopper opened this issue Aug 5, 2023 · 4 comments

Comments

@garrett-hopper
Copy link
Contributor

garrett-hopper commented Aug 5, 2023

Essentially the same as datascript/issues/364

(Technically a homogeneous tuple of :db.type/refs should be supported as well)

#114 (comment)

We will need to support tuple first, then we can think about handle ref in them.

Wanted to create an issue here since tuple types were added in 0.8.5

@huahaiy, curious if you have any sort of insight on how someone might attempt to tackle this?
Should it be handled in Datascript first and have the logic duplicated here, or can it be implemented here directly? (Speaking for my own personal needs. 🙂)

Example:

(let [schema {:children {:db/valueType   :db.type/tuple
                         :db/tupleTypes  [:db.type/ref :db.type/long]
                         :db/cardinality :db.cardinality/many}}
      conn   (d/create-conn nil schema)]
  (d/transact! conn [{:db/id -1}
                     {:children [[-1 0]]}])
  (d/q '[:find [?x ...]
         :where
         [_ :children ?x]]
       @conn) ;; => [[-1 0]] (-1 isn't resolved as a ref/tempid)
  (d/q '[:find [?x ...]
         :where
         [_ :children [?x]]]
       @conn)) ;; => Query for unknown vars exception: [?x] (Unable to deconstruct tuple)
@garrett-hopper
Copy link
Contributor Author

This will need to be implemented in Datalevin directly (rather than in Datascript and duplicated here), as Datascript does not support :db/tupleValue (or :db/tupleValues) at all.

From @huahaiy regarding Datascript not supporting homogenous or heterogeneous tuples

Datascript only supports composite Tuples, because it does not differentiate data types, they are all clojure data, no strings, int, etc

Relevant Slack thread for anyone in the future: https://clojurians.slack.com/archives/C01RD3AF336/p1691260941968389

@garrett-hopper
Copy link
Contributor Author

This appears to have been my own misunderstanding of Datomic's query syntax when it comes to tuples.

(There is still an issue with negative number tempids not being recognized in tuple types.)

It appears to work as expected when I use untuple correctly

(let [schema  {:parent/children {:db/valueType   :db.type/tuple
                                 :db/tupleTypes  [:db.type/ref :db.type/long]
                                 :db/cardinality :db.cardinality/many}
               :parent/name     {:db/valueType :db.type/string
                                 :db/unique    :db.unique/identity}
               :child/name      {:db/valueType :db.type/string
                                 :db/unique    :db.unique/identity}}
      conn    (d/create-conn nil schema)
      tempids (:tempids
               (d/transact! conn [{:db/id      -1
                                   :child/name "Child 1"}
                                  {:db/id      -2
                                   :child/name "Child 2"}
                                  {:db/id       -3
                                   :parent/name "Parent 1"}
                                  {:db/id       -4
                                   :parent/name "Parent 2"}]))]
  ;; Work around tempids not working in tuple types
  (d/transact! conn [{:db/id           (tempids -3)
                      :parent/children [[(tempids -1) 0]
                                        [(tempids -2) 1]]}
                     {:db/id           (tempids -4)
                      :parent/children [[(tempids -1) 1]
                                        [(tempids -2) 0]]}])
  (d/q (template
        [:find ?parent ?parent-name ?child-name ?position
         :where
         [?parent :parent/name ?parent-name]
         [?parent :parent/children ?children]
         [(untuple ?children) [?child ?position]]
         [?child :child/name ?child-name]])
       @conn))

@garrett-hopper
Copy link
Contributor Author

The desired code without the workaround would be this:

(let [schema  {:parent/children {:db/valueType   :db.type/tuple
                                 :db/tupleTypes  [:db.type/ref :db.type/long]
                                 :db/cardinality :db.cardinality/many}
               :parent/name     {:db/valueType :db.type/string
                                 :db/unique    :db.unique/identity}
               :child/name      {:db/valueType :db.type/string
                                 :db/unique    :db.unique/identity}}
      conn    (d/create-conn nil schema)]
  (d/transact! conn [{:db/id      -1
                      :child/name "Child 1"}
                     {:db/id      -2
                      :child/name "Child 2"}
                     {:db/id       -3
                      :parent/name "Parent 1"
                      :parent/children [[-1 0]
                                        [-2 1]]}
                     {:db/id       -4
                      :parent/name "Parent 2"
                      :parent/children [[-1 1]
                                        [-2 0]]}])
  (d/q (template
        [:find ?parent ?parent-name ?child-name ?position
         :where
         [?parent :parent/name ?parent-name]
         [?parent :parent/children ?children]
         [(untuple ?children) [?child ?position]]
         [?child :child/name ?child-name]])
       @conn))

@garrett-hopper
Copy link
Contributor Author

untuple is just an alias for identity (built_ins.cljc#L132), therefore identity could be used in its place for the same purpose. (Though untuple does signal intent better)
Using one or the other is required because destructuring is only supported in clauses that are function calls. (You can't destructure a tuple in a pattern matching clause.)

Also, homogenous tuples can be destructured using ...
E.g.

[?e :tuple ?tuple]
[(untuple ?tuple) [?x ...]]

In which case ?x will be bound to each item in ?tuple as expected.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant