Is "first item is not empty" faster than "count > 0"?

@JohnMark had suggested “first item is not empty” to me recently and it sounds like it would be faster because there’s no counting involved, but I just want to confirm before I go changing this in a hundred places throughout my app.

Anyone? Thanks!

5 Likes

My experience is that such conditions ARE faster than :count. I believe that, the way Bubble works, if you have a condition that is testing for that state, that state will evaluate to true as soon as the list being tested reaches the non-empty state. (That is, if you are evaluating :count, bubble must wait until the entire search or filter or whatever is done to establish the count. But if you’re just checking for non-zero list length it does not have to do that – as non-zero list length is established as soon as any item populates to the list.)

Note that, for certain types of things, you may have to test for list’s:first item’s some_field is not empty.

Specifically, this is an issue for lists of complex (multi-field/multi-key) data returned via the API Connector. Like if you have an API that returns a list of people and their birthdays, like:

{first name: John, last name: Smith, dob: 12/18/1972},
{first name: Jane, last name: Doe, dob: 11/04/1943},
etc.

… those things are not saveable/searchable (in Bubble parlance – see my various whines about the “API Ghetto” data type) and one side-effect of this is that a filtered list of those things cannot be evaluated at the list level, but can be evaluated at the field level.

So, in this case, you might test for something like:

API List of Things:filtered(some filter criteria)'s first name is not empty

Of course, you can do this anyway whether not you’re required to.

ONE INTERESTING THING I have noticed if you’re doing this (or have to do this because of what I describe above): There seems to be a difference in how fast various data types can be evaluated as empty!

For example, I have an API List like this that has fields like:

Start date (a date), end date (a date), description (a text)

Evaluating:

List of that Type’s:filtered:first item’s description is not empty

seems to be faster than evaluting:

List of that Type’s:filtered:first item’s start date is not empty

It’s interesting.

7 Likes

Never thought of doing it that way. Interesting.

Thanks guys!

2 Likes

Fascinating, thanks for sharing in such detail, @keith! Okay, I’m off to do a lot of editing of searches :slight_smile:

As with all things: take one of the pages or things where u might find the biggest benefit from this and TEST before changing everywhere. If it’s not worth the performance improvement (or you can’t detect any), then spend the time somewhere else! :+1:

Its also possible that its faster to retrieve the count than the first non-empty item, Josh mentioned something about counts being done wholly on the database side where this optimisation is possible.

Even testing may not show a true comparison, as indexes are built automatically, and may speed things up when they have been completed.

Certainly an interesting environment!

Count uses less code and is always returned with db calls, making your app code more holistic. You can also us it for calculations, which will certainly save perceived time when matters of UIUX are concerned.
I agree with @keith that all should be tested. I use chrome’s inspector.
Is anyone finding this to be a problem?

Here’s a quick test:

Editor: https://bubble.io/page?type=page&name=count-vs-first-item&id=phil-testing&tab=tabs-1
Preview: https://phil-testing.bubbleapps.io/version-test/count-vs-first-item?debug_mode=true

I have 2000 users. I’ll let you refer to the editor to see the full code, but briefly:

-In the first row, I’m searching for all users. “count > 0” usually wins over “first item is not empty” by a fraction of a second.
-In the second row, I’m searching for all users where the Name isn’t empty. “first item is not empty” wins over “count > 0”. I thought the first row searches may be impacting the second row results, so I deleted the first row and “first item is not empty” still wins over “count > 0”

Anyway, it’s only 1 test, but even this 1 test shows that it seems to depend on the search, as others have said above.

I’m sure my testing methodology is flawed. Feel free to add your own tests on the page to flesh this out further.

2 Likes

I made a test 8 months ago with 10,000 contacts. It was really too slow with count. I end up with ‘first item is not empty’. The difference was really important. Has Bubble changed the way to split ressources in December, maybe that portion isn’t vital anymore? You will know when you reach very important traffic. In your case, speaking about users, and you notice almost ‘no difference’ with 2000, it’s maybe good news. I will say don’t change unless very important difference noticeable.

3 Likes

Good observation, @mishav: In MY case (because I have some lists of “API Ghetto” data types), the counting for those seems to be done on the client side, which may explain why in those particular cases, :count is less performant.

YMMW as they say!

ERRATTA DEPARTMENT:

