MongoDB $in inside $cond - mongodb

I burned out my brain trying to group and count itens in MongoDB. There are lots of posts but no one is that i need.
This is an exemple based on:
styvane answer
db.VIEW_HISTORICO.aggregate([
{
$match: { PartnerId: "2021", DT_RECEBIDO: {$gte: "2019-03-10 00:00:00", $lte: "2019-03-12 23:59:59"}}
},
{
$group: { _id: null,
Controle1: { $sum: {$cond: [{ $gt: ['$CD_EVENTO', 0]}, 1, 0]}},
Controle2: { $sum: {$cond: [{ $lt: ['$CD_EVENTO', 0]}, 1, 0]}}
}
}
])
I need to group based on $in and $match together, with more then one result.
ONE RESULT WORKING
db.VIEW_HISTORICO.aggregate([
{
$match: { PartnerId: "2021", DT_RECEBIDO: {$gte: "2019-03-10 00:00:00", $lte: "2019-03-12 23:59:59"}, "CD_EVENTO": { $in: ["K127", "9027"] }, }
},
{
$count : "Controles"
}
])
Using MSSQL i got best perfermance this way:
SELECT
SUM(CASE WHEN CD_EVENTO = 'K102' THEN 1 ELSE 0 END) AS Interfone,
SUM(CASE WHEN CD_EVENTO IN('9015', '9016', '9017', '9018', '9019', '9020', '9021', '9022', '9023', '9024', '9025', '9026', 'K154', 'K155') THEN 1 ELSE 0 END) AS Tag,
SUM(CASE WHEN CD_EVENTO IN('9027', '9028', '9029', '9030', '9031', '9032', '9033', '9034', 'K127') THEN 1 ELSE 0 END) AS Controle,
SUM(CASE WHEN CD_EVENTO IN('K203', 'K204') THEN 1 ELSE 0 END) AS QrCode,
SUM(CASE WHEN CD_EVENTO IN('K183', 'K184') THEN 1 ELSE 0 END) AS Convite
FROM VIEW_ANALYTICS
WHERE DT_RECEBIDO BETWEEN GETDATE()-30 AND GETDATE()
I hard tryied but I couldnt translate my MSSQL to MongoDB. My query.
db.VIEW_HISTORICO.aggregate([
{
$match: { PartnerId: "2021", DT_RECEBIDO: {$gte: "2019-03-10 00:00:00", $lte: "2019-03-12 23:59:59"}}
},
{
$group: { _id: null,
Controle1: { $sum: {$cond: [{ "CD_EVENTO": { $in: ["K127", "9027"] }}, 1, 0]}},
Controle2: { $sum: {$cond: [{ "CD_EVENTO": { $in: ["K154", "K155"] }}, 1, 0]}}
}
}
])

Syntax for $in operator is a bit different than what you're trying to do here. It takes an array of two elements: first one represent a reference to a field (must start with dollar sign) and second one is an array of values, so your $group stage should look like below:
db.VIEW_HISTORICO.aggregate([
{
$group: { _id: null,
Controle1: { $sum: {$cond: [{ $in: ["$CD_EVENTO", ["K127", "9027"]]}, 1, 0]}},
Controle2: { $sum: {$cond: [{ $in: ["$CD_EVENTO", ["K154", "K155"]]}, 1, 0]}},
}
}
])

Related

Count the documents and sum of values of fields in all documents of a mongodb

I have a set of documents modified from mongodb using
[{"$project":{"pred":1, "base-url":1}},
{"$group":{
"_id":"$base-url",
"invalid":{"$sum": { "$cond": [{ "$eq": ["$pred", "invalid"] }, 1, 0] }},
"pending":{"$sum": { "$cond": [{ "$eq": ["$pred", "null"] }, 1, 0] }},
}},
]
to get the below documents
[{'_id': 'https://www.example1.org/', 'invalid': 3, 'pending': 6},
{'_id': 'https://example2.com/', 'invalid': 10, 'pending': 4},
{'_id': 'https://www.example3.org/', 'invalid': 2, 'pending': 6}]
How to get the count of documents and sum of other fields to obtain the following result
{"count":3, "invalid":15,"pending":16}
you just need a $group stage with $sum
playground
The $sum docs and here has good examples
db.collection.aggregate([
{
$group: {
_id: null,
pending: {
$sum: "$pending"
},
invalid: {
$sum: "$invalid"
},
count: {
$sum: 1 //counting each record
}
}
},
{
$project: {
_id: 0 //removing _id field from the final output
}
}
])

