How to add an extra field in a sub document in MongoDB? - mongodb

I've just started working with MongoDB. And I have a document like this:
{
"_id": "12345"
"body": "Here is the body"
"comments":[
{
"name": "Person 1"
"comm": "My comment"},
{
"name": "Person 2"
"comm": "Comment 2"}
]
"author":"Author 1"
}
And I want to change this document to :
{
"_id": "12345"
"body": "Here is the body"
"comments":[
{
"name": "Person 1"
"comm": "My comment"
"checks_": 1
},
{
"name": "Person 2"
"comm": "Comment 2"
"checks_": 4
}
]
"author": "Author 1"
}
I've tried:
db.coll.update({ "_id":12345},{ "$set":{ "comments" :{ "checks_": 1}}})
And this removed all sub documents within comments and added {checks_:1} to it.
Where am I going wrong?

So what you are doing wrong is that the $set operator is doing exactly what it should, and it is replacing only the comments field with the value you have specified. This is not adding an additional document to the array.
You need to be specific and use "dot notation" to "indentify" which array element you are replacing. So to get to your result, you need two updates:
db.coll.update({ "_id":12345},{ "$set":{ "comments.0.checks_" : 1 }})
db.coll.update({ "_id":12345},{ "$set":{ "comments.1.checks_" : 4 }})
That is at least until the next version (as of writing) of MongoDB is released, where you can do bulk updates. And that will not be long now.

A little more geneirc solution (for MongoDb 3.6+):
db.coll.update(
{},
{$set: {"comments.$[element].checks_": 1}},
{multi: false, arrayFilters: [{"element.name": {$eq: "Person 1"}}]}
)
This will add field into specific sub document from the list, matching criteria (name = 'Person 1').

Adding my two cents here.
If you want to add a field to the all the cells, with the same value (in this example: 1 will be added to all of them). You can use the following command:
db.coll.updateMany( {"_id": 12345}, {"$set": {"comments.$[].checks_": 1} }});
And you will get
{
"_id": "12345"
"body": "Here is the body"
"comments":[
{
"name": "Person 1"
"comm": "My comment"
"checks_": 1
},
{
"name": "Person 2"
"comm": "Comment 2"
"checks_": 1
},
...
{
"name": "Person 300"
"comm": "Comment 300"
"checks_": 1
}
]
"author": "Author 1"
}

Related

how to update nested array of object in mongodb

Here is my db collection in mongoDB. I am using mongoose to update the data inside the comments array
{
"author": {
"email": "user#example.com",
"userName": "John"
},
"_id": "63bc20741475b40323d6259f",
"title": "this is blog",
"description": "description",
"blogBanner": "https://github.com//routers/userRouter.js",
"views": "0",
"comments": [
{
"userId": "63b919840ae5303938fb1c17",
"comment": "first comment",
"_id": "_id:63bbdbdb7018eabb752c0e58
},
{
"userId": "63b919840ae5303938fb1c17",
"comment": "second comment",
"_id": "63bbdbdb7018eabb752ce55"
}
],
"reaction": [
{
"userEmail": "gias#gmail.com",
"react": "love"
}
],
"date": "2023-01-09T14:09:32.810Z",
"__v": 0
}
here i want to update the first comment by using comment._id: 63bbdbdb7018eabb752c0e58
how can i update the single comment by using comment._id ;
use-case : when user want to update his/her comment by using comment._id
here i want to update the first comment by using comment._id: 63bbdbdb7018eabb752c0e58
how can i update the single comment by using comment._id;
use-case : when user want to update his/her comment by using comment._id
Please, read the following docs:
https://www.mongodb.com/docs/manual/reference/operator/update/positional/#update-documents-in-an-array
Model.update(
{
'_id': '63bc20741475b40323d6259f',
'comments._id':'63bbdbdb7018eabb752ce55'
},
{ $set: { "comments.$.comment" : 'Edited Text' } }
)

Get specific fields from mongodb and return as object array

