Mongoose - how to move object to another collection - mongodb

My db include following collections:
users
deleted_users
My code is following:
const name = { type: String, required: true, index: { unique: true } };
const UserSchema = new mongoose.Schema({ name });
const DeletedUserSchema = new mongoose.Schema({ name }, {
versionKey: 'version',
});
const UserModel = mongoose.model('User', UserSchema);
const DeletedUserModel = mongoose.model('Deleted_user', DeletedUserSchema);
router.put('/:id/move', (req, res) => {
UserModel.findOne(
{ _id: id }
).then((user) => {
if (!user) {
return fail(...);
}
console.log(`moving user width id ${id}`);
const newUser = new DeletedUserModel(user);
return newUser.save()
.then(
() => {
console.log('ok');
})
.catch((err) => {
console.log('catch err ', err);
});
});
}
but I always receive
{ Error
at model.wrappedPointCut [as save] (/~/prj/node_modules/mongoose/lib/services/model/applyHooks.js:111:29)
at UserModel.findOne.then (/~/prj/src/routes/user/index.js:123:20)
at process._tickDomainCallback (internal/process/next_tick.js:135:7)
message: 'No matching document found for id "58dd804c434bdc1848d491cd"',
name: 'VersionError' }

Can you check that this id you are querying is not a String but an ObjectId because I think you are passing a String as id.

Related

Model.populate() is not return document in Mongoose

I have two schema,
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
// Create the User Schema
const UserSchema = new Schema({
email: {
type: String
},
password: {
type: String
}
});
module.exports = User = mongoose.model("users", UserSchema);
OR
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
// Create the Status Schema
const StatusSchema = new Schema({
admin_id:{
type: Schema.Types.ObjectId,
ref: 'users'
},
text:{
type: String
},
});
module.exports = Status = mongoose.model("Status", StatusSchema, "Status");
then i use the populate in my api route:
router.get(
"/",
passport.authenticate("jwt", {
session: false,
}),
(req, res) => {
try {
Status.find({}).populate('admin_id').exec(err, data=>{
console.log(data); // return a blank array : []
return res.sendStatus(200)
})
}
} catch (error) {
res.sendStatus(500);
}
}
);
When i call this route i got an empty array [] .... Any idea what i do wrong? I should mention that i have inserted records in status collection for both admin_id
Is there any onther way to do this ?
There is a lot of ways to do this.
You sould use this,
Status.find({}).then((doc) => {
if (doc) {
Status.populate(doc, { path: "admin_id", model: "users" }, function (
err,
data
) {
if (err) throw err;
console.log(data); //here is your documents with admin user
});
}
});

How can I catch the mongodb error to pass the test in jest?

I create a user schema using mongoose.
/src/server/models/User.ts:
import { model, Schema } from "mongoose";
export const UserSchema = new Schema({
address: {
type: String,
},
email: {
required: true,
type: String,
unique: true,
},
name: {
required: true,
type: String,
unique: true,
},
});
const User = model("User", UserSchema);
export default User;
I try to test the insertion of a user object that missed the name in order to get an error returned back from mongodb:
/src/tests/db.spec.ts:
import { MongoMemoryServer } from "mongodb-memory-server";
import mongoose, { Model } from "mongoose";
import { UserSchema } from "../server/models/User";
let mongoServer: MongoMemoryServer;
const opts = {
useCreateIndex: true,
useNewUrlParser: true,
useUnifiedTopology: true,
};
describe("Users", () => {
let User: Model<any>;
beforeAll(async () => {
mongoServer = new MongoMemoryServer();
const mongoUri = await mongoServer.getConnectionString();
const db = await mongoose.connect(mongoUri, opts);
User = db.model("User", UserSchema);
});
afterAll(async () => {
await mongoose.disconnect();
await mongoServer.stop();
});
describe("User Creation", () => {
it("Should returns an error if a name is missing", async () => {
const newUser = new User({
address: "address",
email: "user#gmail.com",
});
const createdUser = await User.create(newUser);
expect(createdUser).toThrow("User validation failed");
});
});
});
The test failed and I got this error:
● Users › User Creation › Should returns an error if a name is missing
ValidationError: User validation failed: name: Path `name` is required.
at new ValidationError (node_modules/mongoose/lib/error/validation.js:31:11)
at model.Object.<anonymous>.Document.invalidate (node_modules/mongoose/lib/document.js:2413:32)
at p.doValidate.skipSchemaValidators (node_modules/mongoose/lib/document.js:2262:17)
at node_modules/mongoose/lib/schematype.js:1058:9
How can I fix that?
According to jest/rejects, I used rejects to pass my test:
it("Should returns an error if a name is missing", async () => {
const newUser = new User({
address: "address",
email: "user#gmail.com",
});
await expect(User.create(newUser)).rejects.toThrow("Path `name` is required")
});

