mongoose referencing not working when object_id is string - mongodb

I have two mongo collections, one for department and another one for users
var DeptSchema = new Schema({
name: {
type: String,
required: 'Department Name Missing.'
}},{
timestamps: true
});
DeptSchema.plugin(sequenceGenerator, {
field: '_id',
startAt: '0001',
prefix: 'DEPT'
});
module.exports = mongoose.model('departments', DeptSchema);
//User or staff schema definition
var UserSchema = new Schema({
name: {
type: String,
required: 'User Name Missing.'
},
role: {
type: String,
required: 'Role Missing.',
enum: ['admin', 'sales']
},
passwordHash: String,
passwordSalt: String,
departmentId: {
type: Schema.Types.ObjectId,
required: 'Department Id Missing.',
ref: 'departments'
}
},{
timestamps: true
});
UserSchema.plugin(sequenceGenerator, {
field: '_id',
startAt: '0001',
prefix: 'staff'
});
module.exports = mongoose.model('users', UserSchema);
Now i have inserted one document in departments collection with _id as 'DEPT0001' but while registering a user with data
{ name: 'rahul agarwal',
username: 'rahul43',
password: 'rev#888',
departmentId: 'DEPT0001',
role: 'admin' }
on postman it gives error
{
"errors": {
"departmentId": {
"message": "Cast to ObjectID failed for value \"DEPT0001\" at path \"departmentId\"",
"name": "CastError",
"stringValue": "\"DEPT0001\"",
"kind": "ObjectID",
"value": "DEPT0001",
"path": "departmentId",
"reason": {
"message": "Cast to ObjectId failed for value \"DEPT0001\" at path \"departmentId\"",
"name": "CastError",
"stringValue": "\"DEPT0001\"",
"kind": "ObjectId",
"value": "DEPT0001",
"path": "departmentId"
}
}
},
"_message": "users validation failed",
"message": "users validation failed: departmentId: Cast to ObjectID failed for value \"DEPT0001\" at path \"departmentId\"",
"name": "ValidationError"}
My question should i not use customized _id field for reference, if its feasible, so why I can't reference it.

Related

mongodb/mongoose + graphql When referencing a model with the id, the data is null

My apologies if this doesn't make too much sense, I am starting to get more comfortable with Mongodb using mongoosejs and also using graphql. I am trying to create a db that will contain transactions made by users, said transactions should be part of a specific category.
These are my typedefs:
type Auth {
token: ID!
user: User
}
type User {
_id: ID
email: String
expenses: [Expense]
}
type Category {
_id: ID
name: String
}
type Expense {
_id: ID
expenseName: String
expenseAmount: Float
expenseDate: String
category: Category
}
type Query {
me: User
users: [User]
user(email: String!): User
expenses(email: String!): [Expense]
expense(id: ID!): Expense
categories: [Category]
}
type Mutation {
login(email: String!, password: String!): Auth
addCategory(name: String!): Category
addUser(email: String!, password: String!, firstName: String!, lastName: String!): Auth
addExpense(expenseName: String!, expenseAmount: Float!, categoryId: ID!): Expense
}
And my resolver for the expense/transaction:
addExpense: async (parent, args, context) => {
if(context.user) {
const expense = await Expense.create({
expenseName: args.expenseName,
expenseAmount: args.expenseAmount,
category: args.categoryId,
});
await User.findByIdAndUpdate(
{ _id: context.user._id },
{ $push: { expenses: expense._id } },
{ new: true }
);
return expense;
}
throw new AuthenticationError("You must be logged in!");
}
Just in case you need it, these are my schemas:
For the user schema:
const userSchema = new Schema({
email: {
type: String,
required: true,
unique: true,
match: [/.+#.+\..+/, "Must match an email address!"],
},
password: {
type: String,
required: true,
minlength: [8, "Password must be at least 8 characters long!"],
},
firstName: {
type: String,
required: true,
},
lastName: {
type: String,
required: true,
},
expenses: [
{
type: Schema.Types.ObjectId,
ref: "Expense",
}
]
});
For the expense/transaction:
const expenseSchema = new Schema({
expenseName: {
type: String,
required: true,
minlenght: 1,
},
expenseAmount: {
type: Number,
required: true,
min: 0.01,
},
expenseDate: {
type: Date,
default: Date.now,
},
category: {
type: Schema.Types.ObjectId,
ref: "Category",
required: true,
}
});
For the category creation:
const categorySchema = new Schema({
name: {
type: String,
required: true,
},
});
So when I run it in apollo server after adding an expense/transaction, it shows everything but it will not show the name for the category, it will always show null:
"data": {
"me": {
"_id": "630a42ea7f7be79ba1d4114c",
"email": "admin#test.com",
"expenses": [
{
"_id": "630af7b795e5fd336ba708be",
"expenseName": "Mom",
"expenseAmount": 70,
"expenseDate": "1661663159375",
"category": {
"_id": "630af5fd2d4b15974802c026",
"name": null
}
},
{
"_id": "630af85695e5fd336ba708c2",
"expenseName": "Mom",
"expenseAmount": 70,
"expenseDate": "1661663318985",
"category": {
"_id": "630a432b7f7be79ba1d4114e",
"name": null
}
}
]
}
}
}
Could someone help me figure out what am I missing? Apologies if I am sharing too much

