In Firebase database I have a list of Bookings.
Each Booking as the following structure
{
userUid: string,
status: string,
moreStuff: {
....
}
}
I need to select all the bookings associated to a certain user (i.e. whose userUid is equal to the uid of the user, which is known by by app) which have a certain status (e.g. status = confirmed).
I can select the Bookings belonging to a specific user using the following query
db.list('bookings', {
query: {
orderByChild: 'userUid',
equalTo: user.uid
}
})
but I have no idea if I can add the additional select condition e.g. status = confirmed
In a firebase query you can't filter on more than a single field. I have encountered this restriction many times.
I think your challenge would become easier if you model your data differently. I believe that you have a one to many relationship between Users and Bookings. That is, a user may have many bookings but a booking may only have one user. If this is the case, I would create a top level node "bookingLists" which contains separate booking list for each user. The structure is illustrated below. The path "bookingList/< userId >" contains the booking list for a given user. You can access the list if you know the userId. It will contain all bookings for that user. You can then query on a single field in each booking to filter by.
bookingLists
<userId>
bookings
<bookingKey>
status
...other fields
If you need to further filter the booking, you could filter on the client side using observable operations map and filter. This will scale, as long as, each user doesn't have too large a booking list.
Related
I can't manage to determine what is the better way of organizing my database for my app :
My users can create items identified by a unique ID.
The queries I need :
- Query 1: Get all the items created by a user
- Query 2 : From the UID of an item, get its creator
My database is organized as following :
Users database
user1 : {
item1_uid,
item2_uid
},
user2 : {
item3_uid
}
Items database
item1_uid : {
title,
description
},
item2_uid : {
title,
description
},
item3_uid : {
title,
description
}
For the query 2, its quite simple but for the query 2, I need to parse all the users database and list all the items Id to see if there is the one I am looking for. It works right now but I'm afraid that it will slow the request time as the database grows.
Should I add in the items data a row with the user id ? If yes the query will be simpler but I heard that I am not supposed to have twice the same data in the database because it can lead to conflicts when adding or removing items.
Should I add in the items data a row with the user id ?
Yes, this is a very common approach in the NoSQL world and is called denormalization. Denormalization is described, in this "famous" post about NoSQL data modeling, as "copying of the same data into multiple documents in order to simplify/optimize query processing or to fit the user’s data into a particular data model". In other words, the main driver of your data model design is the queries you plan to execute.
More concretely you could have an extra field in your item documents, which contain the ID of the creator. You could even have another one with, e.g., the name of the creator: This way, in one query, you can display the items and their creators.
Now, for maintaining these different documents in sync (for example, if you change the name of one user, you want it to be updated in the corresponding items), you can either use a Batched Write to modify several documents in one atomic operation, or rely on one or more Cloud Functions that would detect the changes of the user documents and reflect them in the item documents.
I am testing out DynamoDB for a serverless app I am building. I have successfully modeled all of my application's query patterns except one. I was hoping someone could provide some guidance. Here are the details:
Data Model
There are three simple entities: User (~1K records), Product (~100K), ActionItem (~100/product).
A User has a many-to-many relationship with Product.
A Product has a one-to-many relationship with ActionItem.
The Workflow
There's no concept of "Team" for this app. Instead, a user is assigned a set of products which they (and others) are responsible for managing. The user picks the oldest items from their products' action item list, services the item and then closes it.
The use case I am trying to model is: As a user, show me all action items for products to which I am assigned.
Any help would be greatly appreciated.
Really only two options...
If you can store the list of products within the 400KB limit of DDB record, then you could have a record like so...
Hash Key: userID
Sort KEY: "ASSIGNED_PRODUCTS"
Otherwise,
Hash key: UserID
Sort key: "#PRODUCT#10001-54502"
userID in the above might be the raw userid, or if using a GSI, might be something like "#USER#user-id"
I've been reading many SO posts about specific querying situations and I've hit a roadblock on mine when trying to determine the best approach without going overboard on read requests.
I have an app where a user can follow multiple artists and on the home page it should display the upcoming submissions sorted by date. I want to limit the number of reads to 15, but the way my structure is set up, I cannot limit overall reads but just reads to a specific artist. Let me explain:
For a specific user, I save all artists that a user follows into an array:
user: {
userUsername: 'jlewallen18',
userArtistUIDs: [1,4,8,9]
}
To establish a connection to a submission from an artist a user follows, I add the artistUID to the submission:
submission: {
submissionArtistUID: 4,
submissionTitle: 'New Album'
submissionReleaseDate: 1550707200
}
Now the way I find what I need to display on the home page for a specific user is to loop through all the artist id's the user followers and query for submissions with that id. I can then subscribe to that observable using combineLatest from rxjs and receive my results.
this.submission$ = user.userArtistUIDs.map((id) => {
return this.db.collection$('submissions', ref => ref
.where('submissionArtistUID', '==', id));
});
This works great, but wont scale well against my read quota, because it queries for every single release from every artist I follow. So if I follow 300 artists, each with 3 releases, I would have 900 reads sent to the app and I'd have to sort and slice the final array to cut it to 15 on the client side.
This is what I'm looking for:
With my list of artistIDs -> query for submissions that contain the
artistID in my array -> sort by ascending order -> limit to 15.
Thus my read count would only be 15. I know that for NoSQL there isn't a one trick pony, but I am struggling to figure out the optimal method as there will be more reads than writes on my application.
With Firestore there is a limit() argument that can be passed along with the query, see here
Would this work?
this.submission$ = user.userArtistUIDs.map((id) => {
return this.db.collection$('submissions', ref => ref
.where('submissionArtistUID', '==', id).limit(15));
});
Am looking at the data structure in this post and want to know how you would go about getting the emails of users who belong to a certain group when they could belong to several groups and the GroupID stored against that user is the current group they are participating in?
Do you store the email addresses with the userid under the "members" or, instead, for each member of the group, get that user's email address from the "users" document userid (this would mean iterating through the group/members collection and doing a query for each user. Not very efficient).
Am used to SQL so this is all new to me.
You should have a single node for each user
/users/UID/emails/
/users/UID/emailunread/
/users/UID/settings/
/users/UID/details/
/users/UID/payments/
So you can simply do a subscription for a singular node path this.myDatasubscription = this.DB.list('users/' + this.uid).snapshotChanges() ensuring changes like new emails or account settings will detected and rolled out in real time back to the app, so your are using angular/ng or something similar client side then your variables {{this.email_list}} should update real time with no page changes.
Take a look at this one.
error: Property 'getChildren' does not exist on type 'DataSnapshot'
I'm creating an order form and a schema defined for an Order (certain required fields such as address, customer info, items selected and their quantities, etc).
a. User visits site.
b. A unique ID is generated for their session as well as a timestamp.
var userSession = {
_id: createId(),
timestamp: new Date(),
};
var sessionId = userSession._id;
c. The userSession is placed in local storage.
storeInLocalStorage('blahblah', sessionObject);
d. An Order object is created with the sessionId as the only field so far.
var newOrder = {
sessionId: sessionId;
};
e. Obviously at this point the Order object won't validate according to the schema so I can't store it in Mongo. BUT I still want to store it in Mongo so I can later retrieve incomplete orders, or orders in progress, using the sessionID generated on the user's initial visit.
This won't work because it fails validation:
Orders.insert(newOrder);
f. When a user revisits the site I want to be able to get the incomplete order from Mongo and resume:
var sessionId = getLocalStorage('blahblah')._id;
var incompleteOrder = Orders.findOne({'sessionId', sessionId});
So I'm not sure how to go about doing this while accomplishing these points.
I want full simpleschema validation on the Orders collection when the user is entering in items on the forms and when the user is intending to submit a full, complete order.
I want to disable simpleschema validation on the Orders collection and still allow storing into the DB so that partial orders can be stored for resumption at a later time.
I can make a field conditionally required using this here but that would mean 50+ fields would be conditionally required just for this scenario and that seems super cumbersome.
It sounds like you want to have your cake, and eat it too!
I think the best approach here would be keep your schema and validation on the Orders collection, but store incomplete orders elsewhere.
You could store them in another collection (with a more relaxed schema) if you want them on the server (possibly for enabling resume on another device for the logged in user) , or more simply in Local Storage, and still enable the resume previous order behaviour you are wanting.
Only write to the Orders collection when the order is complete (and passes validation).
Here's a variation on #JeremyK's answer: add an inProgress key to your order of type [Object]. This object would have no deeper validation. Keep your in progress order data in there until the order is final then copy/move all the relevant data into the permanent keys and remove the inProgress key. This would require that you make all the real keys optional of course. The advantage is that the object would maintain its primary key throughout the life cycle.
I think this particular case has been solved; but just in case, you can skip Simple Schemma validations by accessing MongoDB native API via Collection#rawCollection():
Orders.rawCollection().insert(newOrder);
While this question is very old in the meantime there is a better solution. You probably use simple schema together with collection2. Collection2 has the ability to set multiple schemas based on a selector and then validate against the correct schema based on it.
https://github.com/Meteor-Community-Packages/meteor-collection2#attaching-multiple-schemas-to-the-same-collection
e.g. you could have a selector {state: 'finished'} and only apply the full schema to these documents while having another selctor, e.g. {state: 'in-progress'} for unfinished orders with a schema with optional fields.