In Meteor.js, use multiple MongoInternals.RemoteCollectionDriver with same collection name - mongodb

MongoInternals.RemoteCollectionDriver("mongodb://#{server.ip}:#{server.port}/#{server.dbName}")
If I call multiple remote MongoDB methods and if there are collecitons with the same names, Meteor throws the error something like this, "collectionName/insert is already exist..."
I think Meteor creates each collection's methods internally so that control each collection, but I need to control several MongoDB at a time with some reasons.
How can I avoid this situation?
In addition,
I realize I can use Npm Mongo driver directly like this without any NPM package involving.
var MongoClient = MongoInternals.NpmModules.mongodb.module.MongoClient;
// Connection URL
var url = 'mongodb://localhost:27017/myproject';
// Use connect method to connect to the Server
MongoClient.connect(url, function(err, db) {
console.log("Connected correctly to server");
try {
var collection = db.collection('documents');
collection.find({}).toArray(function(err, docs){
console.log(err);
console.log(docs);
});
}
catch(err) {
console.log(err);
}
db.close();
});
But this still forces me to control each DB with the Node.js callback style.
Is there any idea to avoid this?

I have been checking this issue, and I found a way to do it.
The solutions that I had seen to connect several databases where:
storageServerDriver = new MongoInternals.RemoteCollectionDriver("mongodb://ip:port/dbName")
#Collection = new Mongo.Collection("collection", { _driver: storageServerDriver })
But as you mentioned before, with two collections with same name, an error was thrown (internally Meteor identifies the collections by their name, so it tries to override the structure of the collection already created).
Anyway, to fix this, you can use the following hack:
storageServerDriver = new MongoInternals.RemoteCollectionDriver("mongodb://ip:port/dbName")
#CollectionTwo = storageServerDriver.open('collection')

Related

Mongoose/Express - problems using findById()

I have a database of activities in MongoDB, had all the basic CRUD operations working fine but now at the point in developing the front end of the app where I need to do a GET request for a single activity in the database. I have working PUT and DELETE requests for single activities but for some reason the GET one just isn't playing ball - it's returning an array of objects rather than a single object with that ID.
I'm currently using Postman to make the requests while I iron this problem out. Mongoose version is 5.12.13.
router.get('/:id', async (req, res) => {
try {
const activity = await Activities.findById(req.params.id)
res.json(activity).send()
} catch (error) {
res.send(error.message)
}
})
Then making a request using Postman to http://localhost:5000/api/activities?id=60968e3369052d084cb6abbf (the id here is just one I've copied & pasted from an entry in the database for the purposes of troubleshooting)
I'm really stumped by this because I can't understand why it's not working! The response I get in Postman is an array of objects, like I said, which seems to be the entire contents of the database rather than just one with the queried ID...
Try calling exec on your findById, findById returns a query, you need to call exec to execute your query.
Without the call to the exec function, your 'activity' variable is a mongoose query object.
router.get('/:id', async (req, res) => {
try {
const activity = await Activities.findById(req.params.id).exec();
res.json(activity).send()
} catch (error) {
res.send(error.message)
}
});
Docs for findById
https://mongoosejs.com/docs/api.html#model_Model.findById
Edit:
As righty spotted by Dang, given your code is inspecting req.params the URL you're calling needs updating to:
http://localhost:5000/api/activities/60968e3369052d084cb6abbf

text index required for $text query in the MongoDB with Express

I built a Express back-end which is connected to local MongoDB
when I create a post to find out the specific term, it returns the error like this
MongoError: text index required for $text query
How can I fix this error?
this is my Express code
app.post("/api/search",(req,res)=> {
const term = req.body.searchTerm;
console.log(term); // Here I can check the request coming well
MongoClient.connect(url,(err,db)=> {
if(err) throw err;
const dbo = db.db("Exercise");
dbo.collection("exercise")
.find({$text:{$search:term}})
.toArray((error, result) => {
if (error) {
console.log(error);
return;
} else {
console.log("Success");
res.send(result);
}
});
});
});
Thank you in advance!
What you can do to solve your problem is simple. You can create a "text" on the field that you want search in you collection like this:
db.reviews.createIndex( { comments: "text" } )
Note: assumes "comments" is the field as seen in the docs
However, for the best results you might want to create a dynamic default Search index in MongoDB Atlas. It's free and easy. Because it is a dynamic default, you do not need to do any configuration. The results and performance will likely be better if you are powering a search experience.
I hope this info helps.

