Categories
MongoDB

Using MongoDB with Mongoose — Maps and Getters

To make MongoDB database manipulation easy, we can use the Mongoose NPM package to make working with MongoDB databases easier.

In this article, we’ll look at how to use Mongoose to manipulate our MongoDB database.

Array Mixed Type

If we specify an empty array as the type, then the type is considered a mixed type.

For instance, if we have:

const mongoose = require('mongoose');
const connection = "mongodb://localhost:27017/test";
mongoose.connect(connection, { useNewUrlParser: true });
const db = mongoose.connection;
db.on('error', () => console.error('connection error:'));
db.once('open', () => {
  console.log('connected')
});
const Empty1 = new mongoose.Schema({ any: [] });
const Empty2 = new mongoose.Schema({ any: Array });
const Empty3 = new mongoose.Schema({ any: [mongoose.Schema.Types.Mixed] });
const Empty4 = new mongoose.Schema({ any: [{}] });

then they all have a any field with the mixed type.

Maps

Mongoose version 5.1.0 or later has the Map type to store key-value pairs.

For example, we can write:

const mongoose = require('mongoose');
const connection = "mongodb://localhost:27017/test";
mongoose.connect(connection, { useNewUrlParser: true });
const db = mongoose.connection;
db.on('error', () => console.error('connection error:'));
db.once('open', () => {
  console.log('connected')
});
const userSchema = new mongoose.Schema({
  socialMediaHandles: {
    type: Map,
    of: String
  }
});

const User = mongoose.model('User', userSchema);
console.log(new User({
  socialMediaHandles: {
    github: 'abc',
    twitter: '@abc'
  }
}).socialMediaHandles);

We created the userSchema with the Map type.

The of property sets the type of the value.

Then we can pass in the object to specify the key-value pairs for the map.

We can also write:

const mongoose = require('mongoose');
const connection = "mongodb://localhost:27017/test";
mongoose.connect(connection, { useNewUrlParser: true });
const db = mongoose.connection;
db.on('error', () => console.error('connection error:'));
db.once('open', () => {
  console.log('connected')
});
const userSchema = new mongoose.Schema({
  socialMediaHandles: {
    type: Map,
    of: String
  }
});

const User = mongoose.model('User', userSchema);
const user = new User({
  socialMediaHandles: {}
});
user.socialMediaHandles.set('github', 'abc');
user.set('socialMediaHandles.twitter', '@abc');
console.log(user.socialMediaHandles.get('github'));
console.log(user.get('socialMediaHandles.twitter'));
user.save();

We can access the socialMediaHandles property or put the property name in the string in the first argument of the set method.

And we can get the value by the key with the get method.

Getters

We can add getters to our schema fields.

For example, we can write:

const mongoose = require('mongoose');
const connection = "mongodb://localhost:27017/test";
mongoose.connect(connection, { useNewUrlParser: true });
const db = mongoose.connection;
db.on('error', () => console.error('connection error:'));
db.once('open', () => {
  console.log('connected')
});
const root = 'https://s3.amazonaws.com/mybucket';

const userSchema = new mongoose.Schema({
  name: String,
  picture: {
    type: String,
    get: v => `${root}${v}`
  }
});

const User = mongoose.model('User', userSchema);

const doc = new User({ name: 'Val', picture: '/123.png' });
console.log(doc.picture);
console.log(doc.toObject({ getters: false }).picture);

to add a getter to the picture field with the get method.

By default the picture property has the return value of the getter.

But we can also set the getters property to false to get the picture field’s value.

Conclusion

We can add array mixed types, getters, and maps to a schema field.

Categories
MongoDB

Using MongoDB with Mongoose — Buffer, Boolean, ObjectId and Array Types

To make MongoDB database manipulation easy, we can use the Mongoose NPM package to make working with MongoDB databases easier.

In this article, we’ll look at how to use Mongoose to manipulate our MongoDB database.

Buffer

We can declare the Buffer type with the Buffer constructor:

