We have seen the usage of mongoose.Schema in
Chapter 22, and Chapter 24.
A short recap of something you know:
myg171/mongooseUser0.js
"use strict";
/*
* include more sophisticated mongodb functionality
* mongoose enforces schemas, mongodb doesn't
*/
const mongoose = require("mongoose");
/*
* create schema for user object
* build corresponding model as an object
* Wex19, lesson 17
*/
const userSchema = mongoose.Schema({ // with simple attribute: types
name: String,
email: String,
zipcode: Number,
created: Date
});
const User0 = mongoose.model("User", userSchema, 'user'); // create model
// /\ /\ /\ /\
// | | | |
// | | | --- collection name in db
// | | ------------- schema from above
// | ----------------------- name of model in mongo
// ---------------------------------------------- my variable name for the model
/*
* connect to mongodb server
*/
const dbname = "testUser0";
const constr = `mongodb://localhost:27017/${dbname}`;
const conparam = {
useNewUrlParser: true,
useUnifiedTopology: true
};
mongoose.connect(constr, conparam);
const db = mongoose.connection;
db.once("open", function() {
console.log("Connected to server by mongoose")
});
/*
* create concrete object
*/
let user0 = new User0({
name: "Niels",
email: "nmla@iba.dk",
zipcode: 6000,
created: "2020-03-12"
});
/*
* insert user0 object into database, the C of CRUD
* save is a mongoose method
*/
user0.save(function(error, savedDocument) {
if (error) console.log("primary:\n" + error);
console.log(savedDocument);
});
/*
* alternative way of inserting, the C of CRUD
* create below includes the save functionality
*/
User0.create(
{
name: "nmla",
email: "nmla@iba.dk",
zipcode: 6000,
created: "2020-03-12"
},
function(error, savedDocument) {
if (error) console.log("alternative:\n" + error);
console.log(savedDocument);
db.close(); // if forgotten batch job doesn't stop by itself
// must be here to preserve asynchronicity
}
);
console.log("Asynchronous? If I come first, yes!");
Looking at code lines 13 through 18
userSchema is the definition of a
mongoose.Schema, and mongoose.model
defines the mongoose object User from the
schema, and assigns user as its collection
name for the MongoDB database. The type designations are
in shorthand notation.
The surrounding code makes it testable from the CLI. Test it and check that it works by looking into the database with the mongo CLI client. You may also try changing one of the strings to a number to verify that no validation takes place. The types given in the schema is a mild way of adding thumb screws to what the user may input. The application just seems to apply regular JavaScript coercion to the user entered data.
myg171/mongooseUser1.js
"use strict";
/*
* include more sophisticated mongodb functionality
* mongoose enforces schemas, mongodb doesn't
*/
const mongoose = require("mongoose");
/*
* create schema for user object
* build corresponding model as an object
* Wex19, lesson 17
*/
const userSchema = mongoose.Schema({ // with complex attributes
name: {
type: String, // coercion toString
required: true // must be present AND have value (not null)
},
email: {
type: String,
required: true,
lowercase: true, // coerces (?) to lower case
unique: true // not a validator(?)
},
zipcode: {
type: Number,
min: [1000, "Zip code too short"], // validator and its error msg
max: 9999 // validator, relies on std error msg
},
created: {
type: Date, // if value invalid, force to start UNIX era
default: Date.now // default value if none given
}
});
const User0 = mongoose.model("User", userSchema, 'user'); // create model
// /\ /\ /\ /\
// | | | |
// | | | --- collection name in db
// | | ------------- schema from above
// | ----------------------- name of model in mongo
// ---------------------------------------------- my variable name for the model
/*
* connect to mongodb server
*/
const dbname = "testUser0";
const constr = `mongodb://localhost:27017/${dbname}`;
const conparam = {
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true
};
mongoose.connect(constr, conparam);
const db = mongoose.connection;
db.once("open", function() {
console.log("Connected to server by mongoose")
});
/*
* create concrete object
*/
let user0 = new User0({
name: "Niels",
email: "nmla@iba.dk",
zipcode: 6000
});
/*
* insert user0 object into database, the C of CRUD
* save is a mongoose method
*/
user0.save(function(error, savedDocument) {
if (error) console.log("primary:\n" + error);
console.log(savedDocument);
});
/*
* alternative way of inserting, the C of CRUD
* create below includes the save functionality
*/
User0.create(
{
name: "nmla",
email: "nmla@iba.dk",
zipcode: 9000
},
function(error, savedDocument) {
if (error) console.log("alternative:\n" + error);
console.log(savedDocument);
db.close(); // if forgotten batch job doesn't stop by itself
// must be here to preserve asynchronicity
}
);
console.log("Asynchronous? If I come first, yes!");
Here, in lines 14 through 31, you will find rules for the properties of the object to be specified in a complex syntax. In the previous example properties were defined with shorthand notation. Here each property is specified in longhand, ie the full syntax as an object with several properties making room for more nuances.
In our previous work with objects in JavaScript we defined objects
by creating prototypes by adding properties, and methods to
the prototype given by the language. Then in turn we created
instances by using Object.create(User); if
User was the object in question. With the objects
created from mongoose schemas we can achieve the same. So far
we showed the creation of properties. The next example will
add methods.
myg171/User.js
"use strict";
const mongoose = require("mongoose");
/*
* create schema for user object
* build corresponding model as an object
* Wex19, lesson 17
*/
const userSchema = mongoose.Schema({ // with complex attributes
name: {
type: String, // coercion toString
required: true // must be present AND have value (not null)
},
email: {
type: String,
required: true,
lowercase: true, // coerces (?) to lower case
unique: true // if not a validator, what?
},
zipcode: {
type: Number,
min: [1000, "Zip code too short"], // validator and its error msg
max: 9999 // validator, relies on std error msg
},
created: {
type: Date, // if value invalid, force to start UNIX era
default: Date.now // default value if none given
}
});
userSchema.methods.getInfo = function () {
return `Name: ${this.name}, Email: ${this.email}, Zipcode: ${this.zipcode}`;
}
userSchema.methods.findNeighbours = function () {
return this.model("User").find({zipcode: this.zipcode}).exec();
}
module.exports = mongoose.model("User", userSchema, 'user');Implementing and testing the extended constraints.
myg171/mongooseUser5.js
"use strict";
/*
* include more sophisticated mongodb functionality
* mongoose enforces schemas, mongodb doesn't
*/
const mongoose = require("mongoose");
const User0 = require("./User.js");
let foo = process.argv[2] || "foo";
let bar = Number(process.argv[3]) || 6000;
/*
* connect to mongodb server
*/
const dbname = "testUser0";
const constr = `mongodb://localhost:27017/${dbname}`;
const conparam = {
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true
};
mongoose.connect(constr, conparam);
const db = mongoose.connection;
db.once("open", function() {
console.log("Connected to server by mongoose")
});
/*
* create concrete object
*/
let user0 = new User0({
name: foo,
email: `${foo}@iba.dk`,
zipcode: bar
});
/*
* insert user0 object into database, the C of CRUD
* save is a mongoose method
*/
user0.save(function(error, savedDocument) {
if (error) console.log("primary:\n" + error);
db.close();
});
console.log("Asynchronous? If I come first, yes!");
Implementing and testing one of the methods.
myg171/mongooseUser6.js
"use strict";
/*
* include more sophisticated mongodb functionality
* mongoose enforces schemas, mongodb doesn't
*/
const mongoose = require("mongoose");
const User = require("./User.js");
let foobar = process.argv[2] || "foobar";
/*
* connect to mongodb server
*/
const dbname = "testUser0";
const constr = `mongodb://localhost:27017/${dbname}`;
const conparam = {
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true
};
mongoose.connect(constr, conparam);
const db = mongoose.connection;
db.once("open", function() {
console.log("Connected to server by mongoose")
});
let user0;
User.findOne({
name: foobar
}).then(function (result) {
user0 = result;
console.log(user0.getInfo()); // using object method
})
console.log("Asynchronous? If I come first, yes!");
Implementing and testing the other method. You will notice the code to assure asynchronous behaviour because that method involves reading the database.
myg171/mongooseUser7.js
"use strict";
/*
* include more sophisticated mongodb functionality
* mongoose enforces schemas, mongodb doesn't
*/
const mongoose = require("mongoose");
const User = require("./User.js");
let foobar = process.argv[2] || "foobar";
/*
* connect to mongodb server
*/
const dbname = "testUser0";
const constr = `mongodb://localhost:27017/${dbname}`;
const conparam = {
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true
};
mongoose.connect(constr, conparam);
const db = mongoose.connection;
db.once("open", function() {
console.log("Connected to server by mongoose")
});
let user0;
User.findOne({
name: foobar
}).then(function (result) {
user0 = result;
return user0.findNeighbours(); // using another object method
}).then(function (result) {
console.log(result);
db.close();
}).catch(function (err) {
console.error(err);
});
console.log("Asynchronous? If I come first, yes!");