Fork me on GitHub
#datascript
<
2017-03-09
>
qqq00:03:25

has anyone built a nice UI tool for interacting with datascript?

qqq00:03:38

something where (1) I can browse all elements, (2) write queries, and (3) run transactions

qqq00:03:54

basically I want a "datascript shell" as an HTML UI element

Niki06:03:43

Iā€™m not aware of anything

Niki06:03:55

but I thought about something like that, of course :)

qqq06:03:39

@tonsky: we should sign a petition to get the dev of datascript to implement it šŸ™‚

Niki06:03:10

yeah, I know, that guy is so lazy!

qqq07:03:43

@tonsky: have you played with datastore? it doesn't support full datascript, like join, but the two seems very closely matched

Niki09:03:51

which datastore is that?

Aron11:03:05

i always want to build an "adminer" kind of thing, but i never have the time

Aron11:03:30

also, what's the simplest setup for toggling a value in datascript between true/false?

qqq11:03:50

@tonsky: Google Cloud https://cloud.google.com/appengine/docs/standard/python/datastore/ It's basically an EAV store, where Attributes are called "Property"

qqq11:03:01

iirc, the indexing model are also very siilar

misha11:03:57

Query q =
    new Query("Person")
        .setFilter(new FilterPredicate("lastName", FilterOperator.EQUAL, targetLastName));

PreparedQuery pq = datastore.prepare(q);
Entity result = pq.asSingleEntity();
eew

qqq11:03:30

@misha: yeah, wouldn't you rather query that using datalog?

qqq11:03:50

@misha: help me convince @tonsky to build a datascript layer over google data store šŸ™‚

misha11:03:27

build it yourself opieop

qqq11:03:10

I'm in the process of doing exactly that

qqq11:03:19

as my backend is gae, so I have no choice but to use datastore

misha11:03:23

how building over datastore is useful? what is the usecase?

Aron11:03:24

anyone a suggestion for boolean toggling?

Aron11:03:36

focus on the short term pls šŸ™‚

misha11:03:01

"as my backend is gae, so I have no choice but to use datastore make people implement things for me" kappa

qqq11:03:26

@misha: I'm not sure if I'm missing your humor, or if you're missing my humor, but the šŸ™‚ in "@misha: help me convince @tonsky to build a datascript layer over google data store :-)" meant I wasn't really expecting tonsky to implement it

Aron11:03:04

how did slack turn into twitchchat

qqq11:03:57

but I do think, from a tech perpsective, gae datastore is an excellent persistenticy backend for datascript

misha11:03:02

there is also :db.fn/cas, but writing toggle fn would be cleaner(?) in use I guess

Aron11:03:45

cas is not available for me

Aron11:03:50

tried that yesterday

misha11:03:02

js? tough luck šŸ˜„

Aron11:03:10

datascript-mori module

Aron11:03:29

and i am not sure which part of this test is the response to my question, seems parallel

misha11:03:40

no idea what that is, but see if you can write a transact function

misha11:03:06

you want to toggle w/o knowing current value, right?

Aron11:03:24

would be the most convenient

misha11:03:58

write a transact function. link is a tx-fn example in a test

misha11:03:17

otherwise, if you already hold value in the code:

(ds/transact! conn [[:db/add id :my.bool/flag (not v)]])

Aron11:03:51

if i do that it will fill up the db, adding a new value every time, wouldn't it?

Aron11:03:57

i had that issue before

Aron11:03:11

seems like for most things i should use db functions

misha11:03:11

1. :my.bool/flag has to be {:db/cardinality :db.cardinality/one} to be updated "in-place" 2. datascript does not keep old values like datomic does, so if you don't keep db-before-transaction db value yourself - it will be updated "in-place"

misha11:03:27

no idea what datascript-mori does

misha11:03:20

(let [conn (ds/create-conn {:my.bool/flag {:db/cardinality :db.cardinality/one}})]
  (ds/transact! conn [{:my.bool/flag true}])
  (prn [:bfore (into [] (ds/datoms @conn :eavt))])
  (ds/transact! conn [[:db/add 1 :my.bool/flag false]])
  (prn [:after (into [] (ds/datoms @conn :eavt))]))