Query wont return data from relationship

I'm following this video series.
Here i have problem with getting data about user who created the event in following script
here's my app.js
const express = require('express');
const bodyParser = require('body-parser');
const graphqlHttp = require('express-graphql')
const { buildSchema } = require('graphql');
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
const Event = require('./models/event');
const User = require('./models/user');
const app = express();
const conString = `mongodb+srv://${process.env.MONGO_USER}:${process.env.MONGO_PASSWORD}#clusterpl-qiufl.mongodb.net/${process.env.MONGO_DB}?retryWrites=true&w=majority`
app.use(bodyParser.json());
app.use(
'/graphql',
graphqlHttp({
schema: buildSchema(`
type Event {
_id: ID!
title: String!
description: String!
price: Float!
date: String!
creator: User!
}
type User {
_id: ID!
email: String!
password: String!
createdEvents: [Event!]
}
input UserInput {
email: String!
password: String!
}
input EventInput {
title: String!
description: String!
price: Float!
date: String!
}
type RootQuery {
events: [Event!]!
}
type RootMutation {
createEvent(eventInput: EventInput): Event
createUser(userInput: UserInput): User
}
schema {
query: RootQuery,
mutation: RootMutation
}
`) ,
rootValue: {
events: () => {
return Event.find().populate('creator')
.then(events => {
console.log(events)
return events.map(event => {
console.log(event)
return {
...event._doc,
_id: event.id
};
});
})
.catch(err => {
throw err;
})
},
..
},
graphiql: true
})
);
mongoose.connect(conString, {useNewUrlParser: true}).then(
() => {console.log('Success !')},
err => { console.log(err) }
)
app.listen(3000);
user.js and event.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const userSchema = new Schema({
email: {
type: String,
required: true
},
password: {
type: String,
required: true
},
createdEvents: [
{
type: Schema.Types.ObjectId,
ref: 'Event'
}
]
});
module.exports = mongoose.model('User', userSchema)
const mongoose = require('mongoose');
const Schema = mongoose.Schema
const eventSchema = new Schema({
title: {
type: String,
required: true
},
description: {
type: String,
required: true
},
price: {
type: Number,
required: true
},
date: {
type: Date,
required: true
},
creator: [
{
type: Schema.Types.ObjectId,
ref: 'User'
}
]
});
module.exports = mongoose.model('Event', eventSchema);
once i submit this graphql query
query{
events {
creator {
email
}
}
}
returns "message": "Cannot return null for non-nullable field User.email.",
I'm completly new to graphql and any answer would be much appreciate.
maybe this might helps someone else, i don't know what happens there but i got expected results, by adding
const user = userId => {
return User.findById(userId).then(user => {
return { ...user._doc, _id: user.id };
})
.catch(err => {
throw err;
});
}
method and used it in event resolver function like this
events: () => {
return Event.find()
.populate('creator')
.then(events => {
return events.map(event => {
console.log('ev',event._doc)
return {
...event._doc,
_id: event.id,
// creator: {
// ...event._doc.creator._doc,
// _id: event._doc.creator.id
// }
creator: user.bind(this, event._doc.creator)
};
})
})
.catch(err => {
throw err;
})
},

How create relationship between two collections

