Query to count the number of documents for each user - mongodb

I have a collection named 'captures' and the documents within it have the field 'username'
a document looks something like this
/* 1 */
{
"_id" : ObjectId("622b951a026ca3a73f5a2a1c"),
"username" : "andre",
"data" : {
"metadata" : {
"start" : "2022-02-24T09:32:22.390Z",
...
},
...
}
}
/* 2 */
{
"_id" : ObjectId("9255941b026ca3a73f5a2a1c"),
"username" : "andre",
"data" : {
"metadata" : {
"start" : "2022-05-10T03:12:23.440Z",
...
},
...
}
}
/* 3 */
{
"_id" : ObjectId("7775941b026ca3a73f5a2a1c"),
"username" : "bob",
"data" : {
"metadata" : {
"start" : "2022-05-16T12:24:12.002Z",
...
},
...
}
}
/* 4 */
{
"_id" : ObjectId("3215331b026ca3a73f5a2a1c"),
"username" : "bob",
"data" : {
"metadata" : {
"start" : "2022-05-18T12:24:12.002Z",
...
},
...
}
}
I'd like to return a count of documents for each distinct username, where 'start' is after 2022-02-24T09:32:22.390Z
the above example would return something like:
{ "user" : "andre", "count" : 1 }
{ "user" : "bob", "count" : 2 }
I've tried using count, distinct, aggregate without success...

This is pretty simple to do with the aggregation framework:
[
{
$project: {
_id: 0,
user: '$username',
start: {
$toDate: '$data.metadata.start'
}
}
},
{
$match: {
start: {
$gt: Date('2022-02-24T09:32:22.390Z')
}
}
},
{
$group: {
_id: '$user',
user: {
$first: '$user'
},
count: {
$sum: 1
}
}
}
]
By the way you should store dates as Date objects, not strings, it will make your life easier.

Related

I'm having trouble with a mongodb function that should be finding duplicates

I have the following function that is supposed to be returning the _id value of duplicates based on the email key. However, no matter what I've tried, I can't get the function to return anything other than any empty object. What am I missing here? Or is there a better approach I should be considering?
var duplicates = [];
db.medicallists
.aggregate([
{
$group: {
_id: {
email: "$email"
},
duplicate_ids: { $addToSet: "$_id" },
count: { $sum: 1 }
}
},
{
$match: {
count: { $gt: 1 }
}
},
], { allowDiskUse: true })
.forEach(function(doc) {
doc.duplicate_ids.shift(); // First element skipped for deleting
doc.duplicate_ids.forEach(function(dupId) {
duplicates.push(dupId); // Getting all duplicate ids
});
});
printjson(duplicates);
EDIT:
Here is a sample document:
{
_id : 5a2fed0c8023cf7ea2346067,
primary_spec : "Addiction Medicine",
first_name : "John",
last_name : "Sample",
city : "Las Vegas",
state : "NV",
phone : "1111111111",
fax : "1111111111",
email : "sample#aol.com"
}
I have tested your specific query on a similar data set and it works fine. printjson(duplicates); prints out all the duplicate ids.
Also this will, in fact, remove all the duplicate entries based on email:
db.collection.aggregate([
{
$group: {
_id: {
email: "$email"
},
duplicate_ids: {
$push: "$_id"
},
count: {
$sum: 1
}
}
},
{
$match: {
count: {
$gt: 1
}
}
},
]).forEach(function(doc){
doc.duplicate_ids.shift();
db.collection.remove({
_id: {
$in: doc.duplicate_ids
}
});
})
My starting set was:
{
"_id" : ObjectId("6014331de1ef9ab1f708ddd9"),
"item" : "card",
"email" : "zzz#yahoo.com"
}
{
"_id" : ObjectId("6014331de1ef9ab1f708ddda"),
"item" : "card",
"email" : "eee#yahoo.com"
}
{
"_id" : ObjectId("6014331de1ef9ab1f708dddb"),
"item" : "card",
"email" : "zzz#yahoo.com"
}
{
"_id" : ObjectId("6014331de1ef9ab1f708dddc"),
"item" : "card",
"email" : "aaa#yahoo.com"
}
After running the query, it turned to:
{
"_id" : ObjectId("6014331de1ef9ab1f708ddd9"),
"item" : "card",
"email" : "zzz#yahoo.com"
}
{
"_id" : ObjectId("6014331de1ef9ab1f708ddda"),
"item" : "card",
"email" : "eee#yahoo.com"
}
{
"_id" : ObjectId("6014331de1ef9ab1f708dddc"),
"item" : "card",
"email" : "aaa#yahoo.com"
}
Tested on MongoDB server version: 4.2.7

