Query while iterating on array - javascript

I have Announcements and AnnouncementLikes schema. There is one schema for announcements and other one is for if user liked the specific announcement. My plan is sending announcements with like state to the user that made a request.
So on the below I tried to get like while iterating on announcement list.
const announcements = await AnnouncementModel.paginate(
{},
{
page,
limit,
lean: true
}
)
announcements.docs = announcements.docs.map((annonucement) => ({
...annonucement,
like: (async () => {
const result = await AnnouncementModel.findIfUserLiked(
annonucement.id,
req.userId
)
return result
})()
}))
It is inside of AnnouncementSchema I didnt want to copy all of the schema
static async findIfUserLiked(announcementId: string, userId: string) {
const foundAnnouncementLike = await AnnonucementLikeModel.findOne({
announcementId,
userId
})
return !!foundAnnouncementLike
}
I know that I cant get likes from immediatly invoked function because it returns a promise. So I have to stop iterate somehow and get like before sending to user. So i tried this solution but it didnt work as well
const fullOfPromises = announcements.docs.map((announcement) => announcement.like)
Promise.all(fullOfPromises).then(() => {
res.send(announcements)
})
I am new to mongoose and I dont know how to get user likes in the announcement list.
Thanks in advance

Related

Array.map not returning a value in JavaScript

This is an api endpoint in nextJS that returns the respective authors for an array of posts. The posts is an array of objects that contains a key "authorId". The following code does not work.
const users = posts.map(async (post) => await prisma.user.findUnique({ where: { id: post.authorId }}));
So, I tried the old school way. This thing works
const users = []
for (let i = 0; i < posts.length; i++) {
const user = await prisma.user.findUnique({
where: {
id: posts[i].authorId,
},
});
users.push(user);
}
I thought that the arrow syntax or the implicit return is the issue. So, I tried like this:
const getUsers = async (post: Post) => {
const user = await prisma.user.findUnique({
where: {
id: post.authorId,
},
});
console.log(user);
return user;
};
const users = posts.map(getUsers);
The user object is being logged to the console but its not returning the value (the users is an array of empty objects of times the size of the posts array). Can anyone say where I am doing it wrong?
Note that I'm using prisma as an ORM
Your two code fragments are not equivalent.
getUsers is an async function, so it returns a Promise, that resolves with a user. So, in your last example, the users variable contain an array of promises. What you probably want to do is:
const users = await Promise.all(posts.map(getUsers));
To concurrently get all users from the array of promises.

Render page when database contents changes

So suppose I have a page that displays all the user's posts. Right now when the user creates a post, it stores it into the database. But that new post will not appear unless I log out of the app and log back in. How can I make it so that the post will appear without having to logout?
const [Posts,setPosts] = useState([])
useEffect(()=>{
const fetchUserPosts = async () => {
const res = await client.get('/fetch-user-posts')
setPosts(res.data.posts)
}
fetchUserPosts()
},[])
And in the rendering I just took the Posts and map it to display its contents. I know this problem exists somewhere but I don't know what keywords to google. I have seen posts asking to use JQuery but I bet there is a simpler solution? Any pointers are appreciated.
Take the function of fetchUserPosts out of the useEffect like this:
const fetchUserPosts = async () => {
const res = await client.get('/fetch-user-posts')
setPosts(res.data.posts)
}
Now, useEffect will be like this:
useEffect(() => {
fetchUserPosts()
}, [])
Next, wherever you function of create user posts is, call fetchUserPosts() like this:
const addUserPosts = async () => {
// logic of adding new user post
if (success) {
await fetchUserPosts()
}
}

Angular: returning a value from onValue() in firebase realtime database