MongDb how to count of t null fields and the other one with the count of those with non-null fields?

The two fields named name_id and age_id respectively. Now I would like to find a document that does not have both two fields and count the total numbers.
Below is the code I tried, but it did not work.
db.user.aggregate([{ "$group": {
"_id" : { user_id: "$key_id" },
"requestA_count": { "$sum": {
"$cond": [ { "$ifNull": [{"$name_id", false},{"$age_id",false}] }, 1, 0 ]
} },
{ "$project": {
"_id": 0,
"requestA_count": 1,
} }
])
I think this is what your looking for. If you want to count docs that have either name_id or age_id simply change $and to $or.
https://mongoplayground.net/p/cuAVkYnLUTq
db.collection.aggregate([
{$group: {
_id: {
// Group by bool, has both name_id and age_id
hasIdAndAge: {
$and: [
{$toBool: "$name_id"},
{$toBool: "$age_id"}
]
}
},
// Count sum
count: {$sum: 1}
}},
// Rework to only output one object with both counts
{$group: {
_id: null,
has: {
$sum: {$cond: [
"$_id.hasIdAndAge", "$count", 0
]}
},
hasNot: {
$sum: {$cond: [
"$_id.hasIdAndAge", 0, "$count"
]}
}
}}
])
// Outputs
[
{
"_id": null,
"has": 1,
"hasNot": 4
}
]
Using the $match operator seems more fitting. You could do something like this:
db.user.aggregate([
{ $match: {$and: [{name_id: null},{age_id: null}]}},
{ $count: "null_name&age"}
])
I haven't tested it but that should point you in the right direction.

how to get last character from a string in mongodb?

Data:
{code: "XXXXXXXX1", total: 400},
{code: "YYYYY2", total: 500}
{code: "ZZZZZZ3", total: 100}
{code: "AAA5", totala: 200}
I want to create an aggregate function to group the data above by its last character in the code field. code field is a string and can be varied in length. I only want to get its last index/number. Something like:
db.transactions.aggregate([
{$project: {
last_index: {$getMyLastCharInMyCode: "$code"},
total: 1
}},
{$group: {_id: "$last_index", {total: {$sum: "$total"}}}}
])
I searched the internet and mongodb manuals, it seems impossible. Any ideas? Thank you
There you go:
db.transactions.aggregate({
$addFields: {
"last_index": { $substr: [ "$code", { $subtract: [ { $strLenCP: "$code" }, 1 ] }, 1 ] }
}
})
db.transactions.aggregate([
{"$project": {
total: 1,
code: 1,
last_index: { $substr: [ "$code", { $subtract: [ {"$strLenCP": "$code"}, 1 ] }, -1 ]}
}
},
{"$group": {
"_id": "$last_index",
"total": {"$sum": "$total"}
}
}
])

Mongodb Aggregation count array/set size

Here's my problem:
Model:
{ application: "abc", date: Time.now, status: "1" user_id: [ id1, id2,
id4] }
{ application: "abc", date: Time.yesterday, status: "1", user_id: [
id1, id3, id5] }
{ application: "abc", date: Time.yesterday-1, status: "1", user_id: [
id1, id3, id5] }
I need to count the unique number of user_ids in a period of time.
Expected result:
{ application: "abc", status: "1", unique_id_count: 5 }
I'm currently using the aggregation framework and counting the ids outside mongodb.
{ $match: { application: "abc" } }, { $unwind: "$users" }, { $group:
{ _id: { status: "$status"},
users: { $addToSet: "$users" } } }
My arrays of users ids are very large, so I have to iterate the dates or I'll get the maximum document limit (16mb).
I could also $group by
{ year: { $year: "$date" }, month: { $month: "$date" }, day: {
$dayOfMonth: "$date" }
but I also get the document size limitation.
Is it possible to count the set size in mongodb?
thanks
The following will return number of uniqueUsers per application. This will apply an group operation to a result of a group operation by using pipeline feature of mongodb.
{ $match: { application: "abc" } },
{ $unwind: "$users" },
{ $group: { _id: "$status", users: { $addToSet: "$users" } } },
{ $unwind:"$users" },
{ $group : {_id : "$_id", count : {$sum : 1} } }
Hopefully this will be done in an easier way in the following releases of mongo by a command which gives the size of an array under a projection. {$project: {id: "$_id", count: {$size: "$uniqueUsers"}}}
https://jira.mongodb.org/browse/SERVER-4899
Cheers
Sorry I'm a little late to the party. Simply grouping on the 'user_id' and counting the result with a trivial group works just fine and doesn't run into doc size limits.
[
{$match: {application: 'abc', date: {$gte: startDate, $lte: endDate}}},
{$unwind: '$user_id'},
{$group: {_id: '$user_id'}},
{$group: {_id: 'singleton', count: {$sum: 1}}}
];
Use $size to get the size of set.
[
{
$match: {"application": "abc"}
},
{
$unwind: "$user_id"
},
{
$group: {
"_id": "$status",
"application": "$application",
"unique_user_id": {$addToSet: "$user_id"}
}
},
{
$project:{
"_id": "$_id",
"application": "$application",
"count": {$size: "$unique_user_id"}
}
}
]

Conditional $sum in MongoDB

My collection in mongodb is similar to the following table in SQL:
Sentiments(Company,Sentiment)
Now, I need to execute a query like this:
SELECT
Company,
SUM(CASE WHEN Sentiment >0 THEN Sentiment ELSE 0 END) AS SumPosSenti,
SUM(CASE WHEN Sentiment <0 THEN Sentiment ELSE 0 END) AS SumNegSenti
FROM Sentiments
GROUP BY Company
What should I do to write this query in Mongo? I am stuck at the following query:
db.Sentiments.aggregate(
{ $project: {_id:0, Company:1, Sentiment: 1} },
{ $group: {_id: "$Company", SumPosSenti: {$sum: ? }, SumNegSenti: {$sum: ? } } }
);
As Sammaye suggested, you need to use the $cond aggregation projection operator to do this:
db.Sentiments.aggregate(
{ $project: {
_id: 0,
Company: 1,
PosSentiment: {$cond: [{$gt: ['$Sentiment', 0]}, '$Sentiment', 0]},
NegSentiment: {$cond: [{$lt: ['$Sentiment', 0]}, '$Sentiment', 0]}
}},
{ $group: {
_id: "$Company",
SumPosSentiment: {$sum: '$PosSentiment'},
SumNegSentiment: {$sum: '$NegSentiment'}
}});
Starting from version 3.4, we can use the $switch operator which allows logical condition processing in the $group stage. Of course we still need to use the $sum accumulator to return the sum.
db.Sentiments.aggregate(
[
{ "$group": {
"_id": "$Company",
"SumPosSenti": {
"$sum": {
"$switch": {
"branches": [
{
"case": { "$gt": [ "$Sentiment", 0 ] },
"then": "$Sentiment"
}
],
"default": 0
}
}
},
"SumNegSenti": {
"$sum": {
"$switch": {
"branches": [
{
"case": { "$lt": [ "$Sentiment", 0 ] },
"then": "$Sentiment"
}
],
"default": 0
}
}
}
}}
]
)
If you have not yet migrated your mongod to 3.4 or newer, then note that the $project stage in this answer is redundant because the $cond operator returns a numeric value which means that you can $group your documents and apply $sum to the $cond expression.
This will improve the performance in your application especially for large collection.
db.Sentiments.aggregate(
[
{ '$group': {
'_id': '$Company',
'PosSentiment': {
'$sum': {
'$cond': [
{ '$gt': ['$Sentiment', 0]},
'$Sentiment',
0
]
}
},
'NegSentiment': {
'$sum': {
'$cond': [
{ '$lt': ['$Sentiment', 0]},
'$Sentiment',
0
]
}
}
}}
]
)
Consider a collection Sentiments with the following documents:
{ "Company": "a", "Sentiment" : 2 }
{ "Company": "a", "Sentiment" : 3 }
{ "Company": "a", "Sentiment" : -1 }
{ "Company": "a", "Sentiment" : -5 }
The aggregation query produces:
{ "_id" : "a", "SumPosSenti" : 5, "SumNegSenti" : -6 }
Explaining the snippets above, that uses the array syntax:
PosSentiment: {$cond: [{$gt: ['$Sentiment', 0]}, '$Sentiment', 0]}
is equal to:
PosSentiment: {$cond: { if: {$gt: ['$Sentiment', 0]}, then: '$Sentiment', else: 0} }
The array syntax summarizes the long syntax to just { $cond: [if, then, else] }