How do I move data using update in MongoDB? - mongodb

Assuming I have a document...
{
"_id" : ObjectId("5c4cc127c33477ec8841d317"),
"review_date" : "2019-01-26T04:07:43.345Z",
"comment" : "decent product",
"score" : 4,
"reviewer_id" : "barry"
}
and I want to update it to be...
{
"_id" : ObjectId("5c4cc127c33477ec8841d317"),
"review_date" : "2019-01-26T05:15:13.122Z",
"comment" : "awesome product",
"score" : 5,
"reviewer_id" : "barry",
"history" : [
{
"review_date" : "2019-01-26T04:07:43.345Z",
"comment" : "decent product",
"score" : 4
}
]
}
How would I change this?
In other words, move/copy some of the fields to another area of the document, and replace some existing fields with new values. The purpose is to store history of change within the same document as an array - could be many review changes.

What you plan on doing is add history. You can do this by using db.collection.findOneAndUpdate with $set in place.
An example with a collection name product would be:
db.products.findOneAndUpdate({ _id: ObjectId("5c4cc127c33477ec8841d317") }, {
$set: {
score: 5,
history: [{
"review_date" : "2019-01-26T04:07:43.345Z",
"comment" : "decent product",
"score" : 4
}]
}
})

Related

Update multiple subdocument without exact sub document level condition

I have collection with the following document:
{
"_id" : ObjectId("56cbf9cd75de3ee9057b23c7"),
"title" : "Abc",
"comments" : [
{
"_id" : "",
"message" : "",
"active" : 1,
"status" : 1,
},
{
"_id" : "",
"message" : "",
"active" : 0,
"status" : 1,
},
{
"_id" : "",
"message" : "",
"active" : 1,
"status" : 1,
}
],
}
{
"_id" : ObjectId("553744ae75de3eb9128b4568"),
"title" : "Jhon",
"comments" : [
{
"_id" : "",
"message" : "",
"active" : 1,
"status" : 1,
}
]
}
I need to update all status of comments as 0 where comments.active=1. I tried to solve it as below, but only first record of the comments is updated. Please help me..
db.comments.update({'comments.active':1},{$set:{'comments.$.status':0}})
Per this issue New operator to update all matching items in an array, currently there is no operation to do that in mongodb. Feel so sad, this issue lasts for 6 years.
There could be one work around in mongo shell as below.
> db.comments
.find({})
.forEach(function(doc) {
doc.comments.map(function(c) {
if (c.active == 1) {
c.status = 0;
}
});
db.comments.update(
{_id: doc._id},
{$set: {comments: doc.comments}});
});

Returning only sub document based on subdocument _id which is the items of array field of the mongodb document [duplicate]

