mongo db transaction with multiple collection not working - mongodb

the following function is supposed to do:
save the project into collection 'project_list'
push the uuid of the new project to a client in the collection 'client_list'
with transaction enabled, if anything goes wrong (such as the client id is given in the new project JSON object does not exist in the collection 'client_list'), then both step1 and step2 will be canceled.
noticed that the code is giving the client_id as 'invalid-uuid' to updateOne which will not add the project uuid to the client because the client_id does not exist in the collection but the new project is saved into collection 'project_list' with or without me doing commitTransaction.
I was reading this https://mongoosejs.com/docs/transactions.html which suggest me to abort the transaction by calling await session.abortTransaction(); but i dont know how to implement it.
thanks in advance.
transactionCreateProject: async function (newProjectJson) {
const mongoConnectConfig = {useNewUrlParser: true, useUnifiedTopology: true};
await mongoose.connect(CONSTANTS.ADMIN_MONGO_URL, mongoConnectConfig)
const db = mongoose.connection;
db.on('error', console.error.bind(console, 'MongoDB connection error:'))
db.once("open", function (callback) {
console.log("Connection Succeeded")
});
let project = mongoose.model('project', mongooseProjectSchema)
let client = mongoose.model('client', mongooseClientSchema)
const session = await mongoose.startSession();
session.startTransaction();
try {
console.log(newProjectJson.clientUUID)
let newProjectMongooseObject = new project()
for (let i = 0; i < Object.keys(newProjectJson).length; i++){
// assigning the attributes of new project to mongoose object
newProjectMongooseObject[Object.keys(newProjectJson)[i]] = newProjectJson[Object.keys(newProjectJson)[i]]
}
newProjectMongooseObject['createdAt'] = dateObject.toISOString();
let saveRes = await newProjectMongooseObject.save()
let addProjectToClientRes = await clientMongooseModel.updateOne({ 'uuid': "invalid-uuid"}, {$push: {'projects': { projectID: newProjectJson.uuid}}})
}
catch (e) {
console.log(e)
return null
}
finally {
console.log("in finally, session ending")
session.endSession();
}
return null
},

Related

Cannot read collection from MongoDB

So, I'm trying to connect a mongo database and it's already connected. But my issue is that I can't read collection from mongo in another file, it says: db.collection is not a function.
So this is my db.js file:
const { MongoClient } = require('mongodb');
let connection_string = 'mongodb+srv://username:password#cluster0.3ctoa.mongodb.net/myFirstDatabase?retryWrites=true&w=majority';
let client = new MongoClient(connection_string, {
useNewUrlParser: true,
useUnifiedTopology: true
});
let db = null
export default () => {
return new Promise((resolve, reject) =>{
if (db && client.isConnected()){
resolve(db)
}
client.connect(err => {
if(err){
reject("Error in connection " + err)
}
else{
console.log("Success")
db = client.db("posts")
resolve(db)
coll = db.collection('posts');
}
});
})
};
The thing is I got successfully connected to a database, but when I try to work with collections it says they are not functions.
This is my second file where I already imported connect from db.js, so here is what I want to do:
app.post('/posts', async (req,res)=>{
let db = connect();
let posts = req.body;
let result = await db.collection('posts').insertOne(posts);
})
Here is the exact error I'm getting:
UnhandledPromiseRejectionWarning: TypeError: db.collection is not a function
at _callee$ (C:\Users\Jan\Desktop\Webshop\posts\src\/index.js:21:33)
at tryCatch (C:\Users\Jan\Desktop\Webshop\posts\node_modules\regenerator-runtime\runtime.js:63:40)
at Generator.invoke [as _invoke] (C:\Users\Jan\Desktop\Webshop\posts\node_modules\regenerator-runtime\runtime.js:294:22)
at Generator.next (C:\Users\Jan\Desktop\Webshop\posts\node_modules\regenerator-runtime\runtime.js:119:21)
at asyncGeneratorStep (C:\Users\Jan\Desktop\Webshop\posts\src\index.js:11:103)
at _next (C:\Users\Jan\Desktop\Webshop\posts\src\index.js:13:194)
at C:\Users\Jan\Desktop\Webshop\posts\src\index.js:13:364
at new Promise (<anonymous>)
at C:\Users\Jan\Desktop\Webshop\posts\src\index.js:13:97
at C:\Users\Jan\Desktop\Webshop\posts\src\/index.js:16:1

