specter

Latest version: 1.1.3
igrishaev 2018-07-16T07:31:48.000078Z

thank you @nathanmarz, this combo looks solid!

igrishaev 2018-07-16T09:24:47.000099Z

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)))))

1
alexyakushev 2018-07-16T12:17:11.000310Z

@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.

nathanmarz 2018-07-16T12:37:49.000060Z

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

nathanmarz 2018-07-16T12:37:52.000298Z

will be much faster

igrishaev 2018-07-16T12:38:36.000283Z

thank you, will check that out

igrishaev 2018-07-16T12:39:31.000254Z

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

nathanmarz 2018-07-16T12:42:33.000137Z

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

nathanmarz 2018-07-16T12:43:35.000067Z

path uses inline caching to minimize runtime compilation of paths

igrishaev 2018-07-16T12:44:50.000223Z

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

nathanmarz 2018-07-16T13:02:44.000010Z

(path :foo :bar)

igrishaev 2018-07-16T13:18:13.000152Z

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

igrishaev 2018-07-16T13:18:40.000171Z

as a func argument in my case

nathanmarz 2018-07-16T13:20:06.000144Z

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

nathanmarz 2018-07-16T13:20:27.000515Z

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

igrishaev 2018-07-16T13:22:02.000063Z

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

igrishaev 2018-07-16T13:23:03.000245Z

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

nathanmarz 2018-07-16T13:37:35.000292Z

ultimately it's an optimization

nathanmarz 2018-07-16T13:37:54.000423Z

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

igrishaev 2018-07-16T13:40:50.000372Z

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

nathanmarz 2018-07-16T13:42:47.000421Z

with path

nathanmarz 2018-07-16T13:43:15.000199Z

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

igrishaev 2018-07-16T13:43:37.000148Z

oh, I see now, thanks