This question already has answers here:
Retrieve only the queried element in an object array in MongoDB collection
(18 answers)
Closed 4 years ago.
My schema goes like this
[
{
"_id" : ObjectId("565f0f5d77f0c7bd11bbadd8"),
"blog_id" : ObjectId("56587befdb7224110f007233"),
"comments" : [
{
"user_id" : ObjectId("562fa014888806820e21e0df"),
"user_full_name" : "Niroj Paudel",
"comment" : "pradip is bhole baba",
"_id" : ObjectId("565f0f5d77f0c7bd11bbadd9"),
"dt" : ISODate("2015-12-02T15:33:49.578Z")
},
{
"user_id" : ObjectId("562fa014888806820e21e0df"),
"user_full_name" : "Niroj Paudel",
"comment" : "honkog pokhara... he he ha ha",
"_id" : ObjectId("565f1034fd07cbfc1129db0b"),
"dt" : ISODate("2015-12-02T15:37:24.581Z")
}
],
"record_count" : 2,
"__v" : 0
}]
What I am trying is to select particular item of comments array based on the blog_id and comments._id ..... but instead of returning only the particular comment it is returning the whole document .
Currently I have following query
db.blog_comments..findOne({
"blog_id" : ObjectId("56587befdb7224110f007233"),
"comments._id":ObjectId("565f1034fd07cbfc1129db0b")
})
This query is returning the whole document i.e.-
[
{
"_id" : ObjectId("565f0f5d77f0c7bd11bbadd8"),
"blog_id" : ObjectId("56587befdb7224110f007233"),
"comments" : [
{
"user_id" : ObjectId("562fa014888806820e21e0df"),
"user_full_name" : "Niroj Paudel",
"comment" : "pradip is bhole baba",
"_id" : ObjectId("565f0f5d77f0c7bd11bbadd9"),
"dt" : ISODate("2015-12-02T15:33:49.578Z")
},
{
"user_id" : ObjectId("562fa014888806820e21e0df"),
"user_full_name" : "Niroj Paudel",
"comment" : "honkog pokhara... he he ha ha",
"_id" : ObjectId("565f1034fd07cbfc1129db0b"),
"dt" : ISODate("2015-12-02T15:37:24.581Z")
}
],
"record_count" : 2,
"__v" : 0
}]
but I want just this
[{
"user_id" : ObjectId("562fa014888806820e21e0df"),
"user_full_name" : "Niroj Paudel",
"comment" : "honkog pokhara... he he ha ha",
"_id" : ObjectId("565f1034fd07cbfc1129db0b"),
"dt" : ISODate("2015-12-02T15:37:24.581Z")
}]
What I am missing please suggest..
Thank you
you can use $elemMatch in your projection:
test> db.project_sub.findOne({
... "blog_id" : ObjectId("56587befdb7224110f007233")
... },{
... "comments": { $elemMatch: { _id: ObjectId("565f1034fd07cbfc1129db0b") } }
... })
{
"_id": ObjectId("565f0f5d77f0c7bd11bbadd8"),
"comments": [
{
"user_id": ObjectId("562fa014888806820e21e0df"),
"user_full_name": "Niroj Paudel",
"comment": "honkog pokhara... he he ha ha",
"_id": ObjectId("565f1034fd07cbfc1129db0b"),
"dt": ISODate("2015-12-02T15:37:24.581Z")
}
]
}

Get document based on multiple criteria of embedded collection

