mongoDB group query not working - node.js

So, I have the following query on my routes page:
const testeEa = atendimentos.aggregate([
{$group : {_id: "$id_atendente", Idcount:{$sum:1}}},
{$sort: {_id: 1}},
{ '$group': {
'_id': null,
'eatest': {
'$sum': {
'$cond' : [ { '$eq': ['$status', 'EA'] }, 1, 0]
}
},
//'eatest': {'$push': "$$ROOT"}
} }
]).exec();
What I want to do is: This Idcount is counting how many times id_atendente repeats. I need this to check out how many support calls each person answered.
After this is done, I need to check all the support calls with the 'EA' status.
I have 351 calls with the 'EA' status, and I would like to see who is with this status on the support call.
I guess that I'm missing something on the second $group, I just don't what it is.
This eatest is supposed to be the key that will be used on the view.
By the way, I managed to do a query where I can get the number of support calls per id, I need almost the same thing, the difference is that I only need the ones with the 'EA' status.
EDIT 1
const counts = atendimentos.aggregate([
{ '$group': {
'_id': null,
'fin': {
'$sum': {
'$cond': [ { '$eq': [ '$status', 'F' ] }, 1, 0 ]
}
},
'ea': {
'$sum': {
'$cond': [ { '$eq': [ '$status', 'EA' ] }, 1, 0 ]
}
}
} }
]).exec()
//Faz uma consulta no mongo e guarda o resultado
//na variável monthly
const monthly = atendimentos.aggregate([
{ '$group': {
'_id': {
'year': { '$year': '$date' },
'month': { '$month': '$date' }
},
'sum': { '$sum': 1 }
} },
{ '$group': {
'_id': null,
//Chave usada para renderizar os dados
'back': { '$push': '$$ROOT' }
} },
]).exec();
//Verificar quantas vezes um id_atendente se repete, contar e guardar o numero
const testeAt = atendimentos.aggregate([
{$group : {_id: "$id_atendente", Idcount:{$sum:1}}},
{$sort: {_id: 1}},
{ '$group': {
'_id': null,
//Chave usada para renderizar os dados
'test': {'$push': "$$ROOT"}
} },
]).exec();
const atendente = atendimentos.aggregate([
{ '$group' : {
'_id': "$id_atendente",
'Idcount': { '$sum': 1 }
} },
{ '$sort': { '_id': 1 } }
]).exec();
const testeEa = atendimentos.aggregate([
{ '$group': {
'_id': null,
'eatest': {
'$sum': {
'$cond' : [ { '$eq': ['$status', 'EA'] }, 1, 0]
}
}
} }
]).exec();
Promise.all([counts, monthly, testeAt, testeEa]).then(([counts, monthly, testeAt, testeEa]) => {
Notice that the atendente query and the testeAt are almost the same.
What I would like to do is use this testeEa variable to store the returned value of the queries that return the number of 'EA' status per id_atendente.
If I use try catch I can't do it I guess because the testeEa would be inside of it, and I wouldn't be ble to pass it to my array.
The eatest is returning the correct value by the way.
END OF EDIT
EDIT 2
An example of the data that I want, this is the query that it's working for checking number of calls/id.
{
"_id": 42,
"Idcount": 3
},
{
"_id": 43,
"Idcount": 155
},
{
"_id": 46,
"Idcount": 69
},
{
"_id": 47,
"Idcount": 16
},
{
"_id": 48,
"Idcount": 4
},
{
"_id": 49,
"Idcount": 21
},
{
"_id": 50,
"Idcount": 4
},
This is exactly the way that I want, but the difference is that I want only the ones with the 'EA' status.
Idcount would be the number of how many times an id with the 'EA' status appears.
END OF EDIT 2
Thanks in advance!

When executing a pipeline in the aggregation framework, MongoDB pipes operators into each other.
"Pipe" here takes the Linux meaning: the output of an operator becomes the input of the following operator. The result of each operator is a new collection of documents.
So when Mongo executes the above pipeline, the results from the first two steps
{ '$group' : { /* First pipeline step */
'_id': "$id_atendente",
'Idcount': { '$sum': 1 }
} },
{ '$sort': { '_id': 1 } } /* Second pipeline step */
will be an array of documents with the schema (for example):
[
{ _id: 'fuzz', IdCount: 2 },
{ _id: 'foo', IdCount: 9 },
{ _id: 'bar', IdCount: 4 },
....
]
Now when it executes the third pipeline
{ '$group': {
'_id': null,
'eatest': {
'$sum': {
'$cond' : [ { '$eq': ['$status', 'EA'] }, 1, 0]
}
}
} }
the documents from the previous pipeline are piped into this pipeline expecting documents with a field called status which does not exist hence
the results wont be correct.
You need to run multiple aggregate pipelines in parallel and this can only be achieved in a single query with $facet:
Using MongoDB 3.4.4 and above:
atendimentos.aggregate([
{ '$facet': {
'atendente': [
{ '$group' : {
'_id': "$id_atendente",
'Idcount': { '$sum': 1 }
} },
{ '$sort': { '_id': 1 } }
],
'eatest': [
{ '$group': {
'_id': null,
'eatest': {
'$sum': {
'$cond' : [ { '$eq': ['$status', 'EA'] }, 1, 0]
}
}
} }
]
} }
]).exec((err, result) => console.log(result));
Using MongoDB 3.2 and below:
(async () => {
try {
const atendente = await atendimentos.aggregate([
{ '$group' : {
'_id': "$id_atendente",
'Idcount': { '$sum': 1 }
} },
{ '$sort': { '_id': 1 } }
]).exec();
const eatest = await atendimentos.aggregate([
{ '$group': {
'_id': null,
'eatest': {
'$sum': {
'$cond' : [ { '$eq': ['$status', 'EA'] }, 1, 0]
}
}
} }
]).exec();
const data = { atendente, eatest };
console.log(JSON.stringify(data, null, 4));
} catch (err) {
console.error(err);
}
})();
or with Promise API
(() => {
const atendente = atendimentos.aggregate([
{ '$group' : {
'_id': "$id_atendente",
'Idcount': { '$sum': 1 }
} },
{ '$sort': { '_id': 1 } }
]).exec();
const eatest = atendimentos.aggregate([
{ '$group': {
'_id': null,
'eatest': {
'$sum': {
'$cond' : [ { '$eq': ['$status', 'EA'] }, 1, 0]
}
}
} }
]).exec();
Promise.all([atendente, eatest]).then(([ atendente, eatest ]) => {
const data = { atendente, eatest };
console.log(JSON.stringify(data, null, 4));
}).catch((err) => {
console.error(err);
});
})();

Related

MongoDB: Filter by element of object in a subdocument array in aggregate function

Due to some changes to the schema, I've had a to do refactoring that's broken what was a simple filter in an application, in this instance is isToRead while everything else continues to work.
The document in "Assets" that should be appearing is:
{
"_id": {
"$oid": "ID"
},
"userId": "ID",
"folderId": "ID",
"title": "Title",
"note": "<p><strong>Note.</strong></p>",
"typeOfAsset": "web",
"isFavourite": false,
"createdAt": {
"$date": {
"$numberLong": "1666702053399"
}
},
"updatedAt": {
"$date": {
"$numberLong": "1666702117855"
}
},
"isActive": 3,
"tags": [],
"traits": [
{
"$oid": "6357dae53298948a18a17c60"
}
]
"__v": 0
}
… and the reference document in "Assets_Traits" that I'm attempting to filter against should be:
{
"_id": {
"$oid": "6357dae53298948a18a17c60"
},
"userId": "ID",
"numberOfViews": 1,
"isToRead": true,
"__v": 0
}
I'll share the entire method, which includes the various attempts that — for whatever reason — won't work.
let tags = args.tags ? args.tags.split(',') : []
let tagsToMatch = []
if (tags.length > 0) {
tags.forEach(tag => {
tagsToMatch.push(new mongoose.Types.ObjectId(tag))
})
}
let search = {
...(args.phraseToSearch.length > 0 && {
$search: {
index: 'assets',
compound: {
must: [{
phrase: {
query: args.phraseToSearch,
path: 'title',
slop: 2,
score: { boost: { value: 3 } }
}
}],
should: [{
phrase: {
query: args.phraseToSearch,
path: 'note',
slop: 2
}
}]
}
}
})
}
let project = {
$project: {
_id: 0,
id: '$_id',
userId: 1,
folderId: 1,
title: 1,
note: 1,
typeOfAsset: 1,
isFavourite: 1,
createdAt: 1,
updatedAt: 1,
isActive: 1,
attributes: 1,
preferences: 1,
// ...(args.typeOfAttribute === 'isToRead' && {
// traits: {
// $filter: {
// input: "$traits",
// cond: { $eq: [ "$$this.isToRead", true ] }
// }
// }
// }),
tags: 1,
traits: 1,
score: {
$meta: 'searchScore'
}
}
}
let match = {
$match: {
userId: args.userId,
typeOfAsset: {
$in: args.typeOfAsset === 'all' ? MixinAssets().all : [args.typeOfAsset] // [ 'file', 'folder', 'message', 'note', 'social', 'web' ]
},
...(tagsToMatch.length > 0 && {
tags: {
$in: tagsToMatch
}
}),
...(args.typeOfAttribute === 'isToRead' && {
// $expr: {
// $allElementsTrue: [{
// $map: {
// input: '$traits',
// as: 't',
// in: {
// $and: [
// { $eq: [ "$$t.userId", args.userId ] },
// { $eq: [ "$$t.isToRead", true ] }
// ]
// }
// }
// }]
// }
// $expr: {
// $filter: {
// input: "$traits",
// cond: {
// $and: [
// { $eq: [ "$$this.userId", args.userId ] },
// { $eq: [ "$$this.isToRead", true ] }
// ]
// }
// }
// }
}),
isActive: 3
}
}
let lookup = {}
switch (args.typeOfAttribute) {
case 'areFavourites':
match.$match.isFavourite = true
break
case 'isToRead':
// match.$match.traits = {
// userId: args.userId,
// isToRead: true
// }
// match.$match.traits = {
// $elemMatch: {
// userId: args.userId,
// isToRead: true
// }
// }
// lookup = {
// $lookup: {
// from: 'assets_traits',
// let: { isToRead: '$isToRead' },
// pipeline: [{
// $match: {
// $expr: {
// $eq: [ '$isToRead', true ]
// }
// },
// }],
// localField: 'userId',
// foreignField: 'userId',
// as: 'traits'
// }
// }
break
case 'inTrash':
match.$match.isActive = 2
break
}
let skip = {
$skip: args.skip
}
let limit = {
$limit: args.first
}
let group = {
$group: {
_id: null,
count: { $sum: 1 }
}
}
let sort = {
$sort: {
[args.orderBy]: args.orderDirection === 'asc' ? 1 : -1
}
}
console.info('Queries:getAllAssetsForNarrative()', match.$match)
let allAssets = await Models.Assets.schema.aggregate(
(search.hasOwnProperty('$search')) // Order of criteria is critical to the functioning of the aggregate method.
? [search, project, match, sort, skip, limit]
: [match, project, sort, skip, limit]
// : [match, project, { $unwind: '$traits' }, { $match: { traits: { $elemMatch: { isToRead: true } } } }, sort, skip, limit]
)
let [ totalNumberOfAssets ] = await Models.Assets.schema.aggregate(
(search.hasOwnProperty('$search')) // Order of criteria is critical to the functioning of the aggregate method.
? [search, project, match, group]
: [match, project, group]
// : [match, project, { $unwind: '$traits' }, { $match: { traits: { $elemMatch: { isToRead: true } } } }, group]
)
await (() => {
if (args.phraseToSearch.length > 0) {
const SearchFactory = require('../services/search/search')
const Search = SearchFactory(Models)
Search.insertRecentSearch({
userId: args.userId,
phraseToSearch: args.phraseToSearch.toLowerCase()
})
}
})()
I removed lookup in the final two arrays for the aggregate function because it was becoming too complicated for to me understand what was happening.
Weird thing is, "Tags" match and it's also a reference, while "Assets_Traits" won't return or do anything.
The values for typeOfAsset are: [ 'file', 'folder', 'message', 'note', 'social', 'web' ]
While 'All Assets' is chosen, choosing 'To Read' performs a filter against all types of Assets, and additional filtering would happen when a specific type of Asset is chosen — as explained, this worked before the changes to the schema.
Also, ignore tags because those aren't in use here.
Thoughts appreciated!
You did not provide sample of your input (args) or the constants you use (for example MixinAssets().all which i'm suspecting is problematic).
I constructed my own input for the sake of this answer:
const args = {
typeOfAsset: 'isToRead',
typeOfAttribute: "isToRead",
tagsToMatch: ["tag1", "tag2"],
skip: 0,
first: 1,
orderBy: "_id",
orderDirection: "desc"
}
This produces the following pipeline (using your code):
db.Assets.aggregate([
{
"$match": {
"userId": "123",
"typeOfAsset": {
"$in": [
"isToRead"
]
},
"tags": {
"$in": [
"tag1",
"tag2"
]
},
"isActive": 3
}
},
{
"$project": {
"_id": 0,
"id": "$_id",
"userId": 1,
"folderId": 1,
"title": 1,
"note": 1,
"typeOfAsset": 1,
"isFavourite": 1,
"createdAt": 1,
"updatedAt": 1,
"isActive": 1,
"attributes": 1,
"preferences": 1,
"tags": 1,
"traits": 1,
"score": {
"$meta": "searchScore"
}
}
},
{
"$sort": {
"_id": -1
}
},
{
"$skip": 0
},
{
"$limit": 1
}
])
Which works, as you can see in this Mongo Playground sample.
So what is your issue? As I mentioned I suspect one issue is the MixinAssets().all if args.typeOfAsset === 'all' then you use that value, now if it's an array the match condition looks like this:
typeOfAsset: {
$in: [['web', '...', '...']]
}
This won't match anything as it's an array of arrays, if it's a constant value then again it won't match as the type in the db is different.
I will give one more tip, usually when you want to build a pagination system like this and need both the results and totalResultCount it's common practice to use $facet this way you don't have to execute the pipeline twice and you can improve perfomance, like so:
db.Assets.aggregate([
{
"$match": {
"userId": "123",
"typeOfAsset": {
"$in": [
"isToRead"
]
},
"tags": {
"$in": [
"tag1",
"tag2"
]
},
"isActive": 3
}
},
{
$facet: {
totalCount: [
{
$group: {
_id: null,
count: {
$sum: 1
}
}
}
],
results: [
{
"$project": {
"_id": 0,
"id": "$_id",
"userId": 1,
"folderId": 1,
"title": 1,
"note": 1,
"typeOfAsset": 1,
"isFavourite": 1,
"createdAt": 1,
"updatedAt": 1,
"isActive": 1,
"attributes": 1,
"preferences": 1,
"tags": 1,
"traits": 1,
"score": {
"$meta": "searchScore"
}
}
},
{
"$sort": {
"_id": -1
}
},
{
"$skip": 0
},
{
"$limit": 1
}
]
}
}
])
Mongo Playground

Could not get the number of counts using MongoDB and Node.js

I need query to fetch the total count as per key value of document by matching some value from array using MongoDB. I am explaining my document and Input below.
dataArr=[
{'login_id':9937229853,'location':'Delhi'},
{'login_id':9937229854,'location':'JK'}
]
My document is given below.
feedback:
{
login_id:9937229853,
code: PTP,
remark:'Hello'
},
{
login_id:9937229853,
code: PTP,
remark:'Hii'
},
{
login_id:9937229853,
code: CB,
remark:'aaaaa'
},
{
login_id:9937229854,
code: PTP,
remark:'jjjjj'
},
{
login_id:9937229854,
code: CB,
remark:'dddd'
}
The above is my collection. Here I need as per user input login_id present inside array will match with document and the total count will be fetch as per document key and value. My expected output is given below. I am explaining my code below.
for(var i=0;i<dataArr.length;i++){
var login=dataArr[i]['login_id'];
//console.log('cdocs',dataArr[i]['login_id']);
Feedback.collection.count({login_id:dataArr[i]['login_id']},function(cerr,cdocs){
console.log('cdocs',login);
if (!cerr) {
if(cdocs > 0){
// console.log('login',cdocs);
db.collection.aggregate([
{
$match: {
keywords: { $not: {$size: 0} }
}
},
{ $unwind: "$keywords" },
{
$group: {
_id: {$toLower: '$keywords'},
count: { $sum: 1 }
}
},
{
$match: {
login_id: login
}
}
])
.toArray((err,docs)=>{
if (!err) {
// console.log('count::',docs);
finalArr=docs;
}
})
}
}
})
}
var data={'status':'success','data':finalArr}
res.send(data);
I need the expected result like below.
finalArr=[
{'login_id':9937229853,'location':'Delhi','PTP':2,'CB':1,'remark':3},
{'login_id':9937229854,'location':'JK','PTP':1,'CB':1,'remark':2},
]
But using my code I am getting the blank output. Please help me to resolve this issue.
You can do all this with a single aggregate operation. The first pipeline stage would be filtering the documents in the collection using the input array. You would need to map that array to just a list of ids though in order to use the $in query operator i.e.
const ids = dataArr.map(({ login_id }) => login_id)
which can then be used in the $match pipeline as
const match = { '$match': { 'login_in': { '$in': ids } } }
The next pipeline step will then use the $group stage to group the above filtered documents by the login_id key
const allGroup = { '$group': {
'_id': {
'login_id': '$login_id',
'code': '$code',
'remark': '$remark'
},
'count': { '$sum': 1 }
} }
Another $group pipeline stage to get the remarks counts as a list of key/value documents
const remarksGroup = { '$group': {
'_id': {
'login_id': '$_id.login_id',
'code': '$_id.code'
},
'remarks': {
'$push': {
'k': '$_id.remark',
'v': '$count'
}
},
'count': { '$sum': 1 }
} }
Get the code counts with a similar structure as above
const codeGroup = { '$group': {
'_id': '$_id.login_id',
'codes': {
'$push': {
'k': '$_id.code',
'v': '$count'
}
},
'remarks': { '$first': '$remarks' }
} }
You would need a final pipeline to convert the key/value pairs arrays to objects using $arrayToObject, merge the objects into one using $mergeObjects and replace the root document with the merged docs using $replaceRoot:
const projections = { '$replaceRoot': {
'newRoot': {
'$mergeObjects': [
{ 'login_id': '$_id' },
{ '$arrayToObject': '$codes' },
{ '$arrayToObject': '$remarks' }
]
}
} }
Your full aggregate pipeline operation would be:
(async () => {
try {
const ids = dataArr.map(({ login_id }) => login_id)
const match = { '$match': { 'login_in': { '$in': ids } } }
const allGroup = { '$group': {
'_id': {
'login_id': '$login_id',
'code': '$code',
'remark': '$remark'
},
'count': { '$sum': 1 }
} }
const remarksGroup = { '$group': {
'_id': {
'login_id': '$_id.login_id',
'code': '$_id.code'
},
'remarks': {
'$push': {
'k': '$_id.remark',
'v': '$count'
}
},
'count': { '$sum': 1 }
} }
const codeGroup = { '$group': {
'_id': '$_id.login_id',
'codes': {
'$push': {
'k': '$_id.code',
'v': '$count'
}
},
'remarks': { '$first': '$remarks' }
} }
const projections = { '$$replaceRoot': {
'newRoot': {
'$mergeObjects': [
{ 'login_id': '$_id' },
{ '$arrayToObject': '$codes' },
{ '$arrayToObject': '$remarks' }
]
}
} }
const result = await Feedback.aggregate([
match,
allGroup,
remarksGroup,
codeGroup,
projections
])
/* get the location key */
const data = result.map(item => {
const [{ location }, ...rest] = dataArr.filter(d => d.location_id === item.location_id)
return { location, ...item }
})
console.log(data)
res.send(data)
} catch (err) {
// handle error
}
})()

MongoDB $addToSet to deep nested array of object

Below is my data structure.
{
"_id" : "room1",
"members" : [
{
"_id" : "member1",
"name" : "Michael",
"payments" : [
{
"month": "2018/09"
"amount": "20"
}
]
},
]
}
I want to push below object to Michael's payments
{
"month": "2018/09",
"amount": "5000"
}
In this case, What I want to is overwrite object, because month: "2018/09" already exist. Like below :
{
"_id" : "room1",
"members" : [
{
"_id" : "member1",
"name" : "Michale",
"payments" : [
{
"month": "2018/09"
"amount": "5000"
}
]
},
]
}
And, In case when I want to push object that not exist same month in payments, I want to add this object to payments.
{
"month": "2018/10",
"amount": "2000"
}
So the expected result is
{
"_id" : "room1",
"members" : [
{
"_id" : "member1",
"payments" : [
{
"month": "2018/09"
"amount": "5000"
},
{
"month": "2018/10"
"amount": "2000"
}
]
},
]
}
I tried like below, but it's not working. My code generate duplicated new month object every time I tried. How can I do this properly?
Rooms.update(
{
_id: "room1",
"members._id": "member1",
"members.$.payments": {
$not: {
$elemMatch: {
month: req.body.month
}
}
}
},
{
$addToSet: {
"members.$.payments": {
month: req.body.month,
amount: req.body.value
}
}
},
{ multi: true }, function (err, result) {
console.log(result)
}
)
You can use below command to add without duplicity either in months or amount
Rooms.update(
{
_id: "room1",
"members._id": "member1"
},
{
$addToSet: {
"members.$.payments": {
month: req.body.month,
amount: req.body.value
}
}
},function (err, result) {
console.log(result)
}
)
So I heard I have to determine duplication myself, so below is my code... it's writing now.,,
So Finally this is my code
Clubs.findOne({
uid: req.params.club_id,
"members._id": mongoose.Types.ObjectId(req.params.member_uid)
}, function(err, club){
let member = club.members.filter(el => {
if(el._id.equals(req.params.member_uid)) return el
})
let duplicated = false;
member[0].payments.map(el => {
if(el.month === req.body.month) duplicated = true
})
if(duplicated){
Clubs.update(
{
uid: req.params.club_id,
"members._id": mongoose.Types.ObjectId(req.params.member_uid),
},
{
$set: {
["members.$.payments."+index+".amount"] : req.body.value
}
},
function (err, result, third) {
if (err) throw err
console.log('result')
console.log(result)
res.json({})
}
)
} else {
Clubs.update(
{
uid: req.params.club_id,
"members._id": mongoose.Types.ObjectId(req.params.member_uid),
},
{
$push: {
"members.$.payments" : {
month : req.body.month,
amount: req.body.value
}
}
},
function (err, result, third) {
if (err) throw err
console.log('result')
console.log(result)
res.json({})
}
)
}
})
Perhaps consider changing the structure of your nested array to an object? So change this
{
"payments": [{
"month": "2018/09"
"amount": "5000"
},
{
"month": "2018/10"
"amount": "2000"
}
]
}
to this:
{
"payments": {
"2018/09": "5000",
"2018/10": "2000"
}
}
Then you can do a simple update:
Rooms.update({
_id: "room1",
"members._id": "member1",
"members.payments": {
$exists: true
}
}, {
$set: {
"members.payments." + req.body.month: req.body.value
}
},
)

Sub group Mongoose

I'm trying to get a sub gruop from a query using nodejs and mongoose.
The thing I'm trying to do is the following:
I have this collection:
I Need to count and group all the documents with the same 'intent' and make a subgroup with the 'entity' value, so far I have this running:
try {
//We first get the total interactions from all workspace
let workspace = await Interaction.aggregate([
{ $match: { dateAdded: { $gte: todayStart, $lt: todayEnd }, workspace: workspaceID } },
{ $group: { _id: "$workspace", data: { $sum: 1 } } },
{ $sort: { _id: 1 } }
]).exec();
//We then get the total results from conversations
let results = await Interaction.aggregate([
{ $match: { dateAdded: { $gte: todayStart, $lt: todayEnd }, workspace: workspaceID } },
{ $group: { _id: '$intent', data: { $sum: 1 } } },
{ $sort: { _id: 1 } }
]).exec()
//workspaceItems = workspace.map(function (Interaction) { return Interaction._id; });
return res.json({
total: workspace,
result: results
})
} catch (err) {
console.log(err);
return res.status(500).send(err)
}
The result look like this:
{
"total": [
{
"_id": "Business",
"data": 23
}
],
"result": [
{
"_id": "N/A",
"data": 2
},
{
"_id": "PRODUCTO_BENEFICIOS",
"data": 3
},
{
"_id": "PRODUCTO_DESCRIPCION",
"data": 10
},
{
"_id": "REPORTE_TARJETA_PERDIDA",
"data": 1
},
{
"_id": "REQUISITOS",
"data": 7
}
]
}
I need the result in this way :
{
"total": [
{
"_id": "Business",
"data": 23
}
],
"result": [
{
"_id": "N/A",
"data": 2
},
{
"_id": "PRODUCTO_BENEFICIOS",
"entities: [{"TARJETAS","PROMOCIONES"."ETC..."}],
"data": 3
},
{
"_id": "PRODUCTO_DESCRIPCION",
"entities: [{"TARJETAS","PROMOCIONES"."ETC..."}],
"data": 10
},
{
"_id": "REPORTE_TARJETA_PERDIDA",
"entities: [{"TARJETAS","PROMOCIONES"."ETC..."}],
"data": 1
},
{
"_id": "REQUISITOS",
"entities: [{"TARJETAS","PROMOCIONES"."ETC..."}],
"data": 7
}
]
}
I Hope to be clear, please let me know if you know how to do this using mongoose.
Thank you in advance.
try changing the 2nd query to following
let results = await Interaction.aggregate([
{ $match: { dateAdded: { $gte: todayStart, $lt: todayEnd }, workspace: workspaceID } },
{ $group: { _id: '$intent', entities: {$push: "$entity"}, data: { $sum: 1 } } },
{ $sort: { _id: 1 } }
]).exec()
if you want a unique list of entities you can use $addToSet instead of $push

Find element in array using mongodb

I simply want to count the element in array based on the query. I tried the following command but not solved my problem.
I want to count the element whose TimeStamp is in between "2017-02-17T18:30:00.000Z and "2017-02-18T18:29:59.999Z" on DATA2 array, but it returns only 1.
CODE Executed:
CODE 1
db.ABC.aggregate([{
$match: {
$and: [{
DATA2: {
$exists: true
}
}, {
"DATA2.TimeStamp": {
$gte: require('../../modules/getDates').getFromDate(item),
$lte: require('../../modules/getDates').getToDate(item)
}
}, {
Client_id: "123" /*req.query.client_id*/
}]
}
}, {
$project: {
DATASiz: {
$size: "$DATA2"
},
"has bananas": {
$in: ["DATA2.$.TimeStamp"]
}
}
}], function(err, result) {
console.log(result)
callBack();
})
Code 2
db.abc.find({ $and:[{DATA2: {$exists: true}},{Client_id: "123"},{"DATA2": { $elemMatch: { TimeStamp: { $gte: require('../../modules/getDates').getFromDate(item), $lte: require('../../modules/getDates').getToDate(item) } } }}]
}, function(err, result) {
console.log(JSON.stringify(result))
callBack();
})
Code 3
//db.abc.find //also tried
db.abc.count({
$and: [{
DATA2: {
$exists: true
}
}, {
"DATA2.TimeStamp": {
$gte: require('../../modules/getDates').getFromDate(item),
$lte: require('../../modules/getDates').getToDate(item)
}
}, {
Client_id: "123" /*req.query.client_id*/
}]
},{
"DATA2.$":1
}, function(err, result) {
console.log(result)
callBack();
})
JSON Format:
{
"_id": {
"$oid": "57c7404985737e2c78fde6b3"
},
"ABC": "1304258470",
"Status": "Not Found",
"DATA1": [
{123},{123},{123}
],
"Remark": "Not Found",
"DATA2": [
{
"TimeStamp": "2017-02-18T09:01:43.060Z",
"NdrStatus": "Door Locked",
},
{
"TimeStamp": "2017-02-18T08:09:43.347Z",
"NdrStatus": "HOLD",
},
{
"TimeStamp": "2017-02-20T08:09:43.347Z",
"NdrStatus": "HOLD",
}
]
}
Result:
I am getting the first element of DATA2 using CODE 3 but I know that as per the query 2 elements are to return.
I expect 2 as in count.
Also used $unwind $redact
Thanks in advance.
You can use the $filter and $size operators for this:
var start = require('../../modules/getDates').getFromDate(item),
end = require('../../modules/getDates').getToDate(item);
db.ABC.aggregate([
{
"$match": {
"DATA2": { "$exists": true },
"DATA2.TimeStamp": { "$gte": start, "$lte": end },
"Client_id": "123"
}
},
{
"$project": {
"DATASiz": {
"$size": {
"$filter": {
"input": "$DATA2",
"as": "item",
"cond": {
"$and": [
{ "$gte": ["$$item.TimeStamp", start] },
{ "$lte": ["$$item.TimeStamp", end] }
]
}
}
}
}
}
}
], function(err, result) {
console.log(result);
callBack();
});

Resources