Using Mongoose `pre` hook to get document before findOneAndUpdate()

I am trying to use Mongoose pre and post hooks in my MongoDB backend in order to compare the document in its pre and post-saved states, in order to trigger some other events depending on what's changed. So far however I'm having trouble getting the document via the Mongoose pre hook.
According to the docs "pre hooks work for both doc.save() and doc.update(). In both cases this refers to the document itself... ". So I here's what I tried. First in my model/schema I have the following code:
let Schema = mongoose
.Schema(CustomerSchema, {
timestamps: true
})
.pre("findOneAndUpdate", function(next) {
trigger.preSave(next);
})
// other hooks
}
... And then in my triggers file I have the following code:
exports.preSave = function(next) {
console.log("this: ", this);
}
};
But this is what logs to the console:
this: { preSave: [Function], postSave: [AsyncFunction] }
So clearly this didn't work. This didn't log out the document as I was hoping for. Why is this not the document itself here, as the docs themselves appear to indicate? And is there a way I can get a hold of the document with a pre hook? If not, is there another approach people have used to accomplish this?
You can't retrieve the document in the pre hook.
According to the documentation pre is a query middleware and this refers to the query and not the document being updated.
The confusion arises due to the difference in the this context within each of the kinds of middleware functions. During document pre or post middleware, you can use this to access the document model, but not in the other hooks.
There are three kinds of middleware functions, all of which have pre and post stages.
In document middleware functions, this refers to the document (model).
init, validate, save, remove
In query middleware functions, this refers to the query.
count,find,findOne,findOneAndRemove,findOneAndUpdate,update
In aggregate middleware, this refers to the aggregation object.
aggregate
It's explained here https://mongoosejs.com/docs/middleware.html#types-of-middleware
Therefore you can simply access the document during pre("init"), pre("init"), pre("validate"), post("validate"), pre("save"), post("save"), pre("remove"), post("remove"), but not in any of the others.
I've seen examples of people doing more queries within the other middleware hooks, to find the model again, but that sounds pretty dangerous to me.
The short answer seems to be, you need to change the original query to be document oriented, not query or aggregate style. It does seem like an odd limitation.
As per documentation you pre hook cannot get the document in function, but it can get the query as follow
schema.pre('findOneAndUpdate', async function() {
const docToUpdate = await this.model.findOne(this.getQuery());
console.log(docToUpdate); // The document that findOneAndUpdate() will modify
});
If you really want to access document (or id) in query middleware functions
UserSchema.pre<User>(/^(updateOne|save|findOneAndUpdate)/, async function (next) {
const user: any = this
if (!user.password) {
const userID = user._conditions?._id
const foundUser = await user.model.findById(userID)
...
}
If someone needs the function to hash password when user password changes
UserSchema.pre<User>(/^(updateOne|save|findOneAndUpdate)/, async function (next) {
const user: any = this
if (user.password) {
if (user.isModified('password')) {
user.password = await getHashedPassword(user.password)
}
return next()
}
const { password } = user.getUpdate()?.$set
if (password) {
user._update.password = await getHashedPassword(password)
}
next()
})
user.password exists when "save" is the trigger
user.getUpdate() will return props that changes in "update" triggers

Mongoose No matching document found using id() method. Error caused by asynchronous delete requests

Making asynchronous requests in a loop to delete documents from an embedded collection:
_.each deletedItem, (item) ->
item.$delete()
Erratically throws this error:
{ message: 'No matching document found.', name: 'VersionError' }
When executing:
var resume = account.resumes.id(id);
resume.remove();
account.save(function (err, acct) {
console.log(err);
if(err) return next(err);
res.send(resume);
});
After logging account.resumes and looking through the _id's of all of the resumes, the document I am attempting to find by id, exists in the collection.
530e57a7503d421eb8daca65
FIND:
{ title: 'gggff', _id: 530e57a7503d421eb8daca65 }
IN:
[{ title: 'asddas', _id: 530e57a7503d421eb8daca61 }
{ title: 'gggff', _id: 530e57a7503d421eb8daca65 }
{ title: 'ewrs', _id: 530e57a7503d421eb8daca64 }]
I assume this has to do with the fact that I am performing these requests asynchronously, or that there is a versioning issue, but I have no idea how to resolve it.
It doesn't make any sense to me how when I log the resumes, I can see the resume I attempt to find, yet if I log:
log(account.resumes.id(id));
I get undefined.
UPDATE
I've discovered that my issue is with versioning.
http://aaronheckmann.blogspot.com/2012/06/mongoose-v3-part-1-versioning.html
But I am still unsure how to resolve it without disabling versioning, which I don't want to do.
In mongodb version 3, documents now have an increment() method which manually forces incrementation of the document version. This is also used internally whenever an operation on an array potentially alters array element position. These operations are:
$pull $pullAll $pop $set of an entire array
changing the version key
The version key is customizable by passing the versionKey option to the Schema constructor:
new Schema({ .. }, { versionKey: 'myVersionKey' });
Or by setting the option directly:
schema.set('versionKey', 'myVersionKey');
disabling
If you don’t want to use versioning in your schema you can disable it by passing false for the versionKey option.
schema.set('versionKey', false);
MongooseJs API docs explicitly warn on disabling versioning, and recommend against it. Your issue is due to workflow. If you're updating your collection from the UI, sending the API request and not refreshing your object with the object from the backend -- then attempt to update it again, you'll encounter the error you are reporting. I suggest either consuming/updating the object scope from the API response, then __v is correctly incremented. Or don't send the __v field on the PUT API request, this way it won't conflict with version on the collection in the database.
Another option is -- when requesting the object from the backend, have the API response not send the __v field, this way you don't have to code logic to NOT send it from the frontend. On all your gets for that collection, do either one of the following (depends how you write your queries):
var model = require('model');
var qry = model.find();
qry.select('-__v');
qry.exec(function(err,results){
if(err) res.status(500).send(err);
if(results) res.status(200).json(results);
});
OR
var model = require('model');
model.find({}, '-__v', function(err,results){
if(err) res.status(500).send(err);
if(results) res.status(200).json(results);
});

nodejs/mongodb, remove entire collection

I'm trying to remove all items in a collection.
db.collection('sessions', function(err, collection) {
collection.remove();
});
This is the error I get:
node.js:134
throw e; // process.nextTick error, or 'error' event on first tick
^
TypeError: Cannot call method 'getRequestId' of null
at [object Object].executeCommand (/srv/www/www.cidev.com/nodejs/node_modules/mongodb/lib/mongodb/db.js:778:48)
at Collection.remove (/srv/www/www.cidev.com/nodejs/node_modules/mongodb/lib/mongodb/collection.js:199:26)
at /srv/www/www.cidev.com/nodejs/session/memory/index.js:15:20
at [object Object].collection (/srv/www/www.cidev.com/nodejs/node_modules/mongodb/lib/mongodb/db.js:197:12)
at new <anonymous> (/srv/www/www.cidev.com/nodejs/session/memory/index.js:14:11)
at Object.<anonymous> (/srv/www/www.cidev.com/nodejs/session/memory/index.js:157:16)
at Module._compile (module.js:411:26)
at Object..js (module.js:417:10)
at Module.load (module.js:343:31)
at Function._load (module.js:302:12)
However, I can do this via mongodb fine:
db.sessions.remove();
What's the best way to achieve what I want via node?
Thanks
Going back to this... just to update the question.
store.collection('sessions',function(err, collection){
collection.remove({},function(err, removed){
});
});
I know this is a little late to the party and much has changed, but to remove a collection in node, you do this:
db.collection('someCollection').drop();
From the mongo terminal you do this:
db.someCollection.drop();
It's that simple.
Providing a more recent answer, based on API changes and trying the previous answers myself.
The following example represents a block of code as we use it in our integration tests:
const mongoUrl = 'mongodb://localhost:27017/test-database';
const mongoClient = await MongoClient.connect(mongoUrl, {
useNewUrlParser: true,
useUnifiedTopology: true
});
const database = mongoClient.db();
if (database) {
const collections = await database.listCollections();
await collections.forEach(async collection => {
console.log(`Dropping ${collection.name}`);
await database.collection(collection.name).drop();
});
}
Note, while you could use the following, it will fail if you don't have full permissions over the database, such as in Mongo Cloud:
await database.dropDatabase();
For context, I was using mongodb package 3.5.9 and node 14.15.1.
I'm not sure whether you were able to figure this out, but your code did not work for me...perhaps because of changes in the API? Anyway I was able to remove an entire collection's contents using the following code:
db.CollectionName.remove().exec(function(error) {
if(error) {
console.log('Uh oh: ' + error);
}
else {
console.log(' [Existing Collection Deleted]');
}
});