Collections association with nested in Mongoose - node.js

I had a problem with the association of collections.
I spent 2 days and still did not solve the problem, it's new for me.
My models:
// Schema opened cases
const openedSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: 'user',
required: [true, 'user is required'],
index: true
},
weapon: {
type: Schema.Types.ObjectId,
ref: 'cases.weapons',
required: [true, 'weapon is required'],
index: true
},
sellPrice: {
type: Number,
default: null
},
status: {
type: Number,
default: 0
}
}, {
timestamps: true
});
const opened = mongoose.model('opened', openedSchema);
// list cases
const casesSchema = new Schema({
name: {
type: String,
unique: true,
required: [true, 'name is required']
},
price: {
type: Number,
required: [true, 'price is required']
},
weapons: [ {
weapon: {
type: Schema.Types.ObjectId,
ref: 'weapon',
index: true
}
} ]
}, {
timestamps: false
});
const cases = mongoose.model('cases', casesSchema);
// list weapons
const weaponSchema = new Schema({
name: {
type: String,
unique: true,
required: [true, 'name is required']
},
price: {
type: Number,
required: [true, 'price is required']
},
autoship: {
count: Number,
status: Boolean,
price: Number
}
}, {
timestamps: false
});
const weapon = mongoose.model('weapon', weaponSchema);
That's what documents look like
// cases
{
"_id": {
"$oid": "59653bcfa9ac622e1913e10c"
},
"name": "test case #1",
"price": 256,
"weapons": [
{
"weapon": {
"$oid": "59653bcfa9ac622e1913e10b"
},
"_id": {
"$oid": "59653bcfa9ac622e1913e10d"
}
},
{
"_id": {
"$oid": "59653d3279aeda2fda9fb490"
},
"weapon": {
"$oid": "59653c5d069f562eb0ba4ef3"
}
},
{
"_id": {
"$oid": "59653d38ba04de2fdddc459f"
},
"weapon": {
"$oid": "59653c893a772e2ef7b65a29"
}
}
],
"__v": 0
}
// opened
{
"_id": {
"$oid": "5965d134c8c95972a1a498f5"
},
"updatedAt": {
"$date": "2017-07-12T07:35:16.419Z"
},
"createdAt": {
"$date": "2017-07-12T07:35:16.419Z"
},
"user": {
"$oid": "5965d0d6ea9db872360db98b"
},
"weapon": {
"$oid": "59653bcfa9ac622e1913e10d"
},
"status": 0,
"sellPrice": null,
"__v": 0
}
// weapon
{
"_id": {
"$oid": "59653bcfa9ac622e1913e10b"
},
"name": "AWP | Fever Dream",
"price": 300,
"autoship": {
"status": true,
"price": 167,
"count": 5
},
"__v": 0
}
I need to get a list of open cases with weapons data.
opened -> cases -> weapon
So, I do this:
opened.find()
.populate('cases.weapons')
.then(_opened => {
console.log(_opened);
})
.catch(err => {
logger.error(err);
});
But populate does not work.

Unless I am mistaken, there is no relationship between openedSchema and casesSchema.
It is not opened -> cases -> weapon but opened -> weapon as openedSchema has no field called cases -- which means cases will never be populated.
Based on your schema definition, it should be opened.find().populate('weapon').

Related

Need help in writing an aggregate query

