Categories
MongoDB Node.js Basics

Node.js Basics — Logging with MongoDB

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.

Logging

The MongoDB Node.js client has a logger built into it.

To use it, we can write:

const { MongoClient, Logger } = require('mongodb');
const connection = "mongodb://localhost:27017";
const client = new MongoClient(connection);

async function run() {
  try {
    Logger.setLevel("debug");
    await client.connect();
    const db = client.db("test");
    db.dropCollection('test');
    db.createCollection('test');
    const testCollection = await db.collection('test');
    const result = await testCollection.insertMany([
      { "_id": 1, "name": "apples", "qty": 5, "rating": 3 },
      { "_id": 2, "name": "apples", "qty": 7, "rating": 1 },
      { "_id": 3, "name": "oranges", "qty": 6, "rating": 2 },
      { "_id": 4, "name": "avocados", "qty": 3, "rating": 5 },
    ]);
    console.log(result)
    const cursor = await testCollection.find();
    cursor.forEach(console.dir);
  } finally {
    await client.close();
  }
}
run().catch(console.dir);

to call Logger.setLevel to 'debug' to see all the debug messages when the methods below it are run.

We should see a lot more messages displayed in the console that without the logger.

Filter on a Specific Class

We can filter items when we’re logging so that we don’t see so many items.

For example, we can write:

const { MongoClient, Logger } = require('mongodb');
const connection = "mongodb://localhost:27017";
const client = new MongoClient(connection);

async function run() {
  try {
    Logger.setLevel("debug");
    Logger.filter("class", ["Db"]);
    await client.connect();
    const db = client.db("test");
    db.dropCollection('test');
    db.createCollection('test');
    const testCollection = await db.collection('test');
    const result = await testCollection.insertMany([
      { "_id": 1, "name": "apples", "qty": 5, "rating": 3 },
      { "_id": 2, "name": "apples", "qty": 7, "rating": 1 },
      { "_id": 3, "name": "oranges", "qty": 6, "rating": 2 },
      { "_id": 4, "name": "avocados", "qty": 3, "rating": 5 },
    ]);
    console.log(result)
    const cursor = await testCollection.find();
    cursor.forEach(console.dir);
  } finally {
    await client.close();
  }
}
run().catch(console.dir);

The Logger.filter method with the ['Db'] array lets us display messages that are on the 'Db' class.

We can also customize the logger with our own class.

For example, we can write:

const { MongoClient, Logger } = require('mongodb');
const connection = "mongodb://localhost:27017";
const client = new MongoClient(connection);

class CustomLogger {
  constructor() {
    this.logger = new Logger("A");
  }

log() {
    if (this.logger.isInfo()) {
      this.logger.info("logging A", {});
    }
  }
}

async function run() {
  try {
    const customLogger = new CustomLogger();
    customLogger.log();
    await client.connect();
    const db = client.db("test");
    db.dropCollection('test');
    db.createCollection('test');
    const testCollection = await db.collection('test');
    const result = await testCollection.insertMany([
      { "_id": 1, "name": "apples", "qty": 5, "rating": 3 },
      { "_id": 2, "name": "apples", "qty": 7, "rating": 1 },
      { "_id": 3, "name": "oranges", "qty": 6, "rating": 2 },
      { "_id": 4, "name": "avocados", "qty": 3, "rating": 5 },
    ]);
    console.log(result)
    const cursor = await testCollection.find();
    cursor.forEach(console.dir);
  } finally {
    await client.close();
  }
}
run().catch(console.dir);

We created the CustomLogger class and used its log method to do the logging.

Also, we can call setCurrentLogger to set a custom logger function:

const { MongoClient, Logger } = require('mongodb');
const connection = "mongodb://localhost:27017";
const client = new MongoClient(connection);

