find value in array mongoose+typescript - node.js

I have an schema created with mongoose+typescript like this :
import mongoose, { Schema, Types, Document } from 'mongoose'
import { mongoosePagination, Pagination } from 'mongoose-paginate-ts'
import { Department } from './departmentModel'
import { Designation } from './designationModel'
import { Roles } from './roleModel'
export interface IEmployee extends Document {
userName: string
email: string
firstName: string
lastName: string
designation: Types.ObjectId
department: [Types.ObjectId]
roles: [Types.ObjectId]
projects: string
status: string
reportingManager: [
{
managerId: string
userName: string
},
]
approver: [
{
approverId: string
userName: string
},
]
createdBy: string
updatedBy: string
}
type employeeUser = mongoose.Document & {
userName: string
email: string
firstName: string
lastName: string
designation: Types.ObjectId
department: [Types.ObjectId]
roles: [Types.ObjectId]
projects: string
status: string
reportingManager: [
{
managerId: string
userName: string
},
]
approver: [
{
approverId: string
userName: string
},
]
createdBy: string
updatedBy: string
}
const schema = new Schema(
{
userName: {
type: String,
required: true,
unique: true,
},
email: {
type: String,
required: true,
unique: true,
},
firstName: {
type: String,
required: true,
},
lastName: {
type: String,
required: true,
},
designation: {
type: Schema.Types.ObjectId,
ref: Designation,
required: true,
},
department: [{
type: Schema.Types.ObjectId,
ref: Department,
required: true,
}],
roles: [
{
type: Schema.Types.ObjectId,
ref: Roles,
required: true,
},
],
status: {
type: Boolean,
default: true,
required: true,
},
reportingManager: [
{
managerId: {
type: String,
required: true,
},
userName: {
type: String,
required: true,
},
},
],
approver: [
{
approverId: {
type: String,
required: true,
},
userName: {
type: String,
required: true,
},
},
],
createdBy: {
type: String,
immutable: true,
},
updatedBy: {
type: String,
},
},
{ collection: 'user', timestamps: true },
)
schema.plugin(mongoosePagination)
const User: Pagination<IEmployee> = mongoose.model<employeeUser, Pagination<IEmployee>>('User', schema)
export { User }
And trying using like this::
import { IEmployee, User } from '#/models/user'
const getEmployeeDetailsHelper = async (paginationQuery: unknown): Promise<unknown> => {
try {
return await User.find({department: '623aca76efa7cf01ec7c39ac'}).exec()
} catch (err) {
logger.error(JSON.stringify(err))
return err
}
}
but typescript giving me error like:
No overload matches this call.
Overload 1 of 3, '(callback?: Callback<IEmployee[]>): Query<IEmployee[], IEmployee, {}, IEmployee>', gave the following error.
Argument of type '{ department: string; }' is not assignable to parameter of type 'Callback<IEmployee[]>'.
Object literal may only specify known properties, and 'department' does not exist in type 'Callback<IEmployee[]>'.
Overload 2 of 3, '(filter: FilterQuery<IEmployee>, callback?: Callback<IEmployee[]>): Query<IEmployee[], IEmployee, {}, IEmployee>', gave the following error.
Type 'string' is not assignable to type 'Condition<[ObjectId]>'.
Overload 3 of 3, '(filter: FilterQuery<IEmployee>, projection?: any, options?: QueryOptions, callback?: Callback<IEmployee[]>): Query<IEmployee[], IEmployee, {}, IEmployee>', gave the following error.
Type 'string' is not assignable to type 'Condition<[ObjectId]>'.ts(2769)
user.ts(13, 3): The expected type comes from property 'department' which is declared here on type 'FilterQuery<IEmployee>'
user.ts(13, 3): The expected type comes from property 'department' which is declared here on type 'FilterQuery<IEmployee>'
Please help.

Related

Mongoose $push whole array in database

