Comments (11)
Wow that is tricky. So -1 gets resolved to 2 first, and then it conflicts with upsert in a second clause.
This won’t work in current design, because in DS every transaction clause is executed one after another. Each intermediate DB state is materialized in between. So it’s not possible to “undo” first clause or change tempid binding. It allows us to have intermediate DBs passed into db.fn/call
. If you add some data and then has db.fn/call
in the same transaction, fn’ll see that data. Also, if you insert new record with new unique attribute, you can use lookup refs to it in the same transaction.
This is different from Datomic. Datomic first resolves all tempids and calls all transaction functions, passing the same db-before value to all of them. So later transaction clauses cannot see results of earlier transaction clauses, and sometimes you have to split your transaction to several clauses to get around that. But as a benefit you can re-decide tempid bindings.
This is more like a design issue here. I’m currently happy with DS design, as it has its benefits and more intuitive in my opinion. If this is a serious issue for you, please elaborate more. Right now it looks like it can be easily avoided.
from datascript.
I can't say for sure yet whether it's a serious issue, still working through some edge cases. Making the outcome order-dependent is going to cause other problems, though. Here's an example resolving tempids with refs where changing the order will result in an exception:
(deftest test-upsert
(let [db (d/db-with (d/empty-db {:name { :db/unique :db.unique/identity }
:friend { :db/type :db.type/ref }})
[{:db/id 1 :name "Ivan"}
{:db/id 2 :name "Petr"}])
touched (fn [tx e] (into {} (d/touch (d/entity (:db-after tx) e))))]
;; This test passes
(testing "upsert with references"
(let [tx (d/with db [{:db/id -2 :name "Petr"}
{:db/id -1 :name "Ivan" :friend -2}])]
(is (= {:name "Ivan" :friend {:db/id 2}} (touched tx 1)))
(is (= {:name "Petr"} (touched tx 2)))))
;; Changing the order causes it to fail with
;; {:error :transact/upsert, :attribute :name, :entity {:db/id 3, :name Petr}, :datom #datascript/Datom [2 :name Petr 536870913 true]}
(testing "upsert with references, reversed order of txes, fails"
(let [tx (d/with db [{:db/id -1 :name "Ivan" :friend -2}
{:db/id -2 :name "Petr"}])]
(is (= {:name "Ivan" :friend {:db/id 2}} (touched tx 1)))
(is (= {:name "Petr"} (touched tx 2)))))))
from datascript.
Well, output is order-dependent anyway (you can’t swap additions and retractions for example). In case of tempids, we can sort it out even in the current model, actually. Just need to add some backtracking. Don’t expect it soon though :)
from datascript.
@tonsky Your explanation of clause ordering and tempid resolution makes sense. But do you plan to fix the issue that upserts always fail when in vector form, regardless of clause ordering?
Currently:
(d/with db [{:db/id -1, :id "foo"}]) ;; succeeds
(d/with db [[:db/add -1 :id "foo"]] ;; fails
Is that the intended behavior?
from datascript.
@levand it does not fails, there’s plenty of :db/add -1
in the test base: https://github.com/tonsky/datascript/search?utf8=✓&q=%3Adb%2Fadd+-1
It only causes problems when -1
is used several times during same tx, and it is forced to be resolved to different values (e.g. first it allocates new id, then it is resolved via upsert attribute, etc).
Yes, I’m going to fix this, though cannot promise you exact timeline
from datascript.
It does fail, if :id
is :db.unique/identity
and there already exists a datom with the same attribute (in other words, the 1-clause transaction is just re-asserting something already in the DB).
The map form does an upsert, the vector form throws a uniquness exception.
I'll see if I can get you a PR with a test case later today.
from datascript.
Oh. Can you check with Datomic? It might be intentional.
On Thu, Aug 27, 2015 at 6:51 PM Luke VanderHart [email protected]
wrote:
It does fail, if :id is :db.unique/identity and there already exists a
datom with that ID.The map form does an upsert, the vector form throws a uniquness exception.
I'll see if I can get you a PR with a test case later today.
—
Reply to this email directly or view it on GitHub
#76 (comment).
from datascript.
Test case: #109
This works fine in Datomic.
from datascript.
Thanks! You’re right, upserts are not resolved in vector form at all :( Will fix
from datascript.
I know this issue has been closed, but I wanted to throw in my two cents regarding the design question here: Datomic's behavior makes a lot more sense to me: 1) order-independent id unification and 2) transaction before/after dbs rather than database function before/after dbs. As it is now, hashing order can cause random uniqueness failures if you try to batch add a few maps, where datomic will happily accept the transaction with last-written wins. If your data is internally consistent, such as the same entity twice in the result of a d/pull, then all the writes are the same.
from datascript.
This test fails, but passes if you swap the order of keys in the map. I'll try to see if I can fix it.
(deftest test-ref-upsert
(let [db (d/empty-db {:name {:db/unique :db.unique/identity}
:ref {:db/valueType :db.type/ref}})]
(are [tx res] (= res (tdc/all-datoms (d/db-with db tx)))
[(array-map :ref {:name "Ivan"} :name "Ivan")]
#{[1 :name "Ivan"]
[1 :ref 1]})))
from datascript.
Related Issues (20)
- Cannot compare xxx to xxx HOT 4
- Difference in pull query output between 1.0.1 and 1.3.11 HOT 3
- :db/cas should take a function HOT 3
- Nil values when building clojurescript for chrome extension HOT 3
- equality on ^Entity bypasses db HOT 3
- Improve Skypack "Package Score" for better devX HOT 2
- Attribute of composite tuple starting with colon is not working in js HOT 1
- How to hotfix aot code ?
- How to hot fix aot code?
- Rules - Required bindings doesn't evaluate correctly HOT 1
- Datascript101 link is dead HOT 1
- `get-else` fails when passed a lookup.
- `entity` returns entities for ids that don't exist HOT 2
- Is there any doc to demo idiom way to implement pagination in datascript? HOT 4
- Upsert of datom with composite tuple containing lookup ref fails HOT 5
- Recursive reverse lookups not working quite like it should HOT 1
- Lookup ref in composite tuple during upsert HOT 1
- do we have double vector type? HOT 2
- `:xform` is not called on ref attributes HOT 2
- Query performance with rules is much worse than "equivalent" inline clause HOT 3
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from datascript.