Hi I'm working with my MongoDB and NestJS.
I was testing by query below. And it was what I expected
I only want to get '_id'. So I tested my code too.
// // This is what I use(including comments)
// const { MongoClient, ObjectID } = require('mongodb');
// const url = 'mongodb+srv://alex:~ something';
// console.log(url);
// const client = new MongoClient(url);
// // Database Name
// const dbName = 'testDB';
export async function getCreaterPubGameId(authorID: string) {
await client.connect();
console.log('Connected successfully to server : update, find or insertData');
const db = client.db(dbName);
const collection = db.collection('games');
return new Promise(function (resolve, reject) {
collection.find({ authorID }, { type: '_id' }).toArray((err, doc) => {
if (err) {
reject(err);
} else {
console.log('getAlldata : ', doc);
resolve(doc);
}
});
});
}
After I use this function, I got all the data from MongoDB.
As you see, I used the same syntax. But I got all the data.
Is there anybody who has a good idea??
You need to use projection when using MongoDb Node.js client.
export async function getCreaterPubGameId(authorID: string) {
await client.connect();
console.log('Connected successfully to server : update, find or insertData');
const db = client.db(dbName);
const collection = db.collection('games');
return new Promise(function (resolve, reject) {
collection.find({ authorID }, { "type": 1}).toArray((err, doc) => {
if (err) {
reject(err);
} else {
console.log('getAlldata : ', doc);
resolve(doc);
}
});
});
}
When you pass 1 to any field, it will be added to the prjection field and will only show those schema field. By default _id will be included if you want to not include it pass _id: 0.
export async function getCreaterPubGameId(authorID: string) {
await client.connect();
console.log('Connected successfully to server : update, find or insertData');
const db = client.db(dbName);
const collection = db.collection('games');
// I have to use 'projection'
return new Promise(function (resolve, reject) {
collection.find({ authorID }, { projection: { _id: true }).toArray((err, doc) => {
if (err) {
reject(err);
} else {
console.log('getAlldata : ', doc);
resolve(doc);
}
});
});
}
Link : https://mongodb.github.io/node-mongodb-native/3.2/api/Collection.html#find
Related
I am using ExpressJS and MongoDB to create a blog for myself. I have created a mini library with the mongodb module to request the MongoDB database.
Here is the library:
'use strict'
const { MongoClient, ObjectId } = require('mongodb')
const { config } = require('../config')
const USER = encodeURIComponent(config.mongodb.user)
const PASS = encodeURIComponent(config.mongodb.pass)
const NAME = config.mongodb.name
const HOST = config.mongodb.host
const URL = `mongodb+srv://${USER}:${PASS}#${HOST}/${NAME}?retryWrites=true&w=majority`
const OPTIONS = {
useNewUrlParser: true,
useUnifiedTopology: true
}
class MongoLib {
constructor () {
this.client = new MongoClient(URL, OPTIONS)
this.name = NAME
}
connect () {
if (!MongoLib.connection) {
MongoLib.connection = new Promise((resolve, reject) => {
this.client.connect(err => {
if (err) reject(err)
console.log('Connected successfully to MongoDB.')
resolve(this.client.db(this.name))
})
})
}
return MongoLib.connection
}
getAll (collection, query) {
return this.connect().then(db => {
return db.collection(collection).find({ query }).toArray()
})
}
get (collection, id) {
return this.connect().then(db => {
return db.collection(collection).findOne({ _id: ObjectId(id) })
})
}
create (collection, data) {
return this.connect().then(db => {
return db.collection(collection).insertOne(data)
}).then(result => result.insertedId)
}
update (collection, id, data) {
return this.connect().then(db => {
return db.collection(collection).updateOne({ _id: ObjectId(id) }, { $set: data }, { upsert: true })
}).then(result => result.upsertedId || id)
}
delete (collection, id) {
return this.connect().then(db => {
return db.collection(collection).deleteOne({ _id: ObjectId(id) })
}).then(() => id)
}
}
module.exports = MongoLib
The database is connecting correctly because I have a seed that injects data into the database using the create method of the library that you just saw.
In the service layer, I create a class with a method called getUser, which will call the getAll method of the MongoDB library, to which we pass a query so that it looks for the user.
'use strict'
const MongoLib = require('../lib/mongo')
const bcrypt = require('bcrypt')
class UsersService {
constructor () {
this.collection = 'users'
this.mongoDB = new MongoLib()
}
async getUser ({ email }) {
// { email } is getted by basic authentication as a "username" to login
// I am receiving this data perfectly
const [user] = await this.mongoDB.getAll(this.collection, { email })
// But the problem start here, the value of user is undefined
return user
}
async createUser ({ user }) {
const { name, email, password } = user
const hashedPassword = await bcrypt.hash(password, 10)
const createUserId = await this.mongoDB.create(this.collection, {
name,
email,
password: hashedPassword
})
return createUserId
}
}
module.exports = UsersService
The problem here is that the user value is undefined. I don't understand why it causes conflict. I'm using async-await to wait for the database request to finish, and the data is in the database correctly.
Does anyone have an idea about this error? If more information needs it, please let me know.
Suspect your query is wrong, you are sending { { email: email } } to mongodb
getAll (collection, query) {
return this.connect().then(db => {
return db.collection(collection).find(query).toArray()
})
}
I'm not capturing the connect function to return in the getAllowedEmails function, when I do console.log in allowedEmails, it returns the emails correctly, but when I assign to the variable emails, it is returning empty. I think it's an async await problem but can't figured out.
static async getAllowedEmails() {
var MongoClient = require("mongodb").MongoClient;
//db url
let emails = [];
await MongoClient.connect(url, async function (err, client) {
const db = client.db("data-admin");
var myPromise = () => {
return new Promise((resolve, reject) => {
db.collection("users")
.find({})
.toArray(function (err, data) {
err ? reject(err) : resolve(data);
});
});
};
var result = await myPromise();
client.close();
let allowedEmails = [];
result.map((email) => allowedEmails.push(email.email));
console.log(allowedEmails)
emails = allowedEmails;
});
console.log(emails)
return emails;
}
Your code has couple of issues, I have fixed few and enhanced it, given below is the basic code, try to test it and if everything works then enhance it as needed:
const MongoClient = require("mongodb").MongoClient;
async function getAllowedEmails() {
let client;
try {
const allowedEmails = [];
client = await MongoClient.connect(url);
const db = client.db("data-admin");
const result = await db.collection("users").find({}).toArray();
result.map((email) => allowedEmails.push(email.email));
console.log(allowedEmails)
client.close();
return allowedEmails;
} catch (error) {
(client) && client.close();
console.log(error)
throw error;
}
}
I'm trying to print list of database names following their corresponding collection names using a nested loop with MongoDB Node.js Driver.
Database A:
Collection a
Collection b
Database B:
Collection f
Collection g
Database C:
Collection h
Collection j
but instead I get this:
Database A:
Database B:
Database C:
Collection a
Collection b
Collection f
Collection g
Collection h
Collection j
What am I doing wrong? Is this because listDatabases() and listCollections() sharing the same MongoClient connection and they can't be executed simultaneously or it is just promise thing?
const mongo = require('mongodb').MongoClient;
const url = 'mongodb://127.0.0.1:27017';
mongo.connect(url, {
useNewUrlParser: true,
useUnifiedTopology: true
}, (err, client) => {
if (err) {
console.log(err)
return;
}
// print db names with their corresponding collections
client.db().admin().listDatabases({ nameOnly: true }, (err, result) => {
if (!err) {
result.databases.forEach((db) => {
console.log('Database: ' + db.name);
client.db(db.name).listCollections().toArray((err, collections) => {
collections.forEach((col) => {
console.log(col.name);
});
});
});
} else {
return console.log(err.message);
}
});
});
This is because client.db(db.name).listCollections() is async operation. To put it very plainly the result.databases.forEach is not going to wait for each listing of collections for the current db that just got printed above. Which is evident from the output.
That is how callbacks with async operations behave. This could be made nicer to look with async/await or promise/then syntaxes. If sticking to callback syntax, simply moving console of db inside would achieve the output.
// print db names with their corresponding collections
client
.db("test")
.admin()
.listDatabases({ nameOnly: true }, (err, results) => {
if (err) {
return console.error("Error::", err.message);
}
results.databases.forEach((item, index) => {
client
.db(item.name)
.listCollections()
.toArray((err, collections) => {
if (err) {
return console.error("Error::", err.message);
}
console.info("DATABASE: ", item.name);
collections.forEach(col => {
console.info("COLLECTION: ", col.name);
});
});
});
});
UPDATE: With async/await syntax
const MongoClient = require("mongodb").MongoClient;
const url = "mongodb://localhost:27017";
const client = new MongoClient(url, {
useNewUrlParser: true,
useUnifiedTopology: true
});
const defaultDb = "test";
(async function() {
try {
await client.connect();
//fetching dbs
const dbs = await client
.db(defaultDb)
.admin()
.listDatabases({ nameOnly: true });
for (db of dbs.databases) {
console.info("Database::", db.name);
const collections = await client
.db(db.name)
.listCollections()
.toArray();
for (c of collections) {
console.info("Collection::", c.name);
}
}
client.close();
} catch (err) {
console.log(err.stack);
}
})();
Notice the for..of syntax with await because forEach doesn't behave the way intended Why?. And a plain for(let i=0 ... i++) would also work just fine with await.
try to convert it to promise and use the map to async await the results
thus https://flaviocopes.com/javascript-async-await-array-map/ may be helpfull
I have searched for the solution of the error specified in title.
MongoError: server instance pool was destroyed
I believe it is because misplacement of db.close(). But I am nesting dbo.collection and unable to get the exact solution of this error.
Firstly, I am fetching data (array of ids having status 0) from database and then I am concatenating (each app-id) them one by one with URL to get desired appUrl which will be used for crawling data one by one and then crawled data is meant to be stored into another collection of mongoDB. This process will repeat for each id in the array. But my code is having error of "server instance pool gets destroyed" before storing data into collection. I am doing misplacement of db.close() but I am unable to resolve this. Please help me resolving this error
Here is my code
///* global sitehead */
const request = require('request');
const cheerio = require('cheerio');
//const response = require('response');
const fs = require('fs');
const express = require('express');
const app = express();
var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/";
var dateTime = require('node-datetime');
MongoClient.connect(url, {useNewUrlParser: true}, function (err, db) {
if (err) {
throw err;
} else {
var dbo = db.db("WebCrawler");
var app_id;
var appUrl;
let arr = [];
dbo.collection("Unique_Apps").find({"Post_Status": 0}, {projection: {_id: 0, App_Id: 1}}).toArray(function (err, result)
{
// console.log(result);
if (err) {
throw err;
// console.log(err);
} else {
for (var i = 0; i < result.length; i++)
{
arr[i] = result[i];
}
arr.forEach((el) => {
app_id = el.App_Id;
//console.log(app_id);
appUrl = 'https://play.google.com/store/apps/details?id=' + app_id;
console.log(appUrl);
request(appUrl, function (error, response, html) {
if (!error && response.statusCode === 200) {
//START Crawling ###########
const $ = cheerio.load(html); //cheerio
const appTitle = $('.AHFaub');
const iconUrl = $('.T75of.sHb2Xb').attr("src");
const developedBy = $('.T32cc.UAO9ie').children().eq(0);
const category = $('.T32cc.UAO9ie').children().eq(1);
//store in database collection: "Single_App_Data_Post"
var curr_obj = {App_Id: app_id, App_Name: appTitle.text(),
Icon_Url: iconUrl, Price: "Free", Developed_By: developedBy.text(),
Category: category.text()
};
dbo.collection("Single_App_Data_Post").insertOne(curr_obj, function (err, res) {
console.log("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
if (err) {
throw err;
// console.log(err);
} else {
console.log("inserted....");
} //main else
});
dbo.collection("Unique_Apps").updateOne({App_Id: app_id}, {$set: {Post_Status: 0}}, function (err, res) {
if (err)
throw err;
console.log("1 document updated");
//dbo.close();
});
} else
{
throw error;
}
});
});
}
db.close();
});
} //else
}); //mongoClient connect db
Output
The following is a good start about how to turn callback into promises. Try to use it, execute the code block, by block, understand it and then add your updateOne/insertOne requests into it.
const request = require('request');
const cheerio = require('cheerio');
const fs = require('fs');
const express = require('express');
const app = express();
const MongoClient = require('mongodb').MongoClient;
const dateTime = require('node-datetime');
// Class used to handle the database basic interractions
class DB {
constructor() {
this.db = false;
this.url = "mongodb://localhost:27017/";
}
// Do connect to the database
connect() {
return new Promise((resolve, reject) => {
MongoClient.connect(this.url, {
useNewUrlParser: true,
}, (err, db) => {
if (err) {
console.log('error mongodb connect');
return reject(err);
}
this.db = db;
return resolve(db);
});
});
}
disconnect() {
db.close();
this.db = false;
}
getCollection(name) {
return this.db.db(name);
}
}
// Get the data from the database
function getAppsIds(dbObj) {
return new Promise((resolve, reject) => {
const dbo = dbObj.getCollection('WebCrawler');
dbo.collection('Unique_Apps').find({
'Post_Status': 0,
}, {
projection: {
_id: 0,
App_Id: 1,
}
}).toArray(function(err, result) {
if (err) {
return reject(err);
}
return resolve(result);
});
});
}
function requestPlayStore(idApp) {
return new Promise((resolve, reject) => {
const appUrl = `https://play.google.com/store/apps/details?id=${app_id}`;
request(appUrl, function(error, response, html) {
if (error || response.statusCode !== 200) {
return reject(error);
}
return resolve({
response,
html,
});
});
});
}
// Do treat one id app at a time
function treatOneIdApp(dbObj, idApp) {
return requestPlayStore(idApp)
.then(({
response,
html,
}) => {
// Perform your requests here updateOne and insertOne ...
});
}
const dbObj = new DB();
dbObj.connect()
.then(() => getAppsIds(dbObj))
.then(rets => Promise.all(rets.map(x => treatOneIdApp(dbObj, x.App_Id))))
.then(() => dbObj.disconnect())
.catch((err) => {
console.log(err);
});
I do have a shell script that invokes
mongo --eval "db.copyDatabase('somedatabase', 'somedatabase_duplicate', 'sourcehost')"
to copy a database.
Currently I am stuck with doing the same from within a Node.JS application. Calling
mongoCommand = `db.copyDatabase("somedatabase", "somedatabase_duplicate", "localhost")`;
db.command(mongoCommand, function(commandErr, data) {
if(!commandErr) {
log.info(data);
} else {
log.error(commandErr.errmsg);
}
});
Always resulsts in a "no such command" error message.
Edit for clarification: Using db.admin().command() results in the same problem and using the command suggested in enter link description here, too.
What's the correct way to call this command or, alternatively, to clone a database from Node.JS?
Well, you are trying to copy database which is administration operation so have to do with admin account. Again, to copy database command is copydb.
try running this command in shell, db.copyDatabase and you'll see source of command.
try:
var assert = require('assert');
var MongoClient = require('mongodb').MongoClient;
var url = 'mongodb://localhost:27017/test';
MongoClient.connect(url, function(err, db) {
if (err) {
console.log(err);
}
else {
var mongoCommand = { copydb: 1, fromhost: "localhost", fromdb: "test", todb: "test_dup" };
var admin = db.admin();
admin.command(mongoCommand, function(commandErr, data) {
if (!commandErr) {
console.log(data);
} else {
console.log(commandErr.errmsg);
}
db.close();
});
}
});
//core modules
const assert = require('assert')
const MongoClient = require('mongodb').MongoClient;
const moment = require('moment');
const mongo = require('mongodb')
//custom modules
let { ip, port, database } = require('./dbUpgradeConfig')
const url = `mongodb://${ip}:${port}`
let todayDate = moment().format('DD/MM/YYYY HH:mm')
console.log(todayDate)
const myDate = new Date()
console.log(myDate)
var d = Date(Date.now());
// Converting the number of millisecond in date string
a = d.toString()
// Options for mongoDB
const mongoOptions = { useNewUrlParser: true }
let db
//TODO: handle reconnect
let connect = () => {
return new Promise((resolve, reject) => {
if (db) resolve()
else {
mongo.connect(url, mongoOptions, (err, client) => {
if (err) reject(err)
else {
db = client.db(database)
resolve()
}
})
}
})
}
/**
* #description create duplicate database from current database in mongodb
*/
let CloneDb = () => {
return new Promise((resolve, reject) => {
connect()
.then(() => {
console.log(db)
let mongoCommand = { copydb: 1, fromhost: "localhost", fromdb: "db_name", todb: "db_name_duplicate" }
let adminDB = db.admin()
adminDB.command(mongoCommand, function (commandErr, data) {
if (!commandErr) {
console.log(data)
} else {
console.log(commandErr.errmsg)
}
});
})
})
}
CloneDb().then(data => {
// debugger;
console.log("The clone db", data)
})