In Mongoose, query fields based on array - arrays

I am trying to query documents from a mongodb collection, based on array of input query parameters sent from URL.
Sample Database Data
[
{
"drawings": {
"circle": [],
"square": [
{
"id": "828",
"name": "square"
}
],
"cube": []
},
{
"drawings": {
"circle": [
{
"id": "827",
"name": "circle"
}
],
"square": [],
"cube": []
},
{
"drawings": {
"circle": [],
"square": [],
"cube": [
{
"id": "829",
"name": "cube"
}
]
}
]
Input Query Parameter:
query = ["square","cube"];
Expected Output:
[
{
"drawings": {
"circle": [],
"square": [
{
"id": "828",
"name": "square"
}
],
"cube": []
},
{
"drawings": {
"circle": [],
"square": [],
"cube": [
{
"id": "829",
"name": "cube"
}
]
}
]
Best suited Mongoose Query:
Schema.find({
$or:[
{'drawings.square':{$elemMatch:{ name:'square'}}},
{'drawings.cube':{$elemMatch:{ name:'cube'}}}
]
});
Tried Below method. But, it is not correct.
let draw = ["square","cube"];
let draw_query =[];
for (let a=0; a<draw.length;a++){
draw_query.push("{\"drawings."+ draw[a] +"\':{$elemMatch:{ name:\"" + draw[a] + "\"}}}");
}
It creates array with single quoted strings. It cannot be used.
[ '{"drawings.square":{$elemMatch:{ name:"square"}}}',
'{"drawings.cube":{$elemMatch:{ name:"cube"}}}' ]
How to generate this mongoose query dynamically? or is there any better mongoose query to achieve the expected result.

You can query it directly using dot notation so the query should look like below:
db.collection.find({
$or: [
{
"drawings.square.name": "square"
},
{
"drawings.circle.name": "circle"
}
]
})
You can build it in JS using .map(), try:
var query = ["square","cube"];
var orQuery = { $or: query.map(x => ({ [x + ".name"]: x }) ) }

Related

Mongo Query to modify the existing field value with new value + array of objects

I want to update many documents based on the condition in MongoDB.
MODEL is the collection which has the document with below information.
"info": [
{
"field1": "String1",
"field2": "String2"
},
{
"field1": "String1",
"field2": "String_2"
}
],
"var": "x"
I need to update all the "String1" value of field1 with "STRING_NEW". I used the below query to update but not working as expected.
db.model.updateMany(
{ "info.field1": { $exists: true } },
[
{ "$set": {
"info": {
"$map": {
"input": "$info.field1",
"in": {
"$cond": [
{ "$eq": ["$$this.field1", "String1"] },
"STRING_NEW",
$$this.field1
]
}
}
}
} }
]
)
Please have a look and suggest if anything is to be modified in the above query.
Solution 1
With the update with aggregation pipeline, you should iterate the object in info array and update the iterated object by merging the current object with new field1 field via $mergeObjects.
db.model.updateMany({
"info.field1": "String1"
},
[
{
"$set": {
"info": {
"$map": {
"input": "$info",
"in": {
"$cond": [
{
"$eq": [
"$$this.field1",
"String1"
]
},
{
$mergeObjects: [
"$$this",
{
"field1": "STRING_NEW"
}
]
},
"$$this"
]
}
}
}
}
}
])
Demo Solution 1 # Mongo Playground
Solution 2
Can also work with $[<identifier>] positional filtered operator and arrayFilters.
db.model.updateMany({
"info.field1": "String1"
},
{
"$set": {
"info.$[info].field1": "STRING_NEW"
}
},
{
arrayFilters: [
{
"info.field1": "String1"
}
]
})
Demo Solution 2 # Mongo Playground

MongoDB Remove Empty Object from Array after aggregate function

The output of the db.name.aggregate() function gives output:
[{}, {"abc": "zyx"}, {}, "opk": "tyr"]
Actual output desired :
[{"abc": "zyx"}, "opk": "tyr"]
Firstly, your output is not a valid array. It should be like this:
[{}, {"abc": "zyx"}, {}, {"opk": "tyr"}]
Now, to obtain your desired output, you can add the following $match stage, to
your pipeline:
db.collection.aggregate([
{
"$match": {
$expr: {
"$gt": [
{
"$size": {
"$objectToArray": "$$ROOT"
}
},
0
]
}
}
}
])
Here, we are converting the document to an array using $objectToArray, and then we check whether the size of that array is greater than 0. Only those documents are kept in the output.
Playground link.
What if you data looks like this.
[
{
"arr": [
{},
{
"abc": "zyx"
},
{},
{
"opk": "tyr"
}
]
}
]
The aggregation be like this to remove empty objects
db.collection.aggregate([
{
"$unwind": {
"path": "$arr",
}
},
{
"$match": {
arr: {
"$ne": {}
}
}
},
{
"$group": {
_id: "$_id",
arr: {
$push: "$arr"
}
}
}
])
Outputs
[
{
"_id": ObjectId("5a934e000102030405000000"),
"arr": [
{
"abc": "zyx"
},
{
"opk": "tyr"
}
]
}
]
Demo#mongoplayground
https://mongoplayground.net/p/BjGxzlrlj6s

How to delete all sub array

In my MongoDB database, I have a collection 'produits' with documents like this
{
"_id": {
"$oid": "6048e97b4a5f000096007505"
},
"modeles": [
{
"id": "OppoA3",
"pieces": [
{
"id": "OppoA3avn"
},
{
"id": "OppoA3bat"
}]
]
},
{
"id": "OppoA1",
"pieces": [
{
"id": "OppoA1avn",
},
{
"id": "OppoA1batt",
}
]
}
]
}
How can I delete all modeles.pieces from all my documents.
I managed to delete with a filter on modeles.id but with that code but not on all the collection
db.produits.update(
{marque_id:'OPPO', 'modeles.id':'RENOZ'},
{$set:
{
'modeles.$.pieces': []
}
}
, { multi : true }
)
I would like all documents like this finally
{
"_id": {
"$oid": "6048e97b4a5f000096007505"
},
"modeles": [
{
"id": "OppoA3",
"pieces": []
},
{
"id": "OppoA1",
"pieces": []
}
]
}
Thank you for your help.
I have done a javascript loop like this, but i think it's not best practice
async removePieces(){
var doc
try {
doc = await produitModel.find()
for (var produit of doc) {
for (var modele of produit.modeles) {
const filter = {'marque_id': produit.marque_id, 'modeles.id': modele.id}
const set = {
$set: {
'modeles.$.pieces': []
}
}
await db.collection('produits').updateOne(filter, set)
}
}
console.log('removePieces() ==> Terminé')
} catch(err) {
console.log(err)
}
}
db.produits.update({
modeles: {//This is because your second document will create failure otherwise
$exists: true
}
},
{
$set: {
"modeles.$.pieces": []
}
},
{
multi: true
})

Query nested arrays of subdocuments

EDIT: The query should not user the '&" characters. Those or for updates.
ANSWER: Courtesy of Gibbs
correct:
const result : any = await mongoose.model('Events').update(
{
_id: eventId,
"groups._id": groupId,
"groups.users._id": userId
},
incorrect:
const result: any = await mongoose.model('Events').findOne(
{
_id: eventId,
"groups._id": "5f270416e7964b20d6f8953e",
"groups.$.users": { _id: '5f270877b4942d2528885dbd' }
})
// OR:
{
_id: eventId,
"groups._id": "5f270416e7964b20d6f8953e",
"groups.$.users._id" : '5f270877b4942d2528885dbd',
})
With only the first two predicates,(eventId and groups [$_id]) it returns the event. But the third breaks.
Does anybody know why this returns null (object ids are copied directly from the db)
Thanks!
{
"_id": ObjectId("5f270416e7964b20d6f8953d"),
"lastRounds": [],
"name": "EpicFest",
"start": 1596392488896.0,
"end": 1596392500896.0,
"groups": [
{
"_id": ObjectId("5f270416e7964b20d6f8953e"),
"name": "Vossius",
"users": [
{
"created": 1596393590778.0,
"sessionId": null,
"feedBack": {
"messagesSent": 0,
"messagesSentLiked": 0,
"messagesReceived": 0,
"messagesReceivedLiked": 0,
"userFeedbackReceived": [],
"chatFeedbackReceived": [],
"_id": ObjectId("5f270877b4942d2528885dbe")
},
"_id": ObjectId("5f270877b4942d2528885dbd"),
"image": "someAvatr",
"name": "hans",
"groupId": "5f270416e7964b20d6f8953e",
"results": []
},
]
},
{
"_id": ObjectId("5f270416e7964b20d6f8953f"),
"users": [],
"name": "Ignatius"
}
],
"results": [],
"theses": [],
"rounds": 10,
"schedule": [],
"__v": 4
}
Use the aggregate pipeline to fetch based on the inner sub-document.
In the first match stage, filter by eventId and do a $elemMatch on the inner groupId.
Next, unwind the group and groups.user.
Post unwinding you will have a flat object structure that you can again apply filter upon and, just apply a match stage on groups.user._id now.
const pipeline = [
{
$match: {
_id: eventId,
groups: {
$elemMatch: mongoose.Types.ObjectId('5f270416e7964b20d6f8953e')
}
}
},
{
$unwind: '$groups'
},
{
$unwind: '$groups.users'
},
{
$match: {
'$groups.users._id': mongoose.Types.ObjectId('5f270877b4942d2528885dbd')
}
}
];
const result: any = await mongoose.model('Events').aggregate(pipeline).exec();
Mongo Play
db.collection.find({
_id: ObjectId("5f270416e7964b20d6f8953d"),
"groups._id": ObjectId("5f270416e7964b20d6f8953e"),
"groups.users._id": ObjectId("5f270877b4942d2528885dbd")
})
You tried to use String "5f270416e7964b20d6f8953e". It should be ObjectId
You can use . notation to access a nested element or $elemMatch

How to Update nested Array in RethinkDB using ReQL

I have a question on Updating the array in RethinkDB. My JSON structure looks like below.
{
"LOG_EVENT": {
"ATTRIBUTES": [
{
"ATTRIBUTE1": "TYPE",
"VALUE": "ORDER"
},
{
"ATTRIBUTE2": "NUMBER",
"VALUE": "1234567"
}
],
"EVENT_CODE": [
{
"CODE_NAME": "EVENT_SAVED",
"EVENT_TIMESTAMP": "2015-08-18T00:58:12.421+08:00"
}
],
"MSG_HEADER": {
"BUSINESS_OBJ_TYPE": "order",
"MSG_ID": "f79a672b-f15e-459d-a29b-725486d6401f",
"DESTINATIONS": "3"
}
},
"id": "0de3117e-12dd-4d10-a464-dff391a4513f"
}
Here, I am trying to Update a new event inside my event code
{
"CODE_NAME": "MESSAGE_DELIVERED_TO_APP2",
"EVENT_TIMESTAMP": "2015-08-18T12:58:12.421+08:00"
}
My final JSON will look like below,
{
"LOG_EVENT": {
"ATTRIBUTES": [
{
"ATTRIBUTE1": "TYPE",
"VALUE": "ORDER"
},
{
"ATTRIBUTE2": "NUMBER",
"VALUE": "1234567"
}
],
"EVENT_CODE": [
{
"CODE_NAME": "EVENT_SAVED",
"EVENT_TIMESTAMP": "2015-08-18T00:58:12.421+08:00"
},
{
"CODE_NAME": "MESSAGE_DELIVERED_TO_APP2",
"EVENT_TIMESTAMP": "2015-08-18T12:58:12.421+08:00"
}
],
"MSG_HEADER": {
"BUSINESS_OBJ_TYPE": "order",
"MSG_ID": "f79a672b-f15e-459d-a29b-725486d6401f",
"DESTINATIONS": "3"
}
},
"id": "0de3117e-12dd-4d10-a464-dff391a4513f"
}
Can you help on the ReQL query ?
Tried below, but not working
r.db("test").table("test1").get("0de3117e-12dd-4d10-a464-dff391a4513f")("LOG_EVENT")('EVENT_CODE').update(function(row) {
return {EVENT_CODE: row('EVENT_CODE').map(function(d) {
return r.branch(d.append({
"CODE_NAME": "MESSAGE_DELIVERED_TO_APP2",
"EVENT_TIMESTAMP": "2015-08-18T00:58:12.421+08:00"
}), d)
})
}} )
well here is the code which updates the nested fields of object residing inside an array
r.db('DB').table('LOGS')
.get('ID')
.update({
EVENT_CODE: r.row('EVENT_CODE')
.changeAt(1, r.row('EVENT_CODE').nth(1)
.merge({"CODE_NAME": "MESSAGE_DELIVERED_TO_APP2"}))
})

Resources