I want to start taking advantage of Mongooses document versioning (__v key). I was having an issue actually incrementing the version value, then I found that you have to add this.increment() when executing a query.
Is there a way to have automatically incremented? For now, I just added it to the pre middleware for a update-type queries:
module.exports = Mongoose => {
const Schema = Mongoose.Schema
const modelSchema = new Schema( {
name: Schema.Types.String,
description: Schema.Types.String
} )
// Any middleware that needs to be fired off for any/all update-type queries
_.forEach( [ 'save', 'update', 'findOneAndUpdate' ], query => {
// Increment the Mongoose (__v)ersion for any updates
modelSchema.pre( query, function( next ) {
this.increment()
next()
} )
} )
}
Which seems to work.. But I kinda thought there would already be a way to do this within Mongoose.. am I wrong?
I'd say this is the way to go. pre middleware fits exactly this need, and I don't know any other way. In fact this is what I'm doing in all my schemas.
What you need to be aware of though, is the difference between document and query middleware.
Document middleware are executed for init, validate, save and remove operations. There, this refers to the document:
schema.pre('save', function(next) {
this.increment();
return next();
});
Query middleware are executed for count, find, findOne, findOneAndRemove, findOneAndUpdate and update operations. There, this refers to the query object. Updating the version field for such operations would look like this:
schema.pre('update', function( next ) {
this.update({}, { $inc: { __v: 1 } }, next );
});
Source: mongoose documentation.
For me the simplest way to do that is :
clientsController.putClient = async (req, res) => {
const id = req.params.id;
const data = req.body;
data.__v++;
await Clients.findOneAndUpdate({ _id: id }, data)
.then( () =>
{
res.json(Ok);
}
).catch ( err => {
Error.code = '';
Error.error = err;
res.json(Error);
})
};
Related
I am trying to update my database using the get method, the reason behind updating the DB using the get method is because when the get method will be called a value will be increased automatically.
This is my code where I am trying to update the database inside the get method. It's possible by mongoose but I am using raw MongoDB to update it. is it possible to update? or any other way to update it automatically when /:shortUrl will be requested into the server.
app.get('/:shortUrl', async (req, res) => {
const filter = {
short: req.params.shortUrl
}
const updateDoc = {
$inc: {
clicks: 1
}
}
const urlDoc = await bicycleAffiliateLinksCollection.findOneAndUpdate(filter, updateDoc, {
new: false,
upsert: true
})
console.log(urlDoc.value.short)
res.redirect(`${urlDoc.value.full}?ref=${req.params.shortUrl}`)
res.send({
shortLink: urlDoc.value.short
})
})
You can use $inc operator instead. Try refactoring the code as shown below:
app.get('/:shortUrl', async (req, res) => {
const filter = {
short: req.params.shortUrl
}
const updateDoc = {
$inc: {
clicks: 1
}
}
const urlDoc = await bicycleAffiliateLinksCollection.findOneAndUpdate(filter, updateDoc, {
new: true,
upsert: true
})
console.log(urlDoc)
res.send({
shortLink: urlDoc.short
})
})
upsert will create a new document if missing and new will return contents of the new document.
I tried to find the solutions over here but unable to get success while using $pull as the array values I have does not contain `mongo_id'.
So the scenario is that , I am trying to delete the specific comment of the particular user which I am passing through query params. M
My mongo data looks like this:
Now I am making API Delete request like this : http://localhost:8000/api/articles/learn-react/delete-comment?q=1 on my localhost .
ANd finally my code looks like this:
import express from "express";
import bodyParser from "body-parser";
import { MongoClient } from "MongoDB";
const withDB = async (operations, res) => {
try {
const client = await MongoClient.connect(
"mongodb://localhost:27017",
{ useNewUrlParser: true },
{ useUnifiedTopology: true }
);
const db = client.db("my-blog");
await operations(db);
client.close();
} catch (error) {
res.status(500).json({ message: "Error connecting to db", error });
}
};
app.delete("/api/articles/:name/delete-comment", (req, res) => {
const articleName = req.params.name;
const commentIndex = req.query.q;
withDB(async(db) => {
try{
const articleInfo = await db.collection('articles').findOne({name:articleName});
let articleAllComment = articleInfo.comments;
console.log("before =",articleAllComment)
const commentToBeDeleted = articleInfo.comments[commentIndex];
//console.log(commentToBeDeleted)
// articleAllComment.update({
// $pull: { 'comments':{username: commentToBeDeleted.username }}
// });
articleAllComment = articleAllComment.filter( (item) => item != commentToBeDeleted );
await articleAllComment.save();
console.log("after - ",articleAllComment);
//yaha per index chahiye per kaise milega pta nhi?
//articleInfo.comments = gives artcle comment
res.status(200).send(articleAllComment);
}
catch(err)
{
res.status(500).send("Error occurred")
}
},res);
});
I have used the filter function but it is not showing any error in terminal but also getting 500 status at postman.
Unable to figure out the error?
I believe you'll find a good answer here:
https://stackoverflow.com/a/4588909/9951599
Something to consider...
You can use MongoDB's built-in projection methods to simplify your code.
https://docs.mongodb.com/manual/reference/operator/projection/positional/#mongodb-projection-proj.-
By assigning a "unique ID" to each of your comments, you can find/modify the comment quickly using an update command instead of pulling out the comment by order in the array. This is more efficient, and much simpler. Plus, multiple read/writes at once won't interfere with this logic during busy times, ensuring that you're always deleting the right comment.
Solution #1: The recommended way, with atomic operators
Here is how you can let MongoDB pull it for you if you give each of your comments an ID.
await db.collection('articles').updateOne({ name:articleName },
{
$pull:{ "comments.id":commentID }
});
// Or
await db.collection('articles').updateOne({ name:articleName, "comments.id":commentID },
{
$unset:{ "comments.$":0 }
});
Solution #2 - Not recommended
Alternatively, you could remove it by index:
// I'm using "3" here staticly, put the index of your comment there instead.
db.collection('articles').updateOne({ name:articleName }, {
$unset : { "comments.3":0 }
})
I do not know why your filter is erroring, but I would recommend bypassing the filter altogether and try to utilize MongoDB's atomic system for you.
I'm rewriting a project of mine and was wondering how would I post an array of data where I reuse the return value of a previous post request as their ID. Here's a rough detail of the data structure
Checklist A
[ChecklistItem 1, ChecklistItem 2, ChecklistItem 3] has their ID set as Checklist A
So my current setup is I send Checklist A, get the return value from FaunaDB(which is its unique ID)
then plug it in the array using array.map then resend the array to FaunaDB.
But i don't know how to save the array since the request paramater is already used up.
so i was wondering what's the normal way to do this.
here's a code snippet of the function
app.post('/checklists', (req,res) =>{
const checklist = {
dateCreated: Date.now(),
user: Call(Fn('getUser'),'10049'),
equipmentid: 'PM160'
};
const _checklistItems = [{
componentid: 'AIRLK',
conditionid: 'OK',
equipmentid: 'PM160',
remarks: 'test'
}]
const ckdoc = client.query(
Crt('checklists',checklist))
.then((ret) => {
//would like to catch this ret and plug it into _checklistitems as its ID
//then send the _checklistitems to faunaDB
});
res.send(ckdoc);
});
function Crt(collection,data){
return Create(
Collection(collection),
{data}
)
}
UPDATE
after #eskwayrd pointed out that you can chain client queries within a single express js request. i chained another client query where i save the checklist items collection along with the return reference from a previous query. though i had problems sending the it as an Array, saving it through array.map still worked.
app.post('/checklists', async (req,res) =>{
const checklist = {
dateCreated: Date.now(),
user: Call(Fn('getUser'),'10049'),
equipmentid: 'PM160'
};
const _checklistItems = [{
componentid: 'AIRLK',
conditionid: 'OK',
equipmentid: 'PM160',
remarks: 'test'
}]
var _ref;
console.log(checklist)
await client.query(
Crt('checklists',checklist)
)
.then((ret) => {
_ref = ret.ref
})
_checklistItems.map(item => {
item.checklist = _ref
console.log(item)
client.query(
Crt('checklist_items', item)
)
})
});
Using the Fauna JavaScript driver, the client object that you create is quite reusable; it is not "used up".
Generally, you can chain dependent queries like this:
client.query( ... your FQL query ... )
.then((result) => {
const ref = result.ref
client.query( ... another FQL query involving the ref ...)
.then((result2) => {
console.log(result2)
})
})
Using async and await, you can avoid nesting with something like:
;(async () => {
const result = await client.query( ... FQL query 1 ...)
.then((res) => res)
.catch((err) => console.log(`query 1 failed: ${err}`))
const ref = result.ref
const result2 = await client.query( ... FQL query 2 ...)
.then((res) => res)
.catch((err) => console.log(`query 2 failed: ${err}`))
console.log(result2)
})()
Note that both examples are, effectively, equivalent, and also demonstrates how to extract a value from the reponse.
please suggest me how to make a selection from database comparing the ID in the collection with each element of the array?
Here is the code that unfortunately returns an empty array:
index(req, res) {
Room.find({_id: req.user.rooms.forEach((item)=>{
return item;
})
})
.then((rooms) => {
console.log(rooms)
res.send(rooms)
}
)
.catch(err => res.status(400).json(err));
}
req.user.rooms - each item of this array is ID, that I want to compare with what is in the collection Room.
It's pretty straight-forward in their docs for how to query items in a list.
Your code should look something like this:
index(req, res) {
// Additional validation should be done to make sure that req.user.rooms
// is an array with length > 0. I'll leave that for you to do.
const rooms = req.user.rooms;
Room.find({ _id: rooms})
.then((rooms) => {
console.log(rooms)
res.send(rooms)
})
.catch(err => res.status(400).json(err));
}
Going beyond that, you should really not be doing DB queries from your controller; it's not a good architectural practice This is how it could look in your node service
roomController.js
const RoomService = require("/path/to/services/directory"); // Let services contain all business logic. Don't give them anything related to your web server framework
async index(req, res) {
// Additional validation should be done to make sure that req.user.rooms
// is an array with length > 0. I'll leave that for you to do.
try {
const rooms = await RoomService.retrieveById(req.user.rooms);
res.send( { success: true, data: rooms } ); // We send back the result when we get one
} catch ( err ) {
// We respond to the client with our error, ideally you'll log it too
res.status( err.statusCode ).send( { success: false, error: err } );
}
}
RoomService.js
const Room = require("/path/to/your/model");
module.exports = {
retrieveById: async function(ids) {
try {
return await Room.find({ _id: ids}); // Typically this would be abstracted to a DB layer. but putting it here for brevity
} catch( err ) {
throw new Error( err ); // This is caught in our controller, which we send to client
}
}
};
I use NodeJS to insert documents in MongoDB. Using collection.insert I can insert a document into database like in this code:
// ...
collection.insert(objectToInsert, function(err){
if (err) return;
// Object inserted successfully.
var objectId; // = ???
});
// ...
How can I get the _id of inserted object?
Is there any way to get the _id without getting latest object inserted _id?
Supposing that in same time a lot of people access the database, I can't be sure that the latest id is the id of object inserted.
A shorter way than using second parameter for the callback of collection.insert would be using objectToInsert._id that returns the _id (inside of the callback function, supposing it was a successful operation).
The Mongo driver for NodeJS appends the _id field to the original object reference, so it's easy to get the inserted id using the original object:
collection.insert(objectToInsert, function(err){
if (err) return;
// Object inserted successfully.
var objectId = objectToInsert._id; // this will return the id of object inserted
});
There is a second parameter for the callback for collection.insert that will return the doc or docs inserted, which should have _ids.
Try:
collection.insert(objectToInsert, function(err,docsInserted){
console.log(docsInserted);
});
and check the console to see what I mean.
As ktretyak said, to get inserted document's ID best way is to use insertedId property on result object. In my case result._id didn't work so I had to use following:
db.collection("collection-name")
.insertOne(document)
.then(result => {
console.log(result.insertedId);
})
.catch(err => {
// handle error
});
It's the same thing if you use callbacks.
I actually did a console.log() for the second parameter in the callback function for insert. There is actually a lot of information returned apart from the inserted object itself. So the code below explains how you can access it's id.
collection.insert(objToInsert, function (err, result){
if(err)console.log(err);
else {
console.log(result["ops"][0]["_id"]);
// The above statement will output the id of the
// inserted object
}
});
if you want to take "_id" use simpley
result.insertedId.toString()
// toString will convert from hex
Mongo sends the complete document as a callbackobject so you can simply get it from there only.
for example
collection.save(function(err,room){
var newRoomId = room._id;
});
You could use async functions to get _id field automatically without manipulating data object:
async function save() {
const data = {
name: "John"
}
await db.collection('users').insertOne(data)
return data
}
Returns (data object):
{
_id: '5dbff150b407cc129ab571ca',
name: 'John',
}
Now you can use insertOne method and in promise's result.insertedId
#JSideris, sample code for getting insertedId.
db.collection(COLLECTION).insertOne(data, (err, result) => {
if (err)
return err;
else
return result.insertedId;
});
Similar to other responses, you can grab the variable using async await, es6+ features.
const insertData = async (data) => {
const { ops } = await db.collection('collection').insertOne(data)
console.log(ops[0]._id)
}
Another way to do it in async function :
const express = require('express')
const path = require('path')
const db = require(path.join(__dirname, '../database/config')).db;
const router = express.Router()
// Create.R.U.D
router.post('/new-order', async function (req, res, next) {
// security check
if (Object.keys(req.body).length === 0) {
res.status(404).send({
msg: "Error",
code: 404
});
return;
}
try {
// operations
let orderNumber = await db.collection('orders').countDocuments()
let number = orderNumber + 1
let order = {
number: number,
customer: req.body.customer,
products: req.body.products,
totalProducts: req.body.totalProducts,
totalCost: req.body.totalCost,
type: req.body.type,
time: req.body.time,
date: req.body.date,
timeStamp: Date.now(),
}
if (req.body.direction) {
order.direction = req.body.direction
}
if (req.body.specialRequests) {
order.specialRequests = req.body.specialRequests
}
// Here newOrder will store some informations in result of this process.
// You can find the inserted id and some informations there too.
let newOrder = await db.collection('orders').insertOne({...order})
if (newOrder) {
// MARK: Server response
res.status(201).send({
msg: `Order N°${number} created : id[${newOrder.insertedId}]`,
code: 201
});
} else {
// MARK: Server response
res.status(404).send({
msg: `Order N°${number} not created`,
code: 404
});
}
} catch (e) {
print(e)
return
}
})
// C.Read.U.D
// C.R.Update.D
// C.R.U.Delete
module.exports = router;