How to add mongoose transaction and create a document? - javascript

I want to add a mongoose transaction in the POST method. When creating the transaction it should be creating a document called stock. Can anybody help me figure out what should I do here? I have a node/express/mongoose app with the following:
GoodsRecivedNote controller
router.post('/', async (req, res) => {
const session = await mongoose.startSession()
try {
const _id = await getNextSequence('goodsReceivedNote')
req.body.id = _id
const goodsReceivedNote = new GoodsReceivedNote(req.body)
const stocks = new Stock(req.body)
await goodsReceivedNote.save()
//use mongoose transaction
//creates a loop(data get from the arry called cart in goodsrecivednote)
for (const item of data) {
//insert stock modal(orderNo, packingId, orderSize, poNumber)
item.create({})
//insert(data, {session})
}
await session.commitTransaction()
res.sendStatus(200)
} catch (error) {
await session.abortTransaction()
return res.sendStatus(500)
} finally {
session.endSession()
}
})
GoodsRecivedNote model
const goodsReceivedNoteSchema = new Schema(
{
id: Number,
poNumber: String,
orderedDate: String,
supplier: String,
orderNo: String,
cart: [
{
packingId: Number,
actualSize: String,
orderSize: String,
brandId: Number,
artWork: String,
receivedQty: Number,
grnDate: String,
},
],
},
)
module.exports = mongoose.model(
'GoodsReceivedNote',
goodsReceivedNoteSchema
)
Stock model
const stockSchema = new Schema(
{
id: Number,
poNumber: Number,
orderNo: String,
packingId: Number,
orderSize: String,
receivedQty: Number,
availableQty: Number,
},
)
module.exports = mongoose.model(
'Stock',
stockSchema
)

Maybe you can try something like this
const session = await mongoose.startSession()
session.startTransaction()
const opts = { session }
const stocks = await new Stock(req.body).save(opts)
await goodsReceivedNote.save(opts)
...the rest of your code
When ever you call Save Update or Delete please add opts as option

Answer by 21bn gets the work done but withTransaction() is way better than startTransaction().
I recommend you use withTransaction instead.
const session = await mongoose.startSession();
await session.withTransaction(async (session) => {
// For create..
collection.create({something:"something"},{session:session});
});
For insertmany, updatemany, the rule is basically the same..
collection.updateMany({find_something:"something"},{name:"Some name"},{session:session});
If you want to find a document using session..
collection.findOne({_id: "some_id"}).session(session));

Related

mongoose pull specific data from single document

