When you transact-kv and :put a key with a nil value, it seems that it won't overwrite the key or do anything - no exception or error code. Not sure what the correct behavior should be (?). A clj map can have a nil value for a key... I tried to follow the code through the datalevin and lmdb source code, but I realized that is not an exercise for mere mortals... hat tip to the authors!
Ok... just noticed some unexpected behavior that may not be "correct": -- clj꞉datalevin-kvutils꞉> (d/get-range db "tst-dbi" [:all]) [] clj꞉datalevin-kvutils꞉> (d/transact-kv db [[:put "tst-dbi" 1 222 :data :data]]) nil clj꞉datalevin-kvutils꞉> (d/get-range db "tst-dbi" [:all]) [[1 222]] clj꞉datalevin-kvutils꞉> (d/transact-kv db [[:put "tst-dbi" 1 nil :data :data]]) nil clj꞉datalevin-kvutils꞉> (d/get-range db "tst-dbi" [:all]) [[1 222]] clj꞉datalevin-kvutils꞉> (d/transact-kv db [[:put "tst-dbi" 2 nil :data :data]]) nil clj꞉datalevin-kvutils꞉> (d/get-range db "tst-dbi" [:all]) [[1 222] [2 222]] clj꞉datalevin-kvutils꞉> (d/transact-kv db [[:put "tst-dbi" 3 nil :data :data]]) nil clj꞉datalevin-kvutils꞉> (d/transact-kv db [[:put "tst-dbi" 4 nil :data :data]]) nil clj꞉datalevin-kvutils꞉> (d/get-range db "tst-dbi" [:all]) [[1 222] [2 222] [3 222] [4 222]] clj꞉datalevin-kvutils꞉>
It seems that if you use assign nil to the value in :put, it will take the value from the last :put - probably left in a buffer that wasn't cleared or isn't overwritten by "nil" (?)
it should throw instead
Nil cannot be transacted
Right now it doesn’t transact nil but does transact the previous non-nil value. Would you like me to submit an issue on github about this?
Another option would be to implement a “:put key nil” as “:del key”, which would interpret the intend.
Please ignore as I noticed that you added an issue about it - should have checked first.
... and already fixed in the code!!! woohoo
Basically, nil means absence
E.g. when get-value returns nil, it means that key does not exist
If it is possible to store nil as value, ambiguity would arise
I appreciate your interpretation and implementation of nil as a value in the kv-db, but I’d just like to note that a clojure map does allow {:a nil} and there is no ambiguity as you can distinguish between the key :a being there or not. Now, I am not religious at all about this and just want to understand how the datalevin kv-store deals with that. I was looking at the kv-store as some kind of transactional mutable clojure sorted map… guess that abstraction is still mostly valid with a few caveats about the use of nil. I’d be happy to suggest a few lines of documentation for datalevin, to add a little more clarification about the use of nil.
Sure. PR welcome
The intent is to be consistent with datalog semantics, the “nil is absence” semantics is critical, a key point that makes datalog work, which I will explain in a blog post that hopefully will appear soon
In the kv context, it is possible to give nil a special value, if a persistent ordered map abstraction is something to be upheld
You can file an issue
Appreciate the consideration, but I do not have any use case for that - so please do not put any effort in that feature on my behalf. It was impressive how quickly you acknowledged & fixed that [:put dbi key nil] “unintended feature” and that should give datalevin users confidence that the code is in good hands! As I mentioned before, I tried to understand what datalevin was doing with nil’s and how the functionality compared to a clojure sorted map - there are so many features in the datalevin/lmdb interfaces that it’s difficult for a newbie like me to get a good picture - trying to “map” it to an abstraction I’m familiar with, like a clj’s sorted map, helps.