Fork me on GitHub
#datomic
<
2021-04-24
>
Luan21:04:01

Hello everyone, I have two entities, user and address, I was trying to use tuples to restrict a user to have only addresses with different types, but when I transact one user with address it doesn't put the address type too ":user/id+addrtype [#uuid "c8335aeb-686f-438b-bbb7-06691ac81c69" nil]". This is my tuple:`{:db/ident :user/id+addrtype :db/tupleAttrs [:user/id :address/type]}` . Is that correct or is there any alternative I can use for this problem?

Luan21:04:32

It works when I use :address/type along with :user/id and the same data nested

Joe Lane21:04:15

I can't help unless I see the schema and tx-data

Luan21:04:48

Sorry, I misclicked, this is the schema:

{:db/id #db/id [:db.part/db]
 :db/ident :address/type
 :db/valueType :db.type/string
 :db/unique :db.unique/identity
 :db/cardinality :db.cardinality/one
 :db.install/_attribute :db.part/db}
 {:db/id #db/id [:db.part/db]
  :db/ident :address/street
  :db/valueType :db.type/string
  :db/cardinality :db.cardinality/one
  :db.install/_attribute :db.part/db}
 {:db/id #db/id [:db.part/db]
  :db/ident :user/id
  :db/valueType :db.type/uuid
  :db/unique :db.unique/identity
  :db/cardinality :db.cardinality/one
  :db.install/_attribute :db.part/db}
 {:db/id #db/id [:db.part/db]
  :db/ident :user/name
  :db/valueType :db.type/string
  :db/cardinality :db.cardinality/one
  :db.install/_attribute :db.part/db}
 {:db/id #db/id [:db.part/db]
  :db/ident :user/address
  :db/valueType :db.type/ref
  :db/cardinality :db.cardinality/many
  :db.install/_attribute :db.part/db}
 {:db/ident :user/userid+type
  :db/valueType :db.type/tuple
  :db/tupleAttrs [:user/id :address/type]
  :db/cardinality :db.cardinality/one
  :db/unique :db.unique/identity
  :db.install/_attribute :db.part/db}
And the tx:
{:user/id      #uuid "c8335aeb-686f-438b-bbb7-06691ac81c69"
 :address/type "Home" ;; it it works when I add it here
 :user/name    "Chris"
 :user/address {:db/id (d/tempid :db.part/user)
                :address/type "Home"
                :address/street "St. 222"}}

Joe Lane21:04:11

Validate an experiment for me: I really do want you to use "Foo" here for the address type first transact:

{:db/id (d/tempid :db.part/user)
 :address/type "Foo"
 :address/street "St. 222"}
Then, in a second transaction, transact:
{:address/type "Foo"
 :address/street "St. 123"}
Then: (d/pull a-fresh-db '[*] [:address/type "foo"]) What do you get back?

Luan21:04:26

I used "Foo" instead, using only these two transactions I get: {:db/id 17592186045418, :address/type "Foo", :address/street "St. 123", :user/userid+type [nil "Foo"]}

Joe Lane22:04:39

So it appears that in your schema :address/type is unique like :user/id , and therefore, whenever a second address with {:address/type "Home", :address/street "Route 66"} is added to the system it would overwrite the first user's address. Your users might find this problematic 🙂

Joe Lane22:04:37

The reason the tuple "Works" when you attach it to the user is because you have what is known as a "composite tuple", which can only read attributes from the same entity, not nested ones.

Joe Lane22:04:53

How many :address/type's you expect to have in your application? If the answer is "A small number" you might benefit from creating some new attributes instead of an address entity. Example:

{:user/id #uuid "123..."
 :user/name "Chris"
 :address.home/street "St. 222"
 :address.billing/street "st. 123"
 :address.work/street "Route 66"}

Joe Lane22:04:36

Schema would be something like:

{:db/id #db/id [:db.part/db]
 :db/ident :address.home/street
 :db/valueType :db.type/string
 :db/cardinality :db.cardinality/one
 :db.install/_attribute :db.part/db}
{:db/id #db/id [:db.part/db]
 :db/ident :address.work/street
 :db/valueType :db.type/string
 :db/cardinality :db.cardinality/one
 :db.install/_attribute :db.part/db}
{:db/id #db/id [:db.part/db]
 :db/ident :address.billing/street
 :db/valueType :db.type/string
 :db/cardinality :db.cardinality/one
 :db.install/_attribute :db.part/db}

Joe Lane22:04:49

Then, a user can only have 1 address of each "type"

Luan22:04:23

I was trying to to this composite tuple, but I didn't know it doesn't work on nested entities 🙂 And I also was trying to model it to support another entity like Company to have an address, so I would just ref to it. I will think about this solution, thank you 🙂 .

Joe Lane22:04:09

FWIW @cbluan, companies can use those same attributes:

{:db/id #db/id [:db.part/db]
 :db/ident :address.mailing/street
 :db/valueType :db.type/string
 :db/cardinality :db.cardinality/one
 :db.install/_attribute :db.part/db}

{:db/id #db/id [:db.part/db]
 :db/ident :favorite/color
 :db/valueType :db.type/string
 :db/cardinality :db.cardinality/one
 :db.install/_attribute :db.part/db}

{:db/id #db/id [:db.part/db]
 :db/ident :company/name
 :db/valueType :db.type/string
 :db/cardinality :db.cardinality/one
 :db.install/_attribute :db.part/db}

{:db/id #db/id [:db.part/db]
 :db/ident :company/employees
 :db/valueType :db.type/ref
 :db/cardinality :db.cardinality/many
 :db.install/_attribute :db.part/db}
Then:
{:company/name "Acme
 :favorite/color "Blue"
 :address.mailing/street "Route 66"
 :address.billing/street "123 Nowhere St."
 :company/employees [

{:user/id #uuid "123..."
 :user/name "Chris"
 :favorite/color "Green"
 :address.home/street "St. 222"
 :address.billing/street "st. 123"
 :address.work/street "Route 66"}

]}
Don't think in rectangles columns, instead, try to think in sets of attributes

👍 2
🙂 2
Joe Lane21:04:59

What is the tx-data and full schema @cbluan?