I would like to return the "User" object.
Got error message:
Variable 'user' is used before being assigned.ts(2454)
I tried to use async / await but I can't assign await to "return user" at the end of the code or user= await snapshot.val() because it is located on onValue() scope.
getLoggedInUser(id: string): User {
const db = getDatabase();
var user: User;
onValue(ref(db, '/users/' + id), (snapshot) => {
user = snapshot.val();
// ...
}, {
onlyOnce: true
});
return user;
}
When you call onValue you get the current value from that path in the database, and then also get all updates to that value. Since your callback may be called multiple times, there is no way to return a single value.
If you want to just get the value once and return it, you'll want to use get instead of onValue. Then you can also use async/await.
async getLoggedInUser(id: string): Promise<User> {
const db = getDatabase();
var user: User;
const snapshot = await get(ref(db, '/users/' + id))
user = snapshot.val();
return user;
}
I am actually having a similar issue, although I try to fetch data with paging logic.
We do have thousands of records and to render them nicely (10 - 25 per page) would be the best option anyhow.
const dbRef = query(ref(database, folder), orderByChild(field), startAt(start), limitToLast(limit))
return onValue(dbRef, (snapshot) => {
const values = Object.values(snapshot.val());
return {data: values, total: values.length, page: page}
})
I can see the values inside the onValue, but it seems not to return the value at all. I'm not sure where to go here, the documentation on that is not completely clear to me (a beginner as a developer).

What's the standard way to post data using a return value from another post data in Node - Express / FaunaDB

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.

How to return value of both of the consecutive async functions in JS?

I've recently started with web development, and am a novice in JavaScript.
I'm currently working on a Blog Project and using Firebase as the backend, and Firestore as the Database.
PROJECT-
using the following project structure
Firestore DB ->
-Posts
-post1
-post2
-post3
...
-authors
-author1
-author2
..
-subscribers
...
I'm using this function to retrieve my posts from Firestore.
app.get('/:id', (req, res) => {
const id = req.params.id;
async function getDocument(id) {
const doc = await db.collection('Posts').doc(id).get();
if (!doc.exists) {
console.log('No such document!');
} else {
return doc.data();
}
}
getDocument(id).then(function (data) {
res.render('post',{ articleInfo:data} );
// that send back an object containing post details
})
Now, from the JSON I get from the above function, I want to use the value of "Author" to get the author's details from the another collection(run another async function),
and then send this data along with the data from previous function(used to get post details) together in res.render()
For example
I make a get request for a post and get back it's details. Inside which get the author's name "{..,"author : mike-ross",..} " .
Now, after .then, I use this value to run another async function get this author's JSON from the Authors collection. and pass the result from both the functions together in res.render() to get diplayed on the page.
You could have one async function that does both calls and returns an object with both results:
async function getDocWithDetails (id) {
const doc = await getDocument(id);
const authorInfo = await getAuthorInfo(doc.author);
return {
doc: doc.data(),
authorInfo: doc.data()
}
}
getDocWithDetails(id).then((data) => {
res.render('post', data) // has data.doc and data.authorInfo
});
Just go for it, same way you did the first half :-)
In general (at least at my work projects), it's good practice to setup multiple getters for each collection or one main getter where the collection is a secondary argument.
Here's the second variant with dynamic variant (but the first one is just as valid). Especially if you're not using typescript or flowtype to typecheck what you're passing, then it's more prone to unexpected errors by passing incorrect param, I'm just lazy to write the similar function in the answer twice.
const getDocumentById = async (id, collection) => {
const doc = await db.collection(collection).doc(id).get()
if (!doc.exists) {
throw Error(`Doc with id "${id}" doesn't exist on collection "${collection}"`)
}
return doc.data()
}
Now that we have our generic getter setup, you just need to create a function that fetches both.
As a precursor, I have taken some creative liberties with the answer, since the question is missing some cruicial info about documents.
At the very least (and if you don't you should, it's good firebase practice) I presume you have a link to the authorId inside Posts document.
Posts
-> post
-> id
-> authorId
-> some_other_fields
Authors
-> author
-> id
-> some_other_fields
With the following structure in mind, your answer will look something like this:
const getAuthorByPostId = async (id) => {
try {
const post = await getDocumentById(id, 'Posts')
const { authorId } = post
const author = await getDocumentById(authorId, 'Authors')
// I'm not sure how your res is structured, but could look something like this
res.render('page', { articleInfo: post, authorInfo: author })
} catch (error) {
console.error(error.message)
}
// in case it was unable to fetch data
return null
}

Categories