async function run() {
  try {
    Logger.setLevel("debug");
    Logger.setCurrentLogger((msg, context) => {
      context['foo'] = 'bar';
      msg = `Hello, World. ${msg}`;
      console.log(msg, context);
    });
    await client.connect();
    const db = client.db("test");
    db.dropCollection('test');
    db.createCollection('test');
    const testCollection = await db.collection('test');
    const result = await testCollection.insertMany([
      { "_id": 1, "name": "apples", "qty": 5, "rating": 3 },
      { "_id": 2, "name": "apples", "qty": 7, "rating": 1 },
      { "_id": 3, "name": "oranges", "qty": 6, "rating": 2 },
      { "_id": 4, "name": "avocados", "qty": 3, "rating": 5 },
    ]);
    console.log(result)
    const cursor = await testCollection.find();
    cursor.forEach(console.dir);
  } finally {
    await client.close();
  }
}
run().catch(console.dir);

The msg parameter has the message. The context has additional data about the event that’s logged.

Conclusion

We can add a logger to our MongoDB code with the Node.js MongoDB client.

Categories
MongoDB Node.js Basics

Node.js Basics — Specify Which Fields to Return with MongoDB

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.

Specify Which Fields to Return with MongoDB

We can specify the fields that we want to return for each entry/.

For example, we can wrote:

const { MongoClient } = require('mongodb');
const connection = "mongodb://localhost:27017";
const client = new MongoClient(connection);

async function run() {
  try {
    await client.connect();
    const testCollection = await client.db("test").collection('test');
    await testCollection.deleteMany({})
    const result = await testCollection.insertMany([
      { "_id": 1, "name": "apples", "qty": 5, "rating": 3 },
      { "_id": 2, "name": "bananas", "qty": 7, "rating": 1 },
      { "_id": 3, "name": "oranges", "qty": 6, "rating": 2 },
      { "_id": 4, "name": "avocados", "qty": 3, "rating": 5 },
    ]);
    console.log(result)
    const projection = { name: 1 };
    const cursor = testCollection.find().project(projection);
    await cursor.forEach(console.dir);
  } finally {
    await client.close();
  }
}
run().catch(console.dir);

We call the project method with an object that has the keys that we want to include in the keys.

The value is 1 means we include the given property in the result.

The _id field is returned automatically by default.

So we get:

{ _id: 1, name: 'apples' }
{ _id: 2, name: 'bananas' }
{ _id: 3, name: 'oranges' }
{ _id: 4, name: 'avocados' }

returned.

If we want to disable this, then we can write:

const { MongoClient } = require('mongodb');
const connection = "mongodb://localhost:27017";
const client = new MongoClient(connection);

async function run() {
  try {
    await client.connect();
    const testCollection = await client.db("test").collection('test');
    await testCollection.deleteMany({})
    const result = await testCollection.insertMany([
      { "_id": 1, "name": "apples", "qty": 5, "rating": 3 },
      { "_id": 2, "name": "bananas", "qty": 7, "rating": 1 },
      { "_id": 3, "name": "oranges", "qty": 6, "rating": 2 },
      { "_id": 4, "name": "avocados", "qty": 3, "rating": 5 },
    ]);
    console.log(result)
    const projection = { _id: 0, name: 1 };
    const cursor = testCollection.find().project(projection);
    await cursor.forEach(console.dir);
  } finally {
    await client.close();
  }
}
run().catch(console.dir);

Then we get:

{ name: 'apples' }
{ name: 'bananas' }
{ name: 'oranges' }
{ name: 'avocados' }

We can specify multiple fields in the query.

For example, we can write:

const { MongoClient } = require('mongodb');
const connection = "mongodb://localhost:27017";
const client = new MongoClient(connection);