I'm having an error on my database where the sub array that I push to my database is missing and created a new id which means it detects data pushed inside.
here's the data I pushed. (toDeliver array has 4 objects inside).
I'm trying to send the whole array along with the string outside of the array.
after the request here what I receive on my database which is mongoDB.
the object inside toDeliver array is incomplete and created a ObjectId.
but the string outside of array was save the database.
here's my schema.
const OwnerSchema = mongoose.Schema({
username: {
require: true,
type: String,
},
password: {
require: true,
type: String,
},
isAdmin: {
type: Boolean,
default: true,
},
store: [
{
product_identifier: {
type: String,
require: true,
},
productname: {
type: String,
required: true,
},
price: {
type: Number,
required: true,
},
quantity: {
type: Number,
required: true,
},
categoryfilter: {
type: String,
required: true
},
description: {
type: String,
required: true,
},
specs: {
type: String,
required: true
},
imageBase64: {
type: String,
required: true,
},
timestamp: {
type: String,
required: true,
}
}
],
delivery: [
{
clientname: {
type: String,
required: true
},
address: {
type: String,
required: true
},
email: {
type: String,
required: true
},
number: {
type: Number,
required: true
},
toDeliver: [
{
product_identifier: {
type: String,
require: true,
},
productname: {
type: String,
required: true
},
price: {
type: Number,
required: true
},
}
],
toDeliverPaidViaPaypal: [
{
product_identifier: {
type: String,
require: true,
},
productname: {
type: String,
required: true
},
price: {
type: Number,
required: true
},
}
]
}
]
});
here's my backend.
export const delivery = async (req,res) => {
const { id } = req.params;
console.log(id);
console.log(req.body);
try {
if(!id) return res.status(404).json({ message: 'ID not found' });
await OwnerModels.findByIdAndUpdate(id,
{
$push: {
delivery:
{
clientname: req.body.delivery[0].clientname,
address: req.body.delivery[0].address,
email: req.body.delivery[0].email,
number: req.body.delivery[0].number,
toDeliver:
[{
product_identifier: req.body.delivery[0].toDeliver.product_identifier,
productname: req.body.delivery[0].toDeliver.productname,
price: req.body.delivery[0].toDeliver.price
}]
,
toDeliverPaidViaPaypal: []
}
}
},
{
new: true,
},(err,res)=> {
if(err) return console.log({ error: err });
console.log({ result: res.delivery });
}).clone();
} catch (error) {
res.status(500).json({ message: 'Server error' });
}
}
hope ya guys can help me. thank you
I think you need to add square brackets around the toDeliver object to make it an array like your console object:
$push: {
delivery: {
clientname: req.body.delivery[0].clientname,
address: req.body.delivery[0].address,
email: req.body.delivery[0].email,
number: req.body.delivery[0].number,
toDeliver: [{
product_identifier: req.body.delivery[0].toDeliver.product_identifier,
productname: req.body.delivery[0].toDeliver.productname,
price: req.body.delivery[0].toDeliver.price
}],
toDeliverPaidViaPaypal: []
}
}
Also add "_id: false" to toDelivery in your schema to repress id from being generated for the sub-object:
toDeliver: [
{
product_identifier: {
type: String,
require: true,
},
productname: {
type: String,
required: true
},
price: {
type: Number,
required: true
},
_id: false,
}
],

Mongoose: How to populate across multiple levels and use field selection in the same time?