I have the following document, I need to search for multiple items from the embedded collection"items".
Here's an example of a single SKU
db.sku.findOne()
{
"_id" : NumberLong(1192),
"description" : "Uploaded via CSV",
"items" : [
{
"_id" : NumberLong(2),
"category" : DBRef("category", NumberLong(1)),
"description" : "840 tag visual",
"name" : "840 Visual Mini Round",
"version" : NumberLong(0)
},
{
"_id" : NumberLong(7),
"category" : DBRef("category", NumberLong(2)),
"description" : "Maxi",
"name" : "Maxi",
"version" : NumberLong(0)
},
{
"_id" : NumberLong(11),
"category" : DBRef("category", NumberLong(3)),
"description" : "Button",
"name" : "Button",
"version" : NumberLong(0)
},
{
"_id" : NumberLong(16),
"category" : DBRef("category", NumberLong(4)),
"customizationFields" : [
{
"_class" : "CustomizationField",
"_id" : NumberLong(1),
"displayText" : "Custom Print 1",
"fieldName" : "customPrint1",
"listOrder" : 1,
"maxInputLength" : 12,
"required" : false,
"version" : NumberLong(0)
},
{
"_class" : "CustomizationField",
"_id" : NumberLong(2),
"displayText" : "Custom Print 2",
"fieldName" : "customPrint2",
"listOrder" : 2,
"maxInputLength" : 17,
"required" : false,
"version" : NumberLong(0)
}
],
"description" : "2 custom lines of farm print",
"name" : "Custom 2",
"version" : NumberLong(2)
},
{
"_id" : NumberLong(20),
"category" : DBRef("category", NumberLong(5)),
"description" : "Color Red",
"name" : "Red",
"version" : NumberLong(0)
}
],
"skuCode" : "NF-USDA-XC2/SM-BC-R",
"version" : 0,
"webCowOptions" : "840miniwithcust2"
}
There are repeat items.id throughout the embedded collection. Each Sku is made up of multiple items, all combinations are unique, but one item will be part of many Skus.
I'm struggling with the query structure to get what I'm looking for.
Here are a few things I have tried:
db.sku.find({'items._id':2},{'items._id':7})
That one only returns items with the id of 7
db.sku.find({items:{$all:[{_id:5}]}})
That one doesn't return anything, but it came up when looking for solutions. I found about it in the MongoDB manual
Here's an example of a expected result:
sku:{ "_id" : NumberLong(1013),
"items" : [ { "_id" : NumberLong(5) },
{ "_id" : NumberLong(7) },
{ "_id" : NumberLong(12) },
{ "_id" : NumberLong(16) },
{ "_id" :NumberLong(2) } ] },
sku:
{ "_id" : NumberLong(1014),
"items" : [ { "_id" : NumberLong(5) },
{ "_id" : NumberLong(7) },
{ "_id" : NumberLong(2) },
{ "_id" : NumberLong(16) },
{ "_id" :NumberLong(24) } ] },
sku:
{ "_id" : NumberLong(1015),
"items" : [ { "_id" : NumberLong(5) },
{ "_id" : NumberLong(7) },
{ "_id" : NumberLong(12) },
{ "_id" : NumberLong(2) },
{ "_id" :NumberLong(5) } ] }
Each Sku that comes back has both a item of id:7, and id:2, with any other items they have.
To further clarify, my purpose is to determine how many remaining combinations exist after entering the first couple of items.
Basically a customer will start specifying items, and we'll weed it down to the remaining valid combinations. So Sku.items[0].id=5 can only be combined with items[1].id=7 or items[1].id=10 …. Then items[1].id=7 can only be combined with items[2].id=20 … and so forth
The goal was to simplify my rules for purchase, and drive it all from the Sku codes. I don't know if I dug a deeper hole instead.
Thank you,
On the part of extracting the sku with item IDs 2 and 7, when I recall correctly, you have to use $elemMatch:
db.sku.find({'items' :{ '$all' :[{ '$elemMatch':{ '_id' : 2 }},{'$elemMatch': { '_id' : 7 }}]}} )
which selects all sku where there is each an item with _id 2 and 7.
You can use aggregation pipelines
db.sku.aggregate([
{"$unwind": "$sku.items"},
{"$group": {"_id": "$_id", "items": {"$addToSet":{"_id": "$items._id"}}}},
{"$match": {"items._id": {$all:[2,7]}}}
])

Find all documents where the array elements matches some conditions in reactivemongo

