I'm building a program in Angular/Firestore that will allow users to create forms with questions, (similar to typeform) and then other people can fill out those questions.
The issue I'm having is that admin needs to be able to filter by the dynamically created question answer values.
Here is an example of the data structure:
Form collection:
{
id: 123,
questions: [
{
id: 0,
value: "How many students did you teach today?"
},
{
id: 1,
value: "What school were you at?"
},
{
id: 2,
value: "What was the date?"
},
]
}
UserSubmissions
{
formId: 123,
questions: [
{
id: 0,
value: 10
},
{
id: 1,
value: 'Random school name'
},
{
id: 2,
value: 2021/07/12
},
]
}
So let's say we had 100 form submissions and admin wanted to order by how many students were taught, how could I do something like .orderBy("formSubmissions.questions[0].value") or .orderBy(formSubmissions.questions.0.value").
The second one works, but there would need to be a composite index created for an unknown amount of questions. I could technically have it so you can only order by the first 10 questions or something but I feel like there has to be a better way to doing this. I don't mind restructuring data if I have to.
If you want to order on specific questions, consider creating a subcollection of questions under each form submission. Trying to order/filter on specific array elements is going to either be difficult, not scale beyond 10 items, or both.
Related
In my App a user can track his workouts, which I want to save in cloud firestore. My idea is to store a list of workouts for each month to prevent that a document gets too big. So a document would look something like this:
month: '2022-02',
workouts: [
{
date: '2022-02-01',
exercises: [
{
sets: [{'reps': 12, 'weight': 80}
{'reps': 12, 'weight': 80}
],
},
],
},
{
date: '2022-02-02',
exercises: [
{
sets: [{'reps': 10, 'weight': 90}
{'reps': 10, 'weight': 90}
],
},
],
},
],
Question:
How to fetch for example the documents of the last three months? What happens if I have lets say 500 documents with an monotionally increasing document ID like an ISO-String 2022-02 for each month. Now I want to fetch all month until 2017-05. Will this cause hotspotting?
What is a good practise when storing data like this?
The right approach regarding your document IDs is to use the Uuid package. So ideally you don't want to use a date for the identifier of your documents.
Regarding the dates, what you need is to use the where() clause of Firebase APIs in order to retrieve data in a specific date range.
This question already has answers here:
Firestore - Nested query
(2 answers)
Closed 2 years ago.
I want to store data in following format:
{
"chatName": "Football",
"chatMembers":
[
{
"userId": "nSWnbKwL6GW9fqIQKREZENTdVyq2",
"name": "Niklas"
},
{
"userId": "V3QONGrVegQBnnINYHzXtnG1kXu1",
"name": "Timo"
},
]
}
My goal is to get all chats, where the signed in user with a userId is in the chatMembers list. If the userId of the signed in user is not in the chatMembers property, then that chat should be ignored. Is this possible?
If this is not possible, how can i achive this with subcollections?
My development language is dart, but you can also post solutions in other languages.
My current attempt is this, but this is not working:
_firestore.collection(collectionName).where("chatMembers.userId", isEqualTo: userId).snapshots()
Since August 2018 there is the new array_contains operator which allows filtering based on array values. The doc is here: https://firebase.google.com/docs/firestore/query-data/queries#array_membership
It works very well with arrays of string. However, I think it is not possible to query for a specific property of an object stored in the array. One workaround is to query for the entire object, as follows (in Javascript). Of course this may not be feasible in every situation....
var db = firebase.firestore();
var query = db.collection('chatDocs').where("chatMembers", "array-contains", { userId: "xyz", userName: "abc" });
Renaud Tarnec's, which complete, doesn't work in every case. Situations where only one or not all of the fields are known won't return the expected documents. However, by restructuring the data in the document, a query can be made to work where only one field is known, like a user identifier (uid).
Here's the data structure inside one of the document:
{
"members": {
"user4": {
"active": true,
"userName": "King Edward III",
"avatar": "www.photos.gov/animeGirl5.png"
},
"user7": {
"active": true,
"userName": "Dave K.",
"avatar": "www.photos.gov/gunsAmericanFlag.png"
}
}
Here's the query:
uid = 'user4';
collectionQuery = collectionReference.where(`members.${uid}.active`,"==", true);
In this example, "user4" represents a user who may or may not be in a group. This will return all documents in the collection where the uid "user4" is an active member. This works by only needing to know the UID of the member and works without needing to know their name or avatar uri ahead of time.
I'm creating my first app in Firebase. I have no experience with NoSQL, so working out my data structure is proving to be a challenge. Let's say my app is similar Reddit where users visit the site and read/write posts. I want the app to have a list view where it sorts the post data in several ways, however it is all centered around the date posts where submitted:
Views
Show the latest posts in descending order.
Show the latest posts for a specific tag.
Show the most liked posts in descending order for the last day (24 hours).
I assume the data structure to look this:
{
"posts": {
"post_0": {
"content": "...",
"created_at": 1497112445748,
"likes": 100,
"tags": {
"tag_0": true,
"tag_2": true
}
},
"post_1": {
"content": "...",
"created_at": 1497112549374,
"likes": 30,
"tags": {
"tag_1": true
}
},
"post_2": {
"content": "...",
"created_at": 1497112640376,
"likes": 70,
"tags": {
"tag_1": true,
"tag_2": true
}
},
...
}
}
View 1
This is probably the easiest to resolve. I imagine the script to retrieve the data would be something like this:
const ref = firebase.database().ref("posts");
const query = ref.orderByChild("created_at").limitToLast(50);
query.on("child_added", (snapshot) => {
// Do stuff like add to array for sorting
});
View 2
This is where things get tricky. Since you can only have one orderBy* per query, the only way I can see to pull this off is to have a tags node that duplicates the date and post ID. For example:
{
"tags": {
"tag_2": {
"post_0": {
"created_at": 1497112445748
},
"post_2": {
"created_at": 1497112640376,
}
},
...
}
}
I've read this is the whole concept of denormalization and structuring your data around your views, but isn't there a better way?
View 3
I don't know how to solve this one at all. As the last 1 day is changing every time the view is requested and the likes are fluctuating often, how can I possibly structure my data around this view?
I've read that push keys, which would take place of the post_n key I have in my example, are sequential and can somewhat be relied on as a timestamp. I'm not sure if there's some way to take advantage of that.
I've found a few useful videos by the Firebase team and articles on Medium, but I'm afraid they don't go far enough for me to understand how to accomplish the needs of my app.
Common SQL Queries converted for the Firebase Database
Firebase Data Structures: Pagination
I'm just find this aspect of Firebase really confusing to get my head around to have it return the data I need for my views.
If anybody can provide me with an example of how to accomplish these things, it would be much appreciated! Thanks!
im trying to use firebase to store and retrieve data for my application.. i know that it is recommended to denormalize data and that it may require data replication..
my scenario is as follows:
there are a number of users in the system..
there are a number of posts in the system..
any user should be able to get a list of posts for a particular user..
each posts has a number of users as participants..
i am tempted to use the following structure for this:
users: {
abc: {
name: 'UserA',
profilePicture: 'imageA.png'
},
pqr: {
name: 'UserB',
profilePicture: 'imageB.png'
},
xyz: {
name: 'UserC',
profilePicture: 'imageC.png'
},
...,
...,
...
},
posts: {
def: {
title: 'PostA',
users: {
abc: true,
def: true,
ghi: true,
...,
...,
...
}
},
stu: {
title: 'PostB',
users: {
abc: true,
xyz: true,
...,
...,
...
}
},
...,
...,
...
}
the issue with this is that if i need to show a list of users with each post, i will need to make a query to POST, and then make sequential calls to USER for each user inside that post to get the name/profilePicture data..
if i replicate the user info inside posts as well, the issue becomes that if a user later changes her profilePicture or name, then existing posts will still show the old data..
how can i structure this data better so these cases are efficient?
thanks..
Don't replicate data inside posts. Read Firebase Docs about structuring data
Best practices:
Avoid nesting data
Flatten data structures
if you include data in post you are breaking those 2 rules (and you don't want it).
Multiple calls are not bad.
From everything I have read, it doesn't seem possible to query a multilevel value.
My data structure looks like the following:
{
"dinosaurs": {
"bruhathkayosaurus": {
"meta":{
"addedBy":"John",
"addedDate":"02021987"
},
"appeared": -70000000,
"height": 25
},
"lambeosaurus": {
"meta":{
"addedBy":"Peter",
"addedDate":"12041987"
},
"appeared": -76000000,
"height": 2.1
}
}
}
Without knowing the key name of the dinosaurs, is there anyway to query the meta node retrieving only items added by John.
In JS Something like:
var ref = new Firebase('test.firebaseio.com/dinosaurs');
ref.orderByChild("meta/addedBy")
.equalTo('Peter')
.on("child_added", function(snapshot) {
console.log(snapshot);
});
There are hacky solutions but none are scaleable, should I just flatten this data?
Edit:
I need a code review... would this be an acceptable solution?
var ref = new Firebase('test.firebaseio.com/dinosaurs');
ref.orderByChild("meta")
.on('child_added',function(snap1){
snap1.ref().orderByChild("addedBy")
.equalTo("Peter")
.on('child_added', function(snap2) {
console.log(snap2.val());
})
});
Edit Jan 2016: Since this answer, Firebase has Deep Queries so you can query deeper than 1 level.
Queries can only be 1 level deep. There are a number of solutions but flattening your data and linking/referencing is an option.
In the example above you could create another node that links the user names (parent) to the dinosaurs (children) they added. Then John node can be read and immediately know which dinosaurs he added. Then be able to access other relevant data about that dino; date added, appeared,height etc.
users
John
bruhathkayosaurus
Styracosaurus
Lambeosaurus
Spinosaurus
Peter
Lambeosaurus
Seismosaurus
You will probably want to use uid's instead of names but you get the idea.
Also, it's not clear why there is a meta node in the example listed so it could be flattened thusly:
"dinosaurs": {
"bruhathkayosaurus": {
"addedBy":"John"
"addedDate":"02021987"
"appeared": -70000000
"height": 25
},