How to update multiple elements of a single array in mongoDB - mongodb

I have a document of mongoDb like following
{
_id: "c4bYJWz42T2JzbmHh",
DATA:[
{sno:1, name: xyz, country: xyz},
{sno:2, name: xyz1, country: xyz1},
{sno:3, name: xyz2, country: xyz2}
]
}
Now i want to update name and country on basis of 'sno'.I have used this approach
ExampleDb.update({
_id: data.id
}, {
$set: {
"DATA.$[elem].name": data.name ,
"DATA.$[elem].country" :data.country
}
},
{
arrayFilters: [ { "elem.sno": data.sno } ]
});
ut seems worng as it show following error.
Exception while invoking method 'ExampleDb.update' MongoError: Modifiers operate on fields but we found a Array instead.

Related

Upserting nested fields with a dot(.) in key in MongoDB

I have query in MongoDB for which I'm trying to upsert an inner nested attribute that contains a dot(.) in the key. E.g. a document might look something like: (below is a fictitious example just to highlight the constraint I'm facing.)
const person = {
name: 'Peter',
address: {
'NY.postalCode': 12345,
'CA.postalCode': 23456,
}
}
However, when I try to update one of the nested attribute in address with the below $set operation, I get an additional object NY under address and its subKey postalCode as a result, instead of the flattened attribute within address.
await Person.findByIdAndUpdate(id, {
$set: {
'address.NY.postalCode': 98765,
}
}, { new: true });
// Output
{
name: 'Peter',
address: {
'NY.postalCode': 12345,
'CA.postalCode': 23456,
NY: {
postalCode: 98765,
}
}
}
I've tried using the escape character for dot (\u002e), but get the same output. Also, I have seen some new features for setting fields but only in Mongo v5: https://www.mongodb.com/docs/manual/reference/operator/aggregation/setField/#mongodb-expression-exp.-setField. However, we're using Mongo v4.2 and would not be able to upgrade until a while later.
Would like to check if there are any means to $set nested attributes in address that don't completely replace the address object? i.e. to be able to specifically upsert an inner attribute within address object?
For example, is it possible to use the aggregation framework to workaround this?
I've tried something like the below, but it didn't work - not sure if its something that I did wrong or its probably not possible to use the aggregation pipeline..
Person.aggregate([
{
$match: {
_id: id,
},
},
{
$replaceRoot: { newRoot: {
$mergeObjects: [
'$$ROOT.address',
{
'NY.postalCode': 98765,
},
],
} },
},
]);
For your scenario, you need to achieve the update with aggregation pipeline.
Use $literal to escape the field name with dot.
Via $mergeObjects to merge current address object with { NY.postalCode': 98765 }.
db.collection.update({
"_id": ObjectId("5a934e000102030405000000")
},
[
{
$set: {
"address": {
$mergeObjects: [
"$address",
{
$literal: {
"NY.postalCode": 98765
}
}
]
}
}
}
],
{
new: true
})
Demo # Mongo Playground

$set operator mongo deletes non existing keys?

When I run this command:
var news = {
_id: req.body.newsItemId,
images: {
_id: new ObjectId(),
src: result
}
}
Shop.findOneAndUpdate({
_id: ObjectId(req.body.shopId),
'news._id': ObjectId(req.body.newsItemId)},
{"$set": {
"news.$": news
}
}).exec(function(err, shop){
console.log(err, shop);
})
The non existing field from the news object are overwritten with empty values, so they are removed from the document.
news is a subdocument from shops. So a document looks like:
name: 'test shop',
email: 'email#shop',
products: [],
news: [
{
name: 'test news',
content: 'lorum ipsum....',
summary: 'lorum',
images: [
{
src: 'news/image_1.jpg',
}
]
}
]
After running the command above the news contains only a id and 1 image. I want only to overwrite the images array and leave the other properties alone. I have tried it with only images in the set, but then I get this error Mongoose update 'cannot use the part (..) to traverse the element , that is where the $ comes from.
Solved this by changing the code to this:
var images = [{
src: result
}]
Shop.findOneAndUpdate({
_id: ObjectId(req.body.shopId),
'news._id': ObjectId(req.body.newsItemId)},
{"$set": {
"news.$.images" : images
}
}).exec(function(err, shop){
console.log(err, shop);
return res.status(200).json({message: 'OK'});
})
So only the images object is overwritten now, and that is what I want.

What is the better implementation? MongoDb Query

I need some help with MongoDb. I need to check if an object exists in the database. If it's true, then I need to check if this object has a specific element into array (Products). If not, I need to create this object(Order) with this element(Cookie) in to array(Products).
Example data:
Order {
_id: ObjectId("580bc55f54101f1d18152d88"),
code: "AVG223424",
products: [
{
name: "Cookie"
},
{
name: "Soda"
}
]
}
Finally, what is the better implementation?
Assuming you are using a collection with the name 'Orders'
db
.Orders
.update({
code: "12345"
}, {
$addToSet: {
products: {
name: "Cookie"
}
},
$setOnInsert: {
code: "12345"
}
}, {
upsert: true
});
This query looks for a document with the same 'code,' and if found, will add the object '{name: "Cookie"}' if there is no other Object with the same key/val pairs. If the document does not exist, the '$setOnInsert' command will set the specified fields only if a new document is created.

fetching documents based on nested subdoc array value

I'm trying to get all documents in a collection based on a subdocument array values. This is my data structure in the collection i'm seeking:
{
_id: ObjectId('...'),
name: "my event",
members:
[
{
_id: ObjectId('...'),
name: "family",
users: [ObjectId('...'),ObjectId('...'),ObjectId('...')]
},
{
_id: ObjectId('...'),
name: "work",
users: [ObjectId('...'),ObjectId('...'),ObjectId('...')]
}
]
}
I should point out that the schema of these objects are defined like so:
Events:
{
name: { type: String },
members: {type: [{ type: ObjectId, ref: 'MemberGroup' }], default:[] }
}
MemberGroup:
{
name: { type: String },
users: [{type: mongoose.Schema.Types.ObjectId, ref: 'User'}]
}
and of course User is just an arbitrary object with an id.
What i'm trying to fetch: i want to retrieve all events which has a specific user id in its member.users field.
i'm not sure if its event possible in a single call but here is what i've tried:
var userId = new mongoose.Schema.ObjectId('57d9503ef10d5ffc08d6b8cc');
events.find({members: { $elemMatch : { users: { $in: [userId]} } }})
this syntax work but return no elements even though i know there are matching elements (using robomongo to visualize the db)
So after a long search in stackoverflow i came across the good answare here:
MongoDB: How to find out if an array field contains an element?
my query changed to the following which gave me my desired result:
var userId = new mongoose.Schema.ObjectId('57d9503ef10d5ffc08d6b8cc');
events.find({}, {members: { $elemMatch : { users: { $eq: userId} } }})
notice the use of the second find parameter to specify the query limit instead of the first one (which is odd)

MongoDB Regex $and $or Search Query

I am trying to construct a query that will accept multiple fields that can be searched over using regex for partial field matching that also has a hard constraint on other fields.
Example:
Collection: "Projects"
Required Information: { propertyId: "abc", clientId: "xyz" }
Fields to be Searched: name, serviceType.name, manager.name
Currently, I have a query like this, but if there are no results it returns all the results, which isn't helpful.
{
'$and': [
{ propertyId: '7sHGCHT4ns6z9j6BC' },
{ clientId: 'xyz' },
{ '$or':
[
{ name: /HVAC/gi },
{ 'serviceType.name': /HVAC/gi },
{ 'manager.name': /HVAC/gi }
]
}
]
}
If anyone has any insight into this it would be much appreciated.
Example Document:
{
_id: "abc",
propertyId: "7sHGCHT4ns6z9j6BC",
clientId: "xyz"
name: "16.000.001",
serviceType: {
_id: "asdf",
name: "HVAC"
},
manager: {
_id: "dfgh",
name: "Patrick Lewis",
}
}
The expected result is to only find documents where propertyId = 7sHGCHT4ns6z9j6BC AND one at least one of the following keys: name, serviceType.name, or manager.name match an inputted string, in this case, it's HVAC and if none of the regex fields match, then return nothing.
UPDATE
The issue was with MongoDB, after restarting it, everything worked.
Try following script:
db.collection.find({
$and:[
{propertyId:"7sHGCHT4ns6z9j6BC"},
{
$or:[
{name: /HVAC/i},
{"serviceType.name": /HVAC/i},
{"manager.name": /HVAC/i}
]
}]
})
Query above will return a document or documents if and only if propertyId matches and either of name, serviceType.name or manager.name matches desired regex.