Ignoring NULL values within an aggregate operation in MongoDB - mongodb

I have the following MongoDB aggregate operation which is working fine but it also seems to be returning NULL values.
How can I ignore NULL values against projectIP field?
db.inventory.aggregate(
[
{ $match: {projectIP: { $exists:true }}},
{ $project: {projectIP: "$projectIP",_id : 0}},
{ $group: {_id: "$projectIP"}},
{ $sort: {projectIP: 1}}
];
)

Seems some of the keys contain null values. Add this as well
{ $match: { projectIP: { $exists:true, $ne: null }}}
by replacing the first stage in your query

You can assign a value (0 or anything) to them instead of a null value.
Here how you do it
projectIP: { $ifNull: [ "$projectIP", 0.0 ] }

Related

How do i find the total number of subjectsthat has no prerequisites using agregation?

I have tried several codes but it didn't work.
Example from the database,
one has a prerequisite and one does not have prerequisites and I would like to find the total number of the subject with no prerequisites :
db.Subject.insert(
{
"_id":ObjectId(),
"subject":{
"subCode":"CSCI321",
"subTitle":"Final Year Project",
"credit":6,
"type":"Core",
"assessments": [
{ "assessNum": 1,
"weight":30,
"assessType":"Presentation",
"description":"Prototype demonstration" },
{ "assignNum": 2,
"weight":70,
"assessType":"Implementation and Presentation",
"description":"Final product Presentation and assessment of product implementation by panel of project supervisors" }
]
}
}
)
db.Subject.insert(
{
"_id":ObjectId(),
"subject":{
"subCode":"CSCI203",
"subTitle":"Algorithm and Data Structures",
"credit":3,
"type":"Core",
"prerequisite": ["csci103"]
}})
one of the few codes that I tried using :
db.Subject.aggregate({$group:{"prerequisite":{"$exists": null}, count:{$sum:1}}});
Results :
_getErrorWithCode#src/mongo/shell/utils.js:25:13
doassert#src/mongo/shell/assert.js:18:14
_assertCommandWorked#src/mongo/shell/assert.js:534:17
assert.commandWorked#src/mongo/shell/assert.js:618:16
DB.prototype._runAggregate#src/mongo/shell/db.js:260:9
DBCollection.prototype.aggregate#src/mongo/shell/collection.js:1062:12
#(shell):1:1
You can use $match to eliminate unwanted documents and $group to calculate sum
db.collection.aggregate([
{
$match: {
"subject.prerequisite": {
"$exists": false
}
}
},
{
$group: {
_id: null,
total: {
$sum: 1
}
}
}
])
Working Mongo playground
This can be achieved within a single aggregation pipeline stage i.e. the $group step where you can use the BSON Types comparison order to aggregate the
documents where the 'subjects.prerequisites' field exists and has at least an element. The condition can be used as the group by key i.e. the _id field
in $group.
Consider running the following aggregation pipeline to get the desired results:
db.Subject.aggregate([
{ $group: {
_id: {
$cond: [
{
$or: [
{ $lte: ['$subject.prerequisite', null] },
{
$eq: [
{ $size: { $ifNull: ['$subject.prerequisite', [] ] } },
0
]
}
]
},
'noPrerequisite',
'havePrerequisite'
]
},
count: { $sum: 1 }
} }
])
The first condition in the OR simply returns true if a document does not have the embedded prerequisites field and the other satisfies these set of conditions:
if length of ( prerequisites || [] ) is zero
In the above, $cond takes a logical condition as its first argument (if) and then returns the second argument where the evaluation is true (then) or the third argument where false (else). When used as an expression in the _id field for $group, it groups all the documents into either true/false which is conditionally projected as "noPrerequisite" (true) OR "havePrerequisite" (false) in the group key.
The results will contain both counts for documents where the prerequisite field exists and for those without the field OR it has an empty array.

MongoDB query for null using dot syntax

Im having trouble querying mongodb for null values using the dot syntax of mongo.
Some things in a db:
db.things.insertMany([
{ a: [{ value: 1 }] },
{ a: [{ value: null }] },
{ a: [{ value: 2 }] }
]);
I want to find all of the documents which have the first element in the 'a' array having a null value.
Queries:
db.getCollection('things').count({ "a.0.value": 1 }) => 1 (as expected)
db.getCollection('things').count({ "a.0.value": null }) => 3 (I would expect 1 here also)
I'm at a bit of a loss as to why this is returning all the elements for the second query. It only seems to have this behaviour for array indexed results, which also makes it kind of weird. (eg db.getCollection('things').count({ "a": null }) => 0 as expected)
The only thing I can think of is that its basically cancelling out the whole statement when it has the value null but I don't know how to get around this.
MongoDB v3.4.10
You can use $expr to use aggregation operator and then find the first index using $arrayElemAt which is $equal to null
db.collection.find({ "$expr": { "$eq": [{ "$arrayElemAt": ["$a.value", 0] }, null] } })
MongoPlayground
For the mongo version prior to 3.6
db.collection.aggregate([
{ "$addFields": {
"match": { "$arrayElemAt": ["$a.value", 0] }
}},
{ "$match": { "match": null }}
])
MongoPlayground
If you even want to check with .dot syntax then you have to use $type operator to compare with the null values
db.collection.find({ "a.0.value": { "$type": 10 } })
MongoPlayground

