Going down a chain of documents to retrieve values? - javascript

I have a collection of stories, each collection of stories has a collection of chapters and each collection of chapters have a collection of activities.
I'm trying to figure out an easy way to go through the ocllection of stories, chapters and activities and putting them in an equivalently structured json.
So far my code is tsarting to look a bit hectic and I'm struggling to get it to work right:
var toRet:any = {}
return storiesRef.get()
.then((stories)=>{
stories.docs.forEach((story)=>{
var chaptersRef = story.ref.collection(CHAPTER)
toRet[story.id] = story.data()
return chaptersRef.get()
.then((chapters)=>{
chapters.docs.forEach((chapter)=>{
toRet[story.id][chapter.id] = chapter.data()
var activitiesRef = chapter.ref.collection(ACTIVITY)
return activitiesRef.get()
.then((activities)=>{
activities.docs.forEach(activity => {
toRet[story.id][chapter.id][activity.id] = activity.data()
})
})
})
})
})
})
The code above doesn't work but instead of trying to fix it I feel like I must be missing something as my task should be fairly straightforward interms of getting the values I want.
Is there a way to chain the promises together in a way which makes more sense than a deeply nested loop like this?

Related

Firestore deep query using a cloud function?

I am incredibly new with javascript and I do not entirely understand promises. For simple operations like read or write, I understand that a promise is needed before the code can continue, but I am not entirely sure how to deal with multiple promises at once, specifically after calling .get().
My goal is to query documents quite deep within my Firestore db, and the names of documents in subcollections higher up are not known to me, as they are created by the users. Each user will have places and these places will have guests, and my function intends to search through the guests and select them according to a field value. My code so far is this. Is there a better way?
async function getGuests() {
var results = [];
var users = await db.collection('users').get();
users.forEach(async function(doc) {
var places = await doc.collection('places').get();
places.forEach(async function(doc2) {
var guests = await doc2.collection('guests').where(...).get();
return results.concat(guests);
});
return results;
});
return results;
}
hierarchy looks like:
users/{userID}/places/{place_name}/guests/{guest}
Sounds like you just want to do a collection group query instead. What you're doing right now is massively inefficient if you just want to make a query among all subcollections called "guests".
const querySnapshot = await db.collectionGroup('guests').get();
// iterate queryShapshot to get document contents

Why doesn't strapi's `find()` query return an array?

Context
I have a front end app that requires an array of blog posts from the API, and when you call http://strapi-url/posts/ with a GET request, it returns all the results as objects in an array. Happy days.
Problem
Eventually I want to have more complex GET options with query params, so I need to modify the post controller and write a custom function for find().
When I modify the find() function in api/post/controllers/post.js , and just make it return the result of strapi.query('post').find(), it returns an object with keys rather than an array.
Code
async find(ctx) {
let entity = await.strapi.query('post').find();
return sanitizeEntity(entity, { model: strapi.models.post });
},
I know I could simply convert it into an array in the front end, but feels like a messy solution, and I would rather understand why it doesn't return an array, and what would be the best way to approach a solution.
The code in sanitizeEntity actually does this. You can check it out in the source code(node_modules/strapi-utils/lib/sanitize-entity.js). Also you can see this by dropping the sanitizeEntity row - you will get an array from await.strapi.query('post').find().
You can run the following test (add a custom endpoint) to see the results:
async test2(ctx) {
let entity = await strapi.query('post').find();
ctx.send({
message: 'okay',
posts: entity,
sanitizedPosts: sanitizeEntity(entity, { model: strapi.models.post })
}, 200);
}
You can solve it by making your own custom sanitize function which returns an array OR by processing the result before returning it like so:
let entity = await strapi.query('post').find();
let sanitizedEntity = sanitizeEntity(entity, { model: strapi.models.post });
//process sanitized results to an array
//return the result as array

Using Apollo's writeFragment to update nested list

