i'm using mongoose-data-seed to seed data into mongodb, however it has no mechanism to allow passing of ObjectId() as references to other seed files
I found a way to store the output of each of the seeders in a json file and retrieve the ObjectIds from the previous seeds to use in the current seeder. This way i can reference ObjectIds from previous seeders.
seeding-helper.js
const fs = require('fs');
// const path = require('path');
const seedersTmpDataFolder = 'seeders/bin';
class SeedingHelper {
static saveData(filename, data) {
return new Promise((resolve) => {
fs.writeFile(`${seedersTmpDataFolder}/${filename}.json`, JSON.stringify(data, null, '\t'), (err) => {
if (err) throw err;
resolve();
});
});
}
static readData(filename) {
return new Promise((resolve) => {
fs.readFile(`${seedersTmpDataFolder}/${filename}.json`, 'utf8', (err, data) => {
if (err) throw err;
resolve(JSON.parse(data));
});
});
}
}
module.exports = SeedingHelper;
resourceActions.seeder.js
const { Seeder } = require('mongoose-data-seed');
const mongoose = require('mongoose');
const ResourceAction = require('../models/resourceAction');
const SeedingHelper = require('../helpers/seeding-helper');
const { Types: { ObjectId } } = mongoose;
const data = [
{
_id: ObjectId(),
name: 'test1'
},
{
_id: ObjectId(),
name: 'test2'
},
];
class ResourceActionSeeder extends Seeder {
async shouldRun() { // eslint-disable-line class-methods-use-this
return ResourceAction.count().exec().then(count => count === 0);
}
async run() { // eslint-disable-line class-methods-use-this
let result;
await SeedingHelper.saveData('resourceActions', data)
.then(() => {
result = ResourceAction.create(data);
});
return result;
}
}
module.exports = ResourceActionSeeder;
resources.seeder.js
const { Seeder } = require('mongoose-data-seed');
const mongoose = require('mongoose');
const Resource = require('../models/resource');
const SeedingHelper = require('../helpers/seeding-helper');
const { Types: { ObjectId } } = mongoose;
class ResourcesSeeder extends Seeder {
async shouldRun() { // eslint-disable-line class-methods-use-this
return Resource.count().exec().then(count => count === 0);
}
async run() { // eslint-disable-line class-methods-use-this
let result;
await SeedingHelper.readData('resourceActions')
.then((resourceActionsData) => {
const machinesId = ObjectId();
const actionTest1 = ObjectId(resourceActionsData.find(x => x.name === 'test1')._id);
const actionTest2 = ObjectId(resourceActionsData.find(x => x.name === 'test2')._id);
const data = [
{
_id: machinesId,
name: 'machines',
actions: [
actionTest1,
actionTest2,
],
},
];
result = Resource.create(data);
if (result) SeedingHelper.saveData('resources', data);
});
return result;
}
}
module.exports = ResourcesSeeder;
Related
I have a problem where MongoDB says that my object is not a valid JavaScript Object, Even though it is! This has been staying for days!
Basically, this is an account system that uses MongoDB's client, and the ObjectId for the ID.
I want to be able to fix the MongoError that says object (sent to updateOne, not filter) is not a valid JavaScript object.
Here is the code:
const { MongoClient, ObjectId } = require("mongodb");
const fs = require("node:fs");
const uri = "mongodb://127.0.0.1:27017";
if (!fs.existsSync("./db")) {fs.mkdirSync("./db")};
const client = new MongoClient(uri,{ useUnifiedTopology: true });
async function conn() {
await client.connect();
}
conn();
const database = client.db("login");
const accs = database.collection("accounts");
const myfil = {
_id: new ObjectId('63b6441832087ccc7e3edea2')
};
const users = accs.findOne(myfil);
const path = require("node:path");
const bcrypt = require('bcrypt');
const env = process.env;
var saltRounds = 10;
const AddSet = class AddSet {
constructor(user,pass) {
console.log(pass);
this.set = {[user]:pass};
this.set = Object.keys(this.set).reduce((acc, key) => {
acc[key.toString()] = this.set[key];
return acc;
}, {});
console.log(this.set);
return this.set;
}
}
const Account = class Account {
constructor(user,password) {
conn();
if (!users[user]) {
conn();
accs.updateOne(myfil,bcrypt.hash(password, saltRounds, function(err, hash)
{
try {
var a = ""+user;
return new AddSet(a.toString(),hash);
} catch(err) {
console.error("bcrypt",err);
}
}));
this.assetDir = path.join(path.join(env.SAVED_FOLDER,"/"+this.user),"/assets");
this.metaDir = this.assetDir + '/meta';
this.starterDir = path.join(path.join(env.SAVED_FOLDER,"/"+this.user),"/starters");
this.videoDir = path.join(path.join(env.SAVED_FOLDER,"/"+this.user),"/videos");
var fs = require('fs');
if (!fs.existsSync(this.assetDir)) fs.mkdirSync(this.assetDir, { recursive: true });
if (!fs.existsSync(this.starterDir)) fs.mkdirSync(this.assetDir, { recursive: true });
if (!fs.existsSync(this.videoDir)) fs.mkdirSync(this.assetDir, { recursive: true });
}
}
getAssetDir() {
return this.assetDir;
}
getStarterDir() {
return this.starterDir;
}
getVideoDir() {
return this.videoDir;
}
getMetaDir() {
return this.metaDir;
}
checkSession(pswd) {
conn();
bcrypt.compare(pswd, users[this.user], function(err, result) {
if (result) return true;
else return false;
});
}
}
module.exports = { Account, users };
I tried fixing it, making the keys strings, removing the $set, and it did not work.
I am trying to update a content in Mongodb with the help of function UpdateOne but I keep getting "0 document(s) matched the filter, updated 0 document(s)". I am not sure what I am doing wrong here.
In the end you can see the attached picture from postman where I am sending the request
doucumnets.js
const database = require('../db/database.js');
const ObjectId = require('mongodb').ObjectId;
const documents = {
updateDoc: async function updateDoc(id, newContent) {
let db;
try {
db = await database.getDb();
const filter = { _id: ObjectId(id) };
const options = { upsert: false };
// // const query = {_id: "633abe851b36b295c11f74b5"};
// // const update = { $set: { contents: "Updated!!!"}};
const updatedDoc = {
$set: {
"contents": newContent
},
};
// console.log(filter);
// const result = await db.collection.updateOne( id, newContent, options );
const result = await db.collection.updateOne( filter, updatedDoc, options );
console.log(
`${result.matchedCount} document(s) matched the filter, updated ${result.modifiedCount} document(s)`,
);
return result;
} catch (error) {
return {
errors: {
message: error.message
}
};
} finally {
await db.client.close();
// console.log(db.close());
}
},
};
module.exports = documents;
route/index.js
const express = require('express');
const router = express.Router();
const documentsModel = require('../models/documents.js');
router.put("/update", async(req, res) => {
try {
const oldId = req.body.id;
const newContent = req.body.newContent;
const result = await documentsModel.updateDoc(oldId, newContent);
return res.status(201).json({ data: result});
} catch (errors) {
console.error(errors.message);
res.send(400).send('Problem with server');
}
});
module.exports = router;
I'm working on a API using node, where ModelName means the model built using bookshelf.js like this:
const bookshelf = require('./bookshelf')
const Intake = require('./intake')
const ModelName = bookshelf.Model.extend(
{
tableName: 'modelName',
hasTimestamps: true,
uuid: true,
otherModel: function() {
return this.hasOne(OtherModel)
}
},
{
dependents: ['otherModel']
}
)
module.exports = ModelName
and when testing the following funtion:
deleteModelName(Id) {
try {
return ModelName.forge({ id: Id }).destroy()
} catch (err) {
Logger.error('ModelNameService#deleteModelName :: failed to delete ModelName', > err.stack)
throw err
}
}
With this test:
describe('#deleteModelName', () => {
it('should call deleteModelName', async () => {
/*Creating a ModelName*/
const deleteModelName = sandbox.stub()
deleteModelName.resolves(CreatedModelName)
const result = await ModelNameService.deleteModelName(ModelName.id)
expect(result).to.be.undefined }}
I get this error:
TypeError: ModelName.forge(...).destroy is not a function
I am trying to manage the transnational data using mongoose and node.js but while creating session its throwing the following error.
Error:
TypeError: session.startTransaction is not a function
at DemoProjectService.transferBalance (/home/anil/Desktop/subhrajyoti/project/demo1/service/account.service.js:32:21)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:189:7)
The below is my service where I am trying to create the session.
async transferBalance(req,res) {
let conn = null;
try{
conn = await connectMasterDb();
if(_.isNull(conn)) {
return false;
}
let from = req.body.from;
let to = req.body.to;
let amount = req.body.amount;
const customerCollection = loadMongoModel('Account');
const session = conn.startSession();
session.startTransaction();
const opts = { session, new: true };
const A = await customerCollection.findOneAndUpdate({ name: from }, { $inc: { balance: -amount } }, opts);
if (A.balance < 0) {
// If A would have negative balance, fail and abort the transaction
// `session.abortTransaction()` will undo the above `findOneAndUpdate()`
throw new Error('Insufficient funds: ' + (A.balance + amount));
}
const B = await customerCollection.findOneAndUpdate({ name: to }, { $inc: { balance: amount } }, opts);
await session.commitTransaction();
session.endSession();
conn.disconnect();
if (_.isEmpty(A) && _.isEmpty(B)) {
return [];
}else{
return { from: A, to: B };
}
}catch(error) {
console.log(error);
return false;
}
}
I am explaining my mongodb connection code file below.
const Mongoose = require('mongoose').Mongoose,
fs = require('fs'),
{ ObjectID } = require('mongodb');
class DemoProjectMongo {
async _connect() {
this.dbInstance = null;
const mongooseInstance = new Mongoose();
const mongodebug = false;
const url = `mongodb://admin:admin#localhost:27017/practice`;
const options = {
useNewUrlParser: true,
useCreateIndex: true,
connectTimeoutMS: 5000000,
poolSize: 10000,
useUnifiedTopology: true,
// autoIndex: false
};
this.dbInstance = await mongooseInstance.connect(url, options);
mongooseInstance.set('bufferCommands', false);
mongooseInstance.set('useFindAndModify', false);
if(mongodebug === true) {
mongooseInstance.set('debug', true);
}
return this.dbInstance;
}
async connectMasterDb() {
return await this. _connect();
}
collection(collectionName) {
try{
const path_name = '/home/anil/Desktop/subhrajyoti/project/demo1/model';
const model = `${path_name}/${collectionName}.model.js`;
if (fs.existsSync(model)) {
let SchemaModel = require(model);
return this.dbInstance.model(collectionName, SchemaModel);
}
}catch(error) {
console.log(error);
}
}
isObjectID(value) {
let response = value;
if (_.isArray(response)) {
response = _.map(response, res => {
if (ObjectID.isValid(res)) {
return new ObjectID(res);
}
return res;
});
} else if (ObjectID.isValid(response)) {
response = new ObjectID(response);
}
return response;
}
}
const edQartMongoUtil = new DemoProjectMongo();
module.exports = {
loadMongoModel: edQartMongoUtil.collection.bind(edQartMongoUtil),
connectMasterDb: edQartMongoUtil.connectMasterDb.bind(edQartMongoUtil),
isObjectID: edQartMongoUtil.isObjectID.bind(edQartMongoUtil)
}
Here I want to manage some transitional record but getting the above error. Can anybody help me to resolve this error.
I have Card model and I have an API where I'm looking for a document by ID.
app.post("/api/postcomment", async (req,res) => {
const data = req.body
const reqUrl = req.headers.referer
const re = new RegExp('([a-zA-Z0-9]*$)', 'i')
const fixedUrl = reqUrl.match(re)
try {
await Card.update({_id: fixedUrl}, {$push:{'comments': data}})
const card = await Card.findById(fixedUrl)
return res.json(card)
} catch (err) {
throw err
}
})
It works fine. But now I have few more models. All should work the same way to them. But how can I make this code reusable for every model?
Or maybe there is a way to pass a name of my model to API? and then use it like this:
app.post("/api/postcomment", async (req,res, modelName) => {
const data = req.body
const reqUrl = req.headers.referer
const re = new RegExp('([a-zA-Z0-9]*$)', 'i')
const fixedUrl = reqUrl.match(re)
try {
await modelName.update({_id: fixedUrl}, {$push:{'comments': data}})
const item = await modelName.findById(fixedUrl)
return res.json(item )
} catch (err) {
throw err
}
})
Solution1: You can create two helper functions and call the from the router. Both function accept the model object:
let updateDocument = (model, fixedUrl, data) => {
return model.update({ _id: fixedUrl }, { $push: { comments: data }})
}
let getDocument = (model, fixedUrl) => {
return model.findById(fixedUrl)
}
app.post("/api/postcomment", async (req, res, modelName) => {
const data = req.body
const reqUrl = req.headers.referer
const re = new RegExp('([a-zA-Z0-9]*$)', 'i')
const fixedUrl = reqUrl.match(re)
try {
await updateDocument(Card, fixedUrl, data)
const item = await getDocument(Card, fixedUrl)
return res.json(item )
} catch (err) {
throw err
}
})
Solution2: The much better solution is to create a base class (service), with the common functionality. And extend it for each model:
class BaseService {
constructor(model) {
this.model = model;
}
getDocument(data) {
return this.model.findOne(...);
}
updateDocument(data) {
return this.model.update(...);
}
}
class CardService extends BaseService {
constuctor() {
super(Card);
}
}