How do I filter out null array elements from a group value in a mongo aggregate query?

My aggregate $group is:
{
$group: {
_id: { ["$dayOfYear"]: "$TagDateCreated" },
disqualified: {
$addToSet: {
$cond: {
if: { $in: [ "$TagId", [109,220,115,113,238] ]},
then: "$ContactId",
else: null
}
}
}
}
}
Which gives me a unique set of contactId's as disqualified but I need the null values removed. I've tried omitting the else statement int he $cond and various $filters in the $project that either don't remove anything, remove everything, or error.
You can add next pipeline stage after $group:
{
$addFields: {
disqualified: {
$filter: {
input: "$disqualified",
as: "d",
cond: {
$ne: [ "$$d", null ]
}
}
}
}
}
$addFields will overwrite existing array and using $filter you can remove null value

How to sort in mongo based on a field but give preference to documents satisfying certain condition?

I have a mongodb collection on which I was sorting based on their last updation date using { $sort : { updated_at : -1 } }.
Now I am trying to update my query to still sort by updated_at but give first preference to those documents which satisfy a certain condition like a field's array length in greater than zero i.e. {'img.0': { $exists: true }}
But how do I combine it in the sort query?
My requirement is that the query should first return all the documents in which img length is greater than 0 sorted by updated_at and then list the remaining documents sorted by updated_at
Thanks
Use the aggregation framework where your pipeline creates an extra field that holds a flag representing the criteria above and you can use it as your sort option together with the updated_at field:
db.test.aggregate([
{
"$project": {
"img": 1,
"updated_at": 1,
"other_fields": 1,
"hasImages": {
"$cond": [
{
"$ne": [
{
"$size": { "$ifNull": [ "$img", [] ] }
},
0
]
},
1, 0
]
}
}
},
{
"$sort": {
"hasImages": -1, "updated_at": - 1
}
}
])
Check the test demo below
db.collection("collectionName").find({'img.0': { $exists: true }}).sort({ updated_at : -1 })
or
db.collection.aggregate([
{ $match: {'img.0': { $exists: true }}},
{$sort: {"updated_at" : -1}}
])

How to determine if average is 0 vs null?

I've got a Mongo database where I run some aggregation queries. Here's the simplified query I want to run:
db.coll.aggregate([
{ $group: {
_id: 'fieldA',
fieldB: { $avg: '$fieldB' }
} },
])
It groups data by fieldA and calculates average for fieldB. Anyway, some rows in result set have 0 as value for fieldB. There can be 2 reasons for that:
Average value IS 0.
All documents in a group didn't have fieldB (or had null as a value); and Mongo behavior is to return 0 in that case.
Is it possible to determine which scenario took place for each row in resulting selection without issuing other query and without leaving aggregation pipeline?
UPDATE
I can't filter out non-null fields, as I'm doing aggregation for few fields, like that:
db.coll.aggregate([
{ $group: {
_id: 'fieldA',
fieldB: { $avg: '$fieldB' },
fieldC: { $avg: '$fieldC' }
} },
])
Some of the documents may have fieldB but not fieldC and vice versa.
You can filter the data by using $match before your $group operation.
db.coll.aggregate([
{ $match: { fieldB : {$ne : null }}}},
{ $group: {
_id: 'fieldA',
fieldB: { $avg: '$fieldB' }
} },
])
This way you will get only documents that have fieldB set.
UPDATE
You can't use the $avg that way but you can find out if all values are NULL using $min operator:
db.coll.aggregate([
{ $group: {
_id: 'fieldA',
fieldB: { $avg: '$fieldB' } ,
fieldBAllNullOrMin: { $min: '$fieldB' }
} },
])
The $min operator will return null if all values are null, otherwise it will return min. value (but only in 2.4+ versions of MongoDB).
You can use the $max (or $min) operator to determine whether all
instances of fieldB in a group are null or missing, as the $max (or
$min) operator return null in that case. Given this aggregation
pipeline:
c.aggregate([
{$group: {
_id: '$fieldA',
avg: {$avg: '$fieldB'},
max: {$max: '$fieldB'},
}}
])
with these documents:
c.insert({fieldA: 1, fieldB: 3})
c.insert({fieldA: 1, fieldB: -3})
the result is:
{"_id": 1, "avg": 0, "max": 3}
whereas with these documents:
c.insert({fieldA: 1})
c.insert({fieldA: 1})
the result is:
{"_id": 1, "avg": 0, "max": null}
The null value for the max field tells you that fieldB was null or
missing in all documents in the group.
Hope this helps,
Bruce