In my MongoDB database I've a collection named "recommendation.users" in which i store all the User data.
A document of my collection has the following form:
{
"_id" : ObjectId("542e67e07f724fc2af28ba75"),
"id" : "",
"email" : "luigi#gmail.com",
"tags" : [
{
"tag" : "Paper Goods:Liners - Baking Cups",
"weight" : 2,
"lastInsert" : 1412327492874
},
{
"tag" : "Vegetable:Carrots - Jumbo",
"weight" : 4,
"lastInsert" : 1412597883569
},
{
"tag" : "Paper Goods:Lialberto- Baking Cups",
"weight" : 1,
"lastInsert" : 1412327548205
},
{
"tag" : "Fish:Swordfish Loin Portions",
"weight" : 3,
"lastInsert" : 1412597939124
},
{
"tag" : "Vegetable:Carrots - alberto#gmail.com",
"weight" : 2,
"lastInsert" : 1412597939124
}
]
}
As driver I'm using ReactiveMongo in Scala.
Now I'm writing a method that get a tags: List[String] and search in my collections all the Users where the "tags" elements contains all the value passed as parameter.
In other words i want to find all the documents where the "tags" tag contains documents where "tag" = tags(i) for every element of tags.
How can I make something like this in MongoDB and reactiveMongo??
My method has the following signature:
def findUsers(tags: List[String]): Future[Option[List[User]]] = {
//search all the matching Users
//if exists at least one User return a Some(List(users))
//otherwise returns a None
}
For example if i had the following two documents in my collection:
[
{
"_id" : ObjectId("542e67e07f724fc2af28ba75"),
"id" : "",
"email" : "luigi#gmail.com",
"tags" : [
{
"tag" : "Paper Goods:Liners - Baking Cups",
"weight" : 2,
"lastInsert" : 1412327492874
},
{
"tag" : "Vegetable:Carrots - Jumbo",
"weight" : 4,
"lastInsert" : 1412597883569
},
{
"tag" : "Paper Goods:Lialberto- Baking Cups",
"weight" : 1,
"lastInsert" : 1412327548205
},
{
"tag" : "Fish:Swordfish Loin Portions",
"weight" : 3,
"lastInsert" : 1412597939124
},
{
"tag" : "Vegetable:Carrots - alberto#gmail.com",
"weight" : 2,
"lastInsert" : 1412597939124
}
]
},
{
"_id" : ObjectId("542e67e07f724fc2af28ba75"),
"id" : "",
"email" : "alberto#gmail.com",
"tags" : [
{
"tag" : "Paper Goods:Lialberto- Baking Cups",
"weight" : 1,
"lastInsert" : 1412327548205
},
{
"tag" : "Fish:Swordfish Loin Portions",
"weight" : 3,
"lastInsert" : 1412597939124
},
{
"tag" : "Vegetable:Carrots - alberto#gmail.com",
"weight" : 2,
"lastInsert" : 1412597939124
}
]
}]
If my method is called with findUser(List("Fish:Swordfish Loin Portions", "Vegetable:Carrots - alberto#gmail.com")) my method had to returns the first User.
I know that is possible to do that with a cycle that check if a user had all the given tags, but it's too complicated and verbose. Exists an alternative??
How can I make that??
Use query like
db.test.find({$and:[{"tags.tag":"a"},{"tags.tag":"b"}]})
And you may simplify your life by using this reactivemongo-query https://github.com/sh1ng/ReactiveMongo-Queries
val cursor = collection.find(on[Interests].and(_.eq(_.tags.tag, "Fish:Swordfish Loin Portions"), _.eq(_.tags.tag, "Vegetable:Carrots - alberto#gmail.com"))).cursor[Interests]
Of course you may rewrite it in pure reactivemongo.

Deleting a single object from an array of objects in MongoDB

Say we have the following collection of documents:
{ "_id" : ObjectId("50a69fa904c8310609600be3"), "id" : 100, "city" : "San Francisco", "friends" : [ { "id" : 1, "name" : "John" }, { "id" : 2, "name" : "Betty" }, { "id" : 3, "name" : "Harry" } ] }
{ "_id" : ObjectId("50a69fc104c8310609600be4"), "id" : 200, "city" : "Palo Alto", "friends" : [ { "id" : 1, "name" : "Carol" }, { "id" : 2, "name" : "Frank" }, { "id" : 3, "name" : "Norman" } ] }
{ "_id" : ObjectId("50a69fc304c8310609600be5"), "id" : 300, "city" : "Los Angeles", "friends" : [ { "id" : 1, "name" : "Fred" }, { "id" : 2, "name" : "Neal" }, { "id" : 3, "name" : "David" } ] }
.
.
.
Now let's say that Frank (Palo Alto, id=2) is no longer my friend, and I want to delete him from the collection. I thought the following might work, but it doesn't:
db.test.update({"city":"Palo Alto"},{"$pull":{"friends.name":"Frank"}})
I'd like to be able to do something like that. Delete an object within an array within a collection of documents. How do you do this?
You were close. The query should be like this:
db.test.update({"city":"Palo Alto"},{"$pull":{"friends":{"name":"Frank"}}});
$pull takes an object whose field specifies the field array "friends". The value {"name":"Frank"} represents the query (to run inside the array) to find the element to pull out.