Projecting $facet output to an object - mongodb

I created an aggregation ending with a facet operation, producing the following result:
[
{
"all": [
{
"all": 6948
}
],
"none": [
{
"none": 207
}
]
}
]
What I need is an output like below:
{
all: 6948,
none: 207
}
I tried some $projections but couldn't get rid of the arrays outside and inside of the result. Is this possible with $project or should I try something else?

try this code
db.collection.aggregate([
{
$unwind: "$all"
},
{
$unwind: "$none"
},
{
$project: {
_id: 0,
all: "$all.all",
none: "$none.none"
}
}
])
check in mongo playground

Use project stage after facet as
{$project:{
all:{$arrayElemAt:["$all.all",0]},
none:{$arrayElemAt:["$none.none",0]},
}}

Try this:
{
$project: {
all: {
$arrayElemAt: [
"$all",
0
]
},
none: {
$arrayElemAt: [
"$none",
0
]
}
}
},
{
$project: {
all: "$all.all",
none: "$none.none"
}
}

Related

add condition to most recent values result

mongoplayground
My result include 'mostRecentValues' and I wish to project values only if they are not the same. My struggle is in adding the condition to show only if there's a change between the 2 values (in this case mostRecentValues).
my_coll.create_index([('car_id',1),('timestamp',-1)], unique=True)
When an operation involves turning on an off full documents, think of $group. Adding this to your pipeline should point you in the right direction.
(playground)
db.collection.aggregate([
{
"$group": {
_id: "cars",
"cars": {
"$push": {
"$cond": [
{
"$ne": [
{
"$arrayElemAt": [
"$mostRecentValues",
0
]
},
{
"$arrayElemAt": [
"$mostRecentValues",
1
]
}
]
},
"$$ROOT",
"$$REMOVE"
]
}
}
}
},
{
$unwind: "$cars"
}
])
If you only have 3 fields on each document you could add:
{
$project: {
car_id: "$cars.car_id",
mostRecentTime: "$cars.mostRecentTime",
mostRecentValues: "$cars.MostRecentValues"
}
}

About Mongo Group by where arr.length>0

Just assume following data:
{_id:1,hotelcode:a,availdates:["2020-01-02","2020-02-03"]}
{_id:2,hotelcode:a,availdates:["2020-02-03"]}
{_id:3,hotelcode:b,availdates:[]}
{_id:4,hotelcode:b,availdates:["2020-01-02"]}
{_id:5,hotelcode:c,availdates:["2020-01-02","2020-02-03"]}
I wanna achieve:
select hotelcode,count(hotelcode) from table group by hotelcode where availdates.length>0
What should I do?
I tried:
db.getCollection('spl_rate_27').aggregate([
{$project:{
adlength:{$size:"$avail_dates"}}
},
{$match:{adlength:{$gt:1}}},
{$group:{_id:{hotelcode:"$hotel_code"},total:{$sum:1}}}
])
But I got :
{
"_id" : {
"hotelcode" : null
},
"total" : 99999,0
}
It seems something was wrong...But I can't find it out....
You can do something like following, first get the objects whose availdates is greater than 0
[
{
$match: {
$expr: {
$gt: [
{
$size: "$availdates"
},
0
]
}
}
},
{
$group: {
_id: "$hotelcode",
total: {
$sum: 1
}
}
},
{
$project: {
_id: 0,
hotelcode: "$_id",
total: 1
}
}
]
Working Mongo playground
There are two things you can change.
Instead of $project use $addFields - project restricts fields, addFields adds field to the document
Then use $gte in the query as you need >0.
play
db.collection.aggregate([
{
$addFields: {
adlength: {
$size: "$availdates" //misspelled
}
}
},
{
$match: {
adlength: {
$gte: 1
}
}
},
{
$group: {
_id: {
hotelcode: "$hotelcode" //misspelled
},
total: {
$sum: 1
}
}
}
])
Well , I got inspiration from #Gibbs' answer. And I changed a bit my script:
db.getCollection('table').aggregate([
{$project:{
hotelcode:1, ##I omit this!!!
adlength:{$size:"$availdates"}}
},
{$match:{"adlength":{$gt:0}}},
{$group:{_id:{hotelcode:"$hotelcode"},total:{$sum:1}}}
])
And it works perfectly!
I hope this is what you are expecting.
db.collection.aggregate({
$match: {
"availdates": {
"$gt": "1"
}
}
},
{
$group: {
_id: "$hotelcode",
"records": {
$push: "$$ROOT"
},
"dataCount": {
$sum: 1
}
}
})
Working demo url : Mongo Playground URL

