MongoDB update outter and array-embedded document atomically - mongodb

I have an object in the cart collection:
{
_id: 1,
purchased: 0,
items: [
{
item_id: 5,
count: 0
},
{
item_id: 6,
count: 0
},
]
}
And I would like to atomically increment both purchased and count of item_id: 6. I believe the correct update command is:
db.carts.update({_id: 1, "items.item_id": 6},
{
$inc: {
purchased: 1
"items.$.count": 1
}
})
This seems to work in the console but I am not sure if this is 100% correct. Can anyone spot any issues with this update command or confirm this is correct for my use case?
For scaling this to my app, suppose _id is an ObjectId and item_id is also ObjectId (ie they are unique).

Related

Limit array size inside a document of Mongodb collection [duplicate]

I have a document that lists post item ids for an author separated out by topic. This results in a document such as the following:
{
_id: "sdkafjsadkfjads3023",
Author: "SomeGuy"
RecentPosts: {
"topic-1": {
Count: 4,
Posts: ["postitemid1","postitemid2","postitemid2","postitemid3"]
}
"topic-2": {
Count: 3
Posts: ["postitem5","postitem6","postitem8"]
}
}
}
Most of the time I am doing atomic pushes to each of these post arrays in the same update. What I want to do is limit the arrays above to 10 items at all times. This way, anytime I do a pushall to the same topic/posts. Is what I'm asking even possible, or should I do this a different way?
Thanks in advance
If I understand correctly Capped Collections are what you want.
http://www.mongodb.org/display/DOCS/Capped+Collections
As it turns out, this was a longstanding issue in MongoDB that was since added in MongoDB 2.4 release using the $slice operator.
db.students.update(
{ _id: 1 },
{ $push: { scores: { $each : [
{ attempt: 3, score: 7 },
{ attempt: 4, score: 4 }
],
$sort: { score: 1 },
$slice: -3
}
}
}
)
http://docs.mongodb.org/manual/tutorial/limit-number-of-elements-in-updated-array/

Find query don't limit fields

I got the following query:
query =
{
application: application,
creationTime:{
$gte: start_date.valueOf(),
$lte: end_date.valueOf()
}
},
{
application: 1,
creationTime: 1,
buildSystem: 1,
_id:1
}
var ut_data = db.my_collection.find(query).sort({ _id: -1 }).forEach(function(doc) {print(doc.testStatus)})
Where I want to limit the fields in the result to application, creationTime and buildSystem, to not load the whole documents matching the condition.
Once I print testStatus, it seems like is also available, moreover all fields are available. How can I limit the fields in the result?
(I also tried: {fields:{application: 1, creationTime: 1, buildSystem: 1, _id: 1}} as proposed in Limit Field in Mongodb find() query not working
)
I was passing the two parameters of the query wrongly, so the second part was not considered, I fixed it like this:
query =
[{
application: application,
creationTime:{
$gte: start_date.valueOf(),
$lte: end_date.valueOf()
}
},
{
_id: 1,
application: 1,
creationTime: 1,
buildSystem: 1,
}]
And then, passing each argument separately:
db.my_collection.find(query[0], query[1]).sort({ _id: -1 }).forEach(function(doc) {print(doc.testStatus)})
Or simply passing the query directly to the find method (w/o variables).

mongodb: add an attribute to a subdocument

Given a collection of Users:
db.users.insertMany(
[
{
_id: 1,
name: "sue",
points: [
{ points: 85, bonus: 20 },
{ points: 85, bonus: 10 }
]
},
{
_id: 2,
name: "bob",
points: [
{ points: 85, bonus: 20 },
{ points: 64, bonus: 12 }
]
}]);
How do I add an attribute bonus_raw in every points, with a copy of the value of bonus value? I tried:
db.getCollection('users').update({ },
{$set:{ 'points.$.bonus_raw' : 'points.$.bonus' }}, false, true)
but I get:
The positional operator did not find the match needed from the query. Unexpanded update: points.$.bonus_raw
Updating multiple items in an array is not possible as of now in MongoDB.
To get this done, you will have to query the document, loop over all of your nested documents, and then save it back to MongoDB.
In your case, this can help:-
db.users.find({points: { $exists: true } }).forEach(function (doc){
doc.points.forEach(function (points) {
points.bonus_raw = points.bonus;
});
db.users.save(doc)
});
Also, take care of race conditions while doing an update in this way. See this