const mongoose = require('mongoose');
const connection = "mongodb://localhost:27017/test";
mongoose.connect(connection, { useNewUrlParser: true });
const db = mongoose.connection;
db.on('error', () => console.error('connection error:'));
db.once('open', () => {
  console.log('connected')
});
const Binary = mongoose.model('binary', { binData: Buffer });

Mixed

We can also add a mixed type to let us add anything as the value for a field:

const mongoose = require('mongoose');
const connection = "mongodb://localhost:27017/test";
mongoose.connect(connection, { useNewUrlParser: true });
const db = mongoose.connection;
db.on('error', () => console.error('connection error:'));
db.once('open', () => {
  console.log('connected')
});
const Mixed = mongoose.model('mixed', { any: mongoose.Mixed });

We can’t autodetect and save changes when we use this type since it’s a schema-less type.

ObjectIds

We can specify the ObjectId type to store object IDs.

For example, we can write:

const mongoose = require('mongoose');
const connection = "mongodb://localhost:27017/test";
mongoose.connect(connection, { useNewUrlParser: true });
const db = mongoose.connection;
db.on('error', () => console.error('connection error:'));
db.once('open', () => {
  console.log('connected')
});
const carSchema = new mongoose.Schema({ driver: mongoose.ObjectId });
const Car = mongoose.model('Car', carSchema);
const car = new Car();
car.driver = new mongoose.Types.ObjectId();
console.log(typeof car.driver)
console.log(car.driver instanceof mongoose.Types.ObjectId);
car.driver.toString();

We created a carSchema to store data about cars.

Inside the schema, we have the driver field, which is of type mongoose.ObjectId .

We can create the object ID to assign with the mongoose.Types.ObjectId() method.

Then we can check the types of car.driver . We should get the type is 'object' .

The 2nd console log should be true since driver is of type ObjectId .

Boolean

Another type that we set to fields is the boolean type.

The following values are cast to true :

  • true
  • 'true'
  • 1
  • '1'
  • 'yes'

And these values are cast to false :

  • false
  • 'false'
  • 0
  • '0'
  • 'no'

For example, if we write:

const mongoose = require('mongoose');
const connection = "mongodb://localhost:27017/test";
mongoose.connect(connection, { useNewUrlParser: true });
const db = mongoose.connection;
db.on('error', () => console.error('connection error:'));
db.once('open', () => {
  console.log('connected')
});
const M = mongoose.model('Test', new mongoose.Schema({ b: Boolean }));
console.log(new M({ b: 'nay' }).b);
console.log(mongoose.Schema.Types.Boolean.convertToFalse);
mongoose.Schema.Types.Boolean.convertToFalse.add('nay');
console.log(new M({ b: 'nay' }).b);

We called mongoose.Schema.Types.Boolean.convertToFalse to let us cast 'nay' to false .

Arrays

Mongoose has an array type that lets us specify arrays.

For example, we can write:

const mongoose = require('mongoose');
const connection = "mongodb://localhost:27017/test";
mongoose.connect(connection, { useNewUrlParser: true });
const db = mongoose.connection;
db.on('error', () => console.error('connection error:'));
db.once('open', () => {
  console.log('connected')
});
const ToySchema = new mongoose.Schema({ name: String });
const ToyBoxSchema = new mongoose.Schema({
  toys: [ToySchema],
  strings: [String],
});

We have the ToySchema Mongoose schema, which we specify as the type of the toys array field.

Each entry of the toys array must conform to the ToySchema .

Conclusion

We can specify various types with Mongoose to build our schema.

Categories
MongoDB

Using MongoDB with Mongoose

To make MongoDB database manipulation easy, we can use the Mongoose NPM package to make working with MongoDB databases easier.

In this article, we’ll look at how to use Mongoose to manipulate our MongoDB database.

Getting Started with Mongoose

We can install the package by running:

npm install mongoose --save

Then we can use it by writing:

const mongoose = require('mongoose');
const connection = "mongodb://localhost:27017";
mongoose.connect(connection, { useNewUrlParser: true });
const db = mongoose.connection;
db.on('error', () => console.error('connection error:'));
db.once('open', () => {
  console.log('connected')
});

We connect th our MongoDB database with Mongoose by using the mongoose.connect method.