I want only that data in "present" which is matched with student _id. But for now by this query I'm getting all student counts in every individual students data.
Student Schema
const StudentSchema = new mongoose.Schema(
{
name: {
type: String,
required: [true, "Please Provide Name"],
maxlength: 100,
minlength: 2,
},
email: {
type: String,
required: [true, "Please Provide Email"],
match: [
/^(([^<>()[\]\\.,;:\s#"]+(\.[^<>()[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
"Please Provide a Valid Email",
],
unique: true,
},
number: {
type: String,
required: [true, "Please Provide Number"],
match: [
/^(?:(?:\+|0{0,2})91(\s*[\-]\s*)?|[0]?)?[789]\d{9}$/,
"Please Provide a Valid Number",
],
unique: true,
},
rollNumber: {
type: Number,
required: [true, "Please Provide Roll Number"],
maxlength: 5,
},
departmentID: {
type: mongoose.Types.ObjectId,
ref: "Department",
required: [true, "Please Provide departmentID"],
},
classID: {
type: mongoose.Types.ObjectId,
ref: "Class",
required: [true, "Please Provide classID"],
},
position: {
type: String,
required: [true, "Please Provide Position"],
enum: ["Student"],
default: "Student",
},
password: {
type: String,
required: [true, "Please Provide Password"],
minlength: 6,
},
},
{ timestamps: true }
);
Attendance Schema
const mongoose = require("mongoose");
const { Schema } = mongoose;
const AttendanceSchema = new Schema(
{
date: {
type: String,
required: [true, "Please Provide Date"],
maxlength: 15,
minlength: 5,
},
subjectID: {
type: mongoose.Types.ObjectId,
ref: "Subject",
required: [true, "Please Provide Subject"],
},
studentID: [
{
type: mongoose.Types.ObjectId,
ref: "Student",
required: [true, "Please Provide Student"],
},
],
teacherID: {
type: mongoose.Types.ObjectId,
ref: "Faculty",
required: [true, "Please Provide Teacher"],
},
classID: {
type: mongoose.Types.ObjectId,
ref: "Class",
required: [true, "Please Provide Class"],
},
departmentID: {
type: mongoose.Types.ObjectId,
ref: "Department",
required: [true, "Please Provide Department"],
},
},
{ timestamps: true }
);
module.exports = mongoose.model("Attendance", AttendanceSchema);
The Query I'm Writing
await StudentSchema.aggregate([
{
$match: {
classID: mongoose.Types.ObjectId(`${req.params.id}`),
},
},
{
$project: { createdAt: 0, updatedAt: 0, __v: 0, password: 0 },
},
{
$lookup: {
from: "attendances",
pipeline: [
{
$match: {
subjectID: mongoose.Types.ObjectId(`${req.params.Sid}`),
},
},
{ $unwind: "$studentID" },
{ $group: { _id: "$studentID", count: { $sum: 1 } } },
],
as: "present",
},
},
Data I'm Getting From This Query
{
"subject1": [
{
"_id": "635d40803352895afffdc294",
"name": "D R",
"email": "dugu#gmail.com",
"number": "9198998888",
"rollNumber": 202,
"departmentID": "635a8ca21444a47d65d32c1a",
"classID": "635a92141a081229013255b4",
"position": "Student",
"present": [
{
"_id": "635d40803352895afffdc294",
"count": 3
},
{
"_id": "635eb8898dea5f437789b751",
"count": 2
}
]
},
{
"_id": "635eb8898dea5f437789b751",
"name": "V R",
"email": "v#gmail.com",
"number": "9198998899",
"rollNumber": 203,
"departmentID": "635a8ca21444a47d65d32c1a",
"classID": "635a92141a081229013255b4",
"position": "Student",
"present": [
{
"_id": "635d40803352895afffdc294",
"count": 3
},
{
"_id": "635eb8898dea5f437789b751",
"count": 2
}
]
}
]
}
This type of data I'm trying to achieve. Only that data which will be matched with _id in present array.
{
"subject1": [
{
"_id": "635d40803352895afffdc294",
"name": "D R",
"email": "dugu#gmail.com",
"number": "9198998888",
"rollNumber": 202,
"departmentID": "635a8ca21444a47d65d32c1a",
"classID": "635a92141a081229013255b4",
"position": "Student",
"present": [
{
"_id": "635d40803352895afffdc294",
"count": 3
}
]
},
{
"_id": "635eb8898dea5f437789b751",
"name": "V R",
"email": "v#gmail.com",
"number": "9198998899",
"rollNumber": 203,
"departmentID": "635a8ca21444a47d65d32c1a",
"classID": "635a92141a081229013255b4",
"position": "Student",
"present": [
{
"_id": "635eb8898dea5f437789b751",
"count": 2
}
]
}
]
}
You can just use the let option in the $lookup and pass the studentId, then you could match the proper attendances as part of the $lookup stage, like so:
{
$lookup: {
from: "attendances",
let: {
studentID: "$_id"
},
pipeline: [
{
$match: {
subjectID: 2,
$expr: {
$eq: [
"$$studentID",
"$studentID"
]
}
}
},
{
$unwind: "$studentID"
},
{
$group: {
_id: "$studentID",
count: {
$sum: 1
}
}
}
],
as: "present"
}
}
Mongo Playground
Another alternative would be to filter out the present array at the end but that would be less efficient and will uglify the code.

Api to list ordered product count based on date using node js and mongoDB express

Node.js:
how to create api for list ordered product count based on date .this order model and how to i create the controller for that...
const mongoose = require('mongoose')
const schema = mongoose.Schema
const orderSchema = new schema({
orderID:{type:String,required:true},
customer: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'userdatas',
},
orderItems:
{
// productName: { type: String, required: true },
// qty: { type: Number, required: true },
// amount: { type: String, required: true },
product: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'productsdatas',
},
},
shippingAddress : {
address: { type: String, required: true },
city: { type: String, required: true },
postalCode : { type: String, required: true },
country: { type: String, required: true },
},
orderDate:{type: String,
required: true},
totalAmount: {
type: Number,
required: true,
default: 0.0,
},
},
{
timestamps: true,
}
)
const Order = mongoose.model('Order', orderSchema)
module.exports = Order
mongoDB:
this is mongoDB data for order i should already created and customer and product details i populateded to show the output that time..
{
"_id": {
"$oid": "630f47d44a772822d2370794"
},
"orderID": "1",
"customerName": "vikiram",
"customer": {
"$oid": "630ccd62121a7425d6918e95"
},
"orderItems": {
"product": {
"$oid": "630d95d659998496b12f4f36"
}
},
"shippingAddress": {
"address": "123 IT street",
"city": "chennai",
"postalCode": "6000100",
"country": "india"
},
"orderDate": "25/04/2022",
"totalAmount": 11700000,
"createdAt": {
"$date": {
"$numberLong": "1650886612732"
}
},
"updatedAt": {
"$date": {
"$numberLong": "1661945812732"
}
},
"__v": 0
}
json:
i want json output in postman like this..
{
"data" : {
"May-2019" : 1,
"January-2020" : 1,
"February-2020" : 2,
"April-2020" : 1
}
}
You can achieve it by using aggregation. https://www.mongodb.com/docs/manual/aggregation/

MongoDB: How to aggregate and $group then filter specific date

Employee Schema
const employeeSchema = new mongoose.Schema(
{
name: {
type: String,
required: true,
trim: true,
},
email: {
type: String,
unique: true,
required: true,
trim: true,
lowercase: true,
validate(value) {
if (!validator.isEmail(value)) {
throw new Error('Email is invalid');
}
},
},
password: {
type: String,
required: true,
trim: true,
minLength: 6,
validate(value) {
if (value.toLowerCase().includes('password')) {
throw new Error("Password can not contain a word 'password'.");
}
},
},
birthdate: {
type: Date,
required: true,
},
cellphone: {
type: String,
required: true,
trim: true,
},
gender: {
type: String,
enum: ['남성', '여성'],
required: true,
},
hourly_wage: {
type: Number,
trim: true,
default: 0,
},
timeClocks: [
{
type: new mongoose.Schema({
start_time: {
type: Date,
required: true,
},
end_time: {
type: Date,
},
wage: {
type: Number,
required: true,
},
total: {
type: Number,
},
totalWorkTime: {
type: Number
}
}),
},
],
role: {
type: String,
enum: ['staff'],
default: 'staff',
},
stores: [
{
location: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'Location',
},
},
],
status: {
//현재 재직상태
type: String,
enum: ['재직자', '퇴직자'],
default: '재직자',
},
tokens: [
{
token: {
type: String,
required: true,
},
},
],
},
{
timestamps: true,
}
);
What I have done so far
const employees = shifts.map((d) => d.owner._id);
//timeclock
const temp = await Employee.aggregate([
{
$match: {
_id: { $in: employees },
},
},
{
$sort: { 'timeClocks.start_time': 1 },
},
{
$unwind: { path: '$timeClocks', preserveNullAndEmptyArrays: true },
},
{
$group: {
_id: '$_id',
name: { $first: '$name' },
timeClock: {
$push: '$timeClocks',
},
},
},
]);
My result
{
"shifts": [
{
"_id": "60e05b188be53900280bcdf2",
"date": "2021-07-09T00:00:00.000Z",
"day": "Fri",
"start": "2021-07-09T09:41:00.000Z",
"end": "2021-07-09T21:42:00.000Z",
"owner": {
"_id": "60cd9a3cb4ddcc00285b0df9",
"name": "Dr. dd"
},
"location": "60cd99b1b4ddcc00285b0df3",
"__v": 0
}
],
"timeClock": [
{
"_id": "60cd9a3cb4ddcc00285b0df9",
"name": "Dr. dd",
"timeClock": [
{
"_id": "60def63d19648a00286f0539",
"start_time": "2021-05-04T02:19:00.000Z",
"end_time": "2021-05-04T14:42:00.000Z",
"wage": 8720,
"total": 107735,
"totalWorkTime": 743
},
{
"_id": "60def63f19648a00286f053d",
"start_time": "2021-05-02T08:12:00.000Z",
"end_time": "2021-05-02T22:24:00.000Z",
"wage": 8720,
"total": 123540,
"totalWorkTime": 852
},
{
"_id": "60def64119648a00286f0541",
"start_time": "2021-05-10T20:14:00.000Z",
"end_time": "2021-05-10T22:17:00.000Z",
"wage": 8720,
"total": 17835,
"totalWorkTime": 123
},
}
]
Expected Result(2021-05-10)
{
"shifts": [
{
"_id": "60e05b188be53900280bcdf2",
"date": "2021-07-09T00:00:00.000Z",
"day": "Fri",
"start": "2021-07-09T09:41:00.000Z",
"end": "2021-07-09T21:42:00.000Z",
"owner": {
"_id": "60cd9a3cb4ddcc00285b0df9",
"name": "Dr. dd"
},
"location": "60cd99b1b4ddcc00285b0df3",
"__v": 0
}
],
"timeClock": [
{
"_id": "60cd9a3cb4ddcc00285b0df9",
"name": "Dr. dd",
"timeClock": {
"_id": "60def64119648a00286f0541",
"start_time": "2021-05-10T20:14:00.000Z",
"end_time": "2021-05-10T22:17:00.000Z",
"wage": 8720,
"total": 17835,
"totalWorkTime": 123
},
}
]
I am receiving the 'date string' example('URL/2021-05-10') via params and trying to query all employees that have the same date timeClocks.
also trying to send back everything I queried without different dates from timeClocks.
How can I filter out non-same dates?
You have string 2021-05-10 now you need a $match stage before your group so you can filter out timeClock. Something like:
{ $match: { 'timeClocks.start_time': new Date('2021-05-10') } }
Modify the match stage to your requirements like maybe add $gte or $lte or something like that.

How to merge/partial update on sub document which is an array

const projectSchema = mongoose.Schema({
title: { type: String, required: true, trim: true },
projectId: { type: String, required: true, trim: true, unique: true },
company: { type: mongoose.Schema.Types.ObjectId, ref: 'Company', required: true },
description: { type: String, trim: true },
remark: { type: String, trim: true },
active: { type: Boolean, required: true },
participants: [{
user: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
active: { type: Boolean, required: true },
timeSpent: [{
date: { type: Date, required: true },
hour: { type: Number, required: true }
}]
}]
});
I've searched through a lot of SO questions, but none of them seems to be able to solve my problem. Sometimes I need to update one of the field from one participant, I want to update it in a way that when
//this data to apply update on the first element of the participants array
const partialParticipantUpdate = {
active: false
}
can be easily update to the sub document without rewriting all the other property. Basically like findByIdAndUpdate not findByIdAndReplace. I've tried aggregation but its only for reading data not writing data. I've also tried
let p = await Project.update({
"_id": mongoose.Types.ObjectId("5ee22f90e550c32194fb7a91"),
"participants._id": mongoose.Types.ObjectId("5ee4b5bd4fdcdb0c8813f5f5")
}, {
"$set": { "participants.0": partialParticipantUpdate } })
console.log(p);
However some of the participants property is removed after the update
{
"_id": { "$oid": "5ee22f90e550c32194fb7a91" },
"title": "Geo Chart Example",
"projectId": "myId",
"company": { "$oid": "5ee22ca46c7fc4358ced20a1" },
"description": "asd",
"remark": "re",
"active": true,
"participants": [{
"_id": { "$oid": "5ee4f37f4d8c234cc02d405a" },
"active": false,
"timeSpent": []
},
{
"_id": { "$oid": "5ee4c8a955ed7f23445a3cbc" },
"user": { "$oid": "5ee0bdc318998236706b5e5a" },
"active": true,
"timeSpent": []
}
],
"__v": 0
}
The update did change the first participant's "active" property from true to false, but it also lost some of the property like "user" and "timeSpent" array will get emptied as well
Finally I found the solution
const partialParticipantUpdate = {
active: false
}
let p = await Project.findById("5ee22f90e550c32194fb7a91");
let x = p.participants.id("5ee7224be4e4386084b87d3c");
x.set(partialParticipantUpdate);
let response = await p.save({ validateBeforeSave: false });
console.log(response);
The only downside is that I have to communicate with mongoose twice

$lookup not working whle using on _id field in mongodb(mongoose) [duplicate]

This question already has answers here:
Mongoose always returning an empty array NodeJS
(7 answers)
Querying after populate in Mongoose
(6 answers)
Closed 4 years ago.
I have two models, user and schedule, and i want to combine these two using $lookup and mongoose.
User (model)
name: {
type: String,
required: true
},
firstName: {
String
},
lastName: {
String
},
storeKey: {
type: String,
required: true
},
avatar: String,
birthday: String,
phone: {
type: String
},
doc: String,
email: {
type: String
},
password: {
passwordHash: String,
salt: String
},
active: {
type: Boolean,
default: true
},
deleted: {
type: Boolean,
default: false
},
generalObservations: {
type: String
},
from: {
type: String
},
services: {
type: Number,
default: 0
},
no_shows: {
type: Number,
default: 0
},
// campos para integração
integrationId: String
}, {
timestamps: true
Schedule (model)
store: {
type: String,
required: true
},
customer: {
id: {
type: ObjectId
},
name: {
type: String,
required: true
},
avatar: String,
phone: {
type: String
},
doc: {
type: String
},
},
employee: {
id: {
type: String,
required: true
},
name: {
type: String,
required: true
},
avatar: String,
},
service: {
id: {
type: String
},
name: {
type: String,
required: true
},
filters: [String]
},
info: {
channel: {
type: String,
required: true,
default: 'app'
},
id: String,
name: String
},
scheduleDate: {
type: String,
required: true
},
scheduleStart: {
type: String,
required: true
},
scheduleEnd: {
type: String,
required: true
},
value: {
type: Number,
required: true
},
comissionType: {
type: String,
default: '$'
},
comissionValue: {
type: Number,
default: 0
},
status: {
type: Number,
required: true
},
observation: String,
paymentMethod: {
type: Number,
default: 0
},
paymentValue: String,
paymentChange: String
}, {
timestamps: {
createdAt: 'created',
updatedAt: 'updated'
}
And now my query using mongoose:
User aggregate
User.aggregate([{
$match: {
storeKey: req.body.store,
}
},
{
$group: {
_id: {
id: "$_id",
name: "$name",
cpf: "$cpf",
phone: "$phone",
email: "$email",
birthday: "$birthday"
},
totalServices: {
$sum: "$services"
}
}
},
{
$lookup: {
from: "schedule",
localField: "_id.id",
foreignField: "customer.id",
as: "user_detail"
}
}
the result of my query is a empty array(user_detail) like this:
Result of the query:
{
"_id": {
"id": "5bdb5b16ee9b7a4aa810bc62",
"name": "Jonas com aniversário",
"phone": "11984798494",
"email": "j#jz.com",
"birthday": "Thu Nov 01 2018 16:59:18 GMT-0300 (Hora oficial do Brasil)"
},
"totalServices": 0,
"user_detail": []
}
I don't know why, but the query result is a empty array and i was trying to using $unwind and $match but doesn't work too.
EDIT:
Collection of user
{
"_id": "5b1b1dcce1ab9a12a8eb580f",
"password": {
"salt": "d095f2",
"passwordHash": "b24881ef4c43d28e93bcff5da2ce32e4287aabf77540d2465482a435a5929a63f2ba9fb7e1cc14fa4e8183d83e33854ec6153fbbb872e65a9e3f188892bf56cc"
},
"name": "Anderson Zanardi",
"cpf": "31933765828",
"phone": "11996370565",
"birthday": "1984-03-18",
"email": "dev#wabiz.com.br",
"storeKey": "5b16cceb56a44e2f6cd0324b",
"createdAt": "2018-06-09T00:22:36.464Z",
"updatedAt": "2018-11-06T13:51:37.261Z",
"__v": 0,
"doc": "31933765828",
"active": true,
"from": "app",
"deleted": false,
"services": 80
},
Collection of schedule
{
"_id": "5b1c20d8fc76f904849712c9",
"customer": {
"id": "789456",
"name": "Gabriel Barreto",
"phone": "11995274098",
"cpf": "40735255814"
},
"employee": {
"id": "5b16cebd29bcf613f02b6fb4",
"name": "Anderson Zanardi",
"avatar": ""
},
"service": {
"filters": [
"corte_simples",
"corte_masculino"
],
"id": "service_id",
"name": "Corte Masculino"
},
"store": "5b16cceb56a44e2f6cd0324b",
"scheduleDate": "2018-06-07",
"scheduleStart": "2018-06-28 13:00",
"scheduleEnd": "2018-06-28 13:30",
"status": 1,
"value": 50,
"created": "2018-06-09T18:47:52.862Z",
"updated": "2018-06-09T18:47:52.862Z",
"__v": 0
},
Mongoose pluralize the collection name at the time of creation. So instead of schedule you should use schedules
{ "$lookup": {
"from": "schedules",
"localField": "_id.id",
"foreignField": "customer.id",
"as": "user_detail"
}}
or either import the collection and extract the collection name from it
const Schedule = require('/schedules')
{ "$lookup": {
"from": Schedule.collection.name,
"localField": "_id.phone",
"foreignField": "customer.phone",
"as": "user_detail"
}}

Resources