MongoDB get user which are new today

I am trying to find a user list which is new for day-1. I have written the query to find the users who arrived till the day before yesterday and the list of users arrived yesterday. Now I want minus those data how can I do that in a single aggregate function.
Function to get the list before yesterday
db.chat_question_logs.aggregate([
{
$match : {"createdDate":{$lte: ISODate("2020-04-29T00:00:00Z")}}
},
{
"$project" :
{
_id : 0,
"userInfo.userId":1
}
},
{
"$group": {
"_id": {userId:"$userInfo.userId"},"count": {$sum : 1}}
}
])
similarly for the day-1 is as below
db.chat_question_logs.aggregate([
{
$match : {"createdDate":{$gte: ISODate("2020-04-30T00:00:00Z"),$lte: ISODate("2020-05-01T00:00:00Z")}}
},
{
"$project" :
{
_id : 0,
"userInfo.userId":1
}
},
{
"$group": {
"_id": {userId:"$userInfo.userId"},"count": {$sum : 1}}
}
])
Result JSON are as below
/* 1 */
{
"_id" : {
"userId" : "2350202241750776"
},
"count" : 1
},
/* 2 */
{
"_id" : {
"userId" : "26291570771793121"
},
"count" : 1
},
/* 3 */
{
"_id" : {
"userId" : "2742872209107866"
},
"count" : 5
},
/* 4 */
{
"_id" : {
"userId" : "23502022417507761212"
},
"count" : 1
},
/* 5 */
{
"_id" : {
"userId" : "2629157077179312"
},
"count" : 43
}
How can I find the difference.
It sounds like what you want is to get all users created yesterday (which is the 28th in this example).
db.chat_question_logs.aggregate([
{
$match : { $and: [
{ "createdDate":{$lt: ISODate("2020-04-29T00:00:00Z")} },
{ "createdDate": {$gte: ISODate("2020-04-28T00:00:00Z") }}
] }
},
{
"$project" :
{
_id : 0,
"userInfo.userId":1
}
},
{
"$group": {
"_id": {userId:"$userInfo.userId"},"count": {$sum : 1}}
}
])
Is this what you want?
Hi found the solution which is below
I used the group and first appearance of the Id and then filter record on date which I wanted.The query is as below
db.chat_question_logs.aggregate([
{
$group:
{
_id: "$userInfo.userId",
firstApprance: { $first: "$createdDate" }
}
},
{
$match : { "firstApprance": { $gte: new ISODate("2020-05-03"), $lt: new ISODate("2020-05-05") } }
}
])

MongoDB Query Aggregation