I'm creating server app using nodejs(express) and mongodb(mongoose). I must create relationships between Organization model and Users model. After creating an organization, I want to create a user that will apply to a specific organization. One user can apply to many organizations. How can I do this?
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
// UserShema
const UserSchema = Schema({
login: {
type: String,
require: true,
unique: true
},
password: {
type: String,
require: true
},
organization: {
ref: "Organization",
type: Schema.Types.ObjectId
}
});
// Organization Schema
const OrganizationSchema = Schema({
label: {
type: String
},
users: [{
type: Schema.Types.ObjectId,
ref: "Users"
}]
});
//For now I have simple route for creating an Organization.
// request:
// {
// "label": "testOrg"
// }
exports.createOrganization = async (req, res) => {
try {
const org = await new Organization(req.body);
await org.save();
} catch (error) {
return res.status(500).json({error})
}
}
//And I have this route for user registration
exports.signup = async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
};
const {login} = req.body;
try {
const checkUser = await Users.findOne({login});
if (!checkUser) {
const user = await new Users(req.body);
await user.save();
return res.status(200).json({ user });
} else {
return res.status(400).json({error: "User already exist"})
}
} catch (error) {
return res.status(500).json({error})
}
};
You could embed the organization id into a string into the user document
Like this {
name: "Name",
location: "CA",
organizations: [123456789, 234567890, ...]
}

MongoDB & Mongoose: unable to populate a user's posts with .populate()

I've searched this site for days looking through the many different but similar questions on this topic to no avail.
Here's what I'd like to happen. A user signs in and their posts are automatically linked to the users collection. Eventually I'd like to link posts to the profile it was posted to, but i"m not quite there yet. Here's what I've tried so far.
In the User Schema:
const UserSchema = new Schema({
posts: [{
type: Schema.Types.ObjectId,
ref: 'posts'
}],
firstName: {
type: String,
required: true
},
lastName: {
type: String,
required: true
},
...
});
module.exports = User = mongoose.model('users', UserSchema);
In the Post Schema:
const PostSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: 'users'
},
text: {
type: String,
required: true
},
name: {
type: String
},
...
});
module.exports = Post = mongoose.model('posts', PostSchema);
In my users api, here's how I'm signing the user in and attempting to populate the user's posts:
const User = require('../../models/User');
router.post('/login', (req, res) => {
const { errors, isValid } = validateLoginInput(req.body);
// Check Validation
if (! isValid) {
return res.status(400).json(errors);
}
const email = req.body.email;
const password = req.body.password;
// Find user by email
User.findOne({ email })
.populate('posts')
.then(user => {
if (! user) {
errors.email = 'User not found';
return res.status(400).json(errors);
}
// Check password
bcrypt.compare(password, user.password).then(isMatch => {
if (isMatch) {
// User Matched
// Create JWT Payload
const payload = {
id: user.id,
firstName: user.firstName,
lastName: user.lastName,
name: user.firstName + ' ' + user.lastName,
avatar: user.avatar,
posts: user.posts
};
jwt.sign(
payload,
keys.secretOrKey,
{ expiresIn: 3600 }, (err, token) => {
res.json({
success: true,
token: 'Bearer ' + token,
payload
});
});
} else {
errors.password = 'Password is incorrect';
return res.status(400).json(errors);
}
});
});
});
In the posts api, here's how the post is being submitted:
router.post('/', passport.authenticate('jwt', { session: false }), (req, res) => {
const { errors, isValid } = validatePostInput(req.body);
if (! isValid) {
// Return errors with 400 status
return res.status(400).json(errors)
}
const newPost = new Post({
text: req.body.text,
name: req.body.name,
avatar: req.body.avatar,
user: req.user.id
});
newPost.save().then(post => res.json(post));
});
Currently, all I'm seeing is an empty array and no errors. I've been spinning my wheels on this one for a couple days now so any help would be appreciated. Thanks!
I think you forgot to save the _id of your new post to the User model so that the populate() can lookup the posts to populate:
newPost.save().then(post => {
User.update({ _id: req.user.id }, { $push: { posts: post._id }}, (err) => {
res.json(post));
});
});