Given a collection of documents similar to the following document
{
"description": "janeetjack",
"name": "Pocog bistro janeetjack"
}
where name is a unique field
How do I update all existing documents and add additional fields so it looks like this
{
"userDetails":{
"description": "janeetjack",
"name": "Pocog bistro janeetjack"
},
"userLikes":{
"likes": "food",
"plays": "ball"
},
}
You need to run set and unset on all docs with updateMany
Playground
db.collection.update({},
{
"$set": {
"userDetails.description": "$description",
"userDetails.name": "$name",
"userLikes.like": "abs"
},
"$unset": {
description: 1,
name: 1
}
})
Related
I am working on a Node.js application that is using a MongoDB database with Mongoose. I've been stuck in this thing and didn't come up with the right query.
Problem:
There is a collection named chats which contain embedded documents (rooms) as an array of objects. I want to delete these embedded documents (rooms) through Ids which are in the array.
{
"_id": "ObjectId(6138e2b55c175846ec1e38c5)",
"type": "bot",
"rooms": [
{
"_id": "ObjectId(6138e2b55c145846ec1e38c5)",
"genre": "action"
},
{
"_id": "ObjectId(6138e2b545c145846ec1e38c5)",
"genre": "adventure"
}
]
},
{
"_id": "ObjectId(6138e2b55c1765846ec1e38c5)",
"type": "person",
"rooms": [
{
"_id": "ObjectId(6138e2565c145846ec1e38c5)",
"genre": "food"
},
{
"_id": "ObjectId(6138e2b5645c145846ec1e38c5)",
"genre": "sport"
}
]
},
{
"_id": "ObjectId(6138e2b55c1765846ec1e38c5)",
"type": "duo",
"rooms": [
{
"_id": "ObjectId(6138e21c145846ec1e38c5)",
"genre": "travel"
},
{
"_id": "ObjectId(6138e35645c145846ec1e38c5)",
"genre": "news"
}
]
}
I am converting my array of ids into MongoDB ObjectId so I can use these ids as match criteria.
const idsRoom = [
'6138e21c145846ec1e38c5',
'6138e2565c145846ec1e38c5',
'6138e2b545c145846ec1e38c5',
];
const objectIdArray = idsRoom.map((s) => mongoose.Types.ObjectId(s));
and using this query for the chat collection. But it is deleting the whole document and I only want to delete the rooms embedded document because the ids array is only for the embedded documents.
Chat.deleteMany({ 'rooms._id': objectIdArray }, function (err) {
console.log('Delete successfully')
})
I really appreciate your help on this issue.
You have to use $pull operator in a update query like this:
This query look for documents where exists the _id into rooms array and use $pull to remove the object from the array.
yourModel.updateMany({
"rooms._id": {
"$in": [
"6138e21c145846ec1e38c5",
"6138e2565c145846ec1e38c5",
"6138e2b545c145846ec1e38c5"
]
}
},
{
"$pull": {
"rooms": {
"_id": {
"$in": [
"6138e21c145846ec1e38c5",
"6138e2565c145846ec1e38c5",
"6138e2b545c145846ec1e38c5"
]
}
}
}
})
Example here.
Also you can run your query without the query parameter (in update queries the first object is the query) like this and result is the same. But is better to indicate mongo the documents using this first object.
I have a recursive structure in one of my MongoDB collections, like so:
{
"id": String,
"date": Date,
"text": String
"replies": [{
// Same structure as above
}]
}
I need to update a document in this hierarchy by adding in a reply to a nested document. The guarantees are as follows:
The object to reply to is guaranteed to exist.
The path to the object to which a reply is to be posted is known (i.e., we have a sequence of the _id attributes to navigate).
The recursion depth is not bounded.
Searching SO gives me the following relevant questions:
Querying, 2013 - The recommendation was to switch to a graph DB.
Updating, 2015 - The recommendation was to use the $[] operator.
Based on the latter, my attempt was:
await commCollection.update(
{ _id: topLevel['_id'] },
{
$push: {
'replies.$[].$[comment_arr].replies': {
id: commentId,
text: comment,
date,
replies: []
}
}
},
{ arrayFilters: [{ 'comment_arr.id': responseTo }]}
);
where topLevel is the root document, responseTo is the id attribute of the object to add the reply to. This however, does not seem to work. What am I doing wrong, and how do I achieve this?
Update: An example below. Here's an example of a document from MongoDB Atlas:
{
"_id": {
"$oid": "605fdb8d933c5f50b4d2225e"
},
"id": "mr9pwc",
"username": "recrimination-logistical",
"upvotes": {
"$numberInt": "0"
},
"downvotes": {
"$numberInt": "0"
},
"text": "Top-level comment",
"date": {
"$date": {
"$numberLong": "1616894861300"
}
},
"replies": [{
"id": "dflu1h",
"username": "patrolman-hurt",
"upvotes": {
"$numberInt": "0"
},
"downvotes": {
"$numberInt": "0"
},
"text": "Testing reply level 1!",
"date": {
"$date": {
"$numberLong": "1618387567042"
}
},
"replies": [] // ----> want to add a reply here
}]
}
I've indicated where we want a reply added. responseTo in this case would be dflu1h.
Just need to remove $[] positional
Field name in arrayFilters must be an alphanumeric string beginning with a lowercase letter, Change comment_arr to any alphabets only like commentArr, remove special characters
await commCollection.update(
{ _id: topLevel['_id'] },
{
$push: {
"replies.$[commentArr].replies": {
id: commentId,
text: comment,
date,
replies: []
}
}
},
{ arrayFilters: [{ "commentArr.id": responseTo }] }
)
Playground
I have a mongoDB "ratings" collection which contains the below data
/* 1 */
{
"_id": ObjectId("5f1e13936d702dc8b1aa47c8"),
"ratings": [{
"music": "PN1",
"rating": "23"
},
{
"music": "PN2",
"rating": "24"
}
],
"email": "test1#mail.com",
"username": "test1"
}
/* 2 */
{
"_id": ObjectId("5f1e13f46d702dc8b1aa47c9"),
"ratings": [{
"music": "PN3",
"rating": "45"
},
{
"music": "PN4",
"rating": "65"
}
],
"email": "test2#mail.com",
"username": "test2"
}
I want to modify the PN1 rating for username "test1" and i execute the below code
db.getCollection('ratings').update(
// query
{
"_id": ObjectId("5f1e13936d702dc8b1aa47c8"),
"email": "test1#mail.com"
},
// update
{
"ratings.rating": "8"
},
// options
{
"multi": false,
"upsert": false
);
The code works but instead of modifying just the PN1 rating for test1 user, the entire ratings list for that user was replaced the update. How can i fix this? I only want to modify the PN1 rating for the test1 user
First you'll need to specify which element of the array you want to update, e.g. one that has a music value of PN1. You can do this by adding a field to the query that matches against the ratings.music field like so:
{
"_id": ObjectId("5f1e13936d702dc8b1aa47c8"),
"email": "test1#mail.com",
"ratings.music": "PN1"
}
Now that you matched the document you want to update, you need to tell MongoDB how to update the document. An update document that changes the rating might look like this:
{
$set:
{
"ratings.$.rating": 8
}
}
This works by using the $ positional operator, which will update the first element in the ratings array that has a music field equal to PN1.
Given the following sample data
db.cars.insertMany([
{
"category": "sedan",
"model": {
"manufacturer": {
"en": "Mercedes",
"ru": "Мерседес"
},
"number": "E320"
}
},
{
"category": "SUV",
"model": {
"manufacturer": {
"en": "Audi",
"ru": "Ауди"
},
"number": "Q7"
}
},
])
I can select a category by its' name with the following query
db.cars.find({'category': 'sedan'})
And also, if I want to do mapping for a given field, I can do the following
db.cars.aggregate({$project: {'model.manufacturer': '$model.manufacturer.ru'}})
Now combining those 2, I get
db.cars.aggregate([{$match: {'category': 'SUV'}}, {$project: {'model.manufacturer': '$model.manufacturer.ru'}}])
Now my question is, is this a right approach, and if yes, how do I keep all the other values without typing them in the aggregation query(like {'model': 1, ... })
Adds new fields to documents. $addFields outputs documents that contain all existing fields from the input documents and newly added fields..
The $addFields stage is equivalent to a $project stage that explicitly specifies all existing fields in the input documents and adds the new fields.
Starting in version 4.2, MongoDB adds a new aggregation pipeline stage $set that is an alias for $addFields.
https://docs.mongodb.com/manual/reference/operator/aggregation/addFields/
db.cars.aggregate([
{
$match: {
"category": "SUV"
}
},
{
$addFields: {
"model.manufacturer": "$model.manufacturer.ru"
}
}
])
or for MongoDB >= v4.2
db.cars.aggregate([
{
$match: {
"category": "SUV"
}
},
{
$set: {
"model.manufacturer": "$model.manufacturer.ru"
}
}
])
I'm looking to move an array of subdocuments into a collection of it's own keyed by the owner id. Currently, my collection is formed like this:
"_id": ObjectId("123"),
"username": "Bob Dole",
"logins": [{
"_id": ObjectId("abc123"),
"date": ISODate("2016")
}, {
"_id": ObjectId("def456"),
"date": ISODate("2016")
}]
I'm looking for the best way to write a script that would loop over each user, and move each item in the logins array to it's own "logins" collection, as follows:
{
"_id": ObjectId("abc123"),
"_ownerId": ObjectId("123"),
"date": ISODate("2016")
}
{
"_id": ObjectId("def567"),
"_ownerId": ObjectId("123"),
"date": ISODate("2016")
}
When the script ends, I'd like the login array to be removed entirely from all users.
this query will create new collection using aggregation framework
to see how it looks - just remove $out pipeline phase
db.thinking.aggregate([
{
$unwind:"$logins"
},{
$project:{
_id:"$logins._id",
_ownerId:"$_id",
date:"$logins.date"
}
},
{
$out: "newCollection"
}
])
to delete array records - as suggested in comment:
db.thinking.update({},{ "$unset": { "logins": "" } },{ "multi": true })