Mongodb - Find in deeply nested array - arrays

I am trying to find if type=conv is present in given rs , which is present in d --> ds --> rs --> type.
DOCUMENT
{
"_id" : ObjectId("5ec25873bd796ff191e695b1"),
"c_name" : "c1",
"t_name" : "t1",
"d" : [
{
"name" : "d1",
"ds" : [
{
"name" : "ds1",
"rs" : [
{
"type" : "conv"
}
]
}
]
},
{
"name" : "d2",
"ds" : [
{
"name" : "ds2",
"rs" : []
}
]
}
]
}
QUERY:
filter = {
"$and": [
{"c_name": {'$eq': 'c1'}},
{"t_name": {'$eq': 't1'}},
{"d.name": {'$eq': 'd2'}},
{"d.ds.name": {'$eq': 'ds2'}},
{"d.ds.rs.type": {'$eq': 'conv'}}
]
}
OUTPUT
It is returning me document, i guess it is looking for presence of type = conv in full document, even though it is not present on ds2 ( part of d2), but present on ds1 ( part of d1).
Do we have simpler way to find if it exists or not, I would like to first find and then using array filters , we can update the specific element inside deeply nested array.
Could someone please suggest how should I approach this problem? ( if we have any solution without using aggregation )

You need to use $elemMatch for these type of condition, And you can match strings without $eq operator
filter = {"$and": [
{"c_name": "c1"},
{"t_name": "t1"},
{ "d": { $elemMatch: { "name": "d2", "ds.name": "ds2" , "ds.rs.type": "conv"} } }
]}
With $elemMatch It will search only in the array where all condition will be true

Thanks #puneet , i need to try above. However, i am using now this
filter_query = {
"$and": [
{"c_name": {'$eq': 'c1'}},
{"t_name": {'$eq': 't1'}},
{
'd': {
'$elemMatch': {
'name': 'd2',
'ds': {
'$elemMatch':
{'name': 'ds2',
'rs':
{'$elemMatch':
{
"type": 'conv'}
}
}
}
}
}
}]}
This is also working !!

Related

Update nested array in a mongo document

I have below collection
{
"_id" : ObjectId("5d7e6c54c23c210001108556"),
“parentId" : "5d0162ba69cf130001a16115",
"tasks" : [
{
"_id" : "ae60a8f1",
"taskMetaDataIds” : [
ObjectId("5d55a648e2f7320001e578ac")
],
"name" : “meta 3"
},
{
"_id" : "07544d96",
"taskMetaDataIds" : [
ObjectId("5d55a648e2f732676676676”),
ObjectId("5d55a648e2612333556888”)
],
"name" : “meta 2"
},
],
"name" : “New Topic",
"createdBy" : "01526151-8303-450f-b08b-b36a1760b774",
"createdDate" : ISODate("2019-09-15T16:52:36.150+0000"),
"updatedDate" : ISODate("2019-09-15T16:52:36.150+0000")
}
I am looking for below output . Is there an operator which can directly convert the array of objects into array of strings as shown below. I can do it with a script by looping over the taskMetaDataIds array but I am looking to use a direct mongo operator which suits my purpose.
{
"_id" : ObjectId("5d7e6c54c23c210001108556"),
“parentId" : "5d0162ba69cf130001a16115",
"tasks" : [
{
"_id" : "ae60a8f1",
"taskMetaDataIds” : [
"5d55a648e2f7320001e578ac"
],
"name" : “meta 3"
},
{
"_id" : "07544d96",
"taskMetaDataIds" : [
"5d55a648e2f732676676676”,
“5d55a648e2612333556888”
],
"name" : “meta 2"
},
],
"name" : “New Topic",
"createdBy" : "01526151-8303-450f-b08b-b36a1760b774",
"createdDate" : ISODate("2019-09-15T16:52:36.150+0000"),
"updatedDate" : ISODate("2019-09-15T16:52:36.150+0000")
}
I tried below but it does not seem to be working-
db.getCollection("data").updateOne({"_id":ObjectId("5d7e6c54c23c210001108556")}, [{ $set: { "tasks.taskMetaDataIds": "$tasks.taskMetaDataIds.str" } }])
$set - Set tasks array field.
1.1. $map - Iterate each element in the tasks array and return a new array.
1.1.1. $mergeObjects - Merge the current iterated task object and taskMetaDataIds array (from the result 1.1.1.1).
1.1.1.1. $map - Iterate the current iterated task object's taskMetaDataIds array and returns a new array.
1.1.1.1.1. $toString - Convert each iterate id from ObjectId type to string type.
db.getCollection("data").updateOne({
"_id": ObjectId("5d7e6c54c23c210001108556")
},
[
{
$set: {
"tasks": {
$map: {
input: "$tasks",
as: "task",
in: {
$mergeObjects: [
"$$task",
{
"taskMetaDataIds": {
$map: {
input: "$$task.taskMetaDataIds",
in: {
$toString: "$$this"
}
}
}
}
]
}
}
}
}
}
])
Demo # Mongo Playground

