Mongoose delete Child Document references when parent gets deleted - javascript

Hi all thanks for looking to my question,
I would like to delete a child referenced in a parent
here is the structure:
const parentSchema: = new Schema({
name: String,
child: { type: mongoose.Schema.Types.ObjectId, ref:'Child' },
})
const childSchema: = new Schema({
name: String,
})
the child is saved to its own child collection and the parent contains its reference.
my approach for this is as follow:
parentSchema.statics.deleteByID = async function (id: string) {
try {
const parent = await this.findOne({ id })
const child = await Child.findOne({_id: parent.child })
const childDel = child && await child.remove()
const parentDel = await parent.remove()
console.log(parent, child, childDel, parentDel)
} catch(err) {
throw new Error(err)
}
}
this works just fine, i wanted to know if this is the best approach.

I don't think if mongoose has this feature built-in.
The best you can do is to create a remove middleware as described here:
By the way, to make your existing code shorter, you can use findByIdAndDelete. It returns the deleted document, so with the following code 2 database hits make the job:
const parentDel = await Parent.findByIdAndDelete(id);
const childDel = await Child.deleteOne({_id: parentDel.child});
console.log(parentDel, childDel);
parentDel will look like this:
{
"_id": "5de0114ad068f335b480925a",
"name": "Parent 1",
"child": "5de01144d068f335b4809259",
"__v": 0
}
And childDel will look like this:
{
"n": 1,
"ok": 1,
"deletedCount": 1
}

I think this is the best approach for my problem, hope it helps anyone.
My problem was in thinking that the pre('remove') hook would be triggering on Class call, but it is called only on instance.
So rather than Parent.deleteOne(), i first find the instance i want to delete with findOne(), and then trigger the pre('remove') with parent.remove(), and delete necessary childs...
Here is an example:
parentSchema.pre<ParentDocument>('remove', async function() {
try {
if (this.child01) {
await Child01.deleteOne({ _id: this.child01 })
}
if (this.child02) {
await Child02.deleteOne({ _id: this.child02 })
}
} catch(err) {
throw new Error(err)
}
})
parentSchema.statics.deleteByID = async function (id: string) {
try {
const parent = await this.findOne({ id })
return !!(parent && await parent.remove())
} catch(err) {
throw new Error(err)
}
}

Related

how to add child collection in parent collection in firebase