unable to use $merge on another remote database

I am trying to move documents based on last update time into another remote db.
Currently trying to use $merge to implement the same.
But new database is created on same local connection and not on remote connection.
LOCAL_DB_NAME.aggregate([
{ $merge: { into: { db: "REMOTE_DB_NAME", coll:"COLLECTION_NAME" } }},
]).toArray();
Connection Initialization code:
const MongoClient = require('mongodb').MongoClient;
const url = 'mongodb://xxx:27017';
const dbName = 'local';
const url2 = 'mongodb://xxx:27017';
const dbName2 = 'remote';
var dbClient,dbClientRemote;
MongoClient.connect(url, async function(err, client) {
if(err){
console.log("->", err)
return
}
console.log("Connected successfully to server");
const db = client.db(dbName);
dbClient = client;
MongoClient.connect(url2, async function(err2, client2) {
if(err2){
console.log("->", err2)
return
}
console.log("Connected successfully to server2");
const db2 = client2.db(dbName2);
dbClientRemote = client2;
});
});
It's not possible. You can move into [different DB] collection only for the same instance (Standalone / Replica set).

Mongoose save() does not work when putting documents in collection and uploading files using multer-gridfs-storage

I am trying to build a music app and while working on the back end for the app (using express), I am facing this weird issue of documents not saving in mongo collections.
I made a post route to which user submits form data, which contains the song's mp3 file and the name of the song (it will have more data later on).
I am using multer to parse multipart form data.
I am able to save the mp3 file to mongoDB using multer-gridfs-storage. I want to save the song info such as name, artists etc in a different collection and here is the schema for the collection:
import mongoose from 'mongoose';
const Schema = mongoose.Schema;
const SongsInfo = new Schema({
name: {
type: String,
required: true,
},
});
const Song = mongoose.model('Song', SongsInfo);
export default Song;
index.js file:
import Grid from 'gridfs-stream';
import GridFsStorage from 'multer-gridfs-storage';
const app = express();
const conn = mongoose.createConnection(mongoURI);
let gfs;
conn.once('open', () => {
console.log('Connected to mongodb');
gfs = Grid(conn.db, mongoose.mongo);
gfs.collection('songFiles');
});
// storage engine
const storage = new GridFsStorage({
url: mongoURI,
file: (req, file) => new Promise((resolve, reject) => {
crypto.randomBytes(16, (err, buf) => {
if (err) {
return reject(err);
}
const filename = buf.toString('hex') +
path.extname(file.originalname);
let fileInfo;
fileInfo = {
filename,
bucketName: 'songFiles',
};
resolve(fileInfo);
});
}),
});
let upload;
middleWare(app);
app.post('/api/uploadSong', async (req, res) => {
upload = multer({ storage }).any();
upload(req, res, async (err) => {
console.log('in');
if (err) {
// console.log(err);
return res.end('Error uploading file.');
}
const { name } = req.body;
// push a Song into songs collection
const songInfo = new Song({
name,
});
const si = await songInfo.save(); // (*)
console.log(songInfo);
res.json({
songInfo: si,
file: req.file,
});
});
});
On line (*) the server just freezes until the request gets timed out.
No errors shown on console. Don't know what to do :(
I solved the issue finally!
So what i did was bring the models in index.js file and changed up some stuff here and there..
index.js
const app = express();
mongoose.connect(mongoURI); //(*)
const conn = mongoose.connection; // (*)
let gfs;
conn.once('open', () => {
console.log('Connected to mongodb');
gfs = Grid(conn.db, mongoose.mongo);
gfs.collection('songFiles');
});
// models
const Schema = mongoose.Schema; //(***)
const SongsInfo = new Schema({
name: {
type: String,
required: true,
},
});
const Song = mongoose.model('Song', SongsInfo);
// storage engine
const storage = new GridFsStorage({
url: mongoURI,
file: (req, file) => new Promise((resolve, reject) => {
crypto.randomBytes(16, (err, buf) => {
if (err) {
return reject(err);
}
const filename = buf.toString('hex') + path.extname(file.originalname);
let fileInfo;
if (file.mimetype === 'image/jpeg' || file.mimetype === 'image/png') {
fileInfo = {
filename,
bucketName: 'imageFiles',
};
} else {
fileInfo = {
filename,
bucketName: 'songFiles',
};
}
resolve(fileInfo);
});
}),
});
let upload;
middleWare(app);
app.post('/api/uploadSong', async (req, res) => {
upload = multer({ storage }).any();
upload(req, res, async (err) => {
console.log('in');
if (err) {
return res.end('Error uploading file.');
}
const { name } = req.body;
// push a Song into songs collection
const songInfo = new Song({
name,
});
songInfo.save((er, song) => {
if (!er) {
res.send('err');
} else {
console.log(err, song);
res.send('err');
}
});
});
});
At line (***) I used the same instance of mongoose that was initialized.. in the previous file I imported it again from the package...
Kia ora!
For those stumbling here three years on. I came across this issue as well. Abhishek Mehandiratta has the answer hidden in their code snippet.
The Fix
I went from instantiating mongoose with:
connect(connStr)
to doing:
const conn = createConnection(connStr)
This is the breaking change. So for an easy fix, change your code back to using connect(...).
I too, followed the documentation and Youtube tutorials to alter my code in such a way. It's an unfortunate misleading for developers who have not encountered a need to understand the difference.
I changed it back, and now it's working again (even with 'multer-gridfs-storage'). You can reference the connection with:
import {connection} from "mongoose";
connection.once('open', ...)
Why is this happening?
BenSower writes up the differences between connect and createConnection here. So from my basic understanding of BenSower's write up, you're referencing the wrong connection pool when you create your 'Song' schema, and thus, referencing the wrong pool when saving. I guess this results in a time out, have not looked further, but I'm sure a more in-depth answer exists somewhere.
import mongoose from 'mongoose';
const Song = mongoose.model('Song', SongsInfo);
As BenSower states in their answer "For me, the big disadvantage of creating multiple connections in the same module, is the fact that you can not directly access your models through mongoose.model, if they were defined "in" different connections". They're spot on with this one. Having finally overcome this obstacle, I shall have a look into createConnection on another project. For now, good ol' connect() will fix this issue, and do just fine :D

