I tried to update a field in a document which was long integer. But it was updated to the value '14818435007969200' instead of '14818435007969199'.
db.getCollection('title').updateMany({},
{$set:{'skillId':[NumberLong(14818435007969199)]}})
db.getCollection('title').find({})
{
"_id" : ObjectId("5853351c0274072315da2426"),
"skillId" : [
NumberLong(14818435007969200)
]
}
Is there any solution? I am using robomongo 0.9.0.
The mongo shell treats all numbers as floating point values. So while using the NumberLong() wrapper pass the long value as string or risk the loss for precision or conversion mismatches.
This should work as expected.
db.getCollection('title').updateMany({},
{$set:{'skillId':[NumberLong("14818435007969199")]}})
Just to demonstrate for example.
So when converting 14818435007969199 to binary base 2 value you get 110100101001010100100111000010110001101111011110110000 which when converted back to base 10 is 14818435007969200
You can checkout the floating point arithmetic for more details.
here is an example with where condition in the query
db.CustomerRatibs.update(
{ custRatibId:'8b19bfdbac7b468b9c3edafc37ad5409' },
{ $set:
{
uAt : NumberLong(1536581726000)
}
},
{
multi:false
}
)
Related
My understanding is that NumberDecimal forces the 100th place regardless of numbers and I do see that in the aggregate pipeline but when I look at the raw json pure without mongodb types it reverts back to tenth place and cuts off the 2nd 0.
I have tried $round with 2 decimal places with original 2997 value:
db.getCollection("collection").aggregate([
{
$set:
{
"column_Decimal":
{
$round: [
{
$arrayElemAt:
["$column", 0]
},
2
]
}
}
}
])
and I see
"column_Decimal" : NumberDecimal("2997.00")
Then I switch the view to JSON Pure and it shows this:
"column_Decimal" : 2997.0
How do I avoid this happening and get 2997.00 regardless in Json Raw.
I tried to create index using
db.collection_name.createIndex({"field_name":1})
Then when I'm calling getIndexes() it gives me following results
{
"v" : 2,
"key" : {
"field_name" : 1.0
},
"name" : "field_name_1",
"ns" : "dbname.collection_name"
}
So I wonder why is it floating point "field_name" : 1.0 now? Is it bad? Should I even worry about it? Any way to make it exactly 1?
And out of curiosity: I've noticed I can even successfully call it that way:
db.collection_name.createIndex({"another_field_name":12345})
without it producing any errors. I wonder what's happening in this case.
Your question is actually a couple of questions, but the first does have a brief answer
Q: "Why am I getting a floating point?"
A: Because you are using robomongo, and the interface simply displays the supplied Number type in that way. The mongo shell actually displays this differently
And the second:
Q: "Why can I use 12345 as a value instead of just 1 or -1?"
A: Because its still actually numeric and valid. All MongoDB cares about here is "positive" or "negative". So where "positive" issuing a query that used the index would sort "ascending" by default. But you still would need to supply 1 or -1 to a specific .sort(), since that is all that would be valid.
To demonstrate the latter case, insert some data into your collection:
db.collection_name.insertMany(
[5,1,3].map( v => ({ another_field_name: v }) )
)
And create your index:
db.collection_name.createIndex({ "another_field_name": 12345 })
If you issue a range query, the "ascending" order is used by the "positive" value:
db.collection_name.find({ "another_field_name": { "$gt": 0 } },{ "_id": 0 })
{ "another_field_name" : 1.0 }
{ "another_field_name" : 3.0 }
{ "another_field_name" : 5.0 }
This shows the order of the index being applied even though the actual insertion of values was in a different order. So the index is being clearly applied here.
If you tried to explicitly .sort() using any other value than 1 or -1 on this type of index, then that would produce an error. But of course this would result in "ascending" or "descending" sort respectively, as MongoDB will happily reverse the order of traversal for the index.
If you removed the index and created one using a "negative" value:
db.collection_name.dropIndexes();
db.collection_name.createIndex({ "another_field_name": -54321 });
And then issued the same query:
db.collection_name.find({ "another_field_name": { "$gt": 0 } },{ "_id": 0 })
{ "another_field_name" : 5.0 }
{ "another_field_name" : 3.0 }
{ "another_field_name" : 1.0 }
Then the "descending" order is applied because that is essentially what you told it to do in default handling.
Is this good or bad overall? From a storage point of view it really does not matter, as no matter the actual value presented a BSON Double is still a BSON Double.
You could alternately use NumberInt for a specific 32-bit value as opposed to a 64-bit value as specified in BSON Types, but again the value being 1 or 65,000 or in the reverse as -1 or -65,000 does not change the allocated storage or the basic handling of where it is "postive" or "negative".
For general readability and consistency with arguments to .sort() then as a "matter of opinion", using 1 and -1 is more consistently understood for it's intended purpose.
It is actually the "preferred" implementation as to the specification, and is somewhat echoed in the documentation ( though not to prominently ):
Some drivers may specify indexes, using NumberLong(1) rather than 1 as the specification. This does not have any affect on the resulting index.
I have a collection in MongoDB which makes an increase, the field was initially defined as an Integer, but I find that after the increase was converted to double.
But then I make an update of the document and see that changes to Long.
Is there any way to block these changes in Mongo?
Thanks in advance
Since MongoDB doesn't have a fixed schema per collection, there's no way to prevent such changes on the database side. Make sure that you use the same data type for the field everywhere, including its update operations. The C# driver is pretty smart about this.
Be careful when working with the shell, it can be irritating. Per default, the mongo shell will treat every number as a double, e.g.:
> db.Inc.find().pretty();
{ "_id" : 1, "Number" : 1000023272226647000 }
// this number is waaayyy larger than the largest 32 bit int, but there's no
// NumberLong here. So it must be double.
> db.Inc.update({}, {$inc: {"Number" : 1 }});
> db.Inc.find().pretty();
{ "_id" : 1, "Number" : 1000023272226647000 }
// Yikes, the $inc doesn't work anymore because of precision loss
Let's use NumberLong:
> db.Inc.insert({"Number" : NumberLong("1000023272226647000")});
> db.Inc.update({}, {$inc: {"Number" : 1}});
> db.Inc.find();
{ "Number" : 1000023272226647000, "_id" : 1 }
// Yikes! type conversion changed to double again! Also note
// that the _id field moved to the end
Let's use NumberLong also in $inc:
> db.Inc.insert({"Number" : NumberLong("1000023272226647000")});
> db.Inc.update({}, {$inc: {"Number" : NumberLong("1")}});
> db.Inc.find();
{ "_id" : 1, "Number" : NumberLong("1000023272226647001") }
// This actually worked
In C#, both of the following updates work, Number remains a long:
class Counter { public long Number {get;set;} public ObjectId Id {get;set;} }
var collection = db.GetCollection("Counter");
collection.Insert(new Counter { Number = 1234 });
collection.Update(Query.Null, Update<Counter>.Inc(p => p.Number, 1)); // works
collection.Update(Query.Null, Update.Inc("Number", 1)); // works too
MongoDB is schema-less. Schamalessness provides for easier changes in your data structure but at the cost of the database not enforcing things like type constraints. You need to be disciplined in your application code to ensure that things are persisted in the way you want them to be.
If you need to ensure that the data is always of type Integer then it's recommended to have your application access MongoDB through a data access layer within the application. The data access layer can enforce type constraints (as well as any other constraints you want to put on your objects).
Short answer: There is no way to enforce this in MongoDB.
I am attempting to calculate the total amount of money spent being tracked inside of our database. Each order document contains a field "total_price"
I am attempting to use the following code:
db.orders.aggregate({
$group: {
_id: null,
total: {$sum: "$total_price"}
}
})
Unfortunately, the only output I get is: { "result" : [ { "_id" : null, "total" : 0 } ], "ok" : 1 }
But to verifiy there is actually numerical data stored, and just not being totaled: db.orders.find()[0].total_price this results in 8.99
Any help would be greatly appreciated. I have very little experience using MongoDB. I've only covered the basics at this point.
Thank you in advance.
$sum only works with ints, longs and floats. Right now, there is no operator to parse a string into a number, although that would be very useful. You can do this yourself as is described in Mongo convert all numeric fields that are stored as string but that would be slow.
I would suggest you make sure that your application stores numbers as int/long/float, and that you write a script that iterators over all your documents and updates the value. I would also suggest that you add a feature request at https://jira.mongodb.org/browse/SERVER to add an operator that converts a string to a number.
When I have two MongoDB documents like this...
db.test.insert( {"value" : "10123"} );
db.test.insert( {"value" : "160"} );
The result of a query like:
db.test.find({"value" :{$gt : "12"} });
is..
{ "_id" : ObjectId("4c6d1b92304326161b678b89"), "value" : "160" }
It's obvious, that a string comparison is made, so that my first value is not returned.
Is there any way to cast within the query?
Something like:
db.test.find({ (int) "value" :{$gt : 12} });
would be great. A query like
db.test.find({"value" :{$gt : 12} }); // without the quotes around "12"
returns nothing.
You can use the following JavaScript expression:
db.test.find("this.value > 12")
This uses JavaScript's automatic conversion from string to number.
I have a similar workaround, i find that if you can use the mongo shell, you can write an statement to do this in javascript, but capable of using indexes.
var myItems = []
var it = db.test.find({},{value:1})
while (it.hasNext()){
var item = it.next();
if(parseInt(item.value) > 12)
myItems.push(item);
}
If you want this to run faster than previus solution, you have to ensure the index on the value field.
Type casting in MongoDB is available after version >= 4.0. Check MongoDB's aggregation operator $convert and similar operators. Since you wanted to convert string to int you can use $toInt:
db.collection.find({ $expr: { $gt: [ { $toInt: "$value" }, 12 ] } })
Test : mongoplayground
Note :
Here we're converting value which is a string field to int on the fly & Since it got converted to int - we're comparing it to input of type int. Your output documents will still have original type for value field which is string (we're not actually changing type in response docs, if needed use aggregation & it's stage $project to see int values for field value in response docs).
Since we're using aggregation operators in .find() we need to wrap everything in $expr.
Even though this is pretty common nowadays & is easy to do, but remember we're casting string to int on every Read, rather if you can take care of this on Writes or Updates it would be easy & more efficient.
To convert String into int use this
db.test.find({'year': {$type: 2}}).forEach(
function (x) {
x.value=new NumberInt(x.value);
db.test.save(x)}
)
And after that you can directly query like :
db.test.find({"value" :{$gt : 12} });
$gt wasn't set for this use case. You would have to use regex for strings. Probably easier to just create a new field with the number as a number.