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
2020-02-01T05:30:29.014300Z

is datahike.api/entity supposed to return more than just #:db{:id 229}? I'd like to see all the attributes for a given entity, but it only ever returns the :db/id

2020-02-01T05:34:07.014600Z

here's my short reproduction:

2020-02-01T05:34:25.014800Z

(d/transact db/conn [{:db/ident :item/id
                        :db/valueType :db.type/keyword
                        :db/unique :db.unique/identity
                        :db/cardinality :db.cardinality/one}

                       {:db/ident :item/description
                        :db/valueType :db.type/string
                        :db/cardinality :db.cardinality/one}

                       {:item/id :foo
                        :item/description "bar"}])
  (d/entity @db/conn [:item/id :foo])

2020-02-01T05:34:43.015100Z

;;=> #:db{:id 232}

2020-02-01T05:35:39.016Z

I would expect to see :item/id and :item/description, I think. But I want to make sure I'm using it correctly.

2020-02-01T05:35:56.016300Z

I'm happy to help poke at it if need be

unbalanced 2020-02-01T15:43:06.018Z

@datran it looks like a hashmap but it's not

(type (d/entity @conn [:name "Alice"])) ;;=> datahike.impl.entity.Entity
(into {} (d/entity @conn [:name "Alice"])) ;;=> {:age 20, :name "Alice"}
Check the doc for d/entity 🙂

2020-02-01T15:51:12.018700Z

Thanks! That works perfectly. So, I have to realize the results every time?

unbalanced 2020-02-01T15:51:49.019300Z

Depends on what you want out of it! You can also just use it like a hashmap

unbalanced 2020-02-01T15:52:04.019700Z

just the .toString won't show you what you're looking for

unbalanced 2020-02-01T15:52:40.020100Z

(:age (d/entity @conn [:name "Alice"])) ;;=> 20

2020-02-01T15:53:15.020600Z

ok, now it's all coming together. That's what you get for coding too late

unbalanced 2020-02-01T15:53:31.021Z

hehehe I'm right there with ya

2020-02-01T15:58:15.021800Z

I guess while we're here, how do you model many-to-many joins? In SQL I would create a join table, do I have to create a join entity?

2020-02-01T16:07:04.022500Z

I've got two entities, a warehouse and a widget. The widget has a :widget/type and :widget/description. The warehouse has a lot of properties. I want to create a relationship between the two that can express the quantity of widgets a warehouse has.

2020-02-01T16:07:24.022800Z

In SQL, I would have two tables, widgets and warehouse, and I would create a join table to bridge the two and track the quantity - warehouse_widgets(warehouse_id, widget_id, widget_quantity). How do I do that in datahike?

2020-02-01T16:07:36.023Z

I've got this schema:

2020-02-01T16:07:51.023300Z

(d/transact db/conn [{:db/ident :warehouse/id
                        :db/valueType :db.type/keyword
                        :db/unique :db.unique/identity
                        :db/cardinality :db.cardinality/one}

                       {:db/ident :widget/id
                        :db/valueType :db.type/keyword
                        :db/unique :db.unique/identity
                        :db/cardinality :db.cardinality/one}
                       {:db/ident :widget/description
                        :db/valueType :db.type/string
                        :db/cardinality :db.cardinality/one}
                       ])

2020-02-01T16:08:34.023500Z

And I've tried this:

2020-02-01T16:08:38.023700Z

{:db/ident :widget/count
   :db/valueType :db.type/long
   :db/cardinality :db.cardinality/many}

2020-02-01T16:15:02.024300Z

Hold on, here's a more clear repl history:

2020-02-01T16:15:13.024500Z