How to sort in a specific order of a key in MongoDB

Good day. I have the following documents.
{
id: 1,
status: "sleeping"
}
{
id: 2,
status: "eating"
},
{
id: 3,
status: "drinking"
},
{
id: 4,
status: "drinking"
},
{
id: 5,
status: "sleeping"
},
{
id: 6,
status: "studying"
},
...
How do I sort them by status with the following order? sleeping, drinking, eating, studying
I'm using Angular-Meteor. And I'm trying to publish a collection with a limit in the server since I'm dealing with a very large data set.
I was looking into aggregate which I think is the right solution for this case but Meteor doesn't support it.
Well, I ended up with attaching a number to sort the the documents in the following order: sleeping, drinking, eating, studying
{
id: 1,
status: "sleeping",
statusOrder: 0
}
{
id: 2,
status: "eating",
statusOrder: 2
},
{
id: 3,
status: "drinking",
statusOrder: 1
},
{
id: 4,
status: "drinking",
statusOrder: 1
},
{
id: 5,
status: "sleeping",
statusOrder: 0
},
{
id: 6,
status: "studying",
statusOrder: 3
},
...
It was easier, quite frankly.
But if you are interested with using MongoDB group for your app, I found something that might help. Link here.
The mongodb aggregation framework is available on the server side via the monbro:mongodb-mapreduce-aggregation package (for example). You can use this in your publication.
Another approach is to denormalize your data somewhat and include the sort key in each document. That involves a typical storage vs. speed tradeoff.
MongoDb just only support sort following A -> Z, Z -> A, 0 -> 9, 9 -> 0
Specify in the sort parameter the field or fields to sort by and a value of 1 or -1 to specify an ascending or descending sort respectively.
Example:
db.orders.find().sort( { "status": -1 } )

Trouble updating a specific subdocument with MongoDB

My document looks like:
{
_id: ObjectId("52d317d7b5c4960000587cd4"),
txid: "7e621eeb02874ab039a8566fd36f4591e65eca65313875221842c53de6907d6c",
vin: [
{
_id: ObjectId("52d317d7b5c4960000587ce9"),
meta_address: "321",
meta_amount: 50,
sequence: 4294967295,
txid: "6749762ae220c10705556799dcec9bb6a54a7b881eb4b961323a3363b00db518",
vout: 0
},
{
_id: ObjectId("52d317d7b5c4960000587ce8"),
sequence: 4294967295,
txid: "c04c413576307737f3ad48efe5d509ebc883e1d04822b3a2eccf6a80a4482932",
vout: 0
},
{
txid: "72d4fc43ac576a4b2f1f35e1b310a2d83a1012a36fdc7813ec237646950233cf",
vout: 0,
sequence: 4294967295,
_id: ObjectId("52d317d7b5c4960000587ce7")
}
]
}
My Query is:
{ txid: '7e621eeb02874ab039a8566fd36f4591e65eca65313875221842c53de6907d6c',
'vin.txid': 'c04c413576307737f3ad48efe5d509ebc883e1d04822b3a2eccf6a80a4482932',
'vin.vout': 0 }
and the update is:
{ 'vin.$.meta_address': '321',
'vin.$.meta_amount': 50 }
But when I run it, it updates the first item in the vin array instead of the second. Now, oddly enough, if I change the query to:
{ txid: '7e621eeb02874ab039a8566fd36f4591e65eca65313875221842c53de6907d6c',
'vin.txid': 'c04c413576307737f3ad48efe5d509ebc883e1d04822b3a2eccf6a80a4482932'}
then it works fine. I think the problem is that my query looks for 2 elements in the vin, but I need to search by both. What am I doing wrong?
To get the $ in your update to identify the element that matches both properties in your query, you need to use $elemMatch in your query object:
{ txid: '7e621eeb02874ab039a8566fd36f4591e65eca65313875221842c53de6907d6c',
vin: {$elemMatch: {
txid: 'c04c413576307737f3ad48efe5d509ebc883e1d04822b3a2eccf6a80a4482932',
vout: 0
}}}