I see that someone just liked this old post reply of mine (Is "first item is not empty" faster than "count > 0"? - #2 by keith). Regardless of what I said at that time, the proper way to assess a Bubble list’s emptiness is if the lists's :count is < 1. The basic theorizing that might be present in this reply is wrong and any observations I made at that time were likely inaccurate or anecdotal.

Having much more experience with Bubble internals since that time, I can confidently tell you that (1) a Bubble Search can return its :count in no time at all (in fact, the result of a Search is an object that contains simply a function that can be used to retrieve the individual search results themselves, and (2) that response includes a method (.length()) that returns the number of search results contained therein). While the .length() method is a function, it doesn’t actually seem to make a call to the database, the resultant value seems to just travel along with the Search result. So…

This itself would tell us that the :count property is faster/superior to evaluating “first item is not empty” (which won’t always give us the correct answer anyways as it is possible for a Bubble list to have null/empty values and one of these might be in the first position).

Regardless, the results of the Search action are always an object that includes the .length() method and it’s not actually possible that “first item is not empty” would be faster than assessing the :count. So, the right (and fastest) way to do this is by checking :count. I’d forgotten about all of this discussion from back in the day. Thankfully, most current advice and examples use comparisons of :count to assess the “emptiness” of a list (that is, a list has no items if count is less than zero… if the count is greater than zero, the list has items… and the count parameter is provided as quickly as possible and cannot be slower than assessing whether the :first item is empty, which isn’t even the right thing to check anyway).

Edit: Further elucidation: All JavaScript arrays have a .length parameter which tells us how many elements the array contains. The Bubble List’s .length() method is the same thing, just formatted as a function/method.

6 Likes

This has been worrying me lately.

If you have a “phantom” list entry then the count is zero, but there is something in the list.

Feels like these phantoms are recent, and a result of a delete of the underlying object in the list. They do seem to be cleared out eventually. But I don’t like it one bit.

I like

lists's :count is <= 0.

Which is a legacy of Mainframe assembler language from the 80s !

But I now think yours might be better.

Are lists's :count is <= 0 or lists's :count is < 1 expressions better in some way than more human native lists's :count is 0?

Hey @NigelG, it’s only through building plugins that these edge cases become obvious. There are some bugs in instance.publishState() that make them obvious. You can test this for yourself.

Consider the following: let’s say I have a new custom datatype (aThing) called “Favorite Thing. A Favorite Thing has two fields on it - a Name (text) and a Value (number).

And now I create 10 Favorite Things, all of which have Names, but only one of which has a Value assigned.

If I “Do a Search for…” all Favorite Things and ask :each item’s Value, what do I get?

Well, we will get a list of number type where all of the Values are null, except for the one that is populated. So a list like:

null, null, null, null, null, null, 7.77, null, null, null

Null is not a number, it is an empty array element.

Cool. It is what it is. Well, if I try to pass this array into a plugin and then publish that same numeric array to a numeric list output, the publishState() function will throw an error.

It will basically tell me that all of the values in the array, except for the item at index 7, are invalid. That is, type constraints are enforced for the Bubble plugin developer, but not for native Bubble.

So, if I want to publish this list, I have to replace the nulls (which are a special object that denotes “empty”) with some number. We’d obviously pick 0 as this is how JavaScript handles the numeric equivalence of null.

Some other types of Bubble lists can in fact have null values. Things for example are one. If I have a list of Things, and some position in the list is empty (that is, there is no Thing there), I can in fact just use null to indicate that there is no Thing at that position in the list.

But in the case of dates (as an example similar to numbers), null is not supported. If I want to indicate that some position in a list of dates is empty, I have to pick some arbitrary date that represents empty (in my plugins I represent this as the JavaScript minimum date).

Now all this is obviously a bug. (Dates are objects and Things are objects but a list of Things supports null entries, while a list of dates does not, etc, etc.) But it’s freaking annoying to deal with. (Note that I am the only plugin dev in all of Bubble-dom that understands this and builds Bubble plugins that can deal with these idiosyncrasies, but I’ve pointed them out and they’re no secret.)

There is no practical difference between the various ways we might evaluate whether a list’s count is greater than, less than, or equal to zero, @artemzheg. It doesn’t matter.

The sole point is that to assess a list’s state of having items, it is the count (the length) of the list that we should inspect, not the value of any particular element. The count is always known to us first and there’s in fact no way that it could be otherwise.

1 Like

My question was more about creating a list of those 10 Favourite things (type Favourite Thing) and then one of them accidentally falls off the wall and is deleted.

We should not, in Bubble, need to worry about deleting every reference to that deleted thing. But it appears that it will still be in the list as a null entry for a bit. So the count on the list will be 9. But there will be 10 if you look closely enough.

These “ghost entries” seem to persist for a while.

My very old and set in its ways brain worries that the count might come back as a null or a minus or something else weird. Which is why I do what I do.