I need a query that takes multiple 'companyID's' and return the count for each company.
Currently this query only does this for one companyID and it does not return the id but just 'null' like show below.
I understand that I can use the 'in' operator for multiple companyID's but not sure how I would go about having the query return the count for multiple companyID's
db.getCollection('reg').aggregate([ {
'$match' : {
'$and' : [
{
'companyID' : 11
},
{
'created' : {
'$gte' : 1556726597
}
},
{
'created' : {
$lt : 1580572997
}
}
]
}
},
{
'$project' : {
'testID' : 1,
}
},
{
'$group' : {
'_id' : '$testID',
'registrationsCount' : {'$sum' : 1},
},
},
{
$group: {
_id: null,
count: { $sum: 1 }
}
}
])
The result below
{
"_id" : null,
"count" : 10.0
}
Schema below
{
"_id" : NumberLong(1),
"appUserID" : NumberLong(4294967295),
"companyID" : NumberLong(5),
"created" : NumberLong(1372625588),
"testID" : NumberLong(11),
"isCheckIn" : true,
"lastModified" : NumberLong(1372625588),
"source" : "upload",
"timeArrived" : NumberLong(1343062512),
}
I think you need to do 2 things
first
'$match' : {
'$or': [
'$and' : [
{
'companyID' : 11
},
{
'created' : {
'$gte' : 1556726597
}
},
{
'created' : {
$lt : 1580572997
}
}
]
// ... add the other ids you need
]
}
And second
{
'$group' : {
'_id' : '$testID',
'registrationsCount' : {'$sum' : 1},
},
},
{
$group: {
_id: '$testID', // or '$_id' not sure
count: { $sum: 1 }
}
}
Hope this helps!

How can I correctly output an array of objects in the reverse order from mongodb?

