Cant find duplicate values for array part in mongodb - mongodb

db.school.find({ "merchant" : "cc8c0421-e7fc-464d-9e1d-37e168b216c3" })
this is an example document from school collection of that query:
{
"_id" : ObjectId("57fafasf2323232323232f57682cd42"),
"status" : "wait",
"merchant" : "cc8c0421-e7fc-464d-9e1d-37e168b216c3",
"isValid" : false,
"fields" : { "schoolid" : {
"value" : "2323232",
"detail" : {
"revisedBy" : "teacher",
"revisionDate" : ISODate("2015-06-24T09:22:44.288+0000")
},
"history" : [
]
}}
}
I want to see which has duplcate schoolid. SO i do this:
db.school.aggregate([
{$match:{ "merchant" : "cc8c0421-e7fc-464d-9e1d-37e168b216c3"
{ $group: {
_id: { fields.schoolid.value: "$fields.schoolid.value" },
count: { $sum: 1 }
} },
{ $match: {
count: { $gte: 2 }
} },
{ $sort : { count : -1} },
{ $limit : 10 }
]);
but it gives error.
a lot of errors for a lot of lines
i tried to do like this
_id: { "fields.schoolid.value": "$fields.schoolid.value" },
or
_id: { 'fields.schoolid.value': "$'fields.schoolid.value'" },
but did not work. ow can i use it?

According to the document you provided, there is no fields field, so the group stage can't work. Your query should be :
db.school.aggregate([
{ $match: { "merchant" : "cc8c0421-e7fc-464d-9e1d-37e168b216c3"}},
{ $group: {
_id: { value: "$fields.schoolid.value" },
count: { $sum: 1 }
} },
{ $match: {
count: { $gte: 2 }
} },
{ $sort : { count : -1} },
{ $limit : 10 }
]);
Also note that fields.schoolid.value is not a valid fieldname, you need to enclode it in "" or to remove the "."

Related

Count if a value exists inside the array of objects [ MongoDB ]

I am developing a simple chat application with MongoDB and got stuck into a situation.
My document in database is as
{
"_id" : ObjectId("605a217ed8168f4c262f4782"),
"message" : "Hi, This is a test message",
"created" : ISODate("2021-03-23T17:12:30.000Z"),
"user" : {
"_id" : ObjectId("5977af7df1d8cc4623283b14"),
"name" : "Sender Of Message"
},
"recipients" : [
{
"_id" : ObjectId("5977af7df1d8cc4623283b14"),
"time" : ISODate("2021-03-23T17:12:30.000Z")
},
{
"_id" : ObjectId("5df50a5eaa0e3c3104006101"),
"time" : ISODate("2021-03-23T17:12:35.000Z")
}
],
"target" : {
"_id" : ObjectId("5df50a5eaa0e3c3104006101"),
"name" : "Target Person"
},
"status" : 1
}
When I try to get the last message with the unread count of the user I am always getting 1
Here is the query that I tried on.
db.collection.aggregate([
{ $match: { 'target._id': ObjectId('5df50a5eaa0e3c3104006101'), status: 1 } },
{ $sort: { _id: -1 } },
{
$group: {
_id: '$user._id',
doc: { $first: '$$ROOT' },
unread: {
$sum: {
$cond: {
if: { $ne: [ ObjectId('5df50a5eaa0e3c3104006101'), '$recipients._id' ] },
then: 1,
else: 0
}
}
}
}
}
])
If the collection contains even just the one document above, it is supposed to give 0 as the object inside the recipients array already contains the _id as ObjectId('5df50a5eaa0e3c3104006101'), but I'm getting 1 for the unread count. Any help?
Here is the output that I get from the query
{
"_id" : ObjectId("5977af7df1d8cc4623283b14"),
"doc" : {
"_id" : ObjectId("605a217ed8168f4c262f4782"),
"message" : "Hi, This is a test message",
"created" : ISODate("2021-03-23T17:12:30.000Z"),
"user" : {
"_id" : ObjectId("5977af7df1d8cc4623283b14"),
"name" : "Sender Of Message"
},
"recipients" : [
{
"_id" : ObjectId("5977af7df1d8cc4623283b14"),
"time" : ISODate("2021-03-23T17:12:30.000Z")
},
{
"_id" : ObjectId("5df50a5eaa0e3c3104006101"),
"time" : ISODate("2021-03-23T17:12:35.000Z")
}
],
"target" : {
"_id" : ObjectId("5df50a5eaa0e3c3104006101"),
"name" : "Target Person"
},
"status" : 1
},
"unread" : 1.0
}
I know why its showing with the count as 1
The array recipients contains an object with _id as ObjectId("5977af7df1d8cc4623283b14") inside it, so its a non matching condition. Which is causing the if condition to be satisfied and produce a value 1.
But I need to figure out how to query it to get the actual value.
Please note that I cant use $push operator on recipients array as it might have greater amount of object ( maybe in future )
Thanks for the support, but I have found the answer by myself.
Here is my approch to get the data as per the requirement.
Instead of searching for the records within the array what I did is
Filtered the data array to the _id that I don't need, so the array will have exactly one document or else it will be empty.
When taking the negation of the condition. ie, when there is one value in the array I need the counter to be 0 or else it should be 1
So I used the $size to check the array's size and $filter to filter out the other _ids and then used $sum to increment the counter as required.
db.collection.aggregate([
{ $match: { 'target._id': ObjectId('5df50a5eaa0e3c3104006101'), status: 1 } },
{ $sort: { _id: -1 } },
{
$group: {
_id: '$user._id',
doc: { $first: '$$ROOT' },
unread: {
$sum: {
$cond:{
if: {
$size: {
$filter: {
input: '$recipients',
as: 'item',
cond: { $eq: [ ObjectId('5df50a5eaa0e3c3104006101'), '$$item._id' ] }
}
}
},
then: 0,
else: 1
}
}
}
}
}
])
Try to Use like this:
db.getCollection('test').aggregate([
{ $match: { 'target._id': ObjectId('5df50a5eaa0e3c3104006101'), status: 1
} },
{ $sort: { _id: -1 } },
{ $unwind: { path: "$recipients", preserveNullAndEmptyArrays: true } },
{
$group: {
_id: '$user._id',
doc: { $first: '$$ROOT' },
unread: {
$sum: {
$cond: {
if: { $ne: ['$recipients._id',
ObjectId('5df50a5eaa0e3c3104006101') ] },
then: 1,
else: 0
}
}
}
}
}
])

How to combine results in a Mongo aggregation query

I'm new to aggregation queries in Mongo and been really struggling trying to produce the output I want. I have the following aggregation query:
db.events.aggregate([
{ $match: { requestState: "APPROVED" } },
{ $unwind: { path: "$payload.description" } },
{ $group: { _id: { instr: "$payload.description", bu: "$createdByUser", count: { $sum: 1 } } } }
]);
that returns the following results:
{ "_id" : { "instr" : "ABC-123", "bu" : "BU2", "count" : 1 } }
{ "_id" : { "instr" : "ABC-123", "bu" : "BU1", "count" : 1 } }
{ "_id" : { "instr" : "DEF-456", "bu" : "BU1", "count" : 1 } }
How can I amend the aggregation query so that there are only 2 documents returned instead of 3? With the two "ABC-123" results combined into a single result with a new array of counts with the "bu" and "count" fields i.e.
{ "_id" : { "instr" : "ABC-123", "counts": [ { "bu" : "BU1", "count" : 1 }, { "bu" : "BU2", "count" : 1 } ] } }
Many thanks
You can add another stage to only $group by _id.instr and another stage to $project to your desired output shape
db.events.aggregate([
{
$match: { requestState: "APPROVED" }
},
{
$unwind: { path: "$payload.description" }
},
{
$group: {
_id: { instr: "$payload.description", bu: "$createdByUser", count: { $sum: 1 } }
}
},
{
$group: {
_id: { instr: "$_id.instr" },
counts: { $push: { bu: "$_id.bu", count: "$_id.count" } }
}
},
{
$project: {
_id: { instr: "$_id.instr", counts: "$counts" }
}
}
]);

I need help in this query mongodb with nodejs

The query is returning double value, can you help me with this?
the problem stay in unwind ?
is a consultation that consolidates the debits and credits of the db...
with aggregate...
Code:
My DAO ( MongoDB + NodeJS ) :
ListarDebitosDAO.prototype.consolidado = function(usuario,res,req){
this._connection.open(function(err, mongoclient){
mongoclient.collection("contas", function(err, collection){
collection.aggregate([{
$match : {usuario : usuario}},{
$unwind: "$debitos"
},{
$unwind: "$creditos"
}, {
$group: {
"_id" : usuario,
"debitos": {
$sum: "$debitos.debito"
},
"creditos" : {
$sum: "$creditos.credito"
}
}
}])
.toArray(function(error, results){
console.log(results)
var debitoConsolidado = results[0].debitos;
var creditoConsolidado = results[0].creditos;
res.render('dashboard', {debitoConsolidado,nome_usuario: req.session.nome,creditoConsolidado})
});
// mongoclient.close();
});
});
}
My collection ( exemple ) :
{
"_id" : ObjectId("5a09113b42e54fb49230fa37"),
"usuario" : "sara",
"creditos" : [
{
"nome_do_credito" : "credito inicial",
"credito" : 0
},
{
"nome_do_credito" : "salario",
"credito" : 1200
}
],
"debitos" : [
{
"nome_do_debito" : "debito inicial",
"debito" : 0
},
{
"nome_do_debito" : "Vivo",
"debito" : 200
},
{
"nome_do_debito" : "Vivo",
"debito" : 600
}
]
}
My atual output:
{ "_id" : null, "debitos" : 1600, "creditos" : 3600 }
I need :
{ "_id" : null, "debitos" : 800, "creditos" : 1200 }
enter code here
The problem is that you are trying to use both unwind simultaneously, it is creating duplicate entries hence the sum of credit and debit is coming wrong.
I guess what you can do is do sequential unwind and grouping for both credit and debit
collection.aggregate([{
$match : {usuario : usuario}},{
$unwind: "$debitos"
}, {
$group: {
"_id" : usuario,
"debitos": {
$sum: "$debitos.debito"
},
"creditos" : "$creditos"
}
},
{
$unwind: "$creditos"
},
{
$group: {
"_id" : usuario,
"creditos" : {
$sum: "$creditos.credito"
}
"debitos": "$debitos"
}
}
])
I am sure either this or some variant should work for you.
It can be as simple as this:
db.contas.aggregate([
{
$project: {
_id: 1,
usario: 1,
debitos: {
$reduce: {
input: "$debitos",
initialValue: 0,
in: { $add: [ "$$value", "$$this.debito" ] }
}
},
creditos: {
$reduce: {
input: "$creditos",
initialValue: 0,
in: { $add: [ "$$value", "$$this.credito" ] }
}
}
}
}
]);
You can use just one projection and reduce the arrays by summing their members.

Mongodb Aggregation Rows to Columns

I have the following dataset. I need to group them by Account, and then turn the Element_Fieldname into a column.
var collection = [
{
Account:12345,
Element_Fieldname:"cars",
Element_Value:true
},
{
Account:12345,
Element_Fieldname:"boats",
Element_Value:false
}
]
This was my attempt to convert rows to columns, but its not working.
db.getCollection('my_collection').aggregate([{
$match : {
Element_Fieldname : {
$in : ["cars", "boats"]
}
}
}, {
$group : {
_id : "$Account",
values : {
$addToSet : {
field : "$Element_Fieldname",
value : "$Element_Value"
}
}
}
}, {
$project : {
Account : "$_id",
cars : {
"$cond" : [{
$eq : ["$Element_Fieldname", "cars"]
}, "$Element_Value", null]
},
boats : {
"$cond" : [{
$eq : ["$Element_Fieldname", "day_before_water_bottles"]
}, "$Element_Value", null]
},
}
}
])
This just gives me null in my cars and boats fields. Any help would be great.
And this is my desired results:
var desiredResult = [
{
Account:12345,
cars:true,
boats:false
}
]
this is a big tricky but you will get what you need :-)
please add $match on the top of aggregation pipeline
db.collection.aggregate([{
$project : {
_id : 0,
"Account" : 1,
car : {
$cond : [{
$eq : ["$Element_Fieldname", "cars"]
}, "$Element_Value", null]
},
boats : {
$cond : [{
$eq : ["$Element_Fieldname", "boats"]
}, "$Element_Value", null]
},
}
},
{
$group : {
_id : "$Account",
carData : {
$addToSet : "$car"
},
boatsData : {
$addToSet : "$boats"
}
}
}, {
$unwind : "$carData"
}, {
$match : {
carData : {
$ne : null
}
}
}, {
$unwind : "$boatsData"
}, {
$match : {
boatsData : {
$ne : null
}
}
},
])
and result
{
"_id" : 12345,
"carData" : true,
"boatsData" : false
}
It is not possible to do the type of computation you are describing with the aggregation framework, however there is a proposed $arrayToObject expression which will give you the functionality to peek into the key names, and create new key/values dynamically.
For example, you could do
db.collection.aggregate([
{
"$match": { "Element_Fieldname":{ "$in": ["cars", "boats"] } }
},
{
"$group": {
"_id": "$Account",
"attrs": {
"$push": {
"key": "$Element_Fieldname",
"val": "$Element_Value"
}
}
}
},
{
"$project": {
"Account": "$_id",
"_id": 0,
"newAttrs": {
"$arrayToObject": {
"$map": {
"input": "$attrs",
"as": "el",
in: ["$$el.key", "$$el.val"]
}
}
}
}
},
{
"$project": {
"Account": 1,
"cars": "$newAttrs.cars",
"boats": "$newAttrs.boats"
}
}
])
Vote for this jira ticket https://jira.mongodb.org/browse/SERVER-23310 to get this feature.
As a workaround, mapreduce seems like the available option. Consider running the following map-reduce operation:
db.collection.mapReduce(
function() {
var obj = {};
obj[this.Element_Fieldname] = this.Element_Value;
emit(this.Account, obj);
},
function(key, values) {
var obj = {};
values.forEach(function(value) {
Object.keys(value).forEach(function(key) {
obj[key] = value[key];
});
});
return obj;
},
{ "out": { "inline": 1 } }
)
Result:
{
"_id" : 12345,
"value" : {
"cars" : true,
"boats" : false
}
}

MongoDB aggregate using distinct

I have an aggregation that groups on a date and creates a sum.
db.InboundWorkItems.aggregate({
$match: {
notificationDate: {
$gte: ISODate("2013-07-18T04:00:00Z")
},
dropType: 'drop'
}
}, {
$group: {
_id: {
notificationDate: "$notificationDate"
},
nd: {
$first: "$notificationDate"
},
count: {
$sum: 1
}
}
}, {
$sort: {
nd: 1
}
})
The output is
"result" : [
{
"_id" : {
"notificationDate" : ISODate("2013-07-18T04:00:00Z")
},
"nd" : ISODate("2013-07-18T04:00:00Z"),
"count" : 484
},
{
"_id" : {
"notificationDate" : ISODate("2013-07-19T04:00:00Z")
},
"nd" : ISODate("2013-07-19T04:00:00Z"),
"count" : 490
},
{
"_id" : {
"notificationDate" : ISODate("2013-07-20T04:00:00Z")
},
"nd" : ISODate("2013-07-20T04:00:00Z"),
"count" : 174
},
{
"_id" : {
"notificationDate" : ISODate("2013-07-21T04:00:00Z")
},
"nd" : ISODate("2013-07-21T04:00:00Z"),
"count" : 6
},
{
"_id" : {
"notificationDate" : ISODate("2013-07-22T04:00:00Z")
},
"nd" : ISODate("2013-07-22T04:00:00Z"),
"count" : 339
},
{
"_id" : {
"notificationDate" : ISODate("2013-07-23T04:00:00Z")
},
"nd" : ISODate("2013-07-23T04:00:00Z"),
"count" : 394
},
{
"_id" : {
"notificationDate" : ISODate("2013-07-24T04:00:00Z")
},
"nd" : ISODate("2013-07-24T04:00:00Z"),
"count" : 17
}
],
"ok" : 1
so far so good. What I need to do now is to keep this, but also add a distinct in the criteria (for argument's sake I want to use AccountId). The would yield me the count of the grouped dates only using distinct AccountId. Is distinct even possible within the aggregation framework?
you can use two group commands in the pipeline, the first to group by accoundId, followed by second group that does usual operation. something like this:
db.InboundWorkItems.aggregate(
{$match: {notificationDate: {$gte: ISODate("2013-07-18T04:00:00Z")}, dropType:'drop' }},
{$group: {_id:"accountId",notificationDate:"$notificationDate"}},
{$group: {_id:1, nd: {$first:"$notificationDate"}, count:{$sum:1} }},
{$sort:{nd:1}} )
db.InboundWorkItems.aggregate({
$match: {
notificationDate: {
$gte: ISODate("2013-07-18T04:00:00Z")
},
dropType: 'drop'
}
}, {
$group: {
_id: "$AccountId",
notificationDate: {
$max: "$notificationDate"
},
dropType: {
$max: "$dropType"
}
}
}, {
$group: {
_id: {
notificationDate: "$notificationDate"
},
nd: {
$first: "$notificationDate"
},
count: {
$sum: 1
}
}
}, {
$sort: {
nd: 1
}
})
I think you might actually be looking for a single group (English is a bit confusing) like so:
db.InboundWorkItems.aggregate({
$match: {
notificationDate: {
$gte: ISODate("2013-07-18T04:00:00Z")
},
dropType: 'drop'
}
}, {
$group: {
_id: {
notificationDate: "$notificationDate", accountId: '$accountId'
},
nd: {
$first: "$notificationDate"
},
count: {
$sum: 1
}
}
}, {
$sort: {
nd: 1
}
})
I add the compound _id in the $group because of:
The would yield me the count of the grouped dates only using distinct AccountId.
Which makes me think you want the grouped date count by account ID.