asami

Asami, the graph database https://github.com/threatgrid/asami
2021-06-02T19:01:34.001600Z

@quoll getting somewhere with the datalog-in-JS, finally figured out how to synchronize the result sets with the dom nodes - https://gist.github.com/alandipert/ce0fa7dcb6cd74c4f5ea6679fe29dce1

2021-06-02T19:02:20.002100Z

ended up going bottom-up and diffing results as you suggested. seems to work nicely

šŸ‘ 1
2021-06-02T19:03:12.002800Z

the ANY.VALUE thing is a weird discovery, it seems to be a way to do disjunction based on query param, without changing the query

2021-06-02T19:03:45.003200Z

...being a sentinel value understood by the join loop to match any value

2021-06-02T19:04:02.003500Z

was curious if you'd run across anything like that in your travels, or if it's otherwise a well know thing

quoll 2021-06-02T19:04:55.004Z

I have not used JS very much, so I havenā€™t seen it before, no

2021-06-02T19:06:49.005Z

oh, ANY.VALUE is just the name of the sentinel value. when you pass that particular value in as a query parameter, it matches anything. sort of like a user-supplied wildcard

2021-06-02T19:07:09.005300Z

... as opposed to a (static) wildcard in the query text itself

quoll 2021-06-02T20:14:52.007200Z

OK, I was busy and Iā€™ve thought about it a little now. I think I see what youā€™re saying. So you have a pattern of: [_.e, "status", _.status] where the _.status value is supplied as a parameter, but in this case you have a parameter thatā€™s a wildcard.

quoll 2021-06-02T20:15:31.007700Z

If Iā€™m getting that rightā€¦ I havenā€™t seen that parameterized before

quoll 2021-06-02T20:16:46.009200Z

In Asami, the find and where clauses wouldnā€™t change, but if you didnā€™t want to restrict the status value, youā€™d have to remove the use clause (called :in in Asami)

2021-06-02T20:42:07.010100Z

yes i think that's right, thanks for grokking

2021-06-02T20:45:32.012600Z

it came up for me because i need to (re-)parameterize queries asynchronously, and it didn't seem right for the query pattern itself to change within the "extent" of the subscription established by view

2021-06-02T20:54:42.012800Z

how's asami going btw?

quoll 2021-06-02T20:55:07.013300Z

Up and down. I keep having things pull me away from it, but I keep getting back to it šŸ™‚

quoll 2021-06-02T20:55:23.013700Z

This week, Iā€™m turning it into a document store šŸ˜œ

quoll 2021-06-02T20:55:35.014Z

though today Iā€™m taking the time to write wiki pages

2021-06-02T20:56:22.014900Z

interesting, like a document-flavored frontend layer? or a rethink of the storage etc

quoll 2021-06-02T20:56:31.015100Z

New element to the storage

quoll 2021-06-02T20:57:10.016Z

right now, if you store objects in it, it disassembles the objects into triples, and stores those. If you ask for the object back it reassembles it

quoll 2021-06-02T20:57:32.016400Z

Which works correctly, and itā€™s why I can do round-trips on JSON documents

quoll 2021-06-02T20:57:45.016700Z

But it takes time to rebuild an object to return

quoll 2021-06-02T20:57:56.017Z

so Iā€™m storing the object as well

2021-06-02T20:58:11.017200Z

nice, with an invalidation/rebuild scheme?

quoll 2021-06-02T20:58:29.017400Z

yes

quoll 2021-06-02T20:58:49.017900Z

in case you are really trying to mess the system up by updating triples that belong to objects

quoll 2021-06-02T20:59:36.018600Z

To enable this, all sub-entities now have ownership attributes that connect them to their root object

quoll 2021-06-02T21:01:08.019600Z

If you scroll down a little from https://github.com/threatgrid/asami/wiki/Entity-Structure#structures-and-queries, youā€™ll see it in an example ball-and-stick diagram

quoll 2021-06-02T21:01:24.020Z

All the light lines are the ownership attributes

2021-06-02T21:02:51.020200Z

neat

quoll 2021-06-02T21:03:06.020400Z

