Fork me on GitHub
#datomic
<
2017-05-20
>
marshall00:05:01

Binary Large OBject

marshall00:05:51

As opposed to a Character Large OBject (CLOB)

unbalanced00:05:00

lol you KNOW they came up with that after "blob"

alex-dixon19:05:02

When I upsert a unique/identity attribute, what’s removed exactly? Is the existing entity identifiable by that attribute-value combination removed? i.e. Insert [[1 :unique-attr “foo”] … bunch of other attributes associated with 1]. Insert [2 :unique-attr “foo”]. Are all attributes associated with 1 removed, or just [1 :unique-attr “foo”]?

favila20:05:43

@alex-dixon nothing is removed? I may be misunderstanding you

favila20:05:51

Maybe you can show us a sample transaction?

alex-dixon20:05:35

Thanks. So I don’t have one unfortunately but could try to create one. I’m really just not clear on the basics of what ends up happening when I transact an attribute that is marked :db.unique/identity and I haven’t been able to find an answer. I’ll try again! E.g., if :username is :unique/identity, and I transact [eid2 :username “foo”] when [eid1 :username “foo”] is already in the db, does Datomic remove everything associated with eid1?

favila20:05:42

If eid2 is not a tempid and is different from eid1, you will get an error

favila20:05:02

Datomic never silently retracts anything

favila20:05:04

If eid2 IS a tempid, the tempid will resolve to same eid1, and you will assert on the same entity

alex-dixon20:05:14

Ah. Ok. Yes I should have specified it wouldn’t be a temp id. I guess I’m interested in the behavior of both

favila20:05:24

That is the special behavior of upsetting

favila20:05:38

*upserting

alex-dixon20:05:24

Ok. Thank you. That’s a huge help

favila21:05:00

A simple unique (vs identity) will error out in that case too

favila21:05:33

So db unique identity affects tempid resolution, fundamentally

alex-dixon21:05:44

So if I wanted that behavior, would I just write a database function?

favila21:05:58

What behavior?

alex-dixon21:05:24

Sorry. If I wanted to remove everything about e.g. a “user” entity when I remove :username, or some arbitrary attribute for the entity

favila21:05:08

You mean db.fn/retractEntity?

alex-dixon21:05:15

Yyyes. Kind of. Like I would want to basically declare “nothing about a user exists if ever there is a user without a username”

alex-dixon21:05:51

I know there’s component but don’t see how it could be used at that level (within an entity)

favila21:05:24

The invariant you describe is not enforceable by datomic alone

favila21:05:13

Datomic only keeps attr level invariants

alex-dixon21:05:39

Ah hah. Ok. I’ve had that impression

favila21:05:26

So you need a distinct "delete user" op that your app always goes through

favila21:05:25

You can't have [[:db/retract eid :username "foo"]] automatically result in all eid being retracted

alex-dixon21:05:53

Ok. I thought about whether that behavior could be emulated by having :user, then all attributes associated to that (:username, etc). Then if I retract :user and the attributes are components…

favila21:05:39

If your notion of delete user is merely "ensure there are no references to this rid in the db anymore" db.fn/retractEntity is enough

favila21:05:42

But sometimes other types become invalid by the loss of their reference. You need to know what those are and deal with it

favila21:05:46

E.g. If you had a user-prefs entity with a required user reference attr, and you del the user, what should happen to this entity?

favila21:05:26

Should you error? (Force deleting the user prefs explicitly) shouldnyou transitively delete it automatically? Etc

favila21:05:50

So any entity or cross-entity invariants are your responsibility

alex-dixon21:05:17

Ok. Thank you so much. That makes a lot of sense. It seems like Datomic itself will never put me in the situation if I program correctly. Unfortunately I’m working on a framework (borrowing partially on ideas in Datomic) where I think I’ve introduced this problem as part of the implementation 😅

alex-dixon21:05:03

Seems like a situation where an error very much should be raised

favila21:05:16

Transaction fns corresponding to your "operations" can help you, both organizing your code and keeping always in sync with data, and removing possible race conditions

favila21:05:54

But you face same advantages and disadvantages as db-level triggers in a traditional sql db

alex-dixon21:05:09

Nevertheless it’s tempting to have something like an “essential property” for an entity…otherwise it seems the maintenance surrounding such a case falls to users

favila21:05:20

(Except you also have full history)

favila21:05:38

There are some libs out there that try to do some entity-level enforcement

favila21:05:41

There's also a talk about structuring datomic attribute schema such that entity and type invariants in a db are self-describing

favila21:05:15

(Meaning you could potentially use a smaller handful of generic tx fns for CRUD ops)

favila21:05:51

But most of the time you want an app layer on top of datomic anyway. E.g. A rest interface

favila21:05:12

At the very least is for authorization checks

favila21:05:24

Sorry I am on a phone and am autocorrecting hard

alex-dixon21:05:31

Makes sense. And interesting, thought I was encountering something isolated but sounds like other libs are trying to solve related issues?

alex-dixon21:05:44

Lol. Honestly just grateful for the responses. Thanks again

favila21:05:57

Yes sure. However your life will be easier if you shed as many entity level invariants as possible

favila21:05:34

ERD style design (e.g. For a relational db) tends to produce closed types with rigid entity invariants

favila21:05:08

You can be more open in a graph style db

favila21:05:47

Use namespaces on attrs to resolve ambiguity. Express reference attr invariants in terms of required attrs on the destination entity instead of on a strict entity "type"