Converting to Mongo/mongoose from Mysql with relations - javascript

In a new project I started with MySQL but I want to convert it to mongodb / moongoose.
My pain here is the different relations I have in MySQL.
F.x. I have have users table
uid | firstname | lastname | email
and i housetable
hid | address | zipcode | city | ownerid
where ownerid is a user in the users table
I am a little unsure how I should design me schemas in mongoose to have an option a bit like the MySQL one.
My current user schema is like this
const userSchema = new mongoose.Schema({
firstname: String,
lastname: String,
email: String,
phone: Number,
password: String,
address: String,
zipcode: Number,
city: String,
bankname: String,
bankregno: Number,
bankaccno: Number,
userconfirmed: Boolean,
confirmtoken: String,
resettoken: String,
resettokenexpires: Date,
acceptmarketing: Boolean,
lastloggedin: Date,
disabled: Boolean
})

You can add a field for an array of houses in your userSchema to break it up and allow a one to many relation. You can do this by using subdocuments.
This could look something like this.
const houseSchema = new mongoose.Schema({
address: String,
zipcode: Number,
city: String,
})
const userSchema = new mongoose.Schema({
firstname: String,
lastname: String,
email: String,
phone: Number,
password: String,
bankname: String,
bankregno: Number,
bankaccno: Number,
userconfirmed: Boolean,
confirmtoken: String,
resettoken: String,
resettokenexpires: Date,
acceptmarketing: Boolean,
lastloggedin: Date,
disabled: Boolean,
houses: [houseSchema],
})

you can use mongoose populate, like this
const User = new mongoose.Schema({
House: { type: mongoose.Types.Schema.ObjectId, ref: "House" }
});
const House = new mongoose.Schema({
Number,
Road: String
});
(async () => {
const users = await User.find({}).populate("House");
// users = [
// {
// _id: 373890hfjklfhjkfh,
// House: {
// _id: dhkdhdlhjkld,
// Number: 378930,
// Road: fhjdlhdjkld
// }
// }
// ]
})();

If you are looking to relate both the schemas, this might help you. If you want more info, https://mongoosejs.com/docs/2.7.x/docs/populate.html
const User = mongoose.model('User', new mongoose.Schema({
firstname: {
type: String,
},
lastname: {
type: String,
},
email: {
type: String
}
}));
const Address = mongoose.model('Address', new mongoose.Schema({
owner: {
type: Schema.Types.ObjectId,
ref: 'User'
}
//rest of your address schema
}))

Related

Default values not adding to mongoose Model

So this is an example of my schema I have for a user.
id: String,
email: String,
slug: {
type: Object,
phrase: {type: String, default: null},
},
When I want to define a new user and save that user, I would do the following;
const newUser = new User({
id: 123,
username: "CoolUser",
email: "BillGates#google.com"
});
newUser.save();
But this does not save the "slug" object, It was my understanding, that since I a default value for it, it would auto populate with that default value. What can I do to make it auto generate without having to define the whole schema again when saving a user?
You should add default for slug property, and not for his sub-property. Try changing your schema like this:
slug: {
type: {
phrase: { type: String },
},
default: {
phrase: null
},
},
try this:
const subschema = new Schema({
phrase: {type: String, default: null},
}, { _id: false });
and in your original schema:
id: String,
email: String,
slug: {
type: subschema,
default: () => ({})
},
this should do the trick.
const User = new Schema({
id: String,
about: {
bio: String,
location: String,
website: String,
discord: String,
twitter: String,
default: {
bio: "This is a default bio.",
location: "",
website: "",
discord: "",
twitter: "",
}
}
})
Ok here is the example, I removed some stuff from it just for ease, Basically as you can see I want bio to default to: This is a default bio.
But instead, I get the following error:
throw new TypeError(`Invalid schema configuration: \`${name}\` is not ` +
^
TypeError: Invalid schema configuration: `This is a default bio.` is not a valid type at path `about.default.bio`. See bit ly / mongoose-schematypes for a list of valid schema types.

findOneReplace replacment issue

I want to find a document in my db and replace it with a document that has a new name and new key.
Here is my Schema
const Schema = mongoose.Schema;
const vampireSchema = new Schema({
name: { type: String, required: true },
title: String,
hair_color: {type: String, default: "blonde" },
eye_color: String,
dob: Date,
loves: [String],
location: String,
gender: String,
victims: {type: Number, min: 0}
});
const Vampire = mongoose.model("Vampire", vampireSchema);
module.exports = Vampire;
Here is my executable code
Vampire.findOneAndReplace( { name: "Claudia" }, { name: "Eve", portrayed_by: "Tilda Swinton" }, (err, vamp) => {
if(err){
console.log(err)
}
else{
console.log(vamp)
}
db.close()
})
There are two issues that I can see.
First, you should pass null as the third argument in your findOneAndReplace call. This will set the options to null and should get your code running. This is in my opinion a strange behavior of mongoose.
Vampire.findOneAndReplace(
{ name: "Claudia" },
{ name: "Eve", portrayed_by: "Tilda Swinton" },
null,
(err, vamp) =>
{
if(err){
console.log(err)
}
else{
console.log(vamp)
}
db.close()
})
Secondly, I would recommend adding the portrayed_by to the schema, otherwise, that field will not be in the newly created document. Therefore, I would adjust your schema that way:
const vampireSchema = new Schema({
name: { type: String, required: true },
title: String,
hair_color: {type: String, default: "blonde" },
eye_color: String,
dob: Date,
loves: [String],
location: String,
gender: String,
victims: {type: Number, min: 0},
portrayed_by: String
});

Search in MongoDB with Graphql (Foreign Key)

Good afternoon.
I have 2 collections in mongoDB with a one-to-many relationship, where one of the collections receives the foreign key of the other.
const Sport = mongoose.model('Sport', new Schema({
_id: {
type: String,
default:() => uuid()
},
name: { type: String, required: true }
}))
const Event = mongoose.model('Event', new Schema({
_id: {
type: String,
default:() => uuid()
},
name: { type: String, required: true },
sport_id: { type: String, required: true }
}))
I want to do searches on these collections with graphql and when searching for Event, show the data from 'sport_id' instead of just showing the String. How do I do? How do I make the rootvalue?
type Sport {
_id: String
name: String
}
type Event {
_id: String
name: String
sport_id: Sport
}
type Query {
sport(name: String): [Sport!]
sportById(_id: String): Sport
event(name: String, sport_id: String): Event!
eventById(_id: String): Event
}

Referencing Other Models when Saving Data Express/Mongoose

I have a simple express app where users can log in and post pictures of mountains. I'm having an issue saving the posts the users add. Specifically, the user id that is referenced from another schema(user schema). When I submit the form, I get an undefined error on the "._id" and I'm not sure how to go about fixing it.
Below are my model schemas.
const userSchema = new Schema({
name: String,
email: String,
username: String,
password: String,
});
const canyonSchema = new Schema({
canyon: String,
image: String,
description: String,
cost: Number,
createdAt: { type: Date, default: Date.now },
author: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
username: String
}
});
const Canyon = mongoose.model("Canyon", canyonSchema);
module.exports = Canyon;
const User = mongoose.model('User', userSchema);
module.exports = User
Here is the logic that is supposed to save the information to mongo. When I run, I get a "._id undefined". Any help.
const User = require('../models/user');
const Canyon = require('../models/Canyon');
router.post("/add", , function(req, res){
if(req.body.canyon &&
rea.body.image &&
req.body.description){
const newCanyon = {
canyon: req.body.canyon,
image: req.body.image,
description: req.body.description,
author: {
id: req.user._id,
}, username: req.user.username
};
Canyon.create(newCanyon, function(error, canyon){
if(error){
return next(error)
} else {
req.session.userId = user._id;
return res.redirect('/profile')
}
});
} else {
const err = new Error('All fields required.');
err.status = 400;
return next(err);
}
});
In your model schema, instead of
const canyonSchema = new Schema({
canyon: String,
image: String,
description: String,
cost: Number,
createdAt: { type: Date, default: Date.now },
author: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
username: String
}
});
Make the author itself the reference:
const canyonSchema = new Schema({
canyon: String,
image: String,
description: String,
cost: Number,
createdAt: { type: Date, default: Date.now },
author: { type: mongoose.Schema.ObjectId, ref: 'User', index: true },
});
To create a canyon object,
const newCanyon = {
canyon: req.body.canyon,
image: req.body.image,
description: req.body.description,
author: req.user._id
};
To get the user information embedded in the canyon object when query on canyons, just populate the author in the query,
Canyon.findById(id).populate('author').exec()