I have succesfully used population across multiple levels and field selection separately. But I couldn't find a way to do them both at the same time.
Population across multiple levels
Profile.findOne({ user: req.user.id })
.populate({
path: "user",
populate: { path: "following" },
})
Field selection:
Profile.findOne({ user: req.user.id })
.populate("user", ["name", "avatar"])
I want to do something like this:
NOTE: THIS DOESN'T WORK
Profile.findOne({ user: req.user.id })
.populate({
path: ["user", ["name", "avatar"]],
populate: { path: "following" },
This is the Profile model:
const ProfileSchema = new Schema({
//We want to associate the user with the profile
user: {
//This will associate the user by his id
type: Schema.Types.ObjectId,
ref: "users",
},
// We want a user friendly url
handle: {
type: String,
// required: true, // Even though we are doing pre-validatioon
max: 40,
},
bio: {
type: String,
},
school: {
type: String,
},
major: {
type: String,
},
website: {
type: String,
},
location: {
type: String,
},
// Optional (might add it later)
/**
* status: {
//where they are in their career
type: String,
required: true
},
*/
skills: {
// In the form they will put skill1,skill2,.. and we will turn that into an array
type: [String],
// required: true,
},
interests: {
// In the form they will put interest1,interest2,.. and we will turn that into an array
type: [String],
// required: true,
},
help_offers: {
// In the form they will put help_offer1,help_offer2,.. and we will turn that into an array
type: [String],
// required: true,
},
// Might be volunteering experience
experience: [
{
title: { type: String, required: true },
company: {
type: String,
required: true,
},
location: {
type: String,
},
from: {
type: Date,
required: true,
},
to: {
type: Date,
// Not required, going to be a checkbox
},
current: {
type: Boolean,
default: false,
},
description: {
type: String,
},
},
],
education: [
{
school: { type: String, required: true },
degree: {
type: String,
required: true,
},
fieldofstudy: {
type: String,
required: true,
},
from: {
type: Date,
required: true,
},
to: {
type: Date,
// Not required, going to be a checkbox
},
current: {
type: Boolean,
default: false,
},
description: {
type: String,
},
},
],
social: {
youtube: {
type: String,
},
twitter: {
type: String,
},
facebook: {
type: String,
},
linkedin: {
type: String,
},
instagram: {
type: String,
},
},
date: {
type: Date,
dafault: Date.now,
},
videoURL: {
type: String,
},
});
This is the User model:
const UserSchema = new Schema({
name: {
type: String,
required: true,
},
surname: {
type: String,
required: true,
},
email: {
type: String,
required: true,
},
password: {
type: String,
required: true,
},
avatar: {
type: String,
},
birthday: {
type: String,
required: true,
},
country: {
type: String,
required: true,
},
date: {
type: Date,
default: Date.now,
},
following: [
{
type: Schema.Types.ObjectId,
//The User has only ObjectId in uses array. ref helps us get full fields of User when we call populate() method.
//https://bezkoder.com/mongodb-many-to-many-mongoose/
ref: "users",
},
],
followers: [
{
type: Schema.Types.ObjectId,
ref: "users",
},
],
});
I am not sure if I understood your aim by this
Profile.findOne({ user: req.user.id })
.populate({
path: ["user", ["name", "avatar"]],
populate: { path: "following" },
But if what i understood is what you want, then we're lucky. here is what i got.
await RequestAproves.find()
.populate('user', 'firstName surname email roleId')
.populate({
path: 'request',
populate: [
{ path: 'budgetItem', select: 'code name' },
{ path: 'user', select: 'firstName surname' },
],
}
)
More details will come later, Sorry, I couldnt use your schema description, Incase it doesnt work out, let me know, ill try to use your schema.

How create collection remotely form mlab api in remote-mongodb using nodejs,expressjs?

I am following Brad Traversy's MERN stack development course and In 4th section, I can't make post request to route localhost:5000/api/profile,
after sending post request with data as handle,status,skills which are fields in my collection, it returns error skills field required.
skills is array of strings sent from user-input.
when I checked collection-profile is created or not then it's not created and only one collection is shown as main collection user,
I followed each line of code in tutorial but getting error, I wanted to get Profile collection to be created at remote mlab mongodb,
my profile model code is
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
// Create Schema
const ProfileSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: "users"
},
handle: {
type: String,
required: true,
max: 40
},
company: {
type: String
},
website: {
type: String
},
location: {
type: String
},
status: {
type: String,
required: true
},
skills: {
type: [String],
required: true
},
bio: {
type: String
},
githubusername: {
type: String
},
experience: [
{
title: {
type: String,
required: true
},
company: {
type: String,
required: true
},
location: {
type: String
},
from: {
type: Date,
required: true
},
to: {
type: Date
},
current: {
type: Boolean,
default: false
},
description: {
type: String
}
}
],
education: [
{
school: {
type: String,
required: true
},
degree: {
type: String,
required: true
},
fieldofstudy: {
type: String,
required: true
},
from: {
type: Date,
required: true
},
to: {
type: Date
},
current: {
type: Boolean,
default: false
},
description: {
type: String
}
}
],
social: {
youtube: {
type: String
},
twitter: {
type: String
},
facebook: {
type: String
},
linkedin: {
type: String
},
instagram: {
type: String
}
},
date: {
type: Date,
default: Date.now
}
});
module.exports = Profile = mongoose.model("profile", ProfileSchema);
using validator.js module, I am validating input-fields,
if (Validator.isEmpty(data.skills)) {
errors.skills = "Skills field is required";
}
so, I can't find why exactly I can't find new collection as profile?
I tried using mongodb-locally, but it didn't help.
my Github repo link.
I returned user's details when route /api/users/current and changed response on success
before changing:
res.json({ msg: "Success" });
after:
passport.authenticate("jwt", { session: false }),
(req, res) => {
res.json({
id: req.user.id,
name: req.user.name,
email: req.user.email
});
}
);
Here is my commit history,
then It didn't give error as skills field is required,

GraphQL nested query returning null

