How to handle custom user ordering in Firestore - firebase

From the context of a todo application, the user has a list of todos, if they reorder an item anywhere in the list how could that be saved in Firebase Firestore?
I currently have a collection with ALL todos. They get filtered by user ID and day, but I’d like to allow for custom ordering of todos. How could that be achieved?

In order to maintain the previous costs while also allowing todo items to be ordered per user you can utilize a new field per document in the todo collection. You can attempt to add a field, named lets say "order", which will hold the numerical value for the order of the todo element for each user. For example "0" for first position,"1" for second, and so on so forth. These will then be filtered by user ID and day as mentioned previously.

There is no inbuilt solution available for this. The solution I will suggest is to keep a separate document per user which maintains the order of documents (in this case to do items) in an array and when you show the data to the user in the UI, use the document to order the items on the page.
But keep in mind this approach will increase your database costs because you will need to perform an update to 2 documents whenever a new todo is created.

Related

Choosing the correct Data Structure in Firebase Firestore for my Flutter Todo App

I am currently developing a ToDo app with Flutter and Firebase. The initial situation is as follows:
1. the user has to log in when he wants to create ToDos, i.e. the ToDos would have to be saved best under the UID, right?
2. a user can create multiple lists/categories, for this I also thought about the structure and came up with 2 ideas.
First idea:
I have 2 collections, one with the UserID and all todos and one with the UserID and all categories.
todo ->
title,
createdAt,
done,
category, (link to the second collection with the categories)
...
category ->
name,
icon,
...
Second idea:
I create a single collection per user, the first field is then an array with the name of the category, and in this array are all ToDos.
Unfortunately, I am a complete beginner in Firestore and therefore I am not sure if any of my suggestions would make sense. I hope that the initial situation is well described, otherwise you can still ask questions.
Thank you!
Edit: I just saw a video and I had another idea to create a collection with the UID. This contains all categories as documents and the categories have a subcollection with all Todos.

Firebase Firestore order by 2 fields conditionally

I have some documents in Firestore with two Timestamp fields named lastUpdated and lastProcessed in addition to other fields. lastUpdated field is updated when a user updates the record's fields via web console. lastProcessed field is updated when the backend function processes the document (as a result of user clicking a button).
Following are the possible combinations of these 2 fields.
User has only updated the document, but yet to process (lastUpdated == some_timestamp, lastProcessed == '')
User has updated the document, and then processed (lastUpdated < lastProcessed)
User has updated the document, processed and re-updated (lastUpdated > lastProcessed)
My requirement is to execute a query to get a subset of these records (say top 10), ordered by its most recent timestamp. So when evaluating a record for the ordering, lastUpdated field should be considered for scenarios 1 and 3 above. But lastProcessed field should be considered for scenario 2.
Is this possible with Firestore?
When querying the Firestore database it is not possible to execute the logic you explain in your question (i.e. calculate on the fly the scenario to be applied and define which field shall be used in the query).
One classical solution is to add an extra field to the document which contains the value to be queried for. The value of this field can be calculated (according to the business logic) when you modify the document from your frontend, or via a Cloud Function triggered in the backend each time the doc is changed.
The main advantage of using a Cloud Function is to prevent users modifying the value of this field.

Firebase Batch Writes

I have a flutter app where I'm fetching a List of names based on a certain condition from a top-level collection called /students and displaying it one of my screens.
Now I want the user to select these names, with a CheckBoxListTile and then confirm to add these selected names into a deeply nested subcollection of /departments/cse/2017/6 B 17/students/, how do I accomplish this in an efficient way?
I basically want to write all the documents at once into the sub-collection from my List <Student> selectedStudents while maintaining their firebase userId as the docuemntId in that collection of students.
In Firestore writes are counted in a way that each document will count as one write.
therefor the objective of not hitting a limit will not happen.

Firestore database to accommodate multiple users independently arranging items