async function run() {
  try {
    await client.connect();
    const testCollection = await client.db("test").collection('test');
    await testCollection.deleteMany({})
    const result = await testCollection.insertMany([
      { "_id": 1, "name": "apples", "qty": 5, "rating": 3 },
      { "_id": 2, "name": "bananas", "qty": 7, "rating": 1 },
      { "_id": 3, "name": "oranges", "qty": 6, "rating": 2 },
      { "_id": 4, "name": "avocados", "qty": 3, "rating": 5 },
    ]);
    console.log(result)
    const projection = { _id: 0, rating: 1, name: 1 };
    const cursor = testCollection.find().project(projection);
    await cursor.forEach(console.dir);
  } finally {
    await client.close();
  }
}
run().catch(console.dir);

Then, we get:

{ name: 'apples', rating: 3 }
{ name: 'bananas', rating: 1 }
{ name: 'oranges', rating: 2 }
{ name: 'avocados', rating: 5 }

returned.

Conclusion

We can select the fields we return with MongoDB queries.

Categories
MongoDB Node.js Basics

Node.js Basics — MongoDB Text and Unique Indexes

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.

Text Indexes

Text indexes let us do text searches on queries that have string content.

It can include any field whose value is a string or an array of strings.

For example, we can create the index and use it by wriing”

const { MongoClient } = require('mongodb');
const connection = "mongodb://localhost:27017";
const client = new MongoClient(connection);

async function run() {
  try {
    await client.connect();
    const db = client.db("test");
    const testCollection = await db.collection('test');
    await testCollection.dropIndexes();
    const indexResult = await testCollection.createIndex({ name: "text" }, { default_language: "english" });
    console.log(indexResult)
    await testCollection.deleteMany({})
    const result = await testCollection.insertMany([
      { "_id": 1, "name": "apples", "qty": 5, "rating": 3 },
      { "_id": 2, "name": "bananas", "qty": 7, "rating": 1 },
      { "_id": 3, "name": "oranges", "qty": 6, "rating": 2 },
      { "_id": 4, "name": "avocados", "qty": 3, "rating": 5 },
    ]);
    console.log(result)
    const query = { $text: { $search: "apple" } };
    const projection = { name: 1 };
    const cursor = testCollection
      .find(query)
      .project(projection);
    cursor.forEach(console.dir);
  } finally {
    await client.close();
  }
}
run().catch(console.dir);

We create the index by writing:

const indexResult = await testCollection.createIndex({ name: "text" }, { default_language: "english" });

The createIndex method adds the index to the name field.

The 2nd argument has the options for creating the index.

The default_language sets the index language.

Unique Indexes

We can add a unique index that index fields that don’t store duplicate values.

The _id field has a unique index added to it when the collection is created.

For example, we can write:

const { MongoClient } = require('mongodb');
const connection = "mongodb://localhost:27017";
const client = new MongoClient(connection);

async function run() {
  try {
    await client.connect();
    const db = client.db("test");
    const testCollection = await db.collection('test');
    await testCollection.dropIndexes();
    const indexResult = await testCollection.createIndex({ name: "text" }, { unique: true });
    console.log(indexResult)
    await testCollection.deleteMany({})
    const result = await testCollection.insertMany([
      { "_id": 1, "name": "apples", "qty": 5, "rating": 3 },
      { "_id": 2, "name": "bananas", "qty": 7, "rating": 1 },
      { "_id": 3, "name": "oranges", "qty": 6, "rating": 2 },
      { "_id": 4, "name": "avocados", "qty": 3, "rating": 5 },
    ]);
    console.log(result)
    const query = {};
    const projection { name: 1 };
    const cursor = testCollection
      .find(query)
      .project(projection);
    cursor.forEach(console.dir);
  } finally {
    await client.close();
  }
}
run().catch(console.dir);

to call createIndex with the 2nd argument being an object with the unique property set to true .

If there’re any duplicate values in the field in the collection, then we get the ‘duplicate key error index’ error when we try to create the index.

Conclusion

We can add indexes for text searches and indexing unique values for MongoDB fields.

Categories
MongoDB Node.js Basics

Node.js Basics — MongoDB Promises and Callbacks

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.

Promises and Callbacks

The MongoDB client has methods that return promises.

