Is it possible to retrieve multiple nodes separated by commas from Firebase?
For example, get the name and image properties of an user using:
/users/$USER_ID/name,image
instead of downloading all the user information
The Firebase SDK always retrieves complete nodes from the database. There is no way to select a subset of the nodes.
If you find you want a subset of the nodes and that downloading all nodes would be overly wasteful, you should model your data to have an additional collection where you only keep the properties you want for each user:
user
uid1
name: "TheUnreal"
image: "https://graph.facebook.com/1214076718653094/picture?type=large"
stackid: 3669981
lastquestionid: 40565750
uid2
name: "Frank van Puffelen"
image: "https://www.gravatar.com/avatar/12d378e6a9788ab9c94bbafe242b82b4?s=48&d=identicon&r=PG"
stackid: 209103
lastquestionid: 39984247
users_names_images
uid1
name: "TheUnreal"
image: "https://graph.facebook.com/1214076718653094/picture?type=large"
uid2
name: "Frank van Puffelen"
image: "https://www.gravatar.com/avatar/12d378e6a9788ab9c94bbafe242b82b4?s=48&d=identicon&r=PG"
With this data structure you can choose whether you get a subset of the data or the complete data by querying the corresponding top-level node. Duplicating data like this is fairly common in NoSQL databases, where you often model the data for how your app consumes it.
You can retrieve the nodes using a class. I do it like that. Why don't you manipulate the returned values so as to make it in a way you'd be able to use.
Related
In my firebase db I have 3 collections:
Users
{user_id}: {name: "John Smith"}
Items
{item_id}: {value: 12345}
Actions
{action_id}: {action: "example", user: {user_id}, items:{item_id}}
Basically, instead of storing the Users and Items under the Actions Collection, I just keep an ID. But now I need a list of all actions and this also needs info from the Users and Items Collection. How can I efficiently query firebase so I can get a result that looks like this:
{
action: "example",
user: {
name: "John Smith"
},
item: {
value: 1234
}
}
Unfortunately, there is no such thing in firebase or a similar database, basically, you are looking for a traditional join, which is no recommended thing to do in a NoSQL database.
If you want to do it in firebase, you will need:
Get the element you are looking for from your main collection Actions in this case.
Then you need to do another call to the Items collections where item_id == action.item_id.
Then assign in the actions["Item"] = item_gotten.
This is not a recommended use as I said, usually, when you are using a NoSQL Database you are expecting a denormalize structure, from your application you need to save the whole Item, in the Action JSON, and also in the Item. Yes, you will have duplicate data but this is fine for this kind of model. also you shouldn't expect too many changes in one specific object within your whole object key If you are managing a big set of changes you could be using the incorrect kind of DB.
For aggregation queries reference, you might check: https://firebase.google.com/docs/firestore/solutions/aggregation
I am building an iOS app that is using Cloud Firestore (not Firebase realtime database) as a backend/database.
Google is trying to push new projects towards Cloud Firestore, and to be honest, developers with new projects should opt-in for Firestore (better querying, easier to scale, etc..).
My issue is the same that any relational database developer has when switching to a no-SQL database: data modeling
I have a very simple scenario, that I will first explain how I would configure it using MySQL:
I want to show a list of posts in a table view, and when the user clicks on one post to expand and show more details for that post (let say the user who wrote it). Sounds easy.
In a relational database world, I would create 2 tables: one named "posts" and one named "users". Inside the "posts" table I would have a foreign key indicating the user. Problem solved.
Poor Barry, never had the time to write a post :(
Using this approach, I can easily achieve what I described, and also, if a user updates his/her details, you will only have to change it in one place and you are done.
Lets now switch to Firestore. I like to think of RDBMS's table names as Firestore's collections and the content/structure of the table as the documents.
In my mind i have 2 possible solutions:
Solution 1:
Follow the same logic as the RDBMS: inside the posts collection, each document should have a key named "userId" and the value should be the documentId of that user. Then by fetching the posts you will know the user. Querying the database a second time will fetch all user related details.
Solution 2:
Data duplication: Each post should have a map (nested object) with a key named "user" and containing any user values you want. By doing this the user data will be attached to every post it writes.
Coming from the normalization realm of RDBMS this sounds scary, but a lot of no-SQL documents encourage duplication(?).
Is this a valid approach?
What happens when a user needs to update his/her email address? How easily you make sure that the email is updated in all places?
The only benefit I see in the second solution is that you can fetch both post and user data in one call.
Is there any other solution for this simple yet very common scenario?
ps: go easy on me, first time no-sql dev.
Thanks in advance.
Use solution 1. Guidance on nesting vs not nesting will depend on the N-to-M relationship of those entities (for example, is it 1 to many, many to many?).
If you believe you will never access an entity without accessing its 'parent', nesting may be appropriate. In firestore (or document-based noSQL databases), you should make the decision whether to nest that entity directly in the document vs in a subcollection based on the expect size of that nested entity. For example, messages in a chat should be a subcollection, as they may in total exceed the maximum document size.
Mongo, a leading noSQL db, provides some guides here
Firestore also provided docs
Hope this helps
#christostsang I would suggest a combination of option 1 and option 2. I like to duplicate data for the view layer and reference the user_id as you suggested.
For example, you will usually show a post and the created_by or author_name with the post. Rather than having to pay additional money and cycles for the user query, you could store both the user_id and the user_name in the document.
A model you could use would be an object/map in firestore here is an example model for you to consider
posts = {
id: xxx,
title: xxx,
body: xxx,
likes: 4,
user: {refId: xxx123, name: "John Doe"}
}
users = {
id: xxx,
name: xxx,
email: xxx,
}
Now when you retrieve the posts document(s) you also have the user/author name included. This would make it easy on a postList page where you might show posts from many different users/authors without needed to query each user to retrieve their name. Now when a user clicks on a post, and you want to show additional user/author information like their email you can perform the query for that one user on the postView page. FYI - you will need to consider changes that user(s) make to their name and if you will update all posts to reflect the name change.
I'm a little new to NoSQL and I'm struggling to understand how to design the database when different documents have relationships. I've read a few articles on design patterns for NoSQL, but I seem to get conflicting information. Here is the situation I'm facing.
I have a collection of users and each user has a set of activities that they practice. It looks something like this:
[Collection] Users
- [Doc] usr10343
name: Bob
age: 27
- [Doc] usr19282
name: Jenny
age: 32
[Collection] Activities
- [Doc] act19203
name: Warmup Jog
description: {...}
duration: 10m
- [Doc] act28301
name: Burpies
description: {...}
duration: 8m
Now, the activities have the same name, duration, and description across all users, but there is extra user-specific data, like how many times they did that activity or when the last time was. I am creating a subcollection under each user for their activities, but I see two patterns for how to create it. Either duplicate all of the activity data so each doc in the subcollection looks like this:
- [Doc] instance39201
name: Warmup Jog
description: {...}
duration: 10m
numberOfTimes: 8
lastCompleted: 10/18/2017
which seems like a lot of duplication and a real hassle if I ever want to update the name or description. Or, I could use a reference like this:
- [Doc] instance39201
activity: act19203
numberOfTimes: 8
lastCompleted: 10/18/2017
And that feels like I am using a relational database without the benefit of SQL. Am I missing something. Is there a better way to approach this that I'm not finding? Thanks.
There is not a "one true way" to model all data for all time in a nosql database. Your modeling should suit the queries you intend to make on it. If you don't know your queries, then you can't necessarily know an effective modeling. It's all about efficacy. A model isn't "right" if it doesn't suit the needs of your app.
I understand that enums are not standard type in Dynamo: https://forums.aws.amazon.com/thread.jspa?messageID=836386
However, what is the exact resolution here?
How are we supposed to appropriately represent relations with the generated code?
-- Am I missing something or is the generated code correct and we need to create some custom fields in the dynamo tables and then rewrite the queries?
Example:
type Competition {
id: ID!
name: String!
creator: UserProfile!
startDate: String!
endDate: String!
competitionType: CompetitionType!
competitors: [UserProfile]!
prize: Prize!
}
A competition is created by a user, has a type, a prize, and has competitors. When create resources for this table, the code is clearly missing any information that is derived out of the custom types or enums. Complex schemas will always have this type of structure, so I'm a bit confused on the outputted code and right direction from here.
extend type Mutation {
createCompetition(input: CreateCompetitionInput!): Competition
}
input CreateCompetitionInput {
id: ID!
name: String!
startDate: String!
endDate: String!
## Missing info
}
When AppSync generates the schema automatically it skips these as they are intended to be added manually with additional resolvers. You can define a new query that is attached to each of the custom or enum fields, but the data you are referencing will need to be stamped with something that is unique to the competition so that it can be queried on in relation to this type (as dynamoDB isn't a relational db).
When creating a new Competition you will need to update child fields with something unique to that competition. I.e. each UserProfile that needs to be tracked as a competitor gets stamped with this Competitions ID. Mutations for each of the custom fields need to be handled separately.
This article helped me solve this same question: https://keyholesoftware.com/2018/05/17/go-forth-and-appsync/.
I read a lot about nosql databases lately. I get that rule of thumb is to structure the data based on our view (of course, depends on the use case).
Now, let's say that we have a social app and the user has a profile but he also creates posts and we have to store them in the database.
So, I see some developers choose to do that like so:
Posts
-----UserID
-----------PostID
-----------------username: John
-----------------profileImage: https://...
-----------------posted_photo: https://...
This totally fits the structure base on the view. We'd go into posts and our userID and we could get all the data that our view needs. Now my question is, what happens when the user has made 100K posts and he decides to change his profile photo for example. All of his posts so far contain his old photo, so now, we have to write a method that would cycle through 100K of posts (or all of his posts in general) and update his photo. In 2 hours, he decides that "Nah, I don't like this photo, I'd change it back" and we have to make another 100K queries.
How is that (denormalized data) ok? Sure, its easier, its flat but then we have to make ridiculous amounts of queries to change a single profile photo. What's the way to handle this really?
I've done this storing user's data in a place and setting just the userID as post attribute.
posts:
userID:
postID:
userID: 'user1',
attachedImageURL: 'http:..',
message: 'hey',
reblogID: 'post4',
type: 'audio|poll|quote'
users:
user1:
name: 'john',
profileImage: 'http..'
It requires one more query to Firebase to retrieve user's profile data but it's a good way to solve this. It really depends on how you want to use those data.