well, Iā€™m still writing it

quoll 2021-06-02T21:04:08.021600Z

itā€™s much easier in-memory, since the objects themselves just go into a map. The on-disk version is trickier because I have to serialize everything

2021-06-02T21:08:50.023Z

hm, the example reminds me of stuff i've done in datomic in the past, converting trees to sets of tuples. the document-in interface seems broadly useful. then i see you have something like datomic pull api on the other end. i like it :thumbsup:

quoll 2021-06-02T21:09:41.024Z

The facilities are already there. This will just make it faster (I hope!)

2021-06-02T21:10:11.024900Z

very cool. yeah i don't think datomic has an option to cache entities or anything

quoll 2021-06-02T21:10:20.025200Z

This is why I can load a JSON file in 2 lines of code, and start querying it on line 3

2021-06-02T21:10:57.025800Z

https://github.com/alandipert/intension is how i used to do it

quoll 2021-06-02T21:11:33.026700Z

ohā€¦ and because of misbehaving JSON, I removed some of the type checking. So now entities and attributes can be anything. This is a semantic nightmare, but turns out to be useful. šŸ™‚

2021-06-02T21:11:53.027Z

heck yeah, json compat on any level brings a lot of reach and applicability

2021-06-02T21:12:40.027200Z

you know, i suppose what i'm working on is very similar

2021-06-02T21:13:06.027900Z

updating dom nodes surgically in response to a stream of query changes. it's almost exactly document maintenance with datalog backend

2021-06-02T21:14:48.029400Z

well, modulo all the ways asami is different, and javascript šŸ™ƒ

quoll 2021-06-02T21:17:14.030800Z

I was asked about one file, and ended up exploring it with things like:

(def conn (d/connect "asami:<local://tmpdata>"))
(def tx @(d/transact conn (json/parse-string (slurp "cb.json"))))

