How can I insert an object graph by using MikroORM? - javascript

I'm trying to create and update multiple entities (models) at once. I did this in objection ORM by using insertGraph API which actually inserts entity if it has no id and updates if it has id.
Is there a similar API in MikroORM?
Currently I'm doing this:
app.put('/articles', async (req, res) => {
const save = req.body.articles.map(async (dto) => {
const article = Object.assign(new Article(), dto)
await req.em.persistAndFlush(article)
})
await Promise.all(save)
res.send({ ok: true })
})
but it generates multiple transactions and I want to everything in single transaction.

The problem here is that when using persistAndFlush method, you immediately persist the entity to database by awaiting the promise. Instead you can call em.persistLater(article) to mark it for persisting. Then call em.flush() afterwards, which will commit all changes to database inside single transaction.
app.put('/articles', async (req, res) => {
req.body.articles.forEach(dto => {
const article = Object.assign(new Article(), dto)
req.em.persistLater(article)
})
await req.em.flush() // save everything to database inside single transaction
res.send({ ok: true })
})
You can make it even simpler by preparing all entities into one array, and persistAndFlush that instead:
app.put('/articles', async (req, res) => {
const articles = req.body.articles.map(dto => Object.assign(new Article(), dto))
await req.em.persistAndFlush(articles) // save everything to database inside single transaction
res.send({ ok: true })
})
Also, instead of using Object.assign(), you can use IEntity.assign() method on the entity, which will also take care of creating references from plain identifiers:
const article = new Article().assign(dto)
More about IEntity.assign() can be found in the docs:
https://b4nan.github.io/mikro-orm/entity-helper/
You could also use EntityManager.create() helper, which will construct the entity for you - the benefit of this is that it will automatically handle constructor parameters, passing them to constructor instead of assigning them directly.
const article = req.em.create(Article, dto)

Related

How can I remove bookmarked posts of user (1) from user (2) tab after user (1) deletes his account?