How can I populate properties of array item in mongoDB?

I have 3 collections scopes, groups and users
const ScopesSchema = new mongoose.Schema(
{
name: String,
privilege: String,
}
);
const GroupsSchema = new mongoose.Schema(
{
name: String,
status: String,
}
);
const UserSchema = new mongoose.Schema(
{
security:[{
_id:false,
group: { type: mongoose.Schema.Types.ObjectId, ref: "groups" },
scopes: [{ type: mongoose.Schema.Types.ObjectId, ref: "scopes" }],
}],
},
);
Is possible to populate data from properties group and scopes in a document like this?
{
_id: 44ffbvb...,
security: [{
"group": "44ffbvb...", // ID of document in groups collection
"scopes": ["44ffbvb...","44ffbvb..."] // IDs of documents in scopes collection
}]
}
I'd like to get the information related to group and scopes in order to get a document like this:
{
_id: 44ffbvb...,
security: [{
"group": {
"id" : "44ffbvb...",
"name" : "Name of the group",
"status": "ACTIVE"
},
"scopes": [{name: "ADMINISTRATOR", privilege: "write-only" },{name: "ROOT", privilege: "read-only" }]
}]
}
you can use the npm package
mongoose-autopopulate
The fields you need to be populated, add : autopopulate: true,
and add this line to the schema-
schema.plugin(require('mongoose-autopopulate'));

Populating and selecting multiple sub-documents mongoose

I have a User Model
const UserSchema = new mongoose.Schema({
name: {
type: String,
required: true
}
email: {
type: String,
required: true,
maxlength: 128,
minlength: 5
},
hashedPassword: {
type: String,
required: false
}
});
module.exports = mongoose.model('users', UserSchema);
And a Post Model
const mongoose = require('mongoose');
const PostSchema = new mongoose.Schema({
description: {
type: String,
required: true
},
comments: [{
comment: {
type: String,
required: true
},
postedBy: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'users'
},
postedOn: {
type: Date,
required: true,
default: Date.now
}
}],
postedBy: {
type: mongoose.Types.ObjectId,
required: true,
ref: 'users'
}
});
module.exports = mongoose.model('answers', AnswerSchema);
I want to fetch post with populated "postedBy"(of POST) also select fields of "name and email" from "postedBy"(of POST). Also, I want to populate "postedBy"(inside comments) and select the same field of "name and email" from "postedBy"(inside comments).
Expecting to see a result like
{
"post": [
{
"_id": "*some mongo id*",
"description": "This is a sample Post for testing",
"postedBy": {
"_id": "5e9285669bdcb146c411cef2",
"name": "Rohit Sharma",
"email": "rohit#gmail.com"
},
"comments": [{
"comment": "Test One Comment",
"postedBy": {
"_id": "5e9285669bdcb146c411cef2",
"name": "Rohit Sharma",
"email": "rohit#gmail.com"
},
}],
"postedOn": "2020-04-19T12:28:31.162Z"
}
]
}

Meteor simple schema issue

I'm getting null array value in main_categories. My schema is for brand collection:
Schema Definition
Schema.main_category = new SimpleSchema({
name: {type: String},
icon_image: {type: String},
description: {type: String}
});
Main_Category.attachSchema(Schema.main_category);
Schema.brand = new SimpleSchema({
name: {
type: String,
},
admin_number: {
type: String,
},
company_name: {
type: String,
},
owner_name: {
type: String,
},
owner_number: {
type: String,
},
admin_comment: {
type: String,
},
address: {
type: Schema.address,
},
logo_image: {
type: String
},
staffs: {
type: Array
},
"staffs.$": {
type: Object
},
"staffs.$.type": {
type: String,
allowedValues: ['admin']
},
"staffs.$.user_id": {
type: String
},
main_categories: {
type: [Schema.main_category]
},
sub_categories: {
type: [Schema.sub_category]
},
showcase: {
type: Boolean
}
});
Brand.attachSchema(Schema.brand);
Implementation
"addBrandMethod": function(jsonData) {
var json = {
name: jsonData.brandName,
admin_number: jsonData.adminNumber,
company_name: jsonData.companyName,
address: jsonData.companyAddress,
owner_name: jsonData.ownerName,
owner_number: jsonData.ownerNumber,
admin_comment: "jsonData.adminComment",
logo_image: "fasdfa",
staffs: [{
type: "admin",
user_id: "jaskjjkj"
}],
main_categories: [{
"_id": "uBibwEqaoDkZtXhsR",
"name": "Hair",
"icon_image": "nbdenck",
"description": "Hair Cut with Massage"
}
],
sub_categories: Sub_Category.find().fetch(),
showcase: true
};
Brand.insert(json);
return "Success";
}
Try removing the _id key from the main_categories array.
You didn't specify the _id key in the schema and simple-schema will only add the key when it's a schema that's attached to a collection.
I was getting main_categories object null because main_categories file alphabetically down from brand schema file.. and in brand schema file i was getting object of main_categories schema undefined. when i paste file up to brand schema file then problem solve..

