datahike

https://datahike.io/, Join the conversation at https://discord.com/invite/kEBzMvb, history for this channel is available at https://clojurians.zulipchat.com/#narrow/stream/180378-slack-archive/topic/datahike
unbalanced 2020-02-05T16:19:47.042100Z

@konrad.kuehne this is my working thought process for the tuples: https://github.com/tonsky/datascript/issues/323 The only difference that I can see for Datahike is that the syntax of the schema would match the Datomic schema. I'm concerned that I'm not understanding the full behavior of how tuples work with upserts and queries, though. If you or anyone else in the community has feedback I'd love to hear it.

unbalanced 2020-02-05T16:23:14.042800Z

To me the major value seems to be more convenient syntax for storing refs in tuples (aka vectors)

2020-02-05T16:55:23.044500Z

They're actually very useful for upsert if you mark them db unique identity, and that's the expected behavior with Datomic.

unbalanced 2020-02-05T17:03:22.047200Z

would it be possible to give an example @metasoarous? 😅 For instance, if it's a unique identity, how would an upsert work?

(d/transact conn [{:ref/a+b+c [:a :b :c]})
it's unclear to me, if :ref/a+b+c is a unique identity, how you could modify that

unbalanced 2020-02-05T17:04:44.048600Z

would it work like this?

(d/transact conn [{:db/id [:ref/a+b+c [:a :b :c]] :ref/a+b+c [:a :b :not-c]}])

2020-02-05T17:07:09.050300Z

No; It would work like (d/transact conn [{:a "blah" :b 42 :c :dragonz :extra-data :foo}])

2020-02-05T17:07:26.050800Z

If you have a composite tuple of :a :b :c , then its going to upsert that on whatever existing entity has those particular values

2020-02-05T17:08:11.051700Z

Definitely a major use case

unbalanced 2020-02-05T17:08:30.052100Z

I'm obviously super confused, because :a I thought was a value, a keyword. You're saying that the keyword would resolve to an entity?

2020-02-05T17:08:50.052300Z

?

2020-02-05T17:09:16.052700Z

It doesn't matter what the types are

2020-02-05T17:09:20.052900Z

Works the same

unbalanced 2020-02-05T17:10:07.054Z

there's some bit of info I'm missing, so I'm glad I'm asking this.

2020-02-05T17:10:42.054900Z

I think you could do it the way you're talking about, where you upsert using [:ref/a+b+c ...] as a lookup ref, but I think the intention is that you don't have to think about the tuple so much

unbalanced 2020-02-05T17:10:54.055200Z

I'm not sure how

{:a "blah" :b 42 :c :dragonz :extra-data :foo}
relates to
{:ref/a+b+c [:a :b :c]}

unbalanced 2020-02-05T17:11:10.055500Z

😅

unbalanced 2020-02-05T17:11:15.055700Z

I'm sorry if it's obvious

unbalanced 2020-02-05T17:13:04.058400Z

would the schema be

{
 :a {:db/unique :db.unique/identity}
 :b {:db/unique :db.unique/identity}
 :c {:db/unique :db.unique/identity}
 :ref/a+b+c {:db/valueType :db.valueType/tuple 
             :db/tupleTypes [:db.type/keyword :db.type/keyword :db.type/keyword]
             :db/unique :db.unique/identity}
}
?

2020-02-05T17:13:54.059300Z

In the world I'm imagining, none of :a :b :c are identity (since then it wouldn't make sense to upsert on a tuple of them; one would be enough!)

2020-02-05T17:14:09.059700Z

The composite tuple would be the identity

2020-02-05T17:14:46.060600Z

That would make it so that asserting facts about an entity with a particular set of :a :b :c values would upsert based on the implied tuple.

unbalanced 2020-02-05T17:15:46.061600Z

gotcha. Okay so here's my question effectively

unbalanced 2020-02-05T17:15:54.062Z

Let's say I do this

2020-02-05T17:16:03.062300Z

Here's an example from the docs:

{:db/ident :reg/semester+course+student
 :db/valueType :db.type/tuple
 :db/tupleAttrs [:reg/course :reg/semester :reg/student]
 :db/cardinality :db.cardinality/one
 :db/unique :db.unique/identity}

2020-02-05T17:16:14.062600Z

tupleTypes is not needed here

2020-02-05T17:16:30.063100Z

Because its implicit on tupleAttrs

unbalanced 2020-02-05T17:16:31.063200Z

(d/transact conn [{:a "blah" :b 42 :c :dragonz :extra-data :foo}])
then
(d/transact conn [{:a "heyyyy" :b 42 :c :dragonz :extra-data :foo}])

2020-02-05T17:16:39.063600Z

Maybe that's the part we're not connecting on

unbalanced 2020-02-05T17:16:43.063800Z

how do I know that I'm doing an upsert and not inserting a new tuple?

2020-02-05T17:17:09.064300Z

In those cases, you would be creating a new entity

2020-02-05T17:17:20.064800Z

It would upsert though if you did the following

unbalanced 2020-02-05T17:17:21.064900Z

so how would I upsert on that tuple?

unbalanced 2020-02-05T17:17:29.065200Z

ah sorry

2020-02-05T17:17:39.065500Z

(d/transact conn [{:a "blah" :b 42 :c :dragonz :extra-data :foo}])
then
(d/transact conn [{:a "blah" :42 :c :dragonz :extra-data :BAR!}])

unbalanced 2020-02-05T17:18:14.066100Z

ahhhh I see

2020-02-05T17:18:16.066400Z