Issue Connecting to MongoDB collections

I am using axios and express.js API to connect to my mongo DB. I have a .get() request that works for one collection and doesn't work for any other collection. This currently will connect to the database and can access one of the collections called users. I have another collection setup under the same database called tasks, I have both users and tasks setup the same way and being used the same way in the code. The users can connect to the DB (get, post) and the tasks fails to connect to the collection when calling the get or the post functions. When viewing the .get() API request in the browser it just hangs and never returns anything or finishes the request.
any help would be greatly appreciated!
The project is on GitHub under SCRUM-150.
API connection
MONGO_URI=mongodb://localhost:27017/mydb
Working
methods: {
//load all users from DB, we call this often to make sure the data is up to date
load() {
http
.get("users")
.then(response => {
this.users = response.data.users;
})
.catch(e => {
this.errors.push(e);
});
},
//opens delete dialog
setupDelete(user) {
this.userToDelete = user;
this.deleteDialog = true;
},
//opens edit dialog
setupEdit(user) {
Object.keys(user).forEach(key => {
this.userToEdit[key] = user[key];
});
this.editName = user.name;
this.editDialog = true;
},
//build the alert info for us
//Will emit an alert, followed by a boolean for success, the type of call made, and the name of the
//resource we are working on
alert(success, callName, resource) {
console.log('Page Alerting')
this.$emit('alert', success, callName, resource)
this.load()
}
},
//get those users
mounted() {
this.load();
}
};
Broken
methods: {
//load all tasks from DB, we call this often to make sure the data is up to date
load() {
http
.get("tasks")
.then(response => {
this.tasks = response.data.tasks
})
.catch(e => {
this.errors.push(e);
});
},
//opens delete dialog
setupDelete(tasks) {
this.taskToDelete = tasks;
this.deleteDialog = true;
},
//opens edit dialog
setupEdit(tasks) {
Object.keys(tasks).forEach(key => {
this.taskToEdit[key] = tasks[key];
});
this.editName = tasks.name;
this.editDialog = true;
},
//build the alert info for us
//Will emit an alert, followed by a boolean for success, the type of call made, and the name of the
//resource we are working on
alert(success, callName, resource) {
console.log('Page Alerting')
this.$emit('alert', success, callName, resource)
this.load()
}
},
//get those tasks
mounted() {
this.load();
}
};
Are you setting any access controls in the code?
Also refer to mongoDB's documentation here:
https://docs.mongodb.com/manual/core/collection-level-access-control/
Here is my solution:
In your app.js, have this:
let mongoose = require('mongoose');
mongoose.connect('Your/Database/Url', {
keepAlive : true,
reconnectTries: 2,
useMongoClient: true
});
In your route have this:
let mongoose = require('mongoose');
let db = mongoose.connection;
fetchAndSendDatabase('yourCollectionName', db);
function fetchAndSendDatabase(dbName, db) {
db.collection(dbName).find({}).toArray(function(err, result) {
if( err ) {
console.log("couldn't get database items. " + err);
}
else {
console.log('Database received successfully');
}
});
}