Mongoose not populating previously saved document with reference to newly saved document

all.
I am writing a MEAN stack application, using Mongoose (4.0.6) with Node/Express to interface with MongoDB, and I am running into difficulty populating saved documents when I later save new documents that the existing document should have a reference to. Specifically, in the app I have a user create an instance of a company before creating their admin account for that company, so when the user registers him/herself as an admin, I'd like the company document to populate its users array with the new user.
Here are my schemas for company and user:
User.js...
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ObjectId = Schema.Types.ObjectId;
var userSchema = new Schema({
first_name: { type: String, required: '{PATH} is required!' },
last_name: { type: String, required: '{PATH} is required!' },
username: { type: String, required: '{PATH} is required!', lowercase: true, unique: true },
password: { type: String, required: '{PATH} is required!' },
roles: { type: [String] },
company: { type: ObjectId, ref: 'Company', required: true },
db_permissions: [{ type: ObjectId, ref: 'DataConnection' }],
created_by: { type: ObjectId, ref: 'User' },
created_at: { type: Date, default: Date.now },
updated_at: [{ type: Date, default: Date.now }]
});
var User = mongoose.model('User', userSchema);
module.exports = {
User: User
};
Company.js...
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ObjectId = Schema.Types.ObjectId;
var companySchema = new Schema({
name: { type: String, uppercase: true, required: '{PATH} is required!', unique: true },
industry: { type: String, required: '{PATH} is required!' },
phone: { type: String, required: '{PATH} is required!' },
address_line_1: { type: String, uppercase: true, required: '{PATH} is required!' },
address_line_2: { type: String, uppercase: true },
city: { type: String, uppercase: true, required: '{PATH} is required!' },
state_prov: { type: String, uppercase: true, required: '{PATH} is required!' },
postal_code: { type: String, required: '{PATH} is required!' },
country: { type: String, required: '{PATH} is required!' },
logo_url: String,
users: [{ type: ObjectId, ref: 'User' }],
data_connections: [{ type: ObjectId, ref: 'DataConnection' }],
created_at: { type: Date, default: Date.now },
updated_at: [{ type: Date, default: Date.now }]
});
var Company = mongoose.model('Company', companySchema);
module.exports = {
Company: Company
};
Here is the code in my controller:
User.create(userData, function(err, user) {
if(err) {
if(err.toString().indexOf('E11000') > -1) {
err = new Error('Duplicate email');
}
res.status(400);
return res.send({ reason:err.toString() });
}
console.log('company id: ' + user.company);
Company.findById(user.company)
.populate({path: 'users'})
.exec(function (err, company) {
if (err) return handleError(err);
console.log(company.name + '\'s users now includes ' + company.users);
});
res.send(user);
The company (e.g. TEST53) saves to the database correctly with an empty users array:
{
"_id": "55ae421bf469f1b97bb52d5a",
"name": "TEST53",
"industry": "Construction",
"phone": "2352626254",
"city": "GDFGD",
"country": "United States",
"address_line_1": "DSGDFGH",
"state_prov": "GF",
"postal_code": "45645",
"logo_url": "",
"__v": 0,
"updated_at": [
"2015-07-21T12:59:07.609Z"
],
"created_at": "2015-07-21T12:59:07.597Z",
"data_connections": [],
"users": []
}
Then when I create the user, it saves correctly:
{
"_id": "55ae4238f469f1b97bb52d5b",
"username": "test53#test.com",
"password": "$2a$12$ZB6L1NCZEhLfjs99yUUNNOQEknyQmX6nP2BxBvo1uZGlvk9LlKGFu",
"company": "55ae421bf469f1b97bb52d5a",
"first_name": "Test53",
"last_name": "Admin",
"__v": 0,
"updated_at": [
"2015-07-21T12:59:36.925Z"
],
"created_at": "2015-07-21T12:59:36.550Z",
"db_permissions": [],
"roles": [
"admin"
]
}
And I can see that the correct ObjectId prints to the console for user.company:
company id: 55ae421bf469f1b97bb52d5a
But the company's users array doesn't populate with the user's id, and the console.log inside the .exec function prints 'TEST53's users now includes '.
I have tried several ways of wiring this up, with just 'users' instead of { path: 'users' }, writing a function that pushes the user into the array, using .run instead of .exec, but so far without success.
Is there something obvious I'm missing? Thanks in advance for any suggestions!
You're not actually adding the user to the company.
Try this:
Company.findById(user.company, function (err, company) {
if (err) return handleError(err);
// Add the user to the company's list of users.
company.users.push(user);
// Need to save again.
company.save(function(err) {
if (err) return handleError(err);
console.log(company.name + '\'s users now includes ' + company.users);
});
});
res.send(user);
It seems to me that all you want to do is to update the Company model to add the user, as opposed to actually use the (populated) Company document as a response, so I left out an additional Company.findById(...).populate(...) call.