(d/transact db/conn [{:db/ident :warehouse/id
                        :db/valueType :db.type/keyword
                        :db/unique :db.unique/identity
                        :db/cardinality :db.cardinality/one}

                       {:db/ident :widget/id
                        :db/valueType :db.type/keyword
                        :db/unique :db.unique/identity
                        :db/cardinality :db.cardinality/one}
                       {:db/ident :widget/description
                        :db/valueType :db.type/string
                        :db/cardinality :db.cardinality/one}
                       {:db/ident :widget/warehouse
                        :db/valueType :db.type/ref
                        :db/cardinality :db.cardinality/many}
                       {:db/ident :widget/count
                        :db/valueType :db.type/long
                        :db/cardinality :db.cardinality/many}
                       ])

  (d/transact db/conn [{:warehouse/id :foo}
                       {:warehouse/id :bar}
                       {:widget/warehouse [:warehouse/id :foo]
                        :widget/count 3
                        :widget/id :x}
                       {:widget/warehouse [:warehouse/id :bar]
                        :widget/count 5
                        :widget/id :x}])
  (d/q {:query '{:find [?e ?c ?w-id]
                 :where [[?e :widget/id :x]
                         [?e :widget/count ?c]
                         [?e :widget/warehouse ?w]
                         [?w :warehouse/id ?w-id]]}
        :args [@db/conn]})
  #{[229 3 :foo] [229 5 :bar] [229 3 :bar] [229 5 :foo]}

2020-02-01T16:16:40.025200Z

I want that to return #{[229 3 :foo] [229 5 :bar]}, but it looks like each count is applied to each warehouse

unbalanced 2020-02-01T21:20:51.026300Z

Hmm okay I see what you're trying to do. So -- personally I wouldn't store the count. I use an aggregation to get the count. Let me see if I can work up the example

unbalanced 2020-02-01T21:32:42.027Z

(d/transact conn [{:db/ident       :container/name
                     :db/valueType   :db.type/string
                     :db/unique      :db.unique/identity
                     :db/cardinality :db.cardinality/one}
                    {:db/ident       :container/stuff
                     :db/valueType   :db.type/ref
                     :db/cardinality :db.cardinality/many}

                    {:db/ident       :thing/name
                     :db/valueType   :db.type/string
                     :db/unique      :db.unique/value
                     :db/cardinality :db.cardinality/one}])

  (d/transact conn [{:container/name "a"}
                    {:container/name "b"}])

  (d/transact conn [{:thing/name "thing1"}
                    {:thing/name "thing2"}
                    {:thing/name "thing3"}])
  (d/transact conn [{:container/name "a"
                     :container/stuff [[:thing/name "thing3"]]}
                    {:container/name "b"
                     :container/stuff [[:thing/name "thing1"]
                                       [:thing/name "thing2"]]}])

  (d/q '[:find (count  ?thing) .
         :where
         [?e :container/name "a"]
         [?e :container/stuff ?thing]]
       @conn) ;;=> 1

  (d/q '[:find (count  ?thing) .
         :where
         [?e :container/name "b"]
         [?e :container/stuff ?thing]]
       @conn) ;;=> 2

unbalanced 2020-02-01T21:36:53.027500Z

this site covers it in pretty good detail: http://www.learndatalogtoday.org/chapter/7

2020-02-01T22:40:31.029100Z

That's good to know, I didn't know about the aggregation functions. However, when I play with your example I get a weird result when I try this:

(d/transact db/conn [{:container/name "a"
                        :container/stuff   [[:thing/name "thing3"]]}
                       {:container/name  "b"
                        :container/stuff [[:thing/name "thing1"]
                                          [:thing/name "thing1"]  ; note that I've repeated "thing1"
                                          [:thing/name "thing2"]]}])

(d/q '[:find (count  ?thing) .
         :where
         [?e :container/name "b"]
         [?e :container/stuff ?thing]]
       @db/conn) ;;=> 2 (I would expect 3)

unbalanced 2020-02-01T23:09:46.030200Z

since "thing1" is a unique identity, it will not get added twice. That transaction probably should've thrown an exception though :thinking_face: