I'm making an in app messaging system in which I have to show the list of conversations with their last message and the unread count. My schema is as follows--
var schema = new Schema({
senderID: {
type: Schema.Types.ObjectId,
ref: 'Member'
},
receiversID: [{
type: Schema.Types.ObjectId,
ref: 'Member'
}],
content: {
type: String,
default: ''
},
isRead: {
type: Boolean,
default: false,
},
createdAt: {
type: Number,
default: Date.now
}
});
I did this initially to get all the conversations with their last message --
messageModel.aggregate(
[{ $match: { senderID: userId } },
{ $unwind: '$receiversID' },
{ $sort: { createdAt: -1 } },
{ $group: { _id: '$receiversID', unreadCount: { $sum: { $cond: [{ $eq: ["$isRead", false] }, 1, 0] } }, senderID: { $first: '$senderID' }, receiversID: { $first: '$receiversID' }, content: { $first: '$content' } } },
{ $skip: pagingData.pageSize * (pagingData.pageIndex - 1) },
{ $limit: pagingData.pageSize }
], function (err, docs) {
resolve(docs);
}
);
But it doesn't shows the messages if you are a receiver. I want to show the conversation whether you are receiver or sender.
i use something like this:
{
'$or': [
{
'$and': [
{
'receiversID': userId
}, {
'senderID': toUserId
}
]
}, {
'$and': [
{
'receiversID': toUserId
}, {
'senderID': userId
}
]
},
],
}
Related
I'm trying to query for multiple dates in date ranges in mongoose.
member: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: "Member",
},
point: {
type: Array,
},
recommended: [
{
member: {
type: mongoose.Schema.Types.ObjectId,
ref: "Member",
},
money: { type: Number },
date: {
type: Date,
default: new Date(),
},
},
],
matching: {
type: Array,
},
this is my model
{ const { date } = req.body;
let start = new Date(date[0]);
let end = new Date(date[1]); }
let result = await Income.aggregate([
{
$match: {
_id: mongoose.Types.ObjectId(req.params.id),
recommended: { $elemMatch: { $gt: start, $lt: end } },
},
},
{
$project: {
recommended: {
$filter: {
input: "$recommended",
as: "item",
cond: {
$and: [
{ $gt: ["$$item.date", start] },
{ $lt: ["$$item.date", end] },
],
},
},
},
},
},
]);
I send date from react antd date picker date: ["2022-06-20", "2022-06-25"]
this is controller
I don't understand why it's won't work what is the problem it's no error
api return empty array
How to display "hardest category" based on in which "study" size of notLearnedWords was the highest. MongoDB Aggregation
I have these 3 models:
Study
WordSet
Category
Study model has reference into WordSet, then WordSet has reference into Category.
And based on Studies i'm displaying statistics.
How i can display "The hardest category" based on size of "notLearnedWords" was the highest?
I don't know on which place i should start with that querying.
For now i display "hardestCategory" as element that is most used.
I think that condition would look something like this:
{ $max: { $size: '$notLearnedWords' } } // size of the study with most notLearnedWords
I would achieve a response like this:
"stats": [
{
"_id": null,
"numberOfStudies": 4,
"averageStudyTime": 82.5,
"allStudyTime": 330,
"longestStudy": 120,
"allLearnedWords": 8
"hardestCategory": "Work" // only this field is missing
}
]
I've tried to do it like this:
const stats = await Study.aggregate([
{ $match: { user: new ObjectID(currentUserId) } },
{
$lookup: {
from: 'users',
localField: 'user',
foreignField: '_id',
as: 'currentUser',
},
},
{
$lookup: {
from: 'wordsets',
let: { wordSetId: '$learnedWordSet' },
pipeline: [
{ $match: { $expr: { $eq: ['$_id', '$$wordSetId'] } } },
{
$project: {
_id: 0,
category: 1,
},
},
{ $unwind: '$category' },
{
$group: {
_id: '$category',
count: { $sum: 1 },
},
},
{ $sort: { count: -1 } },
{ $limit: 1 },
{
$lookup: {
from: 'categories',
localField: '_id',
foreignField: '_id',
as: 'category',
},
},
{
$project: {
_id: 0,
category: { $arrayElemAt: ['$category.name', 0] },
},
},
],
as: 'wordSet',
},
},
{
$group: {
_id: null,
numberOfStudies: { $sum: 1 },
averageStudyTime: { $avg: '$studyTime' },
allStudyTime: { $sum: '$studyTime' },
longestStudy: { $max: '$studyTime' },
allLearnedWords: {
$sum: { $size: '$learnedWords' },
},
hardestCategory: {
$first: {
$first: '$wordSet.category',
},
},
studyWithMostNotLearnedWords: { $max: { $size: '$notLearnedWords' } },
},
},
]);
Study
const studySchema = new mongoose.Schema({
name: {
type: String,
},
studyTime: {
type: Number,
},
learnedWords: [String],
notLearnedWords: [String],
learnedWordSet: {
type: mongoose.Schema.Types.ObjectId,
ref: 'WordSet',
},
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
},
});
WordSet
const wordSetSchema = new mongoose.Schema({
name: {
type: String,
},
category: {
type: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Category',
required: true,
},
],
},
});
Category
const categorySchema = new mongoose.Schema({
name: {
type: String,
},
});
I am working on a NodeJS and a Mongoose Project and I have the following two schemas.
UserSchema.js
const UserSchema = mongoose.Schema({
name: {
type: String,
required: true,
trim: true,
},
incharge: {
type: String,
enum: ['Adhihariharan', 'Anuja', 'Dhivya', 'Govind', 'Joann'],
required: true
},
)}
ContactSchema.js
const ContactSchema = new mongoose.Schema(
{
name: {
type: String,
trim: true,
required: [true, 'Please add a name'],
},
status: {
type: String,
enum: [
'Not Called',
'Wrong Number',
'Called/Declined',
'Called/Not Reachable',
'Called/Postponed',
'Called/Accepted',
'Emailed/Awaiting Response',
'Emailed/Declined',
'Emailed/Confirmed',
],
default: 'Not Called',
},
user: {
type: mongoose.Schema.ObjectId,
ref: 'User',
required: true,
},
I am looking for a query which would give me a result which looks as the following:
[
{
_id: "5d7a514b5d2c12c7449be048",
name: "Benita",
incharge: "Joann",
statuses: [
{ status: "Not Called", count: 1 },
{ status: "Called/Accepted", count: 1 },
{ status: "Called/Declined", count: 1 },
{ status: "Called/Not Reachable", count: 1 },
]
},
{
_id: "5d7a514b5d2c12c7449be048",
name: "Febia",
incharge: "Dhivya",
statuses: [
{ "Not Called": 2 },
{ "Called/Postponed": 2 },
{ "Called/Declined": 3 },
{ "Called/Not Reachable": 1 },
]
},
... and so on
]
Here, the integer, is the number of times that status appears for a particular user and in charge is the manager in charge of the user. The _id mentioned is the ID of the user.
The _id, user, in charge belong to the UserSchema and the status belongs to the ContactSchema
I have tried the following query:
teams = await Contact.aggregate([
{
$group: {
_id: { user: '$user', status: '$status' },
count: { $sum: '$count' },
},
},
{
$lookup: {
from: 'members',
localField: '_id.user',
foreignField: '_id',
as: 'user',
},
},
{
$unwind: { path: '$user' },
},
{
$project: {
'user.name': 1,
'user.incharge': 1,
count: 1,
},
},
{
$sort: { 'user.incharge': 1, 'user.name': 1 },
},
]);
And the following was the output:
{
_id: { user: 5ff52b10fa237b001c93ef18, status: 'Not Called' },
count: 1,
user: { name: 'Benita', incharge: 'Joann' }
},
{
_id: { user: 5ff4ca05fa237b001c93ef15, status: 'Not Called' },
count: 2,
user: { name: 'Febia', incharge: 'Dhivya' }
},
{
_id: { user: 5ff4ca05fa237b001c93ef15, status: 'Called/Accepted' },
count: 4,
user: { name: 'Febia', incharge: 'Dhivya' }
}
Can someone please help me get the desired result?
Thanks in advance.
EDIT:
I did try #turivishal's approach but this is what I got:-
{
_id: 5ff52b10fa237b001c93ef18,
name: 'Sadana',
incharge: 'Joann',
statuses: [ [Object] ]
},
{
_id: 5ff4ca05fa237b001c93ef15,
name: 'Sudarshan B',
incharge: 'Joann',
statuses: [ [Object], [Object] ]
}
Can you please tell me how I can access the [Object] inside the status array in mongoose so that I can get a result as below...
{
_id: "5ff4ca05fa237b001c93ef15",
name: "Sudarshan B",
incharge: "Joann",
statuses: [
{ "Not Called": 2 },
{ "Called/Postponed": 2 },
]
},
You can try lookup with aggregation pipeline,
$lookup with contact pass _id in let,
$match user id condition
$group by status and get total count
$project to change name of the key and value
$addFields to convert statuses array to object using $arrayToObject
teams = await User.aggregate([
{
$lookup: {
from: "contact",
let: { user: "$_id" },
pipeline: [
{ $match: { $expr: { $eq: ["$$user", "$user"] } } },
{
$group: {
_id: "$status",
count: { $sum: 1 }
}
},
{
$project: {
_id: 0,
k: "$_id",
v: "$count"
}
}
],
as: "statuses"
}
},
{ $addFields: { statuses: { $arrayToObject: "$statuses" } } }
])
Playground
I want to get the business information with businessId as a reference. However, I can't get the correct data because the previous developer did not use ObjectId type on the model. Now what I want to do is convert the businessId type to objectId withough altering the model, it would be easy if I do it but the old data will be affected, which is not good. Please see below for the model
const scanHistory = new Schema({
businessId: { type: String },
domains: [
{
domainId: { type: String },
scanType: { type: String },
status: { type: String },
reportUrl: { type: String },
scanStart: { type: Date, default: Date.now },
scanFinish: { type: Date, default: Date.now }
}
],
scanStart: { type: Date, default: Date.now },
scanStatus: { type: String },
scanType: { type: String }
});
This is my aggregate query
.collection("scanhistories")
.aggregate([
{
$addFields: {
businessObjId: {
$convert: {
input: "businessId",
to: "objectId",
onError: "Could not convert to type ObjectId."
}
}
}
},
{
$group: {
_id: { $max: "$businessId" },
businessObjId: { $max: "$businessId" },
scanStatus: { $max: "$scanStatus" },
scanStart: { $max: "$scanStart" },
domains: { $max: "$domains" }
}
},
{
$lookup: {
from: "businesses",
as: "businessInfo",
localField: "businessObjId",
foreignField: "_id"
}
},
{
$project: {
_id: 1,
businessObjId: 1,
primaryDomain: { $arrayElemAt: ["$businessInfo.primaryDomain", 0] },
businessName: { $arrayElemAt: ["$businessInfo.businessName", 0] },
frequency: { $arrayElemAt: ["$businessInfo.scanFrequency", 0] },
scanStatus: 1,
domains: 1
}
},
{
$match: {
scanStatus: { $in: ["running", "undef"] },
domains: { $exists: true }
}
}
])
.toArray();
for (let x = 0; x < history.length; x++) {
console.log(history[x]);
}
Now the output is like this which is not the one I expected.
{ _id: 5de09321bdb7cc07b7595de4,
businessObjId: 5de09321bdb7cc07b7595de4,
scanStatus: 'undef',
domains:
[ { _id: 5dfa626300007c243c1528b3,
domainId: '5de09321bdb7cc07b7595de5',
scanType: 'scheduled',
status: 'running',
reportUrl: '',
scanStart: 2019-12-18T17:31:14.754Z,
scanFinish: 2019-12-18T17:31:14.754Z } ] }
The expected result should have been with the lookup businessInfo that I wanted
{ _id: 5de09321bdb7cc07b7595de4,
businessObjId: 5de09321bdb7cc07b7595de4,
scanStatus: 'undef',
domains:
[ { _id: 5dfa626300007c243c1528b3,
domainId: '5de09321bdb7cc07b7595de5',
scanType: 'scheduled',
status: 'running',
reportUrl: '',
scanStart: 2019-12-18T17:31:14.754Z,
scanFinish: 2019-12-18T17:31:14.754Z } ],
primaryDomain: "mydomainxxx.xy",
businessName: "The biz",
scanFrequency: "daily"
}
Can you help me? I am really new to MongoDB and my background is PHP/SQL so all advises will be much appreciated. Thank you!
So I have found the solution for this. It was just a single mistake. :(
So on the aggregate code above where the group pipeline is. I made a mistake here.
Previous code above
{
$group: {
_id: { $max: "$businessId" },
businessObjId: { $max: "$businessId" },
scanStatus: { $max: "$scanStatus" },
scanStart: { $max: "$scanStart" },
domains: { $max: "$domains" }
}
},
Correct one
I change this part here:
businessObjId: { $first: "$businessObjId" }
{
$group: {
_id: { $max: "$businessId" },
businessObjId: { $first: "$businessObjId" },
scanStatus: { $max: "$scanStatus" },
scanStart: { $max: "$scanStart" },
domains: { $max: "$domains" }
}
},
I am trying to create a social networking application which can have connect (followers, following), posts, comments, likes, shares, etc. This is a MVP project, but still i wanted to explore mongoDB for this use case. I am having some doubt regarding the performance of this application.
I have three collection:
Posts: This is where a new post shall be added. This collection contains all the details related to a post.
Schema:
const postSchema = new mongoose.Schema({
user_id: String,
title: String,
text: String,
type: { type: String, enum: ["music", "movie", "tv"] },
mediaUrl: String,
thumbnailUrl: String,
accessType: { type: Number, enum: [1, 2, 3] },
tags: [String],
like_count: Number,
comment_count: Number,
share_count: Number,
insertDate: {
type: Date,
default: () => {
return new Date();
}
}
});
Feeds: This collection just add a metadata of user and post including tags. This i intend to use to get the relevant feed for a particular user.
Schema:
const feedSchema = new mongoose.Schema({
post_id: String,
user_id: String,
isTag: Boolean,
isPublic: Boolean,
insertDate: {
type: Date,
default: () => {
return new Date();
}
},
modifiedDate: {
type: Date,
default: () => {
return new Date();
}
}
});
Connects: This collection is for the relationship of users.
Schema:
const connectSchema = new mongoose.Schema({
followed_by: String,
user_id: String,
insertDate: {
type: Date,
default: () => {
return new Date();
}
}
});
My approach was to first find the posts from feeds collection basis users whom I am following, then fetching the posts from post collection.
Here goes my attempted query:
db.connects.aggregate([
{ $match: { followed_by: "5cbefd61d3b53a4aaa9a2b16" } },
{
$lookup: {
from: "feeds",
let: { user_id: "$user_id" },
pipeline: [{ $match: { $expr: { $or: [{ $eq: ["$user_id", "$$user_id"] }, { isPublic: true }] } } }],
as: "feedData"
}
},
{ $unwind: "$feedData" },
{ $replaceRoot: { newRoot: "$feedData" } },
{ $group: { _id: { post_id: { $toObjectId: "$post_id" }, modifiedDate: { $toLong: "$modifiedDate" } } } },
{ $replaceRoot: { newRoot: "$_id" } },
{ $sort: { modifiedDate: -1 } },
{ $skip: 0 },
{ $limit: 10 },
{
$lookup: { from: "posts", localField: "post_id", foreignField: "_id", as: "postData" }
},
{ $unwind: "$postData" },
{ $replaceRoot: { newRoot: "$postData" } },
{ $addFields: { userId: { $toObjectId: "$user_id" } } },
{
$lookup: { from: "users", localField: "userId", foreignField: "_id", as: "userData" }
},
{
$project: {
post_id: "$_id",
user_id: 1,
title: 1,
text: 1,
typeaccessType: 1,
mediaUrl: 1,
thumbnailUrl: 1,
insertDate: { $toLong: "$insertDate" },
like_count: 1,
comment_count: 1,
share_count: 1,
user_email: { $arrayElemAt: ["$userData.email", 0] },
user_profile_pic: { $arrayElemAt: ["$userData.profile_pic", 0] },
username: { $arrayElemAt: ["$userData.firstname", 0] }
}
}
]).pretty();
Please share your feedback on:
Which index should I use to boost up the performance?
Is there is a better way of doing the same in mongoDB. Also if any part of the query can be optimised?