Then to see if we connected successfully, we can listen to the open event.

Defining Schemas

Now we can define a schema to restrict what we can put on our documents.

This is something that’s not available with the native MongoDB client.

For example, we can write:

const mongoose = require('mongoose');
const connection = "mongodb://localhost:27017";
mongoose.connect(connection, { useNewUrlParser: true });
const db = mongoose.connection;
db.on('error', () => console.error('connection error:'));
db.once('open', () => {
  console.log('connected')
});

const kittySchema = new mongoose.Schema({
  name: String
});

const Kitten = mongoose.model('Kitten', kittySchema);
const baby = new Kitten({ name: 'james' });
baby.save((err, baby) => {
  if (err) {
    return console.error(err);
  }
  console.log(baby.name);
});

We create the schema with the mongoose.Schema constructor.

The object has the fields as the keys and the data type as the values.

Then we define the model with the mongoose.model method.

Then we use the Kitten constructor, and then we call save to save the Kitten object to the database.

The _id will automatically be generated for each inserted entry.

If we want to get the entries, then we can call the find method:

const mongoose = require('mongoose');
const connection = "mongodb://localhost:27017/test";
mongoose.connect(connection, { useNewUrlParser: true });
const db = mongoose.connection;
db.on('error', () => console.error('connection error:'));
db.once('open', () => {
  console.log('connected')
});

const kittySchema = new mongoose.Schema({
  name: String
});

const Kitten = mongoose.model('Kitten', kittySchema);
const baby = new Kitten({ name: 'james' });
baby.save((err, baby) => {
  if (err) {
    return console.error(err);
  }
  console.log(baby.name);
});

Kitten.find((err, kittens) => {
  if (err) return console.error(err);
  console.log(kittens);
})

If we want to find a specific entry we can pass in an object to the first argument of find :

const mongoose = require('mongoose');
const connection = "mongodb://localhost:27017/test";
mongoose.connect(connection, { useNewUrlParser: true });
const db = mongoose.connection;
db.on('error', () => console.error('connection error:'));
db.once('open', () => {
  console.log('connected')
});

const kittySchema = new mongoose.Schema({
  name: String
});

kittySchema.methods.speak = function () {
  console.log(`hello ${this.name}`);
}

const Kitten = mongoose.model('Kitten', kittySchema);
const baby = new Kitten({ name: 'james' });
baby.speak();
baby.save((err, baby) => {
  if (err) {
    return console.error(err);
  }
  console.log(baby.name);
});

Kitten.find({ name: /^james/ },(err, kittens) => {
  if (err) return console.error(err);
  console.log(kittens);
})

If we want to add methods, we can write:

const mongoose = require('mongoose');
const connection = "mongodb://localhost:27017/test";
mongoose.connect(connection, { useNewUrlParser: true });
const db = mongoose.connection;
db.on('error', () => console.error('connection error:'));
db.once('open', () => {
  console.log('connected')
});

const kittySchema = new mongoose.Schema({
  name: String
});

kittySchema.methods.speak = function () {
  console.log(`hello ${this.name}`);
}

const Kitten = mongoose.model('Kitten', kittySchema);
const baby = new Kitten({ name: 'baby' });
baby.speak();
baby.save((err, baby) => {
  if (err) {
    return console.error(err);
  }
  console.log(baby.name);
});

Kitten.find((err, kittens) => {
  if (err) return console.error(err);
  console.log(kittens);
})

We added a method to the Kitten model by adding a method to the methods object property.

Conclusion

We can create schemas, add items, and find items with Mongoose.

Categories
MongoDB

Node.js Basics — Creating and Deploying Packages

Node.js is a popular runtime platform to create programs that run on it.

It lets us run JavaScript outside the browser.

In this article, we’ll look at how to start using Node.js to create programs.

Creating NPM Packages

We can create our own NPM packages to do that, we run npm add user so we can log into NPM to create our package.

Once we typed in the command, we’ll have to enter a username, password, and email address for our account to create an account on NPM.

The email wil be public.

Then we run npm init to create our package’s package.json file.