ES6 promise will not work always with mongodb replication set

I did follow How to use MongoDB with promises in Node.js?. The answer 4 by(https://stackoverflow.com/users/5371505/pirateapp), works well with regular mongodb server. But it will not work always with a mongoDB replication set.
const mongodb = require('mongodb');
const MongoClient = mongodb.MongoClient;
// the url talking to replicaSet does not work, while the url with regular mongoDB sever seems working for me.
// const url = 'mongodb://alexlai:alex1765#arch16GMongo01.yushei.me:27017,arch16GMongo02.yushei.me:27017,arch16GMongo03:27017/YuShei?replicaSet=odroid00&connectTimeoutMS=300000';
url = 'mongodb://172.16.1.108/YuShei';
let db = {
open : open,
}
function open(){
return new Promise((resolve, reject)=>{
MongoClient.connect(url, (err, db) => {
if (err) {
reject(err);
} else {
resolve(db);
}
});
});
}
function close(db){
if(db){
db.close();
}
}
// module.exports = db;
// const db = require('./mongoDBServer.js');
const assert = require('assert');
const collectionName= 'yuTsaiLpr20161021'; // a collection contains 500 docs.
// this will hold the final array taht will be sent to browser
// a global variable will be declared with upper camel
let Array = [];
// this will hold database object for latter use
let Database = '';
// global query string and projection
let Query = {};
let Projection = {};
let Collection ={};
let checkoutCarPromise = new Promise((resolve, reject)=>{
Database = null;
db.open() // no ';' semi-column this is a promise, when successful open will be reolved and return with db object, or reject
.then((db)=>{
Database = db; // save it globally
return db.collection(collectionName);
})
.then((collection)=>{
if(collection == 'undefined') reject('collection not found!!');
Collection = collection; //seave it globally
return(collection);
})
.then((collection)=>{
return collection.find(); // return a cursor
})
.then((cursor)=>{
return cursor.toArray();
})
.then((array)=>{
console.log('array[499]: ', array[499]);
Array.push(array[499]);
})
.then(()=>{ // reread to find this car
return Collection.find({plateText:{$regex: /8920/}});
})
.then((cursor)=>{
return cursor.toArray();
})
.then((array)=>{
Array.push(array);
resolve(Array);
})
})
.catch((err)=>{
return(err);
console.error('the checkoutCarPromiserror is: ', err);
})
Promise.all([checkoutCarPromise]).then(results => {
console.log('checkoutCarPromise last resolve value: ', results[0]);
console.log('Array: ', Array);
Database.close();
})
// this will get you more infos about unhandled process
process.on("unhandledRejection", (reason) => {
console.log(reason)
})