So Im using mongoose and mongo atlas. I have a single document with a structure as follows:
{_id: ObjectId("3u3ui4t432) ,
name: cat1,
items :
[
{cat1_item1: "something"},
{cat1_item2: "something"}
]}
{_id: ObjectId("3u3uir3bi2) ,
name: cat2,
items :
[
{cat2_item1: "something"},
{cat2_item2: "something"}
]}
currently this endpoint retrieves the entire document, i am just trying to access one category at a time based of a param either url or body
//this gets the specific document i want, but i would prefer to get that document through something like this.
// findOne({ category: req.body.category })
app.post('/targetCategory', async (req, res) => {
const categoryName = req.body.category
categoryCollection.findOne({ _id: "62b9353730ac42a7d390f5ad" }, (err,
data) => {
if (err) {
console.log(err)
} else {
res.send(data)
console.log(categoryName)
}
})
})
const mongoose = require('mongoose')
const Schema = mongoose.Schema;
const categorySchema = Schema({
categories: {
}
})
module.exports = mongoose.model('categoryCollection', categorySchema)
basically I want to use params(?) to only access one category at a time to minimise data sent to the frontend. How do i go about using something like findOne() with the param being either category1 or category2 as I only want the array inside. I am using mongoose, node and express.
model
const mongoose = require('mongoose')
const Schema = mongoose.Schema;
const categorySchema = Schema({
name: String,
// items placeholder, modify if required
items: String[]
})
const Category = mongoose.model('Category', categorySchema)
module.exports = Category
the controller you can use
app.post('/listOne', async (req, res) => {
try {
const categoryData = await Category.findOne({name: req.body.category})
res.send({category: categoryData})
}
catch (e) {
console.log(e)
}
})
another controller solution I suggest
app.get('/category/:name', async (req, res) => {
try {
const categoryData = await Category.findOne({name: req.params.name})
res.send({category: categoryData})
}
catch (e) {
console.log(e)
}
})
To find single data in collection you can use this function or read this in mongoose docs https://mongoosejs.com/docs/api.html#model_Model.findOne
// Find one adventure whose `country` is 'Croatia', otherwise `null`
await Adventure.findOne({ country: 'Croatia' }).exec();
// using callback
Adventure.findOne({ country: 'Croatia' }, function (err, adventure) {});
// select only the adventures name and length
await Adventure.findOne({ country: 'Croatia' }, 'name length').exec();
and to get parameter in express you can use this
app.get('/users/:userId/books/:bookId', (req, res) => {
res.send(req.params)
})

Mongoose FindOneAndUpdate not Updating when Pushing an Object to a Nested Array

When trying to push to a nested array it's not updating in my database with the following code:
const obj = {
key: key,
user: user,
description: description,
date: Date.now(),
guildId: guildId,
};
const guild = await this.GuildModel.findOneAndUpdate(
{ guildId: guildId },
{ $push: { 'guildData.commandLogs': obj } },
);
My schema:
const GuildSchema = {
guildId: { type: String },
guildData: {
commandLogs: [CommandLogsSchema],
},
Any idea why my database isn't updating?
I think you might have some typos in your model:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const GuildSchema = new Schema({ guildId: { type: String },
guildData: {
commandLogs: [CommandLogsSchema], })
in your controller, the filter condition that you're passing is not clear, which guildId you are using? also when you call the guildData.commandLogs field to update it you are missing brackets
const filter = { guildId: heretheactualId };
const update = { { $push: { commandLogs: obj} }};
const guild = await this.GuildModel.findOneAndUpdate(
filter,
update,
);
this is not going to fix your problem because I don't know what kind of errors you're having or your database model but these tips can already help.

How can I update nested array in Firestore?

Is there a way to update the likes and replies with firebase firestore?
I have reached that moment when I press on like on a single comment but I have only the reference to the document of the whole post , so I don't have a reference to the comments itself. I also reached the comment with the find method of the arrays but it's not a reference its the comment itself.
Thanks in advance, I am thinking I just have to reconsider how i structure my DB using firestore?
const addLikeToComment = async (
postID: string,
commentID: string,
userID: string
): Promise<void> => {
const currentPost = await getPostById(postID)
const currentPostDocId = currentPost[0].docID!
const currentPostRef = doc(db, 'posts', currentPostDocId)
const commentToAddLikeTo = currentPost[0].comments.find(
(currComment) => currComment.commentID === commentID
)
console.log(commentToAddLikeTo)
if (!commentToAddLikeTo?.likes.includes(userID)) {
await updateDoc(currentPostRef, {
// add the like to comments - currentComment - likes array inside it push the userID
})
} else {
await updateDoc(currentPostRef, {
// remove the like
})
}
}
const addLikeToComment = async (
postID: string,
commentID: string,
userID: string
): Promise<void> => {
const currentPost = await getPostById(postID)
const currentPostDocId = currentPost[0].docID!
const currentPostRef = doc(db, 'posts', currentPostDocId)
const commentToAddLikeTo = currentPost[0].comments.find(
(currComment) => currComment.commentID === commentID
)
const newLikes = commentToAddLikeTo?.likes
if (!commentToAddLikeTo?.likes.includes(userID)) {
newLikes?.push(userID)
} else {
const indexOfUserID = newLikes?.indexOf(userID)
newLikes?.splice(indexOfUserID!, 1)
}
await updateDoc(currentPostRef, {
...currentPost[0],
likes: newLikes,
})
}
This is how I solved this with some help from a co-worker. Basically I am manipulating the likes array directly and then updating the whole post information.

Cannot populate fields in express and Mongodb

I have two models Post and Users and I want to populate the posts fields
const PostSchema = new Schema({
text: {
type: String
},
author: {
type: mongoose.Schema.Types.ObjectId,
ref: 'user'
}
})
const UserSchema = new Schema({
username: {
type: String,
}
})
router.post('/', async (req,res) => {
try {
let joe = new User({username: "joe"})
await joe.save()
let postText = {text: "my name is joe", author: joe._id}
let postByJoe = new Post(postText)
await postByJoe.save()
let users = User.find().populate('author')
res.json({users})
} catch (error) {
console.log(error.message)
res.json(error.message)
}
})
The problem is that I get an error and I don't know what to make of it. I'm not sure at all what the issue is. Any help would be greatly appreciated
"Converting circular structure to JSON\n --> starting at object with constructor 'NativeTopology'\n | property 's' -> object with constructor 'Object'\n | property 'sessionPool' -> object with constructor 'ServerSessionPool'\n --- property 'topology' closes the circle"
What you have in your schema is different, you can only populate the Schema.Types.ObjectId field in your schema. Seems you are trying to populate the author field of the Post model.
Replace this line let users = User.find().populate('author') with this let users = Post.find().populate('user') Your code should be like the one below:
router.post('/', async (req,res) => {
try {
let joe = new User({username: "joe"})
await joe.save()
let postText = {text: "my name is joe", author: joe._id}
let postByJoe = new Post(postText)
await postByJoe.save()
let data = await Post.find().populate('user')
// map through the object returned
const users = data.map((user) => user)
res.json({users})
} catch (error) {
console.log(error.message)
res.json(error.message)
}
})
Read more about mongoose population here

How can I loop through this array inside this instance of my mongoose model?

const postSchema = new mongoose.Schema({
post:[
{postId: String},
{commentComponent: [
{comment: [String]},
]}
]
})
const Posts = mongoose.model('Posts', postSchema)
This is the definition of the schema for modeling the mongodb
const postLinks = await getPostLinks();
const posts = new Posts({
for (let i = 0; i < postLinks.length; i++) {
const comment = await getComment(postLinks[i]) // here it takes postLinks as a paramaeter to get an array of comment
post: [
{postId: postLinks[i]},
{commentComponent: [
{comment: comment}
]}
]
}
})
const result = await posts.save()
is there a way of iterating inside this instance because the for loop here is not working
You need to pass an object to the Posts constructor with a property called post (which probably should be called posts, but will keep the original name below), and for this property, you need to specify an array.
This array can be built by using Array.prototype.map and Promise.all:
const post = await Promise.all(
postLinks.map(async (postLink) => {
const comment = await getComment(postLink);
return {
postId: postLink,
commentComponent: [{ comment }],
};
})
);
const posts = new Posts({ post });
const result = await posts.save();
But if you prefer, you can use the traditional for-loop (more similar to what you were trying to do) as well:
const post = [];
for (let i = 0; i < postLinks.length; i++) {
const comment = await getComment(postLinks[i]);
post.push({
postId: postLinks[i]},
commentComponent: [{ comment }]
});
}
const posts = new Posts({ post });
const result = await posts.save();
Based on your code example I am not certain what you are attempting to do. When using a Model and trying to create you can think of as a new singular record. If you are trying to insert many links into a single record I would suggest comma separating them then inserting that into your MongoDB.
But you cannot iterate inside your Posts class like that.
If I were you I would set up my file something like this:
file: models/Post.js:
const mongoose = require('mongoose');
const PostSchema = new mongoose.Schema({
text: {
type: String,
trim: true,
required: [true, 'Please add some text']
},
link: {
type: String,
required: [true, 'Please add link']
},
createdAt: {
type: Date,
default: Date.now
}
});
module.exports = mongoose.model('Post', PostSchema);
Then create a controller js file
file: controllers/posts.js:
const Post = require('../models/Post');
// #desc Add Post
// #route POST /api/v1/posts
// #access Public
exports.addPost = async (req, res, next) => {
try {
// get post data from the request
// mongo returns a promise so await on it
const post = await Post.create(req.body);
return res.status(201).json({
success: true,
data: post
});
} catch (err) {
if(err.name === 'ValidationError') {
const messages = Object.values(err.errors).map(val => val.message);
return res.status(400).json({
success: false,
error: messages
});
} else {
return res.status(500).json({
success: false,
error: 'Server Error'
});
}
}
}
then in your router file, you can use your controller:
routes/post.js
const express = require('express');
const router = express.Router();
const { addPost } = require('../controllers/posts');
router
.route('/')
.post(addPost);
module.exports = router;

Categories

Resources