So I'm having a problem that I can't think else how can I add another collection in the parent/first collection let say the diagram should something look like this
owner-item:
group-item: [],
single-item: []
Let say in firebase-collection.js it look something like this
import { collection } from 'firebase/firestore'
import { db } from './firebase'
export const owneritemsRef = collection(db,'owner-items')
And then now I'm exporting now the collection by something like this
let uploadUserStorageTask = uploadBytesResumable(list[i],pictures[j].data)
....
},(err) => {
console.log(err)
},() => {
getDownloadURL(uploadUserStorageTask.snapshot.ref)
.then(async (downloadURL) => {
await setDoc(doc(db,`owner-items`,`${currentUser?.email}-${uid}`),{
creator:username,name:name,img:downloadURL,email:currentUser?.email
})
.then( res => {})
.catch(err => {})
})
})
but because I have group-images I want them to set in a group-item collection but I was thinknig await setDoc or something else I should added to make it like a group-item collection in the owner-item parent collection but how can I do it?
I search something related to it and it something like this maybe? LINK...but I want setDoc because I can change my document_id
UPDATE
It is something like this....
LINK
import { doc } from "firebase/firestore";
const messageRef = doc(db, "rooms", "roomA", "messages", "message1");
I'm sorry its about document inside of collection...let say
await setDoc(..., {
group_item: {},
single_item: {},
}
based on my old snippet just add this new thing... Is this the righht way to do it?
or something like subcollection you know what I mean.
UPDATE 2
let say I have owner-items
await setDoc(doc(db,`owner-items`,`${currentUser?.email}-${uid}`),{
group_items:[{
id:1,
img:file-image
}],
single_item:[{
id:1,
img:file-image
}]
})
If I understand correctly your answer and comments, the following, using a batched write, should do the trick.
import { writeBatch, doc } from "firebase/firestore";
const batch = writeBatch(db);
const parentDocRef = doc(db, `owner-items`, `${currentUser?.email}-${uid}`);
batch.set(parentDocRef, {
single_item: [{
id: 1,
img: file - image
}]
});
const group_items = [
{
id: 1,
img: file - image
},
{...}
];
group_items.forEach(elem => {
const groupItemDocRef = doc(db, `owner-items`, `${currentUser?.email}-${uid}`, 'group-items');
batch.set(groupItemDocRef, {
item: elem
});
});
await batch.commit();
Note that a batched write can contain up to 500 operations, so your group_items array shall have maximum 499 elements.

typescript node insert on mongodb database

im a total newbie in js (typescript, mongoDB, node.)
i just found that my code is not behaving as i expected, im getting 6 registers on the mongoDB instead of just one, it should check if the register exists and then update it, i dont know if it is something related to the await / async or i am doing something wrong, thanks in advace, here is my code.
fields.forEach((value) => {
try {
const mongoConnection = new DocumentDbRepository();
let checksIfExists = await mongoConnection.getValue(key, information[uniqueValue]);
if(checksIfExists==null){
let insert = await mongoConnection.insertValue(information);
console.log(insert);
}
if(checksIfExists?.passValue===information.passValue){
console.log('---------update---------');
let sons = Object.values(information.ticketToRide);
information.ticketToRide = sons;
let update = await mongoConnection.updateRegister(information, checksIfExists._id);
console.log(update);
} else {
console.log('---------insert---------');
let sons = Object.values(information.ticketToRide);
information = sons;
let insert = await mongoConnection.insertValue(information);
console.log(insert);
}
} catch (error) {
console.log(error)
}
}
async getValue(uniqueValue: any, keyValue:any) {
if (this._connection == null) {
await this.connect();
}
const db = this._connection.db(DocumentDbRepository.DbName);
const ticketToRide = db.collection("ticketToRide");
const query = {};
query[uniqueValue] = ''+keyValue+'';
const passInfo = await ticketToRide.findOne(query);
return passInfo;
}
async insertValue(information: any) {
if (this._connection == null) {
await this.connect();
}
const db = this._connection.db(DocumentDbRepository.DbName);
const ticketToRide = db.collection("ticketToRide");
let check = await ticketToRide.insertOne(
information
)
return check;
}
First, you don't need to create a connection inside the loop.
Second, mongodb has an update() or updateMany() method that has a special option { upsert: true }. If it is passed, insert will happen automatically.
Usage example:
Person.update( { name: 'Ted' }, { name: 'Ted', age : 50 }, { upsert: true })

Issue with checking and adding document item to collection in MongoDB

I'm new in Nodejs and I'm trying to create Video with hashtag. There are hashtags already storaged in DB, and hashtag that user will create (which will be added when submit video).
For example, I add more than 2 hashtags, the code below works for 2 cases:
If there is no hashtag storaged in DB, it created and add all hashtags to video successfully
If hashtags is already in DB, it added video successfully
But when there are some hashtags already in DB and the other is not. It add only few hashtags to video, not all hashtags added. I don't know why. I want to fix this case.
I have 2 schemas like this:
// Video schema
const videoSchema = new mongoose.Schema({
url: {
type: String
},
hashtag: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'Hashtag'
}
})
and
// Hashtag schema
const hashtagSchema = new mongoose.Schema({
name: {
type: String,
required: true,
trim: true,
}
})
User will POST like this on server. On this example, Tag-In-DB-Already is in DB already and New-Tag is typed by user
{
"url": "https://youtube.com/JvDAQD4",
"hashtag": ["Tag-In-DB-Already", "New-Tag"]
}
videoObj like this
const videoObj = new Video({
url: data.URL,
hashtag: []
})
The checking code like this, I need to push ObjectId (of hashtag) to hashtagArr above. I'm checking for each hashtag, if hashtag not in DB, it will add to DB and then push to array. If hashtag is in DB, it also added to array. I want all hashtags that user submited will be added.
export const addHashtagToVideo = async (hashtagArr, videoObj) => {
await hashtagArr.forEach(hashtagName => { // for each hashtag check
Hashtag.findOne({ name: hashtagName.toLowerCase() }, (err, resp) => {
if (err) return
if (!resp) { // if there is no hashtag in DB
addNewHashtagToDB(hashtagName) // run add new hashtag function
.then(hashtagId => { // newHashtag._id returned from below function
videoObj.hashtag.push({ _id: hashtagId })
})
} else {
videoObj.hashtag.push({ _id: resp._id }) // if found in DB, also pushed to video
}
})
})
}
export const addNewHashtagToDB = async (hashtagName) => {
const newHashtag = await new Hashtag({
name: hashtagName.toLowerCase(),
})
newHashtag.save()
return newHashtag._id
}
Thank you for help
you need to know what is function return...
export const addHashtagToVideo = async (hashtagArr, videoObj) => {
const waitAllDone = hashtagArr.map(async tag => { // tag in hashtagArr
const doc = await addNewHashtagToDB(tag) // find it or create it
return doc.id
})
const ary = await Promise.all(waitAllDone) // [id1, id2]
videoObj.hashtag = ary
// return videoObj
}
export const addNewHashtagToDB = async tag => {
const name = tag.toLowerCase() // whatever tag is, fix it
let doc = await Hashtag.findOne({ name }).exec() // try to find it
if (!doc) {
// not exist
doc = new Hashtag({ name }) // create new doc
await doc.save()
}
return doc
}
another version
export const addHashtagToVideo = async (hashtagArr, videoObj) => {
const waitAllDone = hashtagArr.map(addNewHashtagToDB) // amazing
const ary = await Promise.all(waitAllDone) // [id1, id2]
videoObj.hashtag = ary // replace it to prevent duplicat
// return videoObj
}
export const addNewHashtagToDB = async tag => {
const name = tag.toLowerCase() // whatever tag is, fix it
let doc = await Hashtag.findOne({ name }).exec() // try to find it
if (!doc) {
// not exist
doc = new Hashtag({ name }) // create new doc
await doc.save()
}
return doc.id // return it here
}

mongodb Async-await Post method

how do I post to referenced schemas in mongodb while using async-await. i was able to create the get function but i am having a hard time creating the post and the put.
here is my get function :
I think, in your request body you should only pass issue id and user id. So when you get the task with your get task details API, mongoose will prepopulate the data.
Your request body should look like
{
issue: "5ca2b1f80c2e9a13fcd5b913",
user: "5ca2b1f80c2e9a13fcd5b90b",
record: {
votary: 80,
development: 90,
test: 100
},
date: "2019-03-01T15:00:00.000Z"
};
And then save the task details as
try {
const task = new TaskModel(req.body);
const result= await task.save()
return api.responseJSON(res, 200, result);
} catch (e)
{
// Error
}
Just wrap the code inside of post in a try/catch
export const post: Operation = async (req: express.Request, res: express.Response) => {
try {
const param: any = {};
const task = new TaskModel(req.body);
const newTask = await task.save()
return api.responseJSON(res, 200, newTask);
} catch(err) {
// treat error
}
}
You should not save the complete req.body instead save only those fields which your schema accepts. And according to Task schema issue and user fields should store id but not the complete object which is there in req.body. Please try this and update your post method accordingly:
export const post: Operation = async (req: express.Request, res: express.Response) => {
try {
let param: any = {};
const user = {
id: req.body.user.id
};
const issue = {
id: req.body.issue.id
};
param = req.body;
param.user = user.id
param.issue = issue.id
const task = new TaskModel(param);
const newTask = await task.save()
return api.responseJSON(res, 200, newTask);
} catch (e) {
api.responseJSON(res, 400, e)
}
};

mongodb nodejs driver updateOne doesn't set a new value

I am trying to update a mongodb user document. It is as below
{
"_id":"123",
"email":"sam#example.com"
}
I want to add one field 'name' to this document.
My code is as below
async function test() {
const user = {"_id":"123", "email" : "sam#example.com" };
async function setUsername(user, update) {
await userCollection.updateOne(user, update);
}
await setUsername(user, { $set: { name: "sam"} });
}
test();
However, when I see in the db, I am not able to see the field set in the document.
I am sure I am missing someway how the node driver is implemented, but I am not sure of the issue.
I have even tried using upsert: true option which gave me an error as the document was already existing.
I guess I had given the function name wrong and I didn't create the document before.
posting the final snippet that works:
const { MongoClient } = require("mongodb");
async function test() {
const mclient = await MongoClient.connect("mongodb://localhost:27017/?w=1", {
useNewUrlParser: true
});
const db = mclient.db("test");
const userCollection = db.collection("user");
const user = { _id: "123", email: "sam#example.com" };
function setUsername(user, update) {
return userCollection.updateOne(user, update);
}
await setUsername(user, { $set: { name: "sam" } });
}
(async () => {
test();
})();

Categories