MongoDB object property $exists in nested array - arrays

I have a folowing object structure in my db collection:
{
"name" : "test",
"code" : "test",
"attributes" : [
{
"name" : "test1",
"code" : "code1"
},
{
"name" : "test2",
"code" : "code2",
"value" : true
},
{
"name" : "test3",
"code" : "code3",
"value" : ""
},
{
"name" : "test4",
"code" : "code4"
"value" : [
{
"code" : "code4.1",
"name" : "test4.1"
},
{
"name" : "test4.2"
}
]
}
]
}
So "value" property can be empty string, boolean, array or even not defined at all.
How can I make query to list objects that have non-empty attributes array and don't have "attributes.value" property defined inside at least one object inside array?
p.s. I tried following query:
db.collection.find({"attributes": {$exists: true, $ne: []}, "attributes.value": {$exists: false}})
but query result is empty.

The $elemMatch operator matches documents that contain an array field
with at least one element that matches all the specified query
criteria.
This query work for me:
db.getCollection('testeur').find({ "attributes": {
$exists: true,
$ne: [],
$elemMatch: { "value": {$exists: false } }
}
})

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

Update nested array field for multiple docs at once

I have a huge number of records in the collection on the below structure.
Here I want to update all floor fields as an empty string "", wherever it's "n/a". It should not affect other blocks which already have value for the floor like the First, Second Floor.
Can someone help on this?
{
"id" : "181",
"EID" : "83",
"History" : [
{
"aNum" : "12324",
"dev" : [
{
"type" : "",
"room" : "Office",
"floor" : "Second Floor"
},
{
"type" : "",
"room" : "Bedroom",
"floor" : "n/a"
},
{
"type" : "",
"room" : "Bedroom",
"floor" : "First Floor"
},
{
"type" : "",
"room" : "Bedroom",
"floor" : "n/a"
},
]
}
]
}
With arrayFilters and filtered $[<identifier>] operator.
db.collection.update({},
{
$set: {
"History.$[].dev.$[dev].floor": ""
}
},
{
arrayFilters: [
{
"dev.floor": "n/a"
}
],
multi: true
})
Sample Mongo Playground

Update array at specific index by other filed in MongoDB