;=>
[:bfore [#datascript/Datom [1 :my.bool/flag true 536870913 true]]]
[:after [#datascript/Datom [1 :my.bool/flag false 536870914 true]]]

Aron11:03:25

datascript-mori is just convenience module for js folks so we can use mori functions with datascript entities

Aron11:03:44

because clojurescript is so good, if you don't compile stuff together, they don't work

misha11:03:20

anyway, if it does not hold on to pre-transaction db value (version) for you - datoms count will not grow after cardinality/one attribute change

Aron11:03:26

@misha thanks, it was the cardinality/one thing that i forgot about

Aron20:03:20

is this possible for nested stuff too? so when i save an object with reference, i would like that reference to not add new stuff but to replace the old ones. (right now i am saving address path from history module and each location change adds 3 new datoms)

Aron20:03:27

problem is that the same attribute might not be like this for a different ident

Aron20:03:10

so i am not sure i want to add to every attribute in location object this cardinality/one thing because then i can't save more stuff with same attribute name under different entities

Aron20:03:53

in fact, i tried but obvious it doesn't work because this is a different issue, even though i thought it's the same šŸ˜ž

Aron20:03:15

right now i am not even sure if it's possible to do this without retracting explicitly šŸ˜ž

Aron21:03:23

oh, it works, i just had to set everything unique/identity :((

misha21:03:45

I have an impression you are confusing cardinality with identity

misha21:03:18

;; :foo :cardinality/one is
{:foo "a"}
;; :foo :cardinality/many is
{:foo ["a"]}

Aron21:03:33

that would be interesting. cardinality is about counting stuff and identity is about matching stuff

misha21:03:24

then I did not quite understand you question about growing datoms count on nested ref change

Aron21:03:12

i really wish i could avoid having to add pathname search and hash to the schema

Aron21:03:24

especially with "identity"

misha21:03:57

db = ds.db_with(db, vector( hashMap(DB_ID, vector("DB_IDENT", "history") , "current", toClj(removeNull(history.location)))))
harold

Aron21:03:11

i don't speak twitch language

misha21:03:51

js looks ugly

Aron21:03:17

yeah, i am not going to argue about the aesthetics of the language if it's ok, i would much rather just find out if i am doing something wrong

misha21:03:29

having hard time actually understanding what that code represents

Aron21:03:34

which part of it is hard?

Aron21:03:39

vector is just cljs vector

Aron21:03:31

DB_ID is the :db/id, removeNull removes all null values from any object.

Aron21:03:52

i am using history module and trying to save the current location object into datascript

Aron21:03:51

and i would like to only have the last one, but in order for that to work, i have to add each of the attributes as "identity"

misha21:03:11

so you want to put current url into datascript as a parsed map of path, search, hash, etc. right?

misha21:03:34

and want to have only 1 such destructured url in an entire db, correct?

Aron21:03:47

history.location is {pathname, hash, search}

Aron21:03:58

(and some null stuff that is removed

misha21:03:42

(let [conn (ds/create-conn)
      temp-id -1
      report (ds/transact! conn [{:db/id temp-id :url/path "foo" :url/hash "bar"}])
      actual-id (get (:tempids report) temp-id)]
  (prn (into [] (ds/datoms @conn :eavt)))
  (ds/transact! conn [[:db/add actual-id :url/hash "baz"]])
  (prn (into [] (ds/datoms @conn :eavt))))

=>
[#datascript/Datom [1 :url/hash "bar" 536870913 true]
 <#C07V8N22C|datascript>/Datom [1 :url/path "foo" 536870913 true]]
[#datascript/Datom [1 :url/hash "baz" 536870914 true]
 <#C07V8N22C|datascript>/Datom [1 :url/path "foo" 536870913 true]]

misha21:03:50

that updates in parsed url in place

misha21:03:41

no schema needed at all

Aron21:03:00

ok, so i also have to be able to find it later

Aron21:03:40

wait, i can't read code, sry

Aron21:03:45

let me try again ;D

misha21:03:48

basically all you need to do, is to use the same id when you update stuff

misha21:03:00

ident works too, yeah, why not

Aron21:03:02

dunno, seems to me you just took 2 random attributes and saved some data into it

Aron21:03:08

what am i missing here

misha21:03:25

(let [conn (ds/create-conn {:db/ident {:db/unique :db.unique/identity}})
      report (ds/transact! conn [{:db/ident "history" :url/path "foo" :url/hash "bar"}])]
  (prn (into [] (ds/datoms @conn :eavt)))
  (ds/transact! conn [[:db/add [:db/ident "history"] :url/hash "baz"]])
  (prn (into [] (ds/datoms @conn :eavt))))

=>
[#datascript/Datom [1 :db/ident "history" 536870913 true]
 <#C07V8N22C|datascript>/Datom [1 :url/hash "bar" 536870913 true]
 <#C07V8N22C|datascript>/Datom [1 :url/path "foo" 536870913 true]]
[#datascript/Datom [1 :db/ident "history" 536870913 true]
 <#C07V8N22C|datascript>/Datom [1 :url/hash "baz" 536870914 true]
 <#C07V8N22C|datascript>/Datom [1 :url/path "foo" 536870913 true]]

Aron21:03:44

yeah, so what i ahve more is the "current" attribute with ":db/valueType": ":db.type/ref",

misha21:03:45

3 datoms before and after value change

Aron21:03:42

so, first of all, thanks for your help, i really do not want to seem as ungrateful, this is definitely a good solution for what i described

Aron21:03:25

however i am afraid this means that i can't have any kind of functions working over datascript because i can't use attributes like "current"

Aron21:03:35

i mean general functions.

Aron21:03:06

my vision of all this was that if i have a "current" with reference then i can just save into that and then have a function that takes just the ident name and gives back the current value

Aron21:03:14

if for each value i have to have a different attribute

Aron21:03:17

not sure if it's worth it

misha21:03:53

can you paste a json, representing what you want to have in a db? I think we are running circles here

misha21:03:12

1) you want to be able to get current url from db 2) and to keep all previous ones in db too ?

Aron22:03:01

no, i don't want to keep previous ones.

Aron22:03:06

you can see that i have "current" both for "history" and for "myAddr"

Aron22:03:18

(and if i had saved i could have it for "contraAddr" too

Aron22:03:22

function getCurrentAttr(ident, attr){
    return toJs(ds.q(Datalog.Q`[:find ?v . :in $ ?e ?a :where [?e "current" ?r][?r ?a ?v]]`, db, vector("DB_IDENT", ident), attr))
}

misha22:03:28

you want attribute named "current" on different entities of different type?

misha22:03:55

and you think you can't have those because?

misha22:03:22

[[:db/add [:db/ident :myAddr] :current "foo"]]
will not overwrite or touch or change the value of the "current" attribute of "history"

misha22:03:46

however both "current" attributes has to have the same schema

misha22:03:15

to avoid this you need to use namespaces. in clojure it would look just like this:

:address/current ...
:history/current ...

misha22:03:30

how it would look like in js - have no idea, other than "current_history" and "current_address"

Aron22:03:41

this is datascript

Aron22:03:45

datascript is javascript

Aron22:03:51

šŸ™‚

Aron22:03:33

anyway, thanks for pointing this out, i always run into stuff like this, expecting stuff to work when it can't

misha22:03:39

I mean "conveniently" for you, so you could pull data from datascript and use it with no extra hustle

misha22:03:32

if you have the same schema for the "current" attribute, then there is no problem at all

misha22:03:56

and I still don't fully understand the problem opieop

Aron22:03:36

that looks like XBOCT

Aron22:03:55

@misha so when i used schema that i shared with you but without pathname, search, hash specified as identity, and I added new location, the current got updated as it should but instead of just replacing the old pathname,search,hash, it added them with new entity id.

misha22:03:41

what exactly did you transact?

Aron22:03:09

that's the code i've shown to you before:

db = ds.db_with(db, vector( hashMap(DB_ID, vector("DB_IDENT", "history") , "current", toClj(removeNull(location)))))

misha22:03:15

what is in location? hash-map?

misha22:03:02

and if you would have only 1 entity url in db - what would current represent/contain?

Aron22:03:02

as i described before, it is a js object with pathname, search, hash attributes and some others that get removed because they have null values

Aron22:03:17

what is entity url?

misha22:03:26

yeah, that object

misha22:03:38

you need to set an id inside that object

Aron22:03:53

modifying javascript objects is a big antipattern

misha22:03:03

if you omit id - datascript treats it as a new entity coming in

Aron22:03:37

well, i should be able to use conj

Aron22:03:54

i just don't know the id, that's why i use idents

misha22:03:05

if you transact:

{:db/ident "history"
 :current {:foo bar}}
it will create new entity with :foo=bar

misha22:03:45

to update existing you need to transact

{:db/ident "history"
 :current {:db/ident "history" :foo bar}}

misha22:03:01

or whatever db/ident :foo belongs to

misha22:03:58

then, I just don't understand why would you have a "current" attribute on entity pointing to itself

misha22:03:38

+ if you wish to keep "singletons" what would your "current" attrs do?

Aron22:03:22

not itself

Aron22:03:56

why do you think it points to itself, i shared the data from my datascript, none of the currents are pointing to themselves, they are pointing to nested stuff

misha22:03:55

it is still unclear to me what you are trying to achieve with "current" attrs. when I ask you questions - I understand the answers (I think), but when I get back to your schema and data - those do not match up with my understanding

Aron22:03:04

šŸ™‚

Aron22:03:25

well, that's not your fault, i am probably misusing datascript all kinds of ways

Aron22:03:59

ident is just to find it's current, and current is always a nested map with values, the current values for the ident

Aron22:03:10

that was my reasoning

misha22:03:15

if you have a bunch of singletons, with known "ids/idents", why do you need to link them more?

misha22:03:29

you don't need this

misha22:03:39

ident is just a value of attribute

Aron22:03:42

i don't know what you mean by singleton, in my book singleton is a buzzword that just means "object"

misha22:03:55

key entity - is the id

Aron22:03:56

the objects do not have ids/idents

Aron22:03:06

i have ids/idents and i want to save whatever in them

misha22:03:17

each entity can have unlimited amount of various attributes

misha22:03:37

one of which is called :db/ident (people call it this way since datomic has a built in mechanics around it. in datascript it means nothing special)

misha22:03:59

you "history object" is just 1 for example.

Aron22:03:23

vector("DB_IDENT" "history") is just to find the id

misha22:03:22

that 1 has an attribute :db/ident = "history". only one entity in db can have a combination of attr=val :db/ident = "history", because you said so in schema: {:db/ident {:db/unique :db.unique/identity}}

Aron22:03:23

originally i started using this because i like that datascript flattens large objects if the schema is done well

misha22:03:02

so. you can attach the rest of the "destructured" attributes directrly to 1

Aron22:03:14

but then i have to do manual data mangling

misha22:03:43

it'll look like this:

{:db/id 1
 :db/identity "history"
 :hash "foo"
 :url "bar"
... }

Aron22:03:25

yeah, so actually, it would look like this:

misha22:03:09

and when you update something signed with db/ident=history - you are updating 1

misha22:03:42

no need to split it into 2 levels, and link those with "current" (however it can be done)

Aron22:03:42

ds.db_with(db, vector(hashMap(DB_ID, vector("DB_IDENT", "history"), "hash", location.hash, "search", location.search, "pathname", location.pathname)))

Aron22:03:53

and this might be ok with 3 attributes now

Aron22:03:09

but as i said, i liked the fact that datascript can do this work for me

misha22:03:10

ok, nooow I might see what you mean there

misha22:03:10

you just need to set :db/ident or :db/id on js object before transacting it

misha22:03:42

to let datascript know you want to update existing thing, not create new one

Aron22:03:08

yeah, i understood this from your previous explanation šŸ™‚

Aron22:03:42

it's just that i don't know that id, and i don't like changing objects because there comes the evil DEOPTIMIZATION thing in JiT

misha22:03:12

I just repeated it for myself: first time I wrote it - was a guess answer, but now I feel like it is actual answer šŸ™‚

misha22:03:51

> it's just that i don't know that id but you said you used idents

Aron22:03:02

on the top level to find the top id, but after that, no. so for history i find 1 or 4 or whatever, but then the nested stuff has 5 or 12 or whatever

Aron22:03:12

and there is no ident for that

Aron22:03:34

mostly because i did not know i need it, and then because i don't like changing objects

Aron22:03:54

i wll now go home because i don't want to be at work in 15 minutes when it will be friday šŸ™‚

Aron22:03:03

thanks for all the great help @misha

Aron22:03:22

i sleep on this and probably delete currents and rewrite stuff

misha22:03:43

(let [conn (ds/create-conn {:db/ident {:db/unique :db.unique/identity}
                            :history/location {:db/valueType :db.type/ref}})]
  
  (ds/transact! conn [{:db/ident "history"
                       :history/location {:db/ident "location"
                                          :url "foo"
                                          :hash "bar"}}])
  (ds/transact! conn [[:db/add [:db/ident "location"] :hash "baz"]])
  (prn (into [] (ds/datoms @conn :eavt))))

=>
[#datascript/Datom [1 :db/ident "history" 536870913 true]
 <#C07V8N22C|datascript>/Datom [1 :history/location 2 536870913 true]
 <#C07V8N22C|datascript>/Datom [2 :hash "baz" 536870914 true]
 <#C07V8N22C|datascript>/Datom [2 :url "foo" 536870913 true]
 <#C07V8N22C|datascript>/Datom [2 :db/ident "location" 536870913 true]]

misha22:03:33

if you will have single history and single location, and you want to update location in-place, and keep location linked to history: 1. create 2 idents 2. link them once 3. update just location, providing db/ident to avoid creating new, instead of updating existing one

Aron23:03:46

yeah, this is very close šŸ™‚

Aron23:03:06

and i mean that in a good way