Once we answered all the questions, we should get something like:

{
  "name": "npm-example",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo "Error: no test specified" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

Once we’re done, we can run npm publish to publish our package.

Then once we published our package, we can run:

npm install npm-example

to install our package.

To update the version of our package, we run npm version patch .

Then we can run npm publish to publish the package with the later version.

To keep some files from being published in a package, we can create a .npmignore file which follows the same rules as the .gitignore file.

Conclusion

We can create NPM packages easily with npm .

All we have to do is enter some package data and publish the package.

Categories
Node.js Basics

Node.js Basics — Socket.io Namespace and Rooms

Node.js is a popular runtime platform to create programs that run on it.

It lets us run JavaScript outside the browser.

In this article, we’ll look at how to start using Node.js to create programs.

Namespace and Express Middleware

We can use Socket.io namespaces with Express middleware.

For example, we can write:

index.js

const express = require('express');
const app = express();
const server = require('http').createServer(app);
const options = { /* ... */ };
const io = require('socket.io')(server, options);
const session = require('express-session');
const wrap = middleware => (socket, next) => middleware(socket.request, {}, next);

io.use(wrap(session({ secret: 'cats' })));
io.on('connection', socket => {
  console.log('connect')
});

const adminNamespace = io.of('/admin');

adminNamespace.use((socket, next) => {
  console.log(socket);
  next();
});

adminNamespace.on('connection', socket => {
  socket.on('delete user', (data) => {
    console.log(data);
  });
});

app.get('/', (req, res) => {
  res.sendFile(`${__dirname}/index.html`);
});

server.listen(3000);

index.html

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Minimal working example</title>
</head>

<body>
  <ul id="events"></ul>

  <script src="/socket.io/socket.io.js"></script>
  <script>
    const socket = io('/admin');
    socket.emit('delete user', 'foo')
  </script>
</body>

</html>

In index.js , we added the express-session middleware.

We combine the middleware with Socket.io with the wrap function.

The function returns a function that returns the middleware called by the middleware function with the socket.request and next function together as one.

Then we can call that function with io.use with the express-session options.

Rooms in Namespaces

We can add rooms to namespaces by calling various methods.

To join a room, we can call the socket.join method:

index.js

const express = require('express');
const app = express();
const server = require('http').createServer(app);
const options = { /* ... */ };
const io = require('socket.io')(server, options);
io.on('connection', function (socket) {
  socket.join("room");
  io.sockets.in("room").emit('connectToRoom', "You are in room");
})

app.get('/', (req, res) => {
  res.sendFile(`${__dirname}/index.html`);
});

server.listen(3000);

index.html

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Minimal working example</title>
</head>

<body>
  <ul id="events"></ul>

  <script src="/socket.io/socket.io.js"></script>
  <script>
    const socket = io();
    socket.on('connectToRoom', function (data) {
      document.write(data);
    });
  </script>
</body>

</html>

In index.js , we listen to the connection event.

In the callback, we call socket.join with the room name.

Then we call io.sockets.in method to emit an event in the room.

The emit method has event name the first argument and the 2nd argument is the data we want to send.

In index.html , we listen to the connectToRoom event that we emitted and get the data from the callback parameter.

We can listen to the disconnect event to watch for room disconnections.

For example, we can write:

index.js

const express = require('express');
const app = express();
const server = require('http').createServer(app);
const options = { /* ... */ };
const io = require('socket.io')(server, options);
io.on('connection', function (socket) {
  socket.join("room");
  io.sockets.in("room").emit('connectToRoom', "You are in room");
  socket.on('disconnecting', () => {
    const rooms = Object.keys(socket.rooms);
    console.log(rooms)
  });

  socket.on('disconnect', () => {
    console.log('disconnect')
  });
})

app.get('/', (req, res) => {
  res.sendFile(`${__dirname}/index.html`);
});

server.listen(3000);

We listen to the disconnecting and disconnect events to listen for room disconnections.

socket.rooms has all the room data.

The keys of it have the names of the rooms.

Conclusion

We can listen for room connections and disconnections and emit events to rooms from the server to the client with Socket.io.