[SERVER-15256] Why I can't use parseInt on NumberLong but arithmetics can? Created: 15/Sep/14  Updated: 03/Dec/14  Resolved: 03/Dec/14

Status: Closed
Project: Core Server
Component/s: JavaScript
Affects Version/s: 2.4.9, 2.6.3
Fix Version/s: None

Type: Question Priority: Major - P3
Reporter: Paulina Budzo? Assignee: Ramon Fernandez Marina
Resolution: Done Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Participants:

 Description   

For some reason, using parseInt or parseFloat on NumberLong gives NaN, but using arithmetic operations converts them to ints/floats without issues?

See the following:

> parseInt(NumberLong(2))
NaN
> parseFloat(NumberLong(2))
NaN
> NumberLong(2) + NumberLong(0)
2 <-- this is an int, not NumberLong

So this obviously works:

> parseInt(NumberLong(2) + NumberLong(0))
2

Tested on Mongo 2.4.9 and 2.6.3. This seems like a completely illogical behaviour. This became important since PHP driver converts every number to NumberFloat.



 Comments   
Comment by Jeremy Mikola [ 03/Dec/14 ]

PHP driver saves all ints as NumberLong in mongo (since a few versions ago).

paulina: I assume you're referring to mongo.native_long, whose default value was changed to true in 1.5.0? Since the setting has no storage impact on 32-bit platforms, I assume your PHP environment is 64-bit.

When this setting is true on a 64-bit platform, PHP integers will be serialized as 64-bit integers in BSON (type 0x12 in the BSON spec). When the setting is false, which you may have been relying on in 1.4.x of the driver if your INI never specified a value, the PHP integers are serialized as 32-bit integers in BSON (type 0x10 in the spec), which may result in a loss of precision.

The issue is complicated because the JavaScript shell appears to unserialize 32-bit integers in BSON as floating point numbers, which you referred to as "normal ints." 64-bit integers in BSON are converted to NumberLong objects. While converting 32-bit integers to floating point numbers makes it convenient to interact with the values from the JavaScript shell, it makes it difficult to infer what the original BSON type was.

The following PHP script (using driver 1.5.7) and bsondump output should clarify things:

<?php
 
$m = new MongoClient();
$c = $m->test->foo;
$c->drop();
 
ini_set('mongo.native_long', false);
 
$c->insert(['_id' => 1]);
$c->insert(['_id' => 2.0]);
 
ini_set('mongo.native_long', true);
 
$c->insert(['_id' => 3]);
$c->insert(['_id' => 4.0]);

I then dumped the test.foo collection to disk and dumped its output with debug information so we can inspect the BSON types of each _id value:

$ mongodump -d test -c foo
2014-12-03T16:20:41.444-0500	writing test.foo to dump/test/foo.bson
2014-12-03T16:20:41.445-0500	writing test.foo metadata to dump/test/foo.metadata.json
2014-12-03T16:20:41.448-0500	done dumping test.foo
 
$ bsondump --type=debug dump/test/foo.bson 
--- new object ---
	size : 14
		_id
			type:   16 size: 9
--- new object ---
	size : 18
		_id
			type:    1 size: 13
--- new object ---
	size : 18
		_id
			type:   18 size: 13
--- new object ---
	size : 18
		_id
			type:    1 size: 13

For the first two documents, we can see that _id they were serialized as a 32-bit integer (type 0x10) and float (type 0x01), respectively, when mongo.native_long was false on my 64-bit system. With the second pair of documents, we see that the PHP float was against serialized as a BSON float, but the PHP integer became a 64-bit BSON integer (type 0x12).

Querying for these documents in the shell confirms that 32-bit BSON integers are converted to JavaScript floating point numbers, and that only 64-bit BSON integers are left as-is (i.e. NumberLong):

$ mongo
MongoDB shell version: 2.8.0-rc1
connecting to: test
> db.foo.find()
{ "_id" : 1 }
{ "_id" : 2 }
{ "_id" : NumberLong(3) }
{ "_id" : 4 }

Comment by Paulina Budzo? [ 31/Oct/14 ]

It becomes a problem with PHP driver because doing this:

(new Mongo())->test->test->insert(["test"=>1]);

results in this in mongo:

> db.test.find()
{ "_id" : ObjectId("5453c64423dfe4cc1800834d"), "test" : NumberLong(1) }

I.e. : PHP driver saves all ints as NumberLong in mongo (since a few versions ago).

Problems starts when you're trying to use those values as keys for mapReduce jobs - NumberLong and "normal" int are treated as different values.

Comment by Ramon Fernandez Marina [ 31/Oct/14 ]

NumberLong is a shell wrapper to handle 64-bit integers, and returns an object:

> typeof(NumberLong("2"))
object

parseInt() returns NaN because parseInt() takes a string as a first argument.

Can you please elaborate on how is this related to the PHP driver and why this is an issue?

Comment by Paulina Budzo? [ 31/Oct/14 ]

This seems to be the easiest way to convert NumberLong into int:

> NumberLong(10) + 0
10 <- int

Comment by Paulina Budzo? [ 17/Oct/14 ]

Any update on this please?

Generated at Thu Feb 08 03:37:28 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.