Hey @antony,
Well, I get what you’re saying. There’s a bit of subtlety in terms of what one should be worried about in terms of performance impact versus what one should not. My point was that I do not think that most of what you are thinking about is probably worth optimizing in any special way. I think you are asking about basic stuff. Basic stuff that Bubble is well-designed to handle.
First, I get the sense that your general question is a general one about what Bubble calls “things” (instances of database objects) and how references to things work. And, further, are there good, better, and bad ways to reference things? And I think your second question is kind of along the lines of, “Are there things I should be worried about? And again are there really bad ways to do things?”
Some ways to think about this:
- If a page object has a type (and similarly if a group has a type and a data source has been assigned to that typed group) and is displaying an object of that type, that thing is downloaded to the browser. And pretty much any and all fields on that data object that are allowed by privacy rules that pass get downloaded to the browser. You can see this when you view source on such a page.
Bubble does this so that we have convenient access to any and all fields we might need. Also, presumably, they are downloaded to the client so that they are cached and, for example, on a page of type User, if we reference the User’s First Name a zillion times, we are not doing a zillion lookups to get the First Name. It just happens once(…ish… more on this in a minute).
I wouldn’t worry about this. See Josh’s explanations in the thread above where he answers a bunch of questions related to this and similar topics around Jun '17. Keep in mind: We’re building apps here. There’s going to a bunch of conditional text. There are going to be lots of dynamic pages that are simply “views” into different blobs of data. This is something Bubble excels at and a lot of thought has gone into optimizing for this use case.
- Very similarly, it sounds like you have pages or groups that might be of type “Company” (or “Client” or “Business” or something similar). A Company might have a name and a location and a currency preference. And as I mentioned in my previous reply, such a page might reference something like Company’s Accounting Currency a lot.
If I understand correctly, in a page or a page that has a group like this, referencing the field of a “preloaded” thing field as [Current Page's / Group's] Company's Accounting Currency
has no real additional overhead as this data is already downloaded to the client and it’s just there for our use in a suitably optimized way (basically, the same as if it were a local variable / custom state).
Of course for this to work, we must have retrieved that thing either because the page URL is domain/my-page-of-Company-type/[CompanyUniqueID]
or by feeding the Group’s data source (either by reference or by the results of some Search that resolves to a thing of type Company).
Note that the latter case is analagous/equivalent to having a custom state somewhere on the page of type Company that we have initialized in a similar way. (That Groups have a type/data source just saves us the step of creating the custom state… we are simply using the built-in variable state already available to us in the Group. Using a Group or custom state or a Repeating Group for this has one additional advantage as the Thing we snag can then be an arbitrary list of things, rather than just a single thing or a predefined list of things as in the typed page case.)
The point is that in any of these methods, we are essentially grabbing a “handle” to a Thing and then can reference that thing without incurring additional overhead.
2a. We should note that not all Searches are equally performant and some ways of searching are more efficient than others. It’s sort of easiest to think about this by way of example, which I do in #3, below.
2b. It’s worth noting that, regardless of whether we are dealing with a page with no type or page with any arbitrary type, we always have access to one very specific thing of type User without having to do a search: “Current User”. We always have a handle to Current User
and Current User's field[s]
.
- inefficient ways to reference a thing: If we need repeated access to a thing and multiple fields on that thing, we should be getting that thing via the methods described in #2. But Bubble is very flexible and we could, in fact, create cases where we are repeatedly reference some thing’s field(s) in an extremely inefficient way. Here’s an example:
We might have a built a page without types or be using groups without types and think we’re being all smarty-pants because we can pretty much always get to a thing we want by doing some sort of clever search. But such a search might not be particularly performant and, further, we might inadvertently be forcing Bubble to do way more work than it needs to.
Imagine we are on a page representing an Invoice and for this we need to reference an “Accounting Currency” preference. We might do something like this – we might get a Company’s Accounting Currency (let’s assume that Accounting Currency is not even an object itself but is just a field of type text that contains a single text token like “$”, “£” or “¥”) like so:
Do a search for Companys:filtered (by condition Advanced: This Company's Name is Current Page Invoice's BillingCompanyName):first item's Accounting Currency
This is all kinds of stupid of course, but I’m sure people do stuff like this. This is a lot of heavy lifting just to return a single-character string. Further, having now gotten this (crazy, but it would work) way of returning dollar sign vs pound sign vs yen sign, we might copy that expression into all sorts of places in our page (like various fields in a repeating group) where we need to indicate the currency.
And, every time we simply want to write out “$” we are forcing Bubble to execute this cockamamie search. And the invoice might have dozens of line items in that repeating group. I don’t think the results of such a search would ever be cached unless we specifically force that via saving that search result to a custom state and then referencing the custom state.
(For those who do not understand what the above expression would do and why it’s cockamamie: It is basically a very inefficient search and further it resolves to a string (a text) when what we really need/want to do is retrieve and keep local reference to an object of type Company. Here’s a painfully explanation of what’s wrong:
-
The unconstrained Search for Companys will return ALL company objects (and the contents of all of their fields) in the database and these objects will be downloaded to the browser just so that we can…
-
Do a :filtered operation on them (which happens on the client side – in the browser) and examine but one field – the Company’s Name (presumably a text field) and find a match between that text field and a text field on Invoice (BillingCompanyName). This is further dopey because…
-
It then uses the “Advanced” search operation – which iterates over that list of texts containing all Company names in the database – to find the match (again in the user’s browser, which may be a mobile device of limited processing power). (This should have been written as a constraint on the Search, not as a “post-processing” filter step.)
-
Having found a match we now have a 1-item list containing a single Company object, so we take its first item (the only item in the list, presumably) and snag its Accounting Currency.
-
In doing all of that, all we’ve managed to do is return a single field from Company that is a single-character string ("$"). We’ve not retained any other essential info about the Company
– like we’ll probably need that Company’s address somewhere else on the page and I suppose we could do yet another search to snag that. But what a waste of time and compute resources.
-
And further we just have a static string value. We don’t have any sort of handle on it except as the result of the crazy search. “Here’s the dollar sign we found for you!.. ‘$’ Hooray!” is what this search amounts to.
An improvement (but still very faulty) approach would be to say, “Oh hai, @keith, you’re right… We don’t need everything about all of those Company objects… we just need their Names!” (While this is correct, I am still reaching for my ruler and preparing to rap you firmly across the knuckles.) But before I can do that, you rewrite the expression as:
Do a search for Company Names:filtered (by condition Advanced: This Name is Current Page Invoice's BillingCompanyName):first item's Accounting Currency
Well sure that’s better as now all we’re downloading is a list of texts that represent all Company Names, but this really only addresses PART of point #1 above. That might still be a VERY long list, eh? And it still has all of the problems of points 2 thru 6.
A better (but still not optimal – usually) approach would be to simply return one Company to the browser by ensuring that the search is done on the server side. For example, perhaps we have a group (let’s call it Group: Company) of type Company and we make its data source:
Do a search for Companys (constraint: Name = Current Page's Invoice's BillingCompanyName):first item
This will “work”, but it’s still not really optimal. My question would be, “Why are we referencing a Company object in this implicit, rather than explicit way?” That is, why are we associating an Invoice with a Company by a text field, rather than by a field of type Company? (In the former case, we need to do a search to find the Company. in the latter case, we can just reference Invoice’s Company – once we have a handle to the Invoice, we always have a handle to the associated Company.)
Further, even if there’s a good reason for implicitly referring to the Company via a text field, we shouldn’t have chosen “Company’s Name” (which would not necessarily be unique), we should have chosen “Company’s unique ID” as the text to store in BillingCompanyName.
- This last issue is an interesting one: Are there good reasons for storing an implicit rather than explicit reference from one object to another? (Store the reference as Thing’s unique ID [a text] rather than Thing [a thing]?) For most – even “the vast majority of” – use cases, I’d say no. However, if the referenced Thing is very large and complex, but we only really need to reference some small subset of fields, what Bubble downloads to the browser by default for that referenced Thing may be overkill. I can’t remember if this is a topic discussed here in this thread, but it’s been discussed in various other places. It’s a complex topic on its own.