I have the following document:
"content": [
{
"_id": "5dbef12ae3976a2775851bfb",
"name": "Item AAA",
"itemImages": [
{
"_id": "5dbef12ae3976a2775851bfd",
"imagePath": "https://via.placeholder.com/300",
"imageTitle": "Test 300"
},
{
"_id": "5dbef12ae3976a2775851bfc",
"imagePath": "https://via.placeholder.com/250",
"imageTitle": "Test 250"
}
]
}
and I am wondering if there is a way to return only the data in the array yet with the "name" and document "main _id" so that result set will be
"itemImages": [
{
"_id": "5dbef12ae3976a2775851bfb",
"name": "Item AAA",
"imagePath": "https://via.placeholder.com/300",
"imageTitle": "Test 300"
},
{
"_id": "5dbef12ae3976a2775851bfb",
"name": "Item AAA",
"imagePath": "https://via.placeholder.com/250",
"imageTitle": "Test 250"
}
]
I've tried using mongodb find and aggregate functions but neither helped in retrieving the above results. Thanks for your help
You should be able to get what you want with aggregation.
You'll need to:
unwind the array so there is a separate document for each element
use addFields to add the _id and name to the element
group to reassemble the array
project to get just the field you want
This might look something like:
db.collection.aggregate([
{$unwind:"$itemImages"},
{$addFields: {
"itemImages._id":"$_id",
"itemImages.name":"$name"
}},
{$group:{_id:"$_id", itemImages:{$push:"$itemImages"}}},
{$project:{itemImages:1,_id:0}}
])

Morphia Aggregation Pipeline always returns the same data

I have an item in a collection in MongoDB as follows:
{
"_id": "MY ID",
"myCollection": [{
"code": "Código 1",
"description": "Descripción entidad 1",
},
{
"code": "Código 2",
"description": "Descripción entidad 2",
},
{
"code": "Código 3",
"description": "Descripción entidad 3",
},
... follows to 19
If I use the AggregationPipeline like this:
AggregationPipeline pipeline = getDs().createAggregation(EntidadInfoEntity.class).unwind("myCollection")
.sort(new Sort("myCollection.descripcion", 1)).limit(resultadosPorPagina);
Iterator<EntidadInfoEntity> resultado = pipeline.aggregate(EntidadInfoEntity.class);
while (resultado.hasNext()) {
EntidadInfoEntity entidadInfo = resultado.next();
System.out.println(HerramientasJson.getInstance().convertirAJson(entidadInfo));
}
I get always the same result:
{"myCollection":[{"code":"Código 1","description":"Descripción entidad 1"}
{"myCollection":[{"code":"Código 1","description":"Descripción entidad 1"}
{"myCollection":[{"code":"Código 1","description":"Descripción entidad 1"}
Oddly, if I debug the pipeline I get this inside the "stages" value:
[{ "$unwind" : "$myCollection"}, { "$sort" : { "myCollection.description" : 1}}, { "$limit" : 25}]
And if I try to execute it directly under MongoDB shell with:
db.myEntities.aggregate([{ "$unwind" : "$myCollection"}, { "$sort" : { "myCollection.description" : 1}}, { "$limit" : 25}])
I get the correct result:
{"myCollection":[{"code":"Código 1","description":"Descripción entidad 1"}]}
{"myCollection":[{"code":"Código 2","description":"Descripción entidad 2"}]}
{"myCollection":[{"code":"Código 3","description":"Descripción entidad 3"}]}
Any ideas why could that be? Am I doing something wrong?
Best regards
Note: Some of the data has been rewritten to try to show the problem more clearly. Typing mistakes might occur, but the behaviour is as I explain.
According to Morphia's team, the solution is to remove the "_id" field:
Morphia Aggregation Pipeline always returns the same data #932
And:
Aggregation unwind function does not behave as expected? #854
If you don't need the _id field it's fine. However, if you still need the _id field (my case), you'd need something else...

How can I query an indexed object list in mongodb?

I have some documents in the "company" collection structured this way :
[
{
"company_name": "Company 1",
"contacts": {
"main": {
"email": "main#company1.com",
"name": "Mainuser"
},
"store1": {
"email": "store1#company1.com",
"name": "Store1 user"
},
"store2": {
"email": "store2#company1.com",
"name": "Store2 user"
}
}
},
{
"company_name": "Company 2",
"contacts": {
"main": {
"email": "main#company2.com",
"name": "Mainuser"
},
"store1": {
"email": "store1#company2.com",
"name": "Store1 user"
},
"store2": {
"email": "store2#company2.com",
"name": "Store2 user"
}
}
}
]
I'm trying to retrieve the doc that have store1#company2.com as a contact but cannot find how to query a specific value of a specific propertie of an "indexed" list of objects.
My feeling is that the contacts lists should not not be indexed resulting in the following structure :
{
"company_name": "Company 1",
"contacts": [
{
"email": "main#company1.com",
"name": "Mainuser",
"label": "main"
},
{
"email": "store1#company1.com",
"name": "Store1 user",
"label": "store1"
},
{
"email": "store2#company1.com",
"name": "Store2 user",
"label": "store2"
}
]
}
This way I can retrieve matching documents through the following request :
db.company.find({"contacts.email":"main#company1.com"})
But is there anyway to do a similar request on document using the previous structure ?
Thanks a lot for your answers!
P.S. : same question for documents structured this way :
{
"company_name": "Company 1",
"contacts": {
"0": {
"email": "main#company1.com",
"name": "Mainuser"
},
"4": {
"email": "store1#company1.com",
"name": "Store1 user"
},
"1": {
"email": "store2#company1.com",
"name": "Store2 user"
}
}
}
Short answer: yes, they can be queried but it's probably not what you want and it's not going to be really efficient.
The document structure in the first and third block is basically the same - you have an embedded document. The only difference between are the name of the keys in the contacts object.
To query document with that kind of structure you will have to do a query like this:
db.company.find({ $or : [
{"contacts.main.email":"main#company1.com"},
{"contacts.store1.email":"main#company1.com"},
{"contacts.store2.email":"main#company1.com"}
]});
This query will not be efficient, especially if you have a lot of keys in the contacts object. Also, creating a query will be unnecessarily difficult and error prone.
The second document structure, with an array of embedded objects, is optimal. You can create a multikey index on the contacts array which will make your query faster. The bonus is that you can use a short and simple query.
I think the easiest is really to shape your document using the structure describe in your 2nd example : (I have not fixed the JSON)
{
"company_name": "Company 1",
"contacts":{[
{"email":"main#company1.com","name":"Mainuser", "label": "main", ...}
{"email":"store1#company1.com","name":"Store1 user", "label": "store1",...}
{"email":"store2#company1.com","name":"Store2 user", "label": "store2",...}
]}
}
like that you can easily query on email independently of the "label".
So if you really want to use the other structure, (but you need to fix the JSON too) you will have to write more complex code/aggregation pipeline, since we do not know the name and number of attributes when querying the system. Theses structures are also probably hard to use by the developers independently of MongoDB queries.
Since it was not clear let me show what I have in mind
db.company.save(
{
"company_name": "Company 1",
"contacts":[
{"email":"main#company1.com","name":"Mainuser", "label": "main"},
{"email":"store1#company1.com","name":"Store1 user", "label": "store1"},
{"email":"store2#company1.com","name":"Store2 user", "label": "store2"}
]
}
);
db.company.save(
{
"company_name": "Company 2",
"contacts":[
{"email":"main#company2.com","name":"Mainuser", "label": "main"},
{"email":"store1#company2.com","name":"Store1 user", "label": "store1"},
{"email":"store2#company2.com","name":"Store2 user", "label": "store2"}
]
}
);
db.company.ensureIndex( { "contacts.email" : 1 } );
db.company.find( { "contacts.email" : "store1#company2.com" } );
This allows you to store many emails, and query with an index.

Change all my documents nested document value

In mongos shell how would I go through and change every document in reviews.category to "category 2"
My Documents Structure:
{
"_id": ObjectId("4fb3f443b1445d24fc000000"),
"reviews": {
"0": {
"category": "category 1"
},
"1": {
"category": "category 1"
},
"2": {
"category": "category 1"
},
"3": {
"category": "category 1"
}
}
}
You will have to do this yourself in your application code, by querying the document, and looping over all of your nested documents; and then save it back to MongoDB.
In order to prevent race conditions with this, please have a look at the section compare and swap at http://www.mongodb.org/display/DOCS/Atomic+Operations
There is currently an open ticket for this to add this functionality to MongoDB. You might want to up-vote it: https://jira.mongodb.org/browse/SERVER-1243