CBLite 2.0.0DB22: test if value of a given key is null


#1

By my understanding the implementation of Document.GetDate method always return a non-null value. That is: if I store a null value into a ducument and use document.GetDate(mykey) to retrieve the value, I will get back an instance of DateTimeOffset equals to DateTimeOffset.MinValue.

This way, how should I test if the value of a given key is null? Should I test document.GetDate(mykey) == DateTimeOffset.MinValue? This smells bad… Or should I convert my document .toMutable() before the test? This conversion is not a big cost to do a simple test? Thanks!


#2

DateTimeOffset is a struct value so it is not nullable. It doesn’t really make sense to check a non-nullable field for null. If you know that the field is a date, then check GetDate() to get the value. If you are unsure of the type of the key, then you need to think about how you are managing your data to allow multiple types on the same key. It’s your data, so you should know what is in it right? The library assumes you know what type of data you have and it tries to oblige you, so if you store an invalid value (null) for a given type (DateTimeOffset) that’s on you. You will get back basically the “best effort.” If you call GetInt() on the same null value you will not get null back either, but 0.

If you want to check to see if the document has a value for the given key or not you can use the Document.Contains() method. If you want to check to see if it is null or not it doesn’t make sense to do so with a type that can never be null.


#3

The documentation, as I understand it, says otherwise. It clearly states that a (new 2.0) feature of the typed accessors is that it will never return the wrong type. If the developer uses the wrong getter type, null is returned. Please see:
https://developer.couchbase.com/documentation/mobile/2.0/couchbase-lite/java.html#typed-accessors
The doc is not too clear, I might have read it wrong.
Please clarify.
Thanks
nat


#4

@jamiltz Could you update that for Java and C# to indicate “default value” instead of null

@natgross The documentation is a mistake. It is not possible to return null for struct values.


#5

Thanks @borrrden for your explanation.

In my use case I’m trying to check whether a record has ben edited / logically deleted or not. In this cases the properties “deleted_at” and / or “updated_at” existis in my documents. I’m managing my own deletion logic because in some cases I will need to query by deleted records.

So I think my options are basically:

  • Keep the “craeted_at” and “updated_at” keys and assumes that DateTimeOffset.MinValue is the non-existence of value.
  • Add the keys only when these events occur and use Document.Contains(). This way I’m assuming that when a given key exists the respective event has occurred.

#6

Yes . either of those two options should work.


#7

I was referring to getInt(), which can return a null Integer.
I wish the documentation was right! Because as you say, getInt() returns 0 (zero) which is false info AND NO WAY TO VERIFY if error, because the property might indeed store a true value of 0 (zero)!
If I setString(key, “abcd”), why should getInt(key) return 0?? It should be either null or an exception.

FURTHERMORE, listen to this; On the flip side, if you setInt(key, 1234) then try to getString(key) it DOES return null (which is great). [But] This is inconsistent. It should return an empty string (or “1234”) according to the aforementioned logic.
In order not to ‘shake up’ the api at this stage of the game, maybe we add methods that return Integer/Long/Double objects instead of primitives, then the developer can choose which getter to use.

-nat


#8

@borrrden Please see my last post.
To add to that. (My tests are all in Java.)
If you: setString(key, “Any String That Is Not An ISO Date”)
and then: getDate(key)
Result returned: null!


#9

If I setString(key, “abcd”), why should getInt(key) return 0?? It should be either null or an exception

The getInt(key) method returns a primitive data type so it shouldn’t be null. The getNumber(key) method can be used instead if want to differentiate between non-number and number data type.

For getInt(key), the API could either returns a default value (which is 0) or throw an exception. Throwing an exception or not is debatable, and each developer has a different preference. We decided to return a default value because:

  1. Throwing an exception is overkill for type getters. Most of the time, the data type is already known by developers or application.
  2. Throwing an exception is not preferable to some platforms especially Objective-C and Swift.
  3. Based on demand, we could also add another parameter for a default value to be returned in the future.
  4. There is a good precedence on this, please look at Parse.

On the flip side, if you setInt(key, 1234) then try to getString(key) it DOES return null (which is great). [But] This is inconsistent. It should return an empty string (or “1234”) according to the aforementioned logic.

Null should be a default value for object type.

In order not to ‘shake up’ the api at this stage of the game, maybe we add methods that return Integer/Long/Double objects instead of primitives, then the developer can choose which getter to use.

Developers can use getNumber(key) for this instead.


#10