I need some help designing the Firestore database for my app. The app has three requirements:
Display documents in a recycler view;
The user to rearrange document items via drag and drop; and
Multiple users to access the documents and independently rearrange their items.
The first requirement is straight forward using a Firestore query.
The second requirement I’m planning to have a “position” field in the documents so the query can be “ordered by” the “position” field.
It’s the third requirement where I have difficulties. My first thought is to is to have multiple “position” fields associated with each user and order the query by the “user’s position” field. However, since Firestore requires an index for each query this approach does not work.
Any suggestions as to how best to design my Firestore database to accomplish the app’s three requirements would be appreciated.
You could store each user's ordering in a per-user document or collection (organized separately from the data itself). Simply map each document id to that user's chosen position for it. This means you would also need a separate query to get that ordering, but you wouldn't need a new index at all.
For anyone else that is interested, below describes my changes:
Excluded the “position” field from the Firestore item document. So, now when item documents are retrieved from Firestore they contain no position information. And they are downloaded in no particular order.
I’ve created a new Firestore “state” document that has a “positions” field that is a map with its key as the item’s uid and a position Int as its value. (I’ve also included an Int field that holds the recycler view’s firstVisiblePosition and another that holds its offsetTop so the recycler view can be restored to its last position.)
After the initial loading of item documents, the position values are applied to each item document. And the item documents are sorted by their position.
I handle ADDED, MODIFIED, and REMOVED document change events in my recycler view’s adapter. The change events “oldIndex” and “newIndex” values are not used.
Last, I save the “state” document under a user’s subCollection in the onStop lifecycle call back.
Hope this helps.

Voting on items - how to design database/aws-lambda to minimize AWS costs

I'm working on a website that mostly displays items created by registered users. So I'd say 95% of API calls are to read a single item and 5% are to store a single item. System is designed with AWS API Gateway that calls AWS Lambda function which manipulates data in DynamoDB.
My next step is to implement voting system (upvote/downvote) with basic fetaures:
Each registered user can vote only once per item, and later is only allowed to change that vote.
number of votes needs to be displayed to all users next to every item.
items have only single-item views, and are (almost) never displayed in a list view.
only list view I need is "top 100 items by votes" but it is ok to calculate this once per day and serve cached version
My goal is to design a database/lambda to minimize costs of AWS. It's easy to make the logic work but I'm not sure if my solution is the optimal one:
My items table currently has hashkey slug and sortkey version
I created items-votes table with hashkey slug and sortkey user and also voted field (containing -1 or 1)
I added field votes to items table
API call to upvote/downvote inserts to item-votes table but before checks constraints that user has not already voted that way. Then in second query updates items table with updated votes count. (so 1 API call and 2 db queries)
old API call to show an item stays the same but grabs new votes count too (1 API call and 1 db query)
I was wondering if this can be done even better with avoiding new items-votes table and storing user votes inside items table? It looks like it is possible to save one query that way, and half the lambda execution time but I'm worried it might make that table too big/complex. Each user field is a 10 chars user ID so if item gets thousands of votes I'm not sure how Lambda/DynamoDB will behave compared to original solution.
I don't expect thousands of votes any time soon, but it is not impossible to happen to a few items and I'd like to avoid situation where I need to migrate to different solution in the near future.
I would suggest to have a SET DynamoDB (i.e. SS) attribute to maintain the list of users who voted against the item. Something like below:-
upvotes : ['user1', 'user2']
downvotes : ['user1', 'user2']
When you update the votes using UpdateExpression, you can use ADD operator which adds users to SET only if it doesn't exists.
ADD - Adds the specified value to the item, if the attribute does not
already exist. If the attribute does exist, then the behavior of ADD
depends on the data type of the attribute:
If the existing data type is a set and if Value is also a set, then
Value is added to the existing set. For example, if the attribute
value is the set [1,2], and the ADD action specified [3], then the
final attribute value is [1,2,3]. An error occurs if an ADD action is
specified for a set attribute and the attribute type specified does
not match the existing set type. Both sets must have the same
primitive data type. For example, if the existing data type is a set
of strings, the Value must also be a set of strings.
This way you don't need to check whether the user already upvote or downvote for the item or not.
Only thing you may need to ensure is that the same user shouldn't be present on upvote and downvote set. Probably, you can use REMOVE or ConditionExpression to achieve this.

Resources