I am working on a application in which a ship can be configured using rudders and other stuff. The database structure is sort of nested, and so far I have been keeping my GraphQL queries in correspondence with the database.
That means: I could fetch a ship using some query ship(projectId, shipId), but instead I am using a nested query:
query {
project(id:1) {
id
title
ship(id:1) {
id
name
rudders {
id
position
}
}
}
}
Such a structure of course leads to a lot of nested arrays. For example, if I have just added a new rudder, I would have to retrieve using cache.readQuery, which gives me the project object rather than the rudder list. To add the rudder to the cache, I'd get a long line with nested, destructured objects, making the code hard to read.
So I thought of using GraphQL fragments. On the internet, I see them being used a lot to prevent having to re-type several fields on extensive objects (which I personally find very useful as well!). However, there are not so many examples where a fragment is used for an array.
Fragments for arrays could save all the object destructuring when appending some data to an array that is nested in some cached query. Using Apollo's readFragment and writeFragment, I managed to get something working.
The fragment:
export const FRAGMENT_RUDDER_ARRAY = gql`
fragment rudderArray on ShipObject {
rudders {
id
position
}
}
`
Used in the main ship query:
query {
project(id: ...) {
id
title
ship(id: ...) {
id
name
...rudderArray
}
}
}
${RUDDER_FRAGMENT_ARRAY}
Using this, I can write a much clearer update() function to update Apollo's cache after a mutation. See below:
const [ createRudder ] = useMutation(CREATE_RUDDER_MUTATION, {
onError: (error) => { console.log(JSON.stringify(error))},
update(cache, {data: {createRudder}}) {
const {rudders} = cache.readFragment({
id: `ShipObject:${shipId}`,
fragment: FRAGMENT_RUDDER_ARRAY,
fragmentName: 'rudderArray'
});
cache.writeFragment({
id: `ShipObject:${shipId}`,
fragment: FRAGMENT_RUDDER_ARRAY,
fragmentName: 'rudderArray',
data: {rudders: rudders.concat(createRudder.rudder)}
});
}
});
Now what is my question? Well, since I almost never see fragments being used for this end, I find this working well, but I am wondering if there's any drawbacks to this.
On the other hand, I also decided to share this because I could not find any examples. So if this is a good idea, feel free to use the pattern!

Node.js/Bookshelf - Mapping model where multiple columns in a table reference the ID of another table

I am using node.js with bookshelf as an ORM. I am a serious novice with this technology.
I have a situation where I have several columns in a database table. For the sake of this question, these columns shall be named 'sold_by_id', 'signed_off_by_id' and 'lead_developer_id', and are all columns that will reference a User table with an ID.
In other words, different User's in the system would at any point be associated with three different roles, not necessarily uniquely.
Going forward, I would need to be able to retrieve information in such ways as:
let soldByLastName = JobTicket.soldBy.get('last_name');
I've tried searching around and reading the documentation but I'm still very uncertain about how to achieve this. Obviously the below doesn't work and I'm aware that the second parameter is meant for the target table, but it illustrates the concept of what I'm trying to achieve.
// JobTicket.js
soldBy: function() {
return this.belongsTo(User, 'sold_by_id');
},
signedOffBy: function() {
return this.belongsTo(User, 'signed_off_by_id');
},
leadDeveloper: function() {
return this.belongsTo(User, 'lead_developer_id');
}
Obviously I would need a corresponding set of methods in User.js
I'm not sure where to start, can anyone point me in the right direction??
Or am I just a total idiot? ^_^
Your definitions look right. For using them it will be something like:
new JobTicket({ id: 33 })
.fetch({ withRelated: [ 'soldBy', 'signedOffBy' ] })
.then(jobTicket => {
console.log(jobTicket.related('soldBy').get('last_name');
});
Besides that I would recommend you to use the Registry plugin for referencing other models. That eases the pains of referencing models not yet loaded.

How can i use loads or JS functional programming approach to retrieve attributes from nested data structures?

My goal is to get the attributes from the object, attributes like:
Color, Icon, Name.
(for now lets call project to the object)
The tricky part is that i need to get all the attributes from all the projects where the user is in.
This should work
var projects = [/* Your big array */];
var userID = 1223456;
var userProjects = [];
for(project in projects) {
for (member in project) {
if(member.MemberId == userID) {
userProjects.push(project);
}
}
}
There might be a more optimized solution though.
i solve the issue using filter, map and includes, i forgot to mention that im using immutable data structures.
something like this:
const memberProjects = projects.filter(project => project.get('Members').map(member => member.get('MemberId')).includes(memberId));

Categories