Iterating Object of Array in Couchbase

How to iterate Object of Array to get specific key value pair from JSON object. I am using Couchbase Server. Please suggest N1QL for Couchbase.
Below is my JSON format
{
"A":{
"B":{
"C": {
"D":[
{
"attr":[
{
"Name" : "abc"
}
],
"Name" : "outer"
}
], ​
"E":[
{
"attr":[
{
"Name" : "xyz"
}
],
"Name" : "outer1"
}
​]
}
}
}
}
​
Want to retrieve value for Name attribute inside each array of object C. Please note there are two fields with same name inside Array , so want to just access outer one. Key value pair inside C is dynamic.
Thanks in Advance!
If you really looking for all the names objects of path A.B.C
use the following expression
ARRAY v.Name FOR v WITHIN A.B.C WHEN v.Name IS NOT MISSING END
SELECT ARRAY v.Name
FOR v WITHIN d.A.B.C
WHEN v.Name IS NOT MISSING
END AS val
FROM [{ "A":{ "B":{ "C": { "D":[{"Name" : "test1", "state" : "state1"} ],
"E":[{"Name" : "test2", "state" : "state2"} ],
"F":[{"Name" : "test2", "state" : "state2"} ]
}
}
}
}
] AS d;
{
"val": [
"test1",
"test2",
"test2"
]
}
If you need to pick only some you need to have some other field present in that object consistently.
ARRAY v.Name FOR v WITHIN A.B.C WHEN v.Name IS NOT MISSING AND v.attr IS NOT MISSING END
SELECT ARRAY v.Name FOR v WITHIN d.A.B.C WHEN v.Name IS NOT MISSING AND v.attr IS NOT MISSING END
FROM [{ "A":{ "B":{ "C": { "D":[ { "attr":[ { "Name" : "abc" } ], "Name" : "outer" } ], "E":[ { "attr":[ { "Name" : "xyz" } ], "Name" : "outer1" } ] } } } }] AS d;
If you already know the predefined path (don't use recursive).
Construct ARRAY of the values of the dynamic field object. As the value is already ARRAY flatten by 1 level, Then iterate over ARRAY and get Name. If you need unique use ARRAY_DISTINCT()
ARRAY v.Name FOR v IN ARRAY_FLATTEN((ARRAY nv FOR n:nv IN d.A.B.C END),1) END
SELECT ARRAY v.Name FOR v IN ARRAY_FLATTEN((ARRAY nv FOR n:nv IN d.A.B.C END),1) END AS obj
FROM [{ "A":{ "B":{ "C": { "D":[ { "attr":[ { "Name" : "abc" } ], "Name" : "outer" } ], "E":[ { "attr":[ { "Name" : "xyz" } ], "Name" : "outer1" } ] } } } }] AS d;

How can I insert a new entry inside Multilevel structure of mongoDB database?

I am working on an app that contain details of an academic year in Multilevel structure of mongoDB database.
For this I've created structure like this:
[
{
"_id" : ObjectId("5a1519a71fe8cc4df5888ff5"),
"academicyear" : "2016-2017",
"departments" : [
{
"deptname" : "computer",
"semesters" : [
{
"sem" : "2",
"classes" : [
{
"class" : "1",
"theory" : [
{
"subname" : "DS",
"faculty" : [
{
"facultyname" : "KSS",
"facultydept" : "COMP"
}
]
}
],
"practical" : [
{
"subname" : "DS",
"batch" : [
{
"bname" : "C",
"facultyname" : "KSS",
"facultydept" : "COMP"
}
]
}
]
}
]
}
]
}
]
}
]
Now for the same academic year and for the same department I would like to create a new semester node inside the array of semesters. For that I've tried syntax of $ and use of . operator but it did not worked well. How can I add a new semester node inside that semesters array? Also after creating a new semester node, how can I add a new class in an classes array?
it could be done by means of the positional operator $[],
db.collection.update(
{ "academicyear": "2016-2017", "departments.deptname": "computer"},
{ "$push":
{"departments.$[].semesters":
{
"sem" : "2",
"classes" : [ ...
}
}
}
)
It could also be done by indicating the position in the array if known. "departments.1.semesters", where 1 is the position of 'departments' in the array.
db.collection.update(
{ "academicyear": "2016-2017"},
{ "$push":
{"departments.1.semesters":
{
"sem" : "2",
"classes" : [ ...
}
}
}
)
One way of doing it would be:
var toInsert = {"sem" : "2", "classes" : [ ... }
db.collection.update(
{ "academicyear": "2016-2017", departments: { $elemMatch: { deptname: "computer" } } },
{ $addToSet: { 'departments.$.semesters': toInsert } }
)
The query part will find the document from the list array with id = 2. Then we use $ positional element to add a new element at that array index.
and the same would apply for classes.
db.collection.update(
{ "academicyear": "2016-2017"},
{ "$push":
{"departments.1.semesters.0.classes":
{
"class" : "1",
"theory" : [{ ...
}
}
}
)
Another alternative would be to extract the document, update it on the returned object and save it again.
db.collection.findOne({"academicyear": "2016-2017", "departments.deptname": "computer"}, {'departments.semesters': 1}, (err, doc) => {
if(err) return handleError(res)(err);
if(!doc) return res.send(404).end();
//I guess semester is 0, but you can find the one that you are going to update by some filter
doc.departments[0].semesters[0].classes.push({...});
doc.save( (err) => {
if(err) return handleError(res)(err)
return res.status(200).end();
});
});

How to remove elements from array of an embedded document which are not in my array

This is my document.
{
"group_name" : "Test Group 6 (Edit)",
"created_on" : "1464367826787",
"group_id" : "group_14",
"members" : [
{
"user_id" : "user_7",
"added_on" : "1464367826787"
}
],
"is_deleted" : 0
}
I'm having list of user_ids and I need to remove user_ids which are not in the list of user_ids I have. How to remove "user_50" if input list = ["user_7"] ?
You need to use the $nin operator to select your documents, the updateMany method if you want to update multiple documents or updateOne method to update a single document.
db.users.updateMany(
{},
{ "$pull": { "members": { "user_id": { "$nin": [ "user_7" ] } } }}
)
db.users.update(
{},
{$pull:
{members:{'user_id':{$ne:"user_7"}}}}, {multi:true})

MongoDB: Find all matched array element from single document

I have a mongodb document like this,
{
"_id" : ObjectId("4e8ae86d08101908e1000001"),
"eId": 101,
"about": "test",
"tags" : [
{"name": "jana"},
{"name":"bala"},
{"name":"jk"},
{"name":"charles"}
]
}
I need to find all matched array elements, where the name matched with given array.
db.coll.find({"tags": {"$elemMatch": {"name": {"$in": [/^jana/i, /^charles/i] }}}})
for this query i got the following result
{
"_id" : ObjectId("4e8ae86d08101908e1000001"),
"tags" : [
{
"name" : "jana"
}
]
}
$elemMatch query only return the first matched element, but i want all the matched array element like this,
{
"_id" : ObjectId("4e8ae86d08101908e1000001"),
"tags" : [
{
"name" : "jana"
},
{
"name" : "charles"
}
]
}
Is it possible to get the result like this?
note: i don't want any others fields, I want only the matched array elements along with _id
You can use MongoDB Aggregation Pipeline:
db.coll.aggregate([
{'$unwind': '$tags'},
{'$match':
{"tags.name":
{"$in": [/^jana/, /^charles/i] }
}
},
{'$group':
{
'_id': '$_id',
'tags':
{'$push': '$tags'}
}
}
])
Result : -
{
"result" : [
{
"_id" : ObjectId("5538b214706a90c718f75a41"),
"tags" : [
{
"name" : "jana"
},
{
"name" : "charles"
}
]
}
],
"ok" : 1
}

Resources