They’re async code that can have the status pending before it has the result.

Once it has a result, the it can be fulfilled if the operation is done successfully.

Otherwise, it has the rejected status.

For example, the updateOne method returns a promise.

We can use it by writing:

const { MongoClient } = require('mongodb');
const connection = "mongodb://localhost:27017";
const client = new MongoClient(connection);

async function run() {
  try {
    await client.connect();
    const testCollection = await client.db("test").collection('test');
    await testCollection.deleteMany({})
    const result = await testCollection.insertMany([
      { "_id": 1, "name": "apples", "qty": 5, "rating": 3 },
      { "_id": 2, "name": "bananas", "qty": 7, "rating": 1 },
      { "_id": 3, "name": "oranges", "qty": 6, "rating": 2 },
      { "_id": 4, "name": "avocados", "qty": 3, "rating": 5 },
    ]);
    console.log(result)
    const updateResult = await testCollection
      .updateOne({ name: "apple" }, { $set: { qty: 100 } })
    console.log(updateResult);
  } finally {
    await client.close();
  }
}
run().catch(console.dir);

We call updateOne with the await keyword so that we can get the resolved value once we have it.

updateOne returns a promise, so we can use the await keyword.

If we want to catch errors, then we can write:

const { MongoClient } = require('mongodb');
const connection = "mongodb://localhost:27017";
const client = new MongoClient(connection);

async function run() {
  try {
    await client.connect();
    const testCollection = await client.db("test").collection('test');
    await testCollection.deleteMany({})
    const result = await testCollection.insertMany([
      { "_id": 1, "name": "apples", "qty": 5, "rating": 3 },
      { "_id": 2, "name": "bananas", "qty": 7, "rating": 1 },
      { "_id": 3, "name": "oranges", "qty": 6, "rating": 2 },
      { "_id": 4, "name": "avocados", "qty": 3, "rating": 5 },
    ]);
    console.log(result)
    try {
      const updateResult = await testCollection
        .updateOne({ name: "apple" }, { $set: { qty: 100 } })
      console.log(updateResult);
    } catch (error) {
      console.log(`Updated ${res.result.n} documents`)
    }

} finally {
    await client.close();
  }
}
run().catch(console.dir);

to add a catch block to log any errors that’s raised from the rejected promise.

Callbacks

We can also use callbacks to get the result of an operation.

For example, we can write:

const { MongoClient } = require('mongodb');
const connection = "mongodb://localhost:27017";
const client = new MongoClient(connection);

async function run() {
  try {
    await client.connect();
    const testCollection = await client.db("test").collection('test');
    await testCollection.deleteMany({})
    const result = await testCollection.insertMany([
      { "_id": 1, "name": "apples", "qty": 5, "rating": 3 },
      { "_id": 2, "name": "bananas", "qty": 7, "rating": 1 },
      { "_id": 3, "name": "oranges", "qty": 6, "rating": 2 },
      { "_id": 4, "name": "avocados", "qty": 3, "rating": 5 },
    ]);
    console.log(result)
    testCollection
      .updateOne({ name: "apple" }, { $set: { qty: 100 } }, (error, result) => {
        if (!error) {
          console.log(`Operation completed successfully`);
        } else {
          console.log(`An error occurred: ${error}`);
        }
      })
  } finally {
    await client.close();
  }
}
run().catch(console.dir);

We call updateOne with a callback in the 3rd argument.

This lets us get the async result with the callback instead of returning a promise.

Conclusion

The MongoDB client returns promises or we can use callbacks to get the result from an async operation.

Categories
MongoDB Node.js Basics

Node.js Basics — MongoDB Index Types

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.

Index Types

MongoDB has different text types.

We can add single field indexes to improve performance for queries that specify ascending or descending sort order on a single field of a document.

For example, we can write:

const { MongoClient } = require('mongodb');
const connection = "mongodb://localhost:27017";
const client = new MongoClient(connection);