I have a collection, consist of name and data.
data is an array with 2 elements, each element is the object with code and qty.
{
"_id" : ObjectId("605c666a15d2612ed0afedd2"),
"name" : "Anna",
"data" : [
{
"code" : "a",
"qty" : 3
},
{
"code" : "b",
"qty" : 4
}
]
},
{
"_id" : ObjectId("605c666a15d2612ed0afedd3"),
"name" : "James",
"data" : [
{
"code" : "c",
"qty" : 5
},
{
"code" : "d",
"qty" : 6
}
]
}
I want to update the code of the first element to name of its document. The result I want is
{
"_id" : ObjectId("605c666a15d2612ed0afedd2"),
"name" : "Anna",
"data" : [
{
"code" : "Anna",
"qty" : 3
},
{
"code" : "b",
"qty" : 4
}
]
},
{
"_id" : ObjectId("605c666a15d2612ed0afedd3"),
"name" : "James",
"data" : [
{
"code" : "James",
"qty" : 5
},
{
"code" : "d",
"qty" : 6
}
]
}
I just google to find how to:
update array at a specific index (https://stackoverflow.com/a/34177929/11738185)
db.Collection.updateMany(
{ },
{
$set:{
'data.0.code': '$name'
}
}
)
But the code of the first element in data array is a string '$name', not a value (Anna, James)
{
"_id" : ObjectId("605c666a15d2612ed0afedd2"),
"name" : "Anna",
"data" : [
{
"code" : "$name",
"qty" : 3
},
{
"code" : "b",
"qty" : 4
}
]
},
{
"_id" : ObjectId("605c666a15d2612ed0afedd3"),
"name" : "James",
"data" : [
{
"code" : "$name",
"qty" : 5
},
{
"code" : "d",
"qty" : 6
}
]
}
update a field by the value of another field. It takes me to use pipeline updating (https://stackoverflow.com/a/37280419/11738185): the second param of updateMany is array (pipeline)
db.Collection.updateMany(
{ },
[{
$set:{
'data.0.code': '$name'
}
}]
)
and It adds field 0 to each element in data array
{
"_id" : ObjectId("605c666a15d2612ed0afedd2"),
"name" : "Anna",
"data" : [
{
"0" : {
"code" : "Anna"
},
"code" : "a",
"qty" : 3
},
{
"0" : {
"code" : "Anna"
},
"code" : "b",
"qty" : 4
}
]
},
{
"_id" : ObjectId("605c666a15d2612ed0afedd3"),
"name" : "James",
"data" : [
{
"0" : {
"code" : "James"
},
"code" : "c",
"qty" : 5
},
{
"0" : {
"code" : "James"
},
"code" : "d",
"qty" : 6
}
]
}
I can't find the solution for this case. Could anyone to help me? How can I update array at fixed index by other field. Thanks for reading!
1. update array at a specific index
You can't use internal fields as value of another fields, it will work only when you have external value to update like { $set: { "data.0.code": "Anna" } }.
2. update a field by the value of another field
Update with Aggregation pipeline can't allow to access data.0.code syntax.
You can try using $reduce in update with aggregation pipeline,
$reduce to iterate loop of data array, set empty array in initialValue of reduce, Check condition if initialValue array size is zero then replace code with name and merge with current object using $mergeObjects, else return current object,
$concatArrays to concat current object with initialValue array
db.collection.update({},
[{
$set: {
data: {
$reduce: {
input: "$data",
initialValue: [],
in: {
$concatArrays: [
"$$value",
[
{
$cond: [
{ $eq: [{ $size: "$$value" }, 0] },
{ $mergeObjects: ["$$this", { code: "$name" }] },
"$$this"
]
}
]
]
}
}
}
}
}],
{ multi: true }
)
Playground
I think easier would be another way.
Just save the model before and use it for updating after
var annaModel = nameModel.findOne({_id: "605c666a15d2612ed0afedd2" })
nameModel.findOneAndUpdate({_id: "605c666a15d2612ed0afedd2"},{$set:{'data.0.code': annaModel.name}})

How to group records based on array elements using MongoDB

I have a data collection which contains a set of records in the following format.
{
"_id" : 22,
"title" : "3D User Interfaces with Java 3D",
"isbn" : "1884777902",
"pageCount" : 520,
"publishedDate" : ISODate("2000-08-01T07:00:00Z"),
"thumbnailUrl" : "https://s3.amazonaws.com/AKIAJC5RLADLUMVRPFDQ.book-thumb-images/barrilleaux.jpg",
"longDescription" : "Description",
"status" : "PUBLISH",
"authors" : [
"Jon Barrilleaux"
],
"categories" : [
"Java",
"Computer Graphics"
]
},
{
"_id" : 23,
"title" : "Specification by Example",
"isbn" : "1617290084",
"pageCount" : 0,
"publishedDate" : ISODate("2011-06-03T07:00:00Z"),
"thumbnailUrl" : "https://s3.amazonaws.com/AKIAJC5RLADLUMVRPFDQ.book-thumb-images/adzic.jpg",
"status" : "PUBLISH",
"authors" : [
"Gojko Adzic"
],
"categories" : [
"Software Engineering"
]
}
Please note that the 'categories' is an array.
I want to count the published books for each category. I tried the following solution, but it treated the entire array as one group.
db.books.aggregate([
{
$group:{_id:"$categories", total:{$sum:1}}
}
])
Instead of so, I want to count the number of records for each individual category value inside 'categories' array.
You should first use $unwind which outputs one document for each element in the array.
db.books.aggregate([
{
$unwind : "$categories"
},
{
$group : { _id : "$categories", total: { $sum: 1 } }
}
])

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