Inserted a json got $numberLong back in the returned json

Hi, I am new to mongo and I love the fact I could use it without knowing much! I am storing a json and it has data like:

"FI_CLOB_ORDER_RATE_LIMIT_DUPLICATE_CHILD": {
      "orderLimitName": "Duplicate_Child_Order",
      "windowSize": 1000,
      "rateLimit": 100,
      "actionEnumKey": "FI_CLOB_ORDER_RATE_LIMIT_DUPLICATE_CHILD"
    },
    "FI_CLOB_ORDER_RATE_LIMIT_CHILD_NOTIONAL": {
      "orderLimitName": "Child_Notional",
      "windowSize": 1000,
      "rateLimit": 20000000000.0,
      "actionEnumKey": "FI_CLOB_ORDER_RATE_LIMIT_CHILD_NOTIONAL"
    }

When I retrieve it I get:
"FI_CLOB_ORDER_RATE_LIMIT_DUPLICATE_CHILD": {
      "orderLimitName": "Duplicate_Child_Order",
      "windowSize": 1000,
      "rateLimit": 100,
      "actionEnumKey": "FI_CLOB_ORDER_RATE_LIMIT_DUPLICATE_CHILD"
    },
    "FI_CLOB_ORDER_RATE_LIMIT_CHILD_NOTIONAL": {
      "orderLimitName": "Child_Notional",
      "windowSize": 1000,
     "rateLimit": {"$numberLong": "20000000000"}
      "actionEnumKey": "FI_CLOB_ORDER_RATE_LIMIT_CHILD_NOTIONAL"
    }

As you can see the second rateLimit is returned with $numberLong. Is there a way to get simply the original json back?

My java mongo driver is 3.11.0

Any help will be greatly appreciated.

Hi @Navin_Jha and welcome in the MongoDB Community :muscle: !

The current version of the MongoDB Java driver is 4.2.3. Please make sure to use the correct version of the driver and also not the legacy one. But this won’t solve this “issue”.

Your number is 20 billions. It’s greater than 2,147,483,647 which is the maximum positive value for a 32-bit signed binary integer. The only way for MongoDB (or any computer for that matter) to store this value is in a 64-bit integer == a long.

MongoDB is a BSON database. So it’s capable to handle basic JSON… But also more complex data types like dates, decimal128, … and longs that JSON can’t handle.

So the reason you get a long back using Java, it’s because Java is kind enough to transform automatically for you your 20000000000 into a long and avoid an integer overflow. And I guess you probably have a warning in your code that says that you should actually type 20000000000L instead.

Cheers,
Maxime.

Hi Maxime,

Thanks for the prompt response.

Driver updates happen at a slow pace in large firms as you know.

The data is kept in json files that get loaded to mongo. When retrieved from mongo it is sent to consumers as json. So ideally I would like to keep json intact.

Is there a way to tell mongo:

I am sending
“rateLimit”: 20000000000

please give me back the same in in the returned json and not
“rateLimit”: {"$numberLong": “20000000000”}

I simply do document.toJson() when retrieving

You are sending 20000000000L, not 20000000000. You are sending a long, it’s stored in MongoDB as a long so it’s returned as a long.

It’s not a warning actually, it doesn’t even compile if you try to send a number larger than MAX_INTEGER.

This compiles though:

coll.insertOne(new Document("integer", 20).append("long", 20000000000L));

You can hack the final string that is returned by toJson() before you are sending it, but I’m not even sure this is legit JSON that you are sending in the end.

If you want the same experience for integers and longs, maybe you could use their respective string values instead? With this solution, I’m sure the JSON is actually legit and it’s your client’s problem to deal with the parsing of this value into the right type.

I’m now trying this with Java 4.2.3

import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoCollection;
import org.bson.Document;

public class LongsAreLong {
    public static void main(String[] args) {
        try (MongoClient mongoClient = MongoClients.create("mongodb://localhost/test")) {
            MongoCollection<Document> coll = mongoClient.getDatabase("test").getCollection("coll");
            coll.drop();
            coll.insertOne(new Document("integer", 20).append("long", 20000000000L));
            System.out.println(coll.find().first().toJson());
        }
    }
}

Output:

{"_id": {"$oid": "60bf8bba9e723258bdc82f04"}, "integer": 20, "long": 200000000000000000}

Sooo apparently 200000000000000000 IS a legit “number” value for a JSON file and it’s also the default behaviour for toJson().

Here is my doc in Compass where we can clearly see the types:

image

Cheers,
Maxime.

1 Like

Thank you writing a test code for me! Something I should have done right away instead of dealing with an elaborate unit test.

So I did the same.

I insert:

String jsonString = "{  "product": "2Y", "buyLimit": 1000000, "sellLimit":  20000000000 , "primaryRatio": 100 }"

using

Document document = Document.parse(jsonString );
document.put("_id", "somekey"));
mongoCollection.insertOne(document);

I read like this:

documents = mongoCollection.find();
String jsonStringBack = document.toJson();

I get back:

{  "product": "2Y", "buyLimit": 1000000, "sellLimit": {"$numberLong": "20000000000"}, "primaryRatio": 100 }

I understand your argument for int versus long but shouldn’t I get back the same as I sent?

Any other solution besides sending data as string e.g. “sellLimit”: “20000000000” ?

Thanks Maxime.

This one works! what is the reason?

System.out.println(parse.toJson(JsonWriterSettings.builder().outputMode(JsonMode.RELAXED).build()));  -
{ “product”: “2Y”, “buyLimit”: 1000000, “sellLimit”: 20000000000, “primaryRatio”: 100 }

This one print $numberLong

System.out.println(parse.toJson());
{ “product”: “2Y”, “buyLimit”: 1000000, “sellLimit”: {"$numberLong": “20000000000”}, “primaryRatio”: 100 }
1 Like

Nice. I didn’t know that RELAXED mode. But looks like this is the default behaviour in 4.2.3 because I didn’t get the $numberLong in my example. Update :wink: !

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.