Comments are saved in an array of objects. How can I correctly output them in reverse order (comments from newest to oldest)?
My db:
{"_id":{"$oid":"5e3032f14b82d14604e7cfb7"},
"videoId":"zX6bZbsZ5sU",
"message":[
{"_id":{"$oid":"5e3032f14b82d14604e7cfb8"},
"user":{"$oid":"5e2571ba388ea01bcc26bc96"},"text":"1"
},
{"_id":{"$oid":"5e3032f14b82d14604e7cfb9"},
"user":{"$oid":"5e2571ba388ea01bcc26bc96"},"text":"2"
},
....
]
My sheme Mongoose:
const schema = new Schema({
videoId: { type: String, isRequired: true },
message: [
{
user: { type: Schema.Types.ObjectId, ref: 'User' },
text: { type: String }
},
]
});
My code:
const userComments = await Comment.find(
{ videoId: req.query.videoId },
{ message: { $slice: [skip * SIZE_COMMENT, SIZE_COMMENT] } }
)
.sort({ message: -1 })
.populate('message.user', ['avatar', 'firstName']);
but sort not working;
thanks in advance!
You can simply use $reverseArray to reverse content of an array.
db.collection.aggregate([
{
$addFields:
{
message: { $reverseArray: "$message" }
}
}
])
Collection Data :
/* 1 */
{
"_id" : ObjectId("5e3032f14b82d14604e7cfb7"),
"videoId" : "zX6bZbsZ5sU",
"message" : [
{
"_id" : ObjectId("5e3032f14b82d14604e7cfb8"),
"user" : ObjectId("5e2571ba388ea01bcc26bc96"),
"text" : "1"
},
{
"_id" : ObjectId("5e3032f14b82d14604e7cfb9"),
"user" : ObjectId("5e2571ba388ea01bcc26bc96"),
"text" : "2"
}
]
}
/* 2 */
{
"_id" : ObjectId("5e309318d02e05b694b0b25f"),
"videoId" : "zX6bZbsZ5sUNEWWWW",
"message" : [
{
"_id" : ObjectId("5e3032f14b82d14604e7cfc9"),
"user" : ObjectId("5e2571ba388ea01bcc26bc87"),
"text" : "Old"
},
{
"_id" : ObjectId("5e3032f14b82d14604e7cfd0"),
"user" : ObjectId("5e2571ba388ea01bcc26bc87"),
"text" : "New"
}
]
}
Result :
/* 1 */
{
"_id" : ObjectId("5e3032f14b82d14604e7cfb7"),
"videoId" : "zX6bZbsZ5sU",
"message" : [
{
"_id" : ObjectId("5e3032f14b82d14604e7cfb9"),
"user" : ObjectId("5e2571ba388ea01bcc26bc96"),
"text" : "2"
},
{
"_id" : ObjectId("5e3032f14b82d14604e7cfb8"),
"user" : ObjectId("5e2571ba388ea01bcc26bc96"),
"text" : "1"
}
]
}
/* 2 */
{
"_id" : ObjectId("5e309318d02e05b694b0b25f"),
"videoId" : "zX6bZbsZ5sUNEWWWW",
"message" : [
{
"_id" : ObjectId("5e3032f14b82d14604e7cfd0"),
"user" : ObjectId("5e2571ba388ea01bcc26bc87"),
"text" : "New"
},
{
"_id" : ObjectId("5e3032f14b82d14604e7cfc9"),
"user" : ObjectId("5e2571ba388ea01bcc26bc87"),
"text" : "Old"
}
]
}
Your Query : You can use native MongoDB's $lookup instead of .populate() , So try below :
Comments.aggregate([
{
$addFields:
{
message: { $reverseArray: "$message" }
}
}, {
$lookup: {
from: "User",
let: { ids: "$message.user" },
pipeline: [
{
$match: { $expr: { $in: ["$_id", "$$ids"] } }
},
{ $project: { avatar: 1, firstName: 1, _id: 0 } }
],
as: "userData"
}
}
])
You're going to want to use one of two things:
The MongoDB aggregation framework
Sorting within the application
If you choose to use the MongoDB aggregation framework, you'll likely want to use the $unwind operation to expand the array into separate documents, then sorting those documents with the $sort operation.
You can see more on how to do that in this ticket:
how to sort array inside collection record in mongoDB
You could also do this within your application by first executing a query, and then sorting each of the arrays in the result set.
Best,

MongoDB lookup when foreign field is an array of ids

I have two collections, fruit and salesman . I want my query to return all fruit with comma separated salesman.
Salesman document have array of fruit id
fruit document have id, name ,........
salesman table have id,name, fruits[apple_id,mango_id.......],...
db.getCollection('fruit').aggregate([{ "$unwind": "$fruits" }, { "$lookup": {
"from": "salesman",
"localField": "fruits",
"foreignField": "_id",
"as": "fruitObjects"
}},
{ "$unwind": "$fruitObjects" } ])
even this query is not giving result of $fruitObjects..?
Fruit Document
{
"_id" : ObjectId("5b101caddcab7850a4ba32eb"),
"name" : "Mango"
}
{
"_id" : ObjectId("5b101caddcab7850a4ba32ec"),
"name" : "Pears"
}
{
"_id" : ObjectId("5b101caddcab7850a4ba32de"),
"name" : "apple"
}
{
"_id" : ObjectId("5b101caddcab7850a4ba32fe"),
"name" : "guava"
}
Salesman document
{
"_id" : ObjectId("5b101caddcab7850a4ba3257"),
"name" : "xyz",
"fruits":["5b101caddcab7850a4ba32ec","5b101caddcab7850a4ba32de","5b101caddcab7850a4ba32fe"]
}
{
"_id" : ObjectId("5b101caddcab7850a4ba3258"),
"name" : "abc",
"fruits":["5b101caddcab7850a4ba32eb","5b101caddcab7850a4ba32de"]
}
{
"_id" : ObjectId("5b101caddcab7850a4ba3259"),
"name" : "def",
"fruits":["5b101caddcab7850a4ba32ec"]
}
{
"_id" : ObjectId("5b101caddcab7850a4ba3260"),
"name" : "zxc",
"fruits":["5b101caddcab7850a4ba32ec","5b101caddcab7850a4ba32de","5b101caddcab7850a4ba32eb"]
}
``````````````````````````
Yes #barrypicker is correct - types of fields didn't match which is resulting in empty on salesman, Please try to store both of same type. Meanwhile you can actually convert one to other type - while querying each time, Also you need not to do $unwind, Please try below query :
Query 1:
db.fruit.aggregate([
{
$lookup:
{
from: "salesman",
let: { fruitName: { $toString: '$_id' } },
pipeline: [
{
$match:
{
$expr:
{ $in: ["$$fruitName", "$fruits"] }
}
}, { $project: { name: 1, _id: 0 } }
],
as: "salesman"
}
}])
Result for Query 1:
/* 1 */
{
"_id" : ObjectId("5b101caddcab7850a4ba32eb"),
"name" : "Mango",
"salesman" : [
{
"name" : "abc"
},
{
"name" : "zxc"
}
]
}
/* 2 */
{
"_id" : ObjectId("5b101caddcab7850a4ba32ec"),
"name" : "Pears",
"salesman" : [
{
"name" : "def"
},
{
"name" : "zxc"
}
]
}
/* 3 */
{
"_id" : ObjectId("5b101caddcab7850a4ba32de"),
"name" : "apple",
"salesman" : [
{
"name" : "abc"
},
{
"name" : "zxc"
}
]
}
/* 4 */
{
"_id" : ObjectId("5b101caddcab7850a4ba32fe"),
"name" : "guava",
"salesman" : []
}
Or if you want those names to be in an array :
Query 2 :
db.fruit.aggregate([
{
$lookup:
{
from: "salesman",
let: { fruitName: { $toString: '$_id' } },
pipeline: [
{
$match:
{
$expr:
{ $in: ["$$fruitName", "$fruits"] }
}
}, { $project: { name: 1, _id: 0 } }
],
as: "salesmanList"
}
}, {
$project: {
name: 1, salesman: {
$map:
{
input: "$salesmanList",
as: "each",
in: '$$each.name'
}
}
}
}])
Result for Query 2:
/* 1 */
{
"_id" : ObjectId("5b101caddcab7850a4ba32eb"),
"name" : "Mango",
"salesman" : [
"abc",
"zxc"
]
}
/* 2 */
{
"_id" : ObjectId("5b101caddcab7850a4ba32ec"),
"name" : "Pears",
"salesman" : [
"def",
"zxc"
]
}
/* 3 */
{
"_id" : ObjectId("5b101caddcab7850a4ba32de"),
"name" : "apple",
"salesman" : [
"abc",
"zxc"
]
}
/* 4 */
{
"_id" : ObjectId("5b101caddcab7850a4ba32fe"),
"name" : "guava",
"salesman" : []
}
fruit collection :
/* 1 */
{
"_id" : ObjectId("5b101caddcab7850a4ba32eb"),
"name" : "Mango"
}
/* 2 */
{
"_id" : ObjectId("5b101caddcab7850a4ba32ec"),
"name" : "Pears"
}
/* 3 */
{
"_id" : ObjectId("5b101caddcab7850a4ba32de"),
"name" : "apple"
}
/* 4 */
{
"_id" : ObjectId("5b101caddcab7850a4ba32fe"),
"name" : "guava"
}
salesman collection :
/* 1 */
{
"_id" : ObjectId("5b101caddcab7850a4ba3258"),
"name" : "abc",
"fruits" : [
"5b101caddcab7850a4ba32eb",
"5b101caddcab7850a4ba32de"
]
}
/* 2 */
{
"_id" : ObjectId("5b101caddcab7850a4ba3259"),
"name" : "def",
"fruits" : [
"5b101caddcab7850a4ba32ec"
]
}
/* 3 */
{
"_id" : ObjectId("5b101caddcab7850a4ba3260"),
"name" : "zxc",
"fruits" : [
"5b101caddcab7850a4ba32ec",
"5b101caddcab7850a4ba32de",
"5b101caddcab7850a4ba32eb"
]
}
If you want the entire object from salesman then you could remove { $project: { name: 1, _id: 0 } } in $lookup.
Ref : $lookup, $map
Does this help?
db.salesman.aggregate([
{ $unwind : "$fruits" },
{ $addFields : { "fruitObjectId": { $toObjectId: "$fruits" } } },
{ $lookup : {
"from" : "fruit",
"localField" : "fruitObjectId",
"foreignField" : "_id",
"as" : "fruitObjects"
}
}
])
Or, perhaps from the opposite perspective?
db.fruit.aggregate([
{ $project : {
_id : { $toString : "$_id" },
name : 1
}
},
{ $lookup : {
"from" : "salesman",
"localField" : "_id",
"foreignField" : "fruits",
"as" : "fruitObjects"
}
}
])