Fork me on GitHub
Jakub Holý (HolyJak)16:01:50

Hi! The docs read > Entities and assertions may have attributes that are keywords with a trailing ' character. Here I interpret assertions as [:db/add ...] but if that is correct then I believe the docs lie. I only see support for the :attribute' in entities (namely asami.entities/entity-triples), not in quadruplets. Or? If I am right - should I look into adding support for ' to assertions?


I should be more precise with my terminology, sorry. I consider an assertion to be anything that instantiates data. Entities certainly do this. But you’re right… so does :db/add. Unfortunately, I had a focus on entities when I was writing this, and I did not think of :db/add.


However, I do not want this added to :db/add


Working with entities is about a lot of inferencing of what the user is trying to add/remove, etc

Jakub Holý (HolyJak)16:01:43

My problem is I need to use quadruples (so that references via :id to other new entities transacted in the same tx work) but I also want to ensure that some attributes have at most one value. Schema will solve it but it is surely a way away. So I need a performant way to do what entities do, ie find existing values for an attribute to generate retractions


while :db/add and :db/retract are about raw operations. I don’t want to mess with those

💯 2

Ah… What you’re describing is exactly what the entity code tries to do

Jakub Holý (HolyJak)16:01:56

I know, I read it :) But as described I can't simply use entities 😭


I go straight to the graph API for this. It’s a thin wrapper around a get on a map when in memory, or an index lookup when on disk

Jakub Holý (HolyJak)16:01:09

I saw the writer/resolve-triples (or what's the name, on my phone now). I just need to get hold of Graph...


The function> is an example of this (much simpler than code that tries it in asami.entity)


The first line gets key/value pairs from the graph for a given node.


it uses the pairs->struct function to get any structures that those values represent, but if they’re simple values (e.g. string and numbers) you shouldn’t need to worry about that

Jakub Holý (HolyJak)16:01:58

Thank you! Will have a look

Jakub Holý (HolyJak)16:01:00

How do I get the Graph Type instance? Is it simply (d/db connection)? Though using that risks that the DB changes between I derive the retraction and transact them...


Pass your handle to: (#'asami.core/as-graph handle)


I quoted it like that because it’s actually a private function 🙂


“Handle” here can be a connection, a database, or a graph


All of these wrap (or are already) a graph. That function just pulls out the graph according to the argument. Don’t call it in a loop though, because it calls satisfies?

👍 2
Jakub Holý (HolyJak)18:01:26

Is there any simple way to ensure that my work with the graph to derive retractions and transacting of the retractions are not interposed with another transact? Other than using something like (locking connection ...) ?


Any graph you have is immutable, so it shouldn’t matter. If you’re making modifications, then you need to get the new graph after being modified, because your old graph will have the data previous to modifications.


It’s possible to mutate the graph within the on-disk storage, but I believe that the APIs will completely lock you out of doing that


As for interposing transactions… • If 2 transactions occur in memory, then they will both end up with different graphs, with a shared history up to the point where the transaction started (like a github branch, where both the branch and main have commits after the branch). • If 2 transactions were to (somehow) occur on disk, then you’d get the same behavior. However, there is a lock that should prevent that, and a file lock that should avoid it between processes as well.

👍 2

I’ve actually considered removing the lock, so it would be possible to branch databases, the same way that github lets you branch code bases. However, I haven’t wanted to consider merging, and I’m not sure there’s a lot of point to allowing a branch like this if you aren’t going to merge


(unless someone has a use case)


(“haven’t wanted to consider merging” that’s not actually true. I’ve considered it a lot, and the work is substantial. I haven’t wanted to commit to the work 🙂 )

😅 2
Jakub Holý (HolyJak)23:01:43

Given a db value, is there any non-negligible performance difference between

(graph/resolve-triple (d/graph (d/db db)) node-id :order/descr '?xval)
(d/q '[:find ?xval :where [?e :order/descr ?xval] :in $ ?e] (d/db db) node-id)
? I would typically want to call this a few times for different attributes during an update operation (to find current values for retraction) 🙏


In this case... a difference but maybe not too much. It needs to plan the query (easy, because there's only one pattern), start the resolution with the first pattern (calling resolve), join to the rest of the query (there is none), and then project the result to one column. Personally, for internal operations I always use the resolve-triple, but I think it's pretty quick to do the query here

🙏 1

Sorry for the delay. I went for a 2 hour walk, and I see that you wrote to me 2 hours ago!

Jakub Holý (HolyJak)17:01:34

There is no hurry! I never expect an immediate answer from volunteers. They need life too :-) I wrote that question at the end of my (too long) day anyway :-) Thank you for the insight!


I saw someone try a simple test with Asami and other graphs where they loaded some data and did a simple query like yours above. Asami was the fastest, but it wasn't fair... Simple queries like that get short circuited to turn into a resolve-triple, and don't have to go through the entire process


It's a bit of a shame. I would love to just say that I can write awesome code and be done with it 🤪

🙂 2