Alternative approach to the Bubble’s recent tutorials for list of things

Hello everybody,

I’ve taken a look at some tutorials from the following topic:

In short, I think that these tutorials contain some crucial mistakes.


Regarding How To Build A Pinterest Clone Without Code

Configure your database

User

We’re facing this mistake on many apps. That’s a wrong approach since it negatively affects performance and speed.

Let me explain why I do think that this is wrong.

The Bubble’s unique id for each thing looks like the next one:
1583401182457x366778424039915460

It contains 32 of characters that are equal to 32 of bytes (if we save it as a .txt file).

In the case of Pinterest, a user may have a large number of created pins and saved pins.

Imagine that a user created 100 pins. Also, the same user saved 500 pins because he is a big fan of something.

For example, an interior designer may have a massive list of boards where he/she saves pins for each client apart.

So, the created 100 pins equals to 3299 (with commas) of characters.
That equals to ~3220 bytes (3.22 KB).

The saved 500 pins equals to 16495 (with commas) of characters.
That equals to ~16100 bytes (16.1 KB).

As a result, these two fields (created pins and saved pins) weight ~19,32 KB

Plus, don’t forget about the hidden field called Any field for each table.
This field has values from all the fields of a table.
So, the Any field for a user table contains values of the following fields:

  • Bio
  • Created boards
  • Name
  • Photo
  • Saved pins
  • email
  • Modified Date
  • Created Date

That means that this user weights more than ~38,64 KB.

It affects the following things negatively:

  1. Speed of searches
  2. Parsing data by a RepeatingGroup
  3. Auto object refreshing (Bubble’s WebSockets)
  4. Working/Updating an object (in our case, User)

Speed of searches

It takes more capacity for an app to find things (Do search for) if you have heavy (panzer-like) records.

Also, it takes time and capacity on the client-side when you’re making Parent Group User’ Saved pins:filtered to get the specific records. So, in that case, the system receives and initializes 500 of saved pins from the database when you need to display a couple of them only in a RepeatingGroup (for example, showing the last 5 saved pins sorted by date).
On refresh, the system makes the same again and again on a new refresh.

Does it sound well? I don’t think so.

Parsing data by a RepeatingGroup

So, to display things on a RepeatingGroup, the system makes several steps such as searching and parsing.

A script cannot parse data fast if it contains many data (heavy objects).
So, it cannot display fast 20-25 users on a RepeatingGroup that has a lot of pins.

For example, an admin dashboard should display all the users of the app. It works slowly and glitchy with non-optimized tables.

Auto object refreshing (Bubble’s WebSockets)

As we know, the Bubble has an attractive feature that auto-refreshes objects when data has been changed.
This feature doesn’t work well when you have a couple of tables with the same mistake, and your app is displaying things using several RepeatingGroups.
There are cases where you can notice considerable delays between updates. And that’s normal because the scripts cannot handle so much data fast and easily.

Working/Updating an object (in our case, User)

It also takes more capacity of an app to update a heavy object even in the API Workflow section.

These are the reasons why an app is working well at the start and starts to work slowly and buggy when more users are using this app.

How it should be?

Get rid of the next fields:

  1. Created boards
  2. Created pins
  3. Saved pins

User Table

Board Table

Note: We have a User field here because we cannot update the built-in field called Creator. In many cases, a developer should be able to update this field.
That’s why we may have a separate field for these purposes.

Pin Table

PinDetails Table

Here you can store massive data about a pin.

PinSelected Table

Here you need to store saved pins by a user.

So, now, for example, to display the last 5 saved pins by a user that has 500 saved pins, you need:
Do search for PinSelected where User = Current User sorted by Creation Date.

In that case, you won’t have performance issues.

A Bubble app might work pretty fast and smoothly if an app was built in the right way.

Let me know if you have any questions, please.

Cheers!

34 Likes

In your scenario, your going to have a very large number of pins and boards across your user base, so to find any given user’s pins and boards you would need to search two very large tables in their entirety … this is going to take a very long time so I’m not sure that overall performance would be improved by doing as you suggested?

5 Likes

I see what your saying…

Regarding your last screenshot, I am curious, do you think there are any database implications when you are searching an item with data type = pin?

What I mean is, if you do a search for PinSelected and one of the fields has data type = Pin aren’t you essentially loading every field that is associated with data type pin?

So data type pinSelected (in my thinking) would still carry the same KB weight as the Pin data type

And then what if you have 1,000,000 + pinSelected data items created by thousands of different users.

I definitely see where your coming from, I think it depend too on the context of the app experience/design. If a user has 0-500 pins “saved” maybe it would be faster to display his/her pins on his/her profile page, by saving all the pins directly on the user, as a list.

Crazy headline for basically suggesting an alternative approach. Both are valid.

4 Likes

It works well when you filter (using constraints) records on the server-side.

Check out the following video, please:

This app has a table with 1868420 records. It works well.

7 Likes

It depends on things. You can always add additional fields for advanced querying.

If you want, you can do a test to see how it works with 500 saved things on the user lever. :blush:

The golden rule is to have light tables as much as possible. The User table is the basic one - it should be maximum optimized.

2 Likes

I do not agree that the first one is valid.
It takes too much time and resources to optimize apps that are using the first approach.

You can store a list on the user level only if it may contain a small number of things only.

Please share an app that is using the first approach and works well with many records.

2 Likes

That is indeed snappy — kudos. I actually switched from the approach you describe to the user object model you don’t like because in the app I am attempting to build your structure was taking longer to return results in RGs than using the user object as the primary constraint. App’s still in development and only a few hundred records so perhaps there’s a scale issue at play (what works in small scale doesn’t at large and vice versa)

Thanks!

Please feel free to share some screenshots here or send them as a personal message. I’ll have a look at this to see what I can suggest.

Are there other errors or alternative and more optimal ways you see on other tutorials for how things could be implemented?

Admittedly, I am still a bit skeptical.

Lets say you Do a search for PinDetails so far, I agree - thats very light weight only two data items (description, image)
But then what if a user clicks on that item and wants to be taken to the specific Pin?
It seems to me that you would have to reference to the specific pin.
And by doing that, your initial repeating group to display PinDetails, would also have to contain the data type Pin which doesn’t seem much different than just do a search for Pin right from the beginning…

And again it seems unusual to make a database request, on a users profile page, that parses possibly tens of millions of items, just to find the few Pins saved by the user…

I agree that database constraints are much faster, then having to sort preloaded data (as a list) on front-end. But in a Pinterest example, your looking at tens of millions of records. And to have to search every time one of your possibly millions of users opens another users profile page…

I don’t know man. Your video also seemed to really lag when you were searching “er” in the input field. And if your repeating group was not sorted by constraints - displaying the first 10 or so instances of “er” would not take a long time, as it would just display whatever it found first in its original organization (probably created date).

I think the ultimate architecture is probably a hybrid of the two conflicting approaches in this thread. There’s no way a data request should search through tens of millions of items to find 5 saved pins.


Edit maybe your right. I don’t know. One of the reasons I speculate on this is because (lets use your example PinSelected); If I did a search for pinSelected, and when I look at the debugger, I see all the fields that are associated with Pin.
I understand that referencing only a Unique ID would be faster for some searches; but it seems to load the entire data item… You know what I mean?

I’ve taken a look at several tutorials and see the same issue. For example, the same problem for Follower feature.
I can have a look deeper if you want. Let me know.

1 Like

Obviously depends on what specific actions and views you want to optimize for. But that being said I do agree that in any type of Production app you usually want to stay away from Lists if the results might ever hit 50+ or 100+

If you are not optimizing Page loading speed, and would rather optimize for on page searching, this may be different.

3 Likes

I think the community (and the Bubble team themselves) would appreciate if you or others pointed out any issues you’d see like you’ve done so far, and the alternative or more optimal methods one can use instead. It would also be great to see an official reply from the Bubble team to see what they think because I’m sure many of us are now slightly confused as to how much of the tutorials we should follow and if they’re really the optimal ways to build what we’d like, or if there are other ways that are (much) better

I didn’t mean that you need to search for PinDetails.

The idea of a Details table to store massive data about a thing.
For example, you have the Article table. It should contain such fields as:

  1. Title;
  2. Short description (max 255 characters);
  3. Keywords;
  4. Main Image;
    etc.

A good way to have ArticleDetails table that will contain the following fields:

  1. Description Full;
  2. Images;
    etc.

So, you need to use an ArticleDetails record for an Article when you’re displaying the full information about this article.
You shouldn’t use an ArticleDetails inside of a RepeatingGroup.


I don’t see an issue here.
Do search for PinSelected sorted by Creation Date desc until 10 rows.


Yes, there was a lag. I think the app caused this issue since it is a test app only.
Or the elastic searches had an issue.


There is a way.


Take a look at the following screenshot, please:

If you store a list of things on the user level, you can notice that it preloads records to filter it on the client-side and display the first item only.

You will receive a single thing if you filter it on the server-side if you make a direct search.

3 Likes

Ah ya, I see what you are saying with the “Details” table.
Rather than storing the (possibly enormous) text item, and having to load that everytime, you only save a short description.
I agree 100% with that. I had to double check your screenshot.

Regarding search for pinSelected ya you can sort by created date descending but you still have to parse every single record to make sure that user = current user.
That’s my thinking…

Maybe that is fine though. I honestly don’t know how efficient the querying methods are. Maybe server based calculations can easily examine 10 million + records in seconds.
(technology that is only getting faster with quantum computing)

Thank for the replies. A good discussion

1 Like

Yup.

It works fast and optimized if you use constraints in the right way.

1 Like

Sergey, I really appreciate the offer! I may take you up on it but probably only when I’m in a position to pay you for the work. (since this is what you do at mintflow)

We do not charge for something that doesn’t take long to explain.
Also, I think it will be helpful for others as well if you can post it here.

2 Likes

Yup, sure.

I’ll attach some thoughts here these days.