MongoDB Remove Empty Object from Array after aggregate function - arrays

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

Related

MongoDB query to return docs based on an array size after filtering array of JSON objects?

I have MongoDB documents structured in this way:
[
{
"id": "car_1",
"arrayProperty": [
{
"model": "sedan",
"turbo": "nil"
},
{
"model": "sedan",
"turbo": "60cc"
}
]
},
{
"id": "car_2",
"arrayProperty": [
{
"model": "coupe",
"turbo": "50cc"
},
{
"model": "coupe",
"turbo": "60cc"
}
]
}
]
I want to be able to make a find query that translates into basic English as "Ignoring all models that have 'nil' value for 'turbo', return all documents with arrayProperty of length X." That is to say, the "arrayProperty" of car 1 would be interpreted as having a size of 1, while the array of car 2 would have a size of 2. The goal is to be able to make a query for all cars with arrayProperty size of 2 and only see car 2 returned in the results.
Without ignoring the nil values, the query is very simple as:
{ arrayProperty: { $size: 2} }
And this would return both cars 1 and 2. Moreover, if our array was just a simple array such as:
[1,2,3,'nil]
Then our query is simply:
{
arrayProperty: {
$size: X,
$ne: "nil"
}
}
However, when we introduce an array of JSON objects, things get tricky. I have tried numerous things to no avail including:
"arrayProperty": {
$size: 2,
$ne: {"turbo": "nil"}
}
"arrayProperty": {
$size: 2,
$ne: ["arrayProperty.turbo": "nil"]
}
Even without the $size operator in there, I can't seem to filter by the nil value. Does anyone know how I would properly do this in those last two queries?
use $and in $match
db.collection.aggregate([
{
$match: {
"$and": [
{
arrayProperty: {
$size: 2
}
},
{
"arrayProperty.turbo": {
$ne: "nil"
}
}
]
}
}
])
mongoplayground
use $set first
db.collection.aggregate([
{
"$set": {
"arrayProperty": {
"$filter": {
"input": "$arrayProperty",
"as": "a",
"cond": {
$ne: [
"$$a.turbo",
"nil"
]
}
}
}
}
},
{
$match: {
arrayProperty: {
$size: 1
}
}
}
])
mongoplayground
set a new field of size
db.collection.aggregate([
{
"$set": {
"size": {
$size: {
"$filter": {
"input": "$arrayProperty",
"as": "a",
"cond": {
$ne: [
"$$a.turbo",
"nil"
]
}
}
}
}
}
},
{
$match: {
size: 1
}
}
])
mongoplayground

How to perform Greater than Operator in MongoDb Compass Querying inside inner object collection

Here is my Json in Mongo DB Compass. I am just querying greater than rating products from each collection.
Note: if I am doing with pageCount it is working fine because that is not inside a collection.
{PageCount:{gte:2}} -- works.
Problem with inner arrays collection of collection if anyone matches it displays all.
When we are doing the below query if anyone of the index have greater than 99 it shows all the values.
{"ProductField.ProductDetailFields.ProductDetailInfo.ProductScore.Rating": {$exists:true, $ne: null , $gte: 99}}
----- if I perform above query, I am getting this output.
How to iterate like foreach kind of things and check the condition in MongoDB querying
{
"_id":{
"$oid":"5fc73a7b3fb52d00166554b9"
},
"ProductField":{
"PageCount":2,
"ProductDetailFields":[
{
"PageNumber":1,
"ProductDetailInfo":[
{
"RowIndex":0,
"ProductScore":{
"Name":"Samsung",
"Rating":99
},
},
{
"RowIndex":1,
"ProductScore":{
"Name":"Nokia",
"Rating":96
},
},
{
"RowIndex":2,
"ProductScore":{
"Name":"Apple",
"Rating":80
},
}
]
}
]
}
},
{
"_id":{
"$oid":"5fc73a7b3fb52d0016655450"
},
"ProductField":{
"PageCount":2,
"ProductDetailFields":[
{
"PageNumber":1,
"ProductDetailInfo":[
{
"RowIndex":0,
"ProductScore":{
"Name":"Sony",
"Rating":93
}
},
{
"RowIndex":1,
"ProductScore":{
"Name":"OnePlus",
"Rating":93
}
},
{
"RowIndex":2,
"ProductScore":{
"Name":"BlackBerry",
"Rating":20
}
}
]
}
]
}
}
#Misky How to run this query execute:
While run this query in Mongo Shell - no sql client throws below error. we are using 3.4.9 https://www.nosqlclient.com/demo/
Is this somewhat close to your idea
db.collection.aggregate({
$addFields: {
"ProductField.ProductDetailFields": {
$map: {
"input": "$ProductField.ProductDetailFields",
as: "pdf",
in: {
$filter: {
input: {
$map: {
"input": "$$pdf.ProductDetailInfo",
as: "e",
in: {
$cond: [
{
$gte: [
"$$e.ProductScore.Rating",
99
]
},
{
$mergeObjects: [
"$$e",
{
PageNumber: "$$pdf.PageNumber"
}
]
},
null
]
}
}
},
as: "i",
cond: {
$ne: [
"$$i",
null
]
}
}
}
}
}
}
},
{
$addFields: {
"ProductField.ProductDetailFields": {
"$arrayElemAt": [
"$ProductField.ProductDetailFields",
0
]
}
}
})
LIVE VERSION

How could we merge nested arrays of subdocuments(Array within Array) in MongoDB

I would like to combine arrays within arrays in MongodB
for example,
test collection has documents in following formats
**{
cmp_id :1
depts : [{"dept_id":1, emps:[1,2,3]}, {"dept_id":2, emps:[4,5,6]}, {"dept_id":2, emps:[7,8,9]} ]
}**
I need following output, How can I?
***{
cmp_id :1,
empids : [1,2,3,4,5,6,7,8,9]
}***
You can use below aggregation
db.collection.aggregate([
{
$project: {
depts: {
$reduce: {
input: "$depts",
initialValue: [],
in: {
$concatArrays: [
"$$value",
"$$this.emps"
]
}
}
}
}
}
])
MongoPlayground
db.collection.aggregate(
// Pipeline
[
// Stage 1
{
$unwind: {
path: "$depts"
}
},
// Stage 2
{
$unwind: {
path: "$depts.emps"
}
},
// Stage 3
{
$group: {
_id: '$cmp_id',
empids: {
$push: '$depts.emps'
}
}
},
]
);

mongodb array aggregation extract to parent

I am trying to get first date from inner array in mongodb object and add it to it's parent with aggregation. Example:
car: {
"model": "Astra",
"productions": [
"modelOne": {
"dateOfCreation": "2019-09-30T10:15:25.026+00:00",
"dateOfEstimation": "2017-09-30T10:15:25.026+00:00",
"someOnterInfo": "whatever"
},
"modelTwo": {
"dateOfCreation": "2017-09-30T10:15:25.026+00:00",
"dateOfEstimation": "2019-09-30T10:15:25.026+00:00",
"someOnterInfo": "whatever"
}
]
}
to be turned in
car: {
"model": "Astra",
"earliestDateOfEstimation": "2017-09-30T10:15:25.026+00:00",
"earliestDateOfCreation": "2017-09-30T10:15:25.026+00:00"
}
How can I achieve that?
I'm assuming that modelOne and modelTwo are unknown when you start your aggregation. The key step is to run $map along with $objectToArray in order to get rid of those two values. Then you can just use $min to get "earliest" values:
db.collection.aggregate([
{
$addFields: {
dates: {
$map: {
input: "$car.productions",
in: {
$let: {
vars: { model: { $arrayElemAt: [ { $objectToArray: "$$this" }, 0 ] } },
in: "$$model.v"
}
}
}
}
}
},
{
$project: {
_id: 1,
"car.model": 1,
"car.earliestDateOfEstimation": { $min: "$dates.dateOfEstimation" },
"car.earliestDateOfCreation": { $min: "$dates.dateOfCreation" },
}
}
])
Mongo Playground
EDIT:
First step can be simplified if there's always modelOne, 'modelTwo'... (fixed number)
db.collection.aggregate([
{
$addFields: {
dates: { $concatArrays: [ "$car.productions.modelOne", "$car.productions.modelTwo" ] }
}
},
{
$project: {
_id: 1,
"car.model": 1,
"car.earliestDateOfEstimation": { $min: "$dates.dateOfEstimation" },
"car.earliestDateOfCreation": { $min: "$dates.dateOfCreation" },
}
}
])
Mongo Playground (2)

Updating data type to an Object in mongoDB

I have changed one of the fields of my collection in mongoDB from an array of strings to an array of object containing 2 strings. New documents get inserted without any problem, but when a get method is called to get , querying all the documents I get this error:
Failed to decode 'Students'. Decoding 'photoAddresses' errored
with: readStartDocument can only be called when CurrentBSONType is
DOCUMENT, not when CurrentBSONType is STRING.
photoAddresses is the field that was changed in Students.
I was wondering is there any way to update all the records so they all have the same data type, without losing any data.
The old version of photoAdresses:
"photoAddresses" : ["something","something else"]
This should be updated to the new version like this:
"photoAddresses" : [{photoAddresses:"something"},{photoAddresses:"something else"}]
The following aggregation queries update the string array to object array, only if the array has string elements. The aggregation operator $map is used to map the string array elements to objects. You can use any of the two queries.
db.test.aggregate( [
{
$match: {
$expr: { $and: [ { $isArray: "$photo" },
{ $gt: [ { $size: "$photo" }, 0 ] }
]
},
"photo.0": { $type: "string" }
}
},
{
$project: {
photo: {
$map: {
input: "$photo",
as: "ph",
in: { addr: "$$ph" }
}
}
}
},
] ).forEach( doc => db.test.updateOne( { _id: doc._id }, { $set: { photo: doc.photo } } ) )
The following query works with MongoDB version 4.2+ only. Note the update operation is an aggregation instead of an update. See updateMany.
db.test.updateMany(
{
$expr: { $and: [ { $isArray: "$photo" },
{ $gt: [ { $size: "$photo" }, 0 ] }
]
},
"photo.0": { $type: "string" }
},
[
{
$set: {
photo: {
$map: {
input: "$photo",
as: "ph",
in: { addr: "$$ph" }
}
}
}
}
]
)
[EDIT ADD]: The following query works with version MongoDB 3.4:
db.test.aggregate( [
{
$addFields: {
matches: {
$cond: {
if: { $and: [
{ $isArray: "$photoAddresses" },
{ $gt: [ { $size: "$photoAddresses" }, 0 ] },
{ $eq: [ { $type: { $arrayElemAt: [ "$photoAddresses", 0 ] } }, "string" ] }
] },
then: true,
else: false
}
}
}
},
{
$match: { matches: true }
},
{
$project: {
photoAddresses: {
$map: {
input: "$photoAddresses",
as: "ph",
in: { photoAddresses: "$$ph" }
}
}
}
},
] ).forEach( doc => db.test.updateOne( { _id: doc._id }, { $set: { photoAddresses: doc.photoAddresses } } ) )

Resources