(d/q '[:find (count ?a) . :where [?e ?a ?v]] db)
(d/q '[:find [?a ...] :in $ ?bad-string :where [?e ?a ?v] [(?bad-string ?a)]]
  conn #(and (string? %) (re-find #" " %)))
Which is all easy in other systems as well

2021-06-02T21:18:53.032100Z

yes but we like datalog :simple_smile:

2021-06-02T21:19:37.033100Z

what does the ... in the :find do?

quoll 2021-06-02T21:19:41.033200Z

But then I wanted to find the entities that contain an attribute with a space in themā€¦

(-&gt;&gt; (d/q '[:find [?ancestor ...]
            :where [?ancestor :tg/entity true]
                   [?ancestor ?a+ ?e]
                   [?e ?attr ?v]
                   [(bad-string? ?attr)]] db #(and (string? %) (re-find #" " %)))
     (map #(d/entity db %)))

quoll 2021-06-02T21:20:21.033900Z

Normally a query returns a sequence of bindings, where each binding is a sequence of values that align with the :find clause

quoll 2021-06-02T21:21:53.035600Z

If the :find clause only contains one element, then you get back a sequence of 1 element sequences. e.g.

(["foo"] ["bar"] ["baz])
If you want to, you can send that to a (map first ā€¦) but itā€™s an extra step

2021-06-02T21:22:11.036100Z

gotcha, i like that

quoll 2021-06-02T21:22:17.036300Z

By specifying [?ancestor ā€¦] then you get back a seq of values instead

2021-06-02T21:23:09.037600Z

?a+ is another interesting one, do i understand right that deals with arbitrary depth?

šŸ‘ 1
quoll 2021-06-02T21:23:16.037800Z

Also, if youā€™re getting back a single value, itā€™s annoying to get [[value]]. Even if you use the [?arg ā€¦] syntax you still get [value]. So you use .

quoll 2021-06-02T21:23:59.038100Z

Not only is it arbitrary depth, itā€™s with arbitrary attributes!

2021-06-02T21:24:24.038300Z

yeah that's a really good one

2021-06-02T21:24:44.038800Z

you have some awesome extensions going on, i must say. šŸ’Æ

quoll 2021-06-02T21:24:56.039Z

this operation is typically reserved for a single attribute. Like:

[?person :has-child+ ?child]
Will return a list of all descendants

2021-06-02T21:24:59.039200Z

don't mind if i cram a few of these into JS syntax

quoll 2021-06-02T21:25:09.039400Z

Itā€™s open source for a reason!

šŸ» 1
quoll 2021-06-02T21:27:30.040Z

Following arbitrary attributes to arbitrary depth is fraught! If your graph has a spoke and hub structure (like DNS), then you could traverse the entire graph

quoll 2021-06-02T21:27:55.040300Z

ā€¦ if you happened to start at the wrong spot šŸ™‚

2021-06-02T21:28:50.040500Z

can you have cycles? this could help you find them

quoll 2021-06-02T21:29:16.040700Z

It detects cycles

2021-06-02T21:29:40.040900Z

also, if you have ?a in your :find, do you get the product of the lvars and every ?a in the tree?

quoll 2021-06-02T21:30:44.041100Z

no. I actually cheat a bit

quoll 2021-06-02T21:31:12.041300Z

the ?a value does not bind to a single value per binding, as youā€™d normally expect

quoll 2021-06-02T21:31:27.041500Z

instead, it binds to a vector containing a path

quoll 2021-06-02T21:31:48.041700Z

So you can actually select the path, if thatā€™s what you want

quoll 2021-06-02T21:32:27.041900Z

Iā€™m sure that this will break in some corner cases. I havenā€™t tested that all that much. But for simple queries it provides a neat way to find paths

2021-06-02T21:32:55.042100Z

yeah i think affordances like this are a net win, and i'm sure i don't have nearly the experience working with this stuff that you do

2021-06-02T21:33:15.042500Z

...because these things can be understood in terms of the underlying model, they're not arbitrary like with many other query tools

quoll 2021-06-02T21:35:21.042700Z

The issue isnā€™t calculating it. Itā€™s figuring out how to make these calculations show up in query results in a way that makes sense, and does not require an excessive number of special cases

2021-06-02T21:37:44.042900Z

it makes sense to me so i think you're crushing it

šŸ’– 1
2021-06-02T22:01:05.043600Z

regarding :find syntax, any reason you didn't use e.g. :find ?ancestor to get the flat sequence back?

quoll 2021-06-02T22:03:52.045700Z

In that query, the ?ancestor node was locked (by virtue of the [?ancestor :tg/entity true] constraint. Only the ?e was floating. That ends up getting locked by the pair of constraints: [?e ?attr ?v] [(bad-string? ?attr)]

quoll 2021-06-02T22:04:58.046600Z

so the sequence of nodes leading from the root attribute down to the bad attribute was actually in the ?e

quoll 2021-06-02T22:05:17.046800Z

If thatā€™s what youā€™re asking about?

2021-06-02T22:05:29.047100Z

i'm asking about the syntax choice of [x ...] over x

2021-06-02T22:06:00.048Z

wasn't sure if ... has other uses/meanings, or if simply x is otherwise fraught

quoll 2021-06-02T22:06:42.049100Z

well, I find it easier to work with [:a :b :c :d] than with [[:a] [:b] [:c] [:d]]

2021-06-02T22:06:45.049300Z

..although i'm grateful for the words "locked" and "floating" to describe the lvar status :thumbsup:

quoll 2021-06-02T22:07:15.050100Z

I can get from the latter to the former with (map first) but thatā€™s not repl friendly

2021-06-02T22:07:23.050400Z

oh, i agree with you, i'm asking in particular about the grammar choice of the query map

quoll 2021-06-02T22:07:30.050700Z

Ah!

2021-06-02T22:10:04.051500Z

ah yes, ty

quoll 2021-06-02T22:10:56.051900Z

Alsoā€¦ Iā€™m going to keep saying it: I really wish it wasnā€™t called ā€œDatalogā€

šŸ’Æ 1
2021-06-02T22:24:45.052100Z

seconded

2021-06-02T22:25:16.052700Z

i'm out for now, it was nice catching up šŸ‘‹

quoll 2021-06-02T22:25:44.052900Z

good night!