datalevin

fs42 2024-08-10T05:23:47.965059Z

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!

fs42 2024-08-10T05:34:47.680069Z

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

fs42 2024-08-10T05:37:09.587419Z

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" (?)

Huahai 2024-08-10T11:32:44.563739Z

it should throw instead

Huahai 2024-08-10T11:18:34.865629Z

Nil cannot be transacted

🚀 1
fs42 2024-08-10T15:53:49.875039Z

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?

fs42 2024-08-10T16:16:58.117879Z

Another option would be to implement a “:put key nil” as “:del key”, which would interpret the intend.

fs42 2024-08-10T16:58:34.733499Z

Please ignore as I noticed that you added an issue about it - should have checked first.

fs42 2024-08-10T17:07:24.024129Z

... and already fixed in the code!!! woohoo

Huahai 2024-08-10T11:30:32.756559Z

Basically, nil means absence

Huahai 2024-08-10T11:30:33.209839Z

E.g. when get-value returns nil, it means that key does not exist

Huahai 2024-08-10T11:30:33.603239Z

If it is possible to store nil as value, ambiguity would arise

🎯 2
fs42 2024-08-10T15:50:44.558509Z

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.

Huahai 2024-08-11T11:47:05.785069Z

Sure. PR welcome

Huahai 2024-08-11T11:50:32.504489Z

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

Huahai 2024-08-11T12:08:14.209129Z

In the kv context, it is possible to give nil a special value, if a persistent ordered map abstraction is something to be upheld

Huahai 2024-08-11T12:08:41.523739Z

You can file an issue

fs42 2024-08-11T15:25:33.774769Z

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.