Transferring Entities Between Development Stages

I’m fiddling with entities in a REPL, and because of the way things are set up, it’s very easy to accidentally destroy things. I’m turning off isComponent where I find it, but I still seem to wake up and find pieces missing. Since all my fiddling is done under a single parent entity, “bas”, I’ve figured out I can just pull “bas” whenever I reach a stable point, and then tx on “bas” if I screw something up.

Anyway, when I’m done fiddling, I want to be able to take the child entities and move them bodily to stage and prod, so that I don’t have to repeat the potentially disastrous fiddling anywhere that counts.

This should be fine because the content started from the same source and periodically prod gets copied down to stage and dev, and asserting already true things about an existing entity is not a problem for Datomic—except that I’m adding new entities as well, and the IDs assigned in dev may be used for live data in prod.

In other words, I have template 123 which means “foo” and that’s fine because “foo” is 123 everywhere. But in my fiddling I’m going to add “bar”, and it gets 125 in dev. But, uh oh, a user registered in prod, and HE got the 125 ID, so Bad Things could happen if I try to assert that 125 is a “bar”.

Since “foo” and “bar” can be pulled by their parent “bas”, I was trying to do that, but maybe I need to pull “foo” by itself and “bar” by itself, and then remove the IDs from bar.

I think no matter what, I’ll have to remove the IDs from bar. Or is there an easier way to go about this?

Anyone have any thoughts? This has proven to be quite challenging.

Trying to import the structure I need and getting an error:

:db.error/invalid-entity-id Invalid entity id: 17592186727046

My initial thought was that it didn’t like the structure, somehow, but it is a number. It is a valid number. It’s a number that isn’t in use in the system currently. It’s not a reference.

The only thing I’m seeing on the Internet says you can’t do this: You can’t give the ID for a new entity. That’s weird because I’ve been doing this for the past couple of weeks.

In fact, I just did it again (in the problem area) but with a tree that doesn’t include that offending ID, unless the issue is really that, even if you retract an entity and all its underlying entities, it’s still there on some ghostly DB level, so you can essentially re-insert a previously retracted entity.

Because I think what happened here is that I pulled the tree with my minor tweaks to existing entities first, and that was fine. Then I created the new entity, then I pulled that tree, and that was fine in the environment I created it in, but that tree won’t work in a new environment. :expressionless:

Datomic does not allow you to explicitly specify the entity ID when you are asserting new datoms. The entity ID is generated and assigned automatically by Datomic.

The general approach to resolving your issue is to ensure that you have externally unique identifiers on any entities you reference from your code. If you only ‘talk about’ entities by their external identifiers (i.e. UUIDs or email address or SSN), then you don’t have to worry about which entity ID is given to which entity, the semantics of your system (and your move of information from staging to prod) should be defined by and rely on those external IDs.

OK, sure, but how does that pertain to refs?

I’ve got X referencing Y and Z, and I need to move X, Y and Z so that the refs are maintained.

I guess I just do this with code?

Entities should reference other entities by their external ID.
If you set the attribute for the external identifier to be :db.unique/identity, you can use lookup refs to specify the references between entities.

For example, if you have :my-ent as your external identifier, you can make the reference from one entity (“foo”) to another (“bar”) like this:

{:my-ent "foo"
 :my-ref [:my-ent "bar"]}

Thanks again, Marshall.

So, I shouldn’t use :db/ref? What about when it’s a one-to-many relationship? I thought lookup refs were only good for singles.

In my example :my-ref is an attribute of type :db/ref.

Lookup refs are valid for any attribute that is :db.unique/identity or :db.unique/value. In the silly example above, :my-ent must be set to one of those unique. In the example you identify “target” entities of the reference with the lookup ref. It doesn’t matter if the reference attribute is cardinality one or many. If you had one that was card-many you could assert several “targets” of the ref at once:

{:my-ent "foo"
 :my-ref [[:my-ent "bar"][:my-ent "baz"]]}