When attempting to run the following query I get this error: "Cannot return null for non-nullable field User.first_name.". I am not expecting a null result.
Query:
{
site(site_reference: 123456789) {
site_name
site_managers {
_id
first_name
}
}
}
What I expect to see is:
{
"data": {
"site": {
"site_name": "Test Site",
"site_managers": [
{
"_id": "5bbcd55ff7bd643be4d490fa",
"first_name": "Claire"
},
{
"_id": "5b9fa2e1f66fb32164f4d547",
"first_name": "John"
}
]
}
}
}
My MongoDB stores the _id in an array which it is able to return, however anything else from my user type returns null.
User Schema:
type User {
_id: String!,
first_name: String!,
last_name: String!,
role_id: Int!
email: String!,
password: String,
created_date: String!,
updated_date: String!,
last_login_date: String,
reset_password_token: String
}
type Query {
user(_id: String!): User!
users(role_id: Int): [User!]!
}
User Resolver:
Query: {
user: (parent, args) => User.findById(args._id),
users: (parent, args) => {
if (args.role_id) {
return User.find({ role_id: args.role_id })
}
return User.find({})
},
},
Site Schema:
type Site {
site_name: String!,
use_policy: String!,
redirect_url: String!,
intro_text: String!,
logo: String!,
address_line_1: String!,
address_line_2: String,
address_city: String!,
address_county: String!,
address_postcode: String!,
phone: String!,
site_reference: Int!,
site_managers: [User],
business_id: String!,
connect_duration: Int!,
created_date: String!,
updated_date: String!,
}
type Query {
site(site_reference: Int!): Site!
sites(business_id: Int!): [Site!]!
}
Site Resolver:
Query: {
site: (parent, args) =>
Site.findOne({ site_reference: args.site_reference }),
sites: (parent, args) => Site.find({ business_id: args.business_id }),
},
I imagine I need to do more in my resolver for the site query but I am unsure of what exactly. I have already tried using mongoose.populate but to no avail, the furthest I got was returning an empty array with the populate method.
Thanks in advance for your time.
Edit:
Here's my mongoose schemas
User Schema
const UserSchema = new mongoose.Schema({
first_name: { type: String, required: true },
last_name: { type: String, required: true },
role_id: { type: Number, required: true },
email: { type: String, required: true },
password: { type: String },
created_date: { type: Date, default: Date.now, required: true },
updated_date: { type: Date, default: Date.now, required: true },
last_login_date: { type: Date },
reset_password_token: { type: String },
})
Site Schema:
const SiteSchema = new mongoose.Schema({
site_name: { type: String, required: true },
use_policy: {
type: String,
required: true,
default: config.defaultUsePolicy,
},
redirect_url: {
type: String,
required: true,
default: config.defaultRedirect,
},
intro_text: {
type: String,
required: true,
default: config.defaultIntroText,
},
logo: { type: String, required: true, default: config.defaultLogo },
address_line_1: { type: String, required: true },
address_line_2: String,
address_city: { type: String, required: true },
address_county: { type: String, required: true },
address_postcode: { type: String, required: true },
phone: { type: String, required: true },
site_reference: { type: String, required: true },
site_managers: {type:[mongoose.Schema.Types.ObjectId], required: true}
business_id: { type: mongoose.Schema.Types.ObjectId, required: true },
connect_duration: { type: Number, required: true, default: 0 },
created_date: { type: Date, default: Date.now, required: true },
updated_date: { type: Date, default: Date.now, required: true },
})
If you're using MongoDB and mongoose, you will want to use populate to join your collections. In order to do so, in your schema, the field you're populating needs to not only have a type, but also a ref property that tells mongoose which Model to use to populate the field, for example:
// Note the actual ref should be whatever name you passed to mongoose.model
site_managers: [{type:mongoose.Schema.Types.ObjectId, ref: 'User'}]
Now, use populate inside your resolver:
site: (parent, { site_reference }) => {
return Site.findOne({ site_reference }).populate('site_managers').exec()
}
See the docs for more detailed examples.

Pushing into subdocument with Mongoose

I have this data structure. I would like to push a new sibling into a specific members sibling array:
const FamilySchema = mongoose.Schema({
username: { type: String, required: true },
family_name: { type: String, required: true },
password: { type: String, required: true },
members: [
{
fname: { type: String, required: true },
lname: { type: String, required: true },
mname: { type: String },
birth_date: { type: String },
birth_town: { type: String },
birth_state: { type: String },
death_date: { type: String },
death_town: { type: String },
death_state: { type: String },
short_bio: { type: String },
long_bio: {type: String },
parents: [
{
parent_id: { type: String, required: true }
}
],
siblings: [
{
sibling_id: { type: String, required: true }
}
],
children: [
{
children_id: { type: String, required: true }
}
],
pictures: [
{
url: { type: String, required: true }
}
],
}
]
});
I want to push a new sibling into the sibling array for a SPECIFIC member in the larger family document. I'm trying to do it like this, and it's not working.
Family
.update(
{
_id: FAMILY_ID,
"members._id" : MEMBER_ID
},
{
$push : {
"members.$.siblings" : {
"sibling_id": THE_VALUE_I_WANT_ADDED
}
}
}
);
Any thoughts? My Mongoose knowledge is failing me.

Resources