async function run() {
  try {
    await client.connect();
    const db = client.db("test");
    const testCollection = await db.collection('test');
    await testCollection.dropIndexes();
    const indexResult = await testCollection.createIndex({ name: 1 });
    console.log(indexResult)
    await testCollection.deleteMany({})
    const result = await testCollection.insertMany([
      { "_id": 1, "name": "apples", "qty": 5, "rating": 3 },
      { "_id": 2, "name": "bananas", "qty": 7, "rating": 1 },
      { "_id": 3, "name": "oranges", "qty": 6, "rating": 2 },
      { "_id": 4, "name": "avocados", "qty": 3, "rating": 5 },
    ]);
    console.log(result)
    const query = {};
    const sort = { name: 1 };
    const cursor = testCollection
      .find(query)
      .sort(sort);
    cursor.forEach(console.dir);
  } finally {
    await client.close();
  }
}
run().catch(console.dir);

We call the sort method with the object with the sort order.

Compound index ae indexes that improve performance for queries that specify ascending or descending sort order for multiple fields in a document.

For example, we can add a compound for the name and rating fields and use it by writing:

const { MongoClient } = require('mongodb');
const connection = "mongodb://localhost:27017";
const client = new MongoClient(connection);

async function run() {
  try {
    await client.connect();
    const db = client.db("test");
    const testCollection = await db.collection('test');
    await testCollection.dropIndexes();
    const indexResult = await testCollection.createIndex({ name: 1, rating: 1 });
    console.log(indexResult)
    await testCollection.deleteMany({})
    const result = await testCollection.insertMany([
      { "_id": 1, "name": "apples", "qty": 5, "rating": 3 },
      { "_id": 2, "name": "bananas", "qty": 7, "rating": 1 },
      { "_id": 3, "name": "oranges", "qty": 6, "rating": 2 },
      { "_id": 4, "name": "avocados", "qty": 3, "rating": 5 },
    ]);
    console.log(result)
    const query = {};
    const sort = { name: 1, rating: 1 };
    const cursor = testCollection
      .find(query)
      .sort(sort);
    cursor.forEach(console.dir);
  } finally {
    await client.close();
  }
}
run().catch(console.dir);

We have:

const indexResult = await testCollection.createIndex({ name: 1, rating: 1 });

to add the compound index.

Then we call sort with an object that has both the name and rating fields.

Multikey indexes are indexes that improve performance on queries that specifies ascending or descending index on fields that has an array value.

For example, we can create a multikey index and use it by writing:

const { MongoClient } = require('mongodb');
const connection = "mongodb://localhost:27017";
const client = new MongoClient(connection);

async function run() {
  try {
    await client.connect();
    const db = client.db("test");
    const testCollection = await db.collection('test');
    await testCollection.dropIndexes();
    const indexResult = await testCollection.createIndex({ types: 1 });
    console.log(indexResult)
    await testCollection.deleteMany({})
    const result = await testCollection.insertMany([
      { "_id": 1, "name": "apples", "qty": 5, "rating": 3, "types": ["granny smith", "mcintosh"] },
      { "_id": 2, "name": "bananas", "qty": 7, "rating": 1, "types": ["chiquita", "del monte"] },
      { "_id": 3, "name": "oranges", "qty": 6, "rating": 2, "types": [] },
      { "_id": 4, "name": "avocados", "qty": 3, "rating": 5, "types": [] },
    ]);
    console.log(result)
    const query = { types: "granny smith" };
    const sort = { types: 1 };
    const projection = { types: 1 };
    const cursor = testCollection
      .find(query)
      .sort(sort)
      .project(projection);
    cursor.forEach(console.dir);
  } finally {
    await client.close();
  }
}
run().catch(console.dir);

We have:

const indexResult = await testCollection.createIndex({ types: 1 });

to create the index on the types field.

Then we can make the query on the types field.

Conclusion

MongoDB has various types of indexes to optimize the performance of various kinds of queries.