MongoDB, is there a statement similar to if then elif in sql?

Query is to find people whose age is shown and if their grade is 5 or higher then they will get honours, othewise fail. Can I use $cond with find?
db.test.find({age:{$exists:true}},{$cond: {if: "grade":5, then:"Honours" : true, else: "Honours" : "fail"}})
I also tried
db.test.find({$and: [{age:{$exists:true}}, {grade: {$gte:"5"}}]}, {$set:{"Honours":"true"}}, {multi:true})
But neither come close. Any help/direction is very much appreciated.
Check this.
db.test.aggregate([
{
$match: {
"age": {
"$exists": true
}
}
},
{
$project: {
"Honours": {
$switch: {
branches: [
{
case: {
"$gte": [
"$grade",
5
]
},
then: "true"
}
],
default: "fail"
}
}
}
}
])
.find projection only allows include / exclude fields, neither transform them nor add new field. Use MongoDB aggregation
db.test.aggregate([
{
$match: {
age: {
$exists: true
}
}
},
{
$project: {
Honours: {
$cond: [
{
$gte: [
"$grade",
5
]
},
true,
"fail"
]
}
}
}
])
MongoPlayground

Aggregation: adding fields to mongo document from another object

I have an array which looks like this
const posts = [{ _id: '1', viewsCount: 52 }, ...]
Which corresponds to mongodb documents in posts collection
{
_id: '1',
title: 'some title',
body: '....',
}, ...
I want to perform an aggregation which would result in documents fetched from the posts collection to have a viewsCount field. I'm not sure how I should form my aggregation pipeline:
[
{ $match: {} },
{ $addFields: { viewsCount: ?? } }
]
UPDATE
So far the following code almost does the trick:
[
{ $match: {} },
{ $addFields: { viewsCount: { $arrayElemAt: [posts, { $indexOfArray: [ posts, '$_id' ] } ] } } },
]
But viewsCount in this case turns to be an object, so I guess I need to add $project
UPDATE
I've found out one possible solution which is to use $addFields stage twice - overriding the first viewsCount
[
{ $match: {} },
{ $addFields: { viewsCount: { $arrayElemAt: [posts, { $indexOfArray: [ posts, '$_id' ] } ] } } },
{ $addFields: { viewsCount: '$viewsCount.viewsCount' } }
]
But is there a better/more concise solution?
UPDATE
This pipeline actually works correct:
[
{ $match: {} },
{ $addFields: { viewsCount: { $arrayElemAt: [posts, { $indexOfArray: [ postsIds, '$_id' ] } ] } } },
{ $addFields: { viewsCount: '$viewsCount.viewsCount' } }
]
I have updated the second stage by replacing posts with postsIds
To have a more concise solution (one-stage) you can use $let operator which lets you to define temporary variable that can be then used inside your expression, try:
db.posts.aggregate([
{ $addFields: {
viewsCount: {
$let: {
vars: { viewsCountObj: { $arrayElemAt: [posts, { $indexOfArray: [ posts, '$_id' ] } ] } },
in: "$$viewsCountObj.viewsCount"
}
} }
}
])

MongoDB aggregate array documents

I have a MongoDB collection containing elements like this:
{
"name": "test",
"instances": [
{
"year": 2015
},
{
"year": 2016
},
]
}
How can I get the minimum and maximum value for year within the document named test? E.g. I want to aggregate all documents inside that array, but I can't find the syntax for that. Thanks in advance!
Both $min and $max takes an array as a parameter and in your case path instances.year returns an array so your query can look like below:
db.col.aggregate([
{
$match: { name: "test" }
},
{
$project: {
minYear: { $min: "$instances.year" },
maxYear: { $max: "$instances.year" }
}
}
])
You can use below aggregation
db.collection.aggregate([
{ "$project": {
"maxYear": {
"$arrayElemAt": [
"$instances",
{
"$indexOfArray": [
"$instances.year",
{ "$max": "$instances.year" }
]
}
]
},
"minYear": {
"$arrayElemAt": [
"$instances",
{
"$indexOfArray": [
"$instances.year",
{ "$min": "$instances.year" }
]
}
]
}
}}
])