How To Create Mongoose Schema with Array of Object IDs?

I have defined a mongoose user schema:
var userSchema = mongoose.Schema({
email: { type: String, required: true, unique: true},
password: { type: String, required: true},
name: {
first: { type: String, required: true, trim: true},
last: { type: String, required: true, trim: true}
},
phone: Number,
lists: [listSchema],
friends: [mongoose.Types.ObjectId],
accessToken: { type: String } // Used for Remember Me
});
var listSchema = new mongoose.Schema({
name: String,
description: String,
contents: [contentSchema],
created: {type: Date, default:Date.now}
});
var contentSchema = new mongoose.Schema({
name: String,
quantity: String,
complete: Boolean
});
exports.User = mongoose.model('User', userSchema);
the friends parameter is defined as an array of Object IDs.
So in other words, a user will have an array containing the IDs of other users. I am not sure if this is the proper notation for doing this.
I am trying to push a new Friend to the friend array of the current user:
user = req.user;
console.log("adding friend to db");
models.User.findOne({'email': req.params.email}, '_id', function(err, newFriend){
models.User.findOne({'_id': user._id}, function(err, user){
if (err) { return next(err); }
user.friends.push(newFriend);
});
});
however this gives me the following error:
TypeError: Object 531975a04179b4200064daf0 has no method 'cast'
If you want to use Mongoose populate feature, you should do:
var userSchema = mongoose.Schema({
email: { type: String, required: true, unique: true},
password: { type: String, required: true},
name: {
first: { type: String, required: true, trim: true},
last: { type: String, required: true, trim: true}
},
phone: Number,
lists: [listSchema],
friends: [{ type : ObjectId, ref: 'User' }],
accessToken: { type: String } // Used for Remember Me
});
exports.User = mongoose.model('User', userSchema);
This way you can do this query:
var User = schemas.User;
User
.find()
.populate('friends')
.exec(...)
You'll see that each User will have an array of Users (this user's friends).
And the correct way to insert is like Gabor said:
user.friends.push(newFriend._id);
I'm new to Mongoose myself, so I'm not entirely sure this is right. However, you appear to have written:
friends: [mongoose.Types.ObjectId],
I believe the property you're looking for is actually found here:
friends: [mongoose.Schema.Types.ObjectId],
It may be that the docs have changed since you posted this question though. Apologies if that's the case. Please see the Mongoose SchemaTypes docs for more info and examples.
I would try this.
user.friends.push(newFriend._id);
or
friends: [userSchema],
but i'm not sure if this is correct.

Categories