It's basically the equivalent of a multi-column constraint or index in a table-based database

unbalanced 2020-02-05T17:18:33.066800Z

So you can't change the tuple identity

unbalanced 2020-02-05T17:18:36.067100Z

got it

unbalanced 2020-02-05T17:18:40.067300Z

that makes it easy

2020-02-05T17:18:44.067500Z

Well, you can

unbalanced 2020-02-05T17:18:50.067800Z

if it's a ref?

2020-02-05T17:19:03.068200Z

You would have to use the :db/id of the entity in that case, and then could chance one of a b c

unbalanced 2020-02-05T17:19:11.068500Z

aha

2020-02-05T17:19:26.069100Z

so {:db/id <whatever> :a :jazz}

unbalanced 2020-02-05T17:19:29.069300Z

but you never change the tuple directly

unbalanced 2020-02-05T17:19:37.069600Z

the engine manages that

unbalanced 2020-02-05T17:19:54.069800Z

?

2020-02-05T17:27:27.072600Z

If it's a composite tuple (that is, with :db/tupleAttrs), you at least don't need to change the tuple directly. I actually don't know for sure whether you can update the individual attrs by updating the tuple (something to test), but the bottom line is that they will always be in sync

2020-02-05T17:27:56.073300Z

For a regular tuple (one not created with :db/tupleAttrs, you can certainly change the tuple however you like

2020-02-05T17:28:10.073800Z

Just by manually asserting facts about the tuple attr in question

unbalanced 2020-02-05T17:28:27.074100Z

okay given this example

(d/transact conn [{:a "blah" :42 :c :dragonz :extra-data :BAR!}])
what would the schema be?
{:ref/a+b+c 
  {:db/valueType  :db.type/tuple
   :db/tupleAttrs [:a :b :c]
   :db/unique     :db.unique/identity}
}

2020-02-05T17:28:56.074300Z

Yes; That looks right to me

unbalanced 2020-02-05T17:29:02.074500Z

cool, makes sense to me

unbalanced 2020-02-05T17:29:11.074900Z

Thank you @metasoarous!

2020-02-05T17:31:57.075200Z

Sure thing! Thank you for working on this 🙂

unbalanced 2020-02-05T17:33:32.075500Z

Would you agree with the following behavior?

(def conn (d/empty-db 
{:ref/a+b+c 
  {:db/unique :db.unique/identity 
   :db/valueType :db.type/tuple 
   :db/tupleAttrs [:a :b :c]}}))

(d/transact conn [{:a "a" :b "b" :c "c" :d "d"}]) ;;=> insert tuple
(d/transact conn [{:a "not-a" :b "b" :c "c" :d "d"}]) ;;=> insert different tuple
(d/transact conn [{:a "a" :b "b" :c "c" :d "not-d"}]) ;;=> modifies :d
(d/transact conn [{:db/id [:ref/a+b+c [:a :b :c]] :a "not-a" :b "not-b" :c "not-c"}]) ;;=> modifies tuple values, unrelated to tuple ["not-a" "b" "c"]

2020-02-05T17:35:40.075700Z

Yup; That all checks out to me

unbalanced 2020-02-05T17:42:03.077Z

I don't see anything in the docs about any changes to query or pull behavior.

unbalanced 2020-02-05T17:44:48.078500Z

i.e., this makes sense to me but I'm not sure if this is the behavior:

(d/q '[:find (pull ?e [*]) .
       :where
       [_ :reg/semester+course+student [_ _ ?e]])  ;;=> 
{:db/id         123456
 :student/first "John"
 :student/last  "Doe"
 :student/email "<mailto:johndoe@university.edu|johndoe@university.edu>"}

(d/q '[:find (pull ?e [*]) .
       :where
       [?e :db/id [:reg/semester+course+student [234561 345621 123456]]
  @conn)  ;;=&gt; 
[{:db/id           234561
  :semester/year   2018
  :semester/season :fall}
 {:db/id    345621
  course/id "BIO-101"}
 {:db/id         123456
  :student/first "John"
  :student/last  "Doe"
  :student/email "<mailto:johndoe@university.edu|johndoe@university.edu>"}]

2020-02-05T18:01:12.079100Z

I honestly don't know if you can pattern match into tuples like that. Worth trying. I suspect not, but I could totally be wrong.

2020-02-05T18:02:12.079800Z

You should be able to query by the literal tuple of ids though (as in the second case)

2020-02-05T18:02:57.080300Z

(though I don't think you'd get back what you're thinking; you'd get back the registration)

2020-02-05T18:03:42.081200Z

Important to note that ref tuples aren't super smart about resolving lookup refs and the like; You kind of have to use explicit db/id values, as you do in the second example.

👍 1
unbalanced 2020-02-05T18:58:29.081800Z

thanks, that last point is especially important -- saves a whole bunch of unneeded code

cjsauer 2020-02-05T19:11:58.084800Z

Ah, found it: https://docs.datomic.com/cloud/query/query-data-reference.html#tuple

cjsauer 2020-02-05T19:13:35.086Z

Note untuple as well

💯 1
zane 2020-02-05T19:51:27.086300Z

tuple creates a tuple.

zane 2020-02-05T19:53:21.086700Z

There's untuple for unpacking them, but I'm not clear on how it's distinct from using identity in a function expression. https://docs.datomic.com/cloud/query/query-data-reference.html#untuple

zane 2020-02-05T19:54:03.086900Z

i.e.

[(tuple ?t) [?a ?b ?c]]
seems equivalent to
[(identity ?t) [?a ?b ?c]]