getNumber(key) would work for me.
However as far as primitives defaulting to 0 goes, that was my point; why primitives? Yes I can use getNumber() but a function should never return incorrect data. In a situation where multiple developers are working on various facets of a system, suppose one developer inadvertently uses setString(“AmountPaid”, “995.90”) and then [possibly another developer] in another module of the system uses the same db, calls getDouble(“AmountPaid”) and gets 0! Without any warning or even possibility to know if the data is correct.
Again, folks can use getNumber(), ok. But still…

-nat

ps. I agree that exceptions are overkill and incur an overhead but I was talking from a ‘business logic’ perspective, SOMEONE has to know that if zero is real or an error!


#11

@pasin Problem: Boolean

  1. The primitive getBoolean() defaults to TRUE. (I think usually booleans default to false.)
  2. Main problem (per our discussion above): I don’t have an object equivalent with booleans as with numbers. So, I can’t get back - or check for - null.
    [Tested:]
    a. setString(key, “FALSE”)
    b. Boolean b = getString(key)
    Result: b = TRUE !

Just wondering… Is it humongous job to change api to return objects instead of primitives?
Now, I have not studied the 2.x source yet, and this might be unacceptable and far too complex regarding testing and side effects. But, if this idea is at all feasible, this might be the right time to do it. Before folks release apps using CBL 2.

Please excuse my “butting in” on various issues, but I want CBLite 2.x to be built like a tank!

Thanks;
-nat


#12

Boolean is somewhat special. The current behavior is this : Returns YES if the value exists, and is either true or a nonzero number. This behavior pretty much borrows from c or objective-c.

For the case of setString(key, “FALSE”) or setString(key, “TRUE”), even getBoolean(key) returns Boolean object, it will not return as true, it will return as NULL. We don’t have intention to try to determine the value based on a formatted string (except for JSON Date String when doing getDate(key)). So for this, please check if String is “FALSE” or “TRUE”. Alternatively storing the value as boolean or the number 1 and 0.


#13

NULL is exactly what it should return. Like getNumber() and getDate() which also return NULL. This way, I can check for NULL consistently across all getters.

(There is an old SQL philosophical debate ; What is NULL? Does it mean simply ‘nothing, no data’ or does it mean that the field has a value of NULL. Anyhow…)


#14

I was speaking about C# this whole time so that’s part of the reason we seem to be missing each other. C# does not have a Number class, and it is not possible to return a null value for a struct type. One of the goals is also to keep the API consistent across platforms. If we introduce this API into Java, what would C# do, and what would Objective-C do, etc?

No one has mentioned getValue() yet either, which is the sort of end-all stop for this discussion and it will return an untyped object. If this value is null then the value truly is a null or it is missing. The “casting” between types here happens down at the C++ level, which has no concept of a “null int.” So when you request to get the value of a certain type, the platform will forward that request to the native C++ library and say “I want this value as X” and the C++ library will both simultaneously deserialize and attempt to cast the value.

My belief is that all of this can be handled at the app level. If you are concerned about the case you mentioned with the two devs, you could have it so that your initial data object has a value of -1 (for example). That way, if it is 0, you will know something is amiss. Ultimately the paradigm here is that you are managing your data object, and you should know what type the key contains. Maybe that way of thinking is too abrasive? All we do is store the data you give us, and attempt to give it back as the type you request.

I appreciate your point about wanting to know immediately when you have requested the wrong type. I think that if we decide to do this in the end that it will not be a 2.0 thing though. We’ve already bulldozed over deadline after deadline and it’s getting out of control. I believe that this can be handled in later releases without altering the API though (either via additions, or implementation changes).


#15

I knew you were talking C# because we don’t have a struct in Java. (That is why I linked to the Java documentation I posted.) But the end-result, returning a min value (especially for Date) is what caught my attention. Hence my post, etc. [By the way the CBL Java api for getDate() does return NULL, unlike C#.]
As far as consistent cross-platform API, the Java api can simply have additional getters with slightly modified names that return Objects, so the dev can choose which getter to use.

As far as getValue() goes, as well as doing it myself at the app level - That’s exactly what I have been doing with 1.x! I barely finished a complete framework on top of cbl 1.x when 2.0 changed everything! (AND I didn’t complain!) So, since 2.0 is touting this new feature of typed accessors, I wanted to use it!

Anyhow, 2.x is a much better product and I will hopefully benefit from it as we go forward!

All the best.
-nat


#16

CBL Java api for getDate() does return NULL, unlike C#.

That’s because it is a class in Java, but a struct in C# :wink: