Fork me on GitHub
#specter
<
2018-07-16
>
igrishaev07:07:48

thank you @nathanmarz, this combo looks solid!

igrishaev09:07:47

here is my final version to upsert into the re-frame database:

(defn upsert [db path key-eq new]
  (letfn [(map-eq? [old] (= (get new key-eq) (get old key-eq)))
          (updater [old] [(merge old new)])]
    (let [full-path (conj path s/ALL map-eq?)
          [db2 updated?] (s/replace-in full-path updater db)]
      (if updated?
        db2
        (s/setval (conj path s/AFTER-ELEM) new db)))))

4
alexyakushev12:07:11

@igrishaev FWIW, I had a problem very similar to yours, and for it I implemented a simple custom data structure that keeps values in a vector and a map simultaneously. It implements all IPersistentMap methods, and assoc works as the upsert.

nathanmarz12:07:49

@igrishaev fyi, use the path macro for composing paths instead of conj

nathanmarz12:07:52

will be much faster

igrishaev12:07:36

thank you, will check that out

igrishaev12:07:31

in my case, path is a vector to the actual data subset, say [:tasks :completed]

nathanmarz12:07:33

yea, you should pass that in as (path :tasks :completed)

nathanmarz12:07:35

path uses inline caching to minimize runtime compilation of paths

igrishaev12:07:50

If I have a vector like [:foo :bar], how can I apply it the the path macro then?

nathanmarz13:07:44

(path :foo :bar)

igrishaev13:07:13

sure, but what if both foo and bar come as a vector?

igrishaev13:07:40

as a func argument in my case

nathanmarz13:07:06

if it's dynamic, then declare them using path, not as a vector

nathanmarz13:07:27

(upsert db (path :foo :bar) ...)

igrishaev13:07:02

the problem is I don’t know in advance what subset of the main db will be used.

igrishaev13:07:03

I supposed a user passes an initial path as a vector, then I build the full transformation path

nathanmarz13:07:35

ultimately it's an optimization

nathanmarz13:07:54

if your users declare their paths using path it will be faster, but if they use vectors it will still work

igrishaev13:07:50

ok, so if a user passes smth like (path :foo :bar) as an initial path, how can I extend it?

nathanmarz13:07:15

e.g. (path passed-in-user-path ALL)

igrishaev13:07:37

oh, I see now, thanks