After creating a node.js, express, mongoDb REST api for a social media web app with almost all basic social media actions (login, signup, add a post, delete a post, delete account, follow users ...),
I'm currently facing a problem, where after implementing bookmarking a post feature, I'm unable to come up with a solution to remove a bookmarked post from another user's bookmarked posts page, after the first user deletes his account. I'll provide my code below:
(P. S. Bookmarks is an array inside User model. I'd also like to mention the steps that I initially intended for the task:
Get current user by ID
Then get all posts created by this user, which returns an array, so I mapped it to get each Posts id
After that I fetched all users accross the app, and initially intended to compare the posts that live inside bookmarks array inside each user to the posts that the current user have created. Then I'd pull these same posts out of the bookmarks array from each of these users.
--> I think the logic that I've analyzed is maintainable, but it's just not working with me. This is the Code below:
export const deleteUser = async (req, res) => {
try {
let user = await User.findById(req.params.userId)
const userPosts = await Post.find({ creatorId: user._id })
const allUsers = await User.find()
const myPostsIds = userPosts.map((post) => post._id.toString())
//This is the section I've implemented for my task, but obviously
something isn't right
await Promise.all(
myPostsIds.forEach((id) =>
allUsers.map((user) => {
user.bookmarks.includes(id) &&
user.updateOne({ $pull: { bookmarks: id } })
})
)
)
await Post.deleteMany({ creatorId: user._id })
await user.remove()
res.status(200).json({
message: "Account has been deleted successfully!",
})
} catch (err) {
errorHandler(res, err)
}
}
As mentioned in my comments, the value you pass to Promise.all is no array of Promise/array of async functions.
The 2nd error is inside the (currently) forEach function at the .map() you are not returning anything in the map-call.
So this should do it:
// first convert all ids to a promise
await Promise.all(myPostsIds.map(id => new Promise(resolve => {
// during this, await every test and update
return Promise.all(allUsers.map(user => new Promise(resolve => {
// if it includes the id, cast the update and then resolve
if (user.bookmarks.includes(id)) {
// if found, resolve the promise for this user after the change
user.updateOne({ $pull: { bookmarks: id } }).then(resolve)
} else {
// resolve directly if not found.
resolve()
}
// when all users are done for this id, resolve the Promise for the given id
}))).then(resolve)
})))
An easier to read and shorter method would be:
for (const id of myPostIds) {
for (const user of allUsers) {
if (user.bookmarks && user.bookmarks.includes(id)) {
await user.updateOne({ $pull: { bookmarks: id } });
}
}
}

need help accessing Firestore sub-collection?

I'm a novice when it comes to coding (started teaching myself ~year ago), any help will be much appreciated, thank you in advance.
I saw that there is 3-4 other post on stack overflow on how to access Firestore's sub-collections.
I tried them all and had no luck, hence why I'm posting this as a new question.
right now I have my data set is up as: collection/document.array. And that was fine till now because I just needed to read that data from the array to draw it out in my little React project with .reduce and .map and push new data to that array on input.
this is the code I have right now for getting data from Firestore:
--- in firebase.js ----------------------------------------------------------------------
export const fire = firebase.initializeApp(config);
export const db = fire.firestore()
_________________________________________________________________________________________
--- in events-context.js ----------------------------------------------------------------
const fetchEvents = async () => {
try {
const data = await db.collection('events').get();
setEvents(data.docs.map(doc => ({ ...doc.data(), id: doc.id })));
} catch ({ message }) {
alert(`Error # fetchEvents, Error:${message}`);
}
};
But now I want to add edit and a remove feature, but in order to do that, I need to carry out my array of data into a sub-collection so each individual element from that array had its own id so that I could later target it. so it needs to be set up something like this: collection/document/sub-collection
To access a document inside a collection, you must know the document ID from another source. This can be done by managing the names inside an array of strings or maps that you can then process within your app per your design.
For example: once created, you will have a snapshot of the reference of which you can store the document id inside the parent document:
db.collection("events").doc(id).update({
payers: firebase.firestore.FieldValue.arrayUnion(paySnapshot.ref.id)
})`
Once you have this information, you can append it to the relevant document path using one of the following techniques.
db.collection("events").doc(id).collection("payers").doc(pay_id).get()
db.doc(\events/${id}/payers/${pay_id}`).get()`
I strongly advise against using .get() on a collection without limit() and where() conditions to reduce the reads that can occur.
Try this, it works for me :)
Insert data >>>
const q = query(collection(this.fire, "events"));
const querySnapshot = await getDocs(q);
const queryData = querySnapshot.docs.map((details) => ({
...details.data(),
id: details.id,
}));
console.log(queryData);
queryData.map(async (v, id) => {
await setDoc(doc(this.fire, `events/${auth}/more`, events.title), {
'title': events.title,
'uid': auth,
//your data here
})
})
Read Data >>>
const querySnapshot = await getDocs(collection(this.fire,
`/events/${auth}/more/`)); querySnapshot.forEach((doc) => { //
doc.data() is never undefined for query doc snapshots
console.log(doc.id, " => ", doc.data()); });
return querySnapshot;

Can't synchronise mongoose operation to return an array

As a part of an employee's app management, I want to separate the business logic database operations from my main application file.
The simplest operation is to read all the employees from the database using async/await to synchronize it:
module.exports.getEmployees = async () => {
const employees = await Employee.find();
return employees;
}
in my app.js I typed the following code:
const employee = require(__dirname + "/models/employee.js");
app.get("/employees", (req, res) => {
const employeeList = employee.getEmployees();
employeeList.then(res.send(employeeList));
})
but still, the array shows up empty?
then clause in promise accepts a functions as an argument & this function has a parameter which holds the actual response.
Something like this -
new Promise().then((response) => {
console.log(response);
});
You are doing employeeList.then(res.send(employeeList)); which means the argument to then clause is res.send() which won't work.
Try this -
employeeList.then((list) => {
// please note the response type here depends on the Content-Type set in the response Header
res.send(list);
// In case of normal http server, try this -
res.send(JSON.stringify(list));
});
I hope this helps.

Why do we add req.user object before using a method?

I am understanding some basic CRUD operations on MongoDB, I am having difficulty in understanding why we use req.user before using a method inside the promise below -
Why can't we use return addToProduct() instead of req.user.AddToProduct()
exports.postCart = (req, res, next) => {
const prodId = req.body.productId;
Product.findById(prodId)
.then(product => {
return req.user.addToCart(product);
})
.then(result => {
console.log(result);
})
Because addToCart is a method of the user object and not a variable in scope for the current module.
(And speculating, you are probably adding to the cart of a specific user so you need to tell the method which user's cart to add to.)

Set on firebase and then set firebase claims

So i working with firebase auth and database in order to set new user to data base, if set successful i want to set claims for that user.
So it means i have a promise within a promise:
function setUser(user){
// no need for the database code before this, but userRef is set properly
return userRef.set(user)
.then(succ => {
return firebase.firebase.auth().setCustomUserClaims(user.key, {admin: true})
.then(() => {
console.log("setting claims")
return true;
});
})
.catch(err => {
return err
})
}
calling function:
app.post("/register_user",jsonParser,async (req, res) => {
var user = req.body.user;
let result = await fireBase.setUser(user);
res.send(result);
})
What happens is that i get the set on the database but claims are not set nor i can i see the log. I know its a js question and not firebase one. I tried many different ways (with await) but non worked.
firebase.firebase does not seem correct. You need to be using the admin object which can be initialised using const admin = require('firebase-admin'); This is not part of the firebase db sdk, but the admin one. You can also use the userRef.uid as that gives you the id of the document of the user, if that is what you want, else use your user.key
return admin.auth().setCustomUserClaims(userRef.uid, {
admin: true
}).then(() => {
//on success
});

Categories

Resources