Getting the first item of a sub-list in a list of lists (list flattening)

I have a list of items, each of them with a list of photos. I’m populating a repeating group to show the first photo of each item.
image
Obviously the expression doesn’t work because :first item operates on a list of photos which is the union of many lists of photos. Bubble automatically flattens the sub-lists into one list, losing the separation between the original lists. The result is the first photo of the first item, and the repeating group expects a list, not a single element.
How can I do what I wanted to do?

Instead of using Do a Search for, could you use a List of Photos field in the item, and fetch it from there instead?

So that it would be Item’s Photos:first item.

Couldn’t you just set the number of rows and columns to 1?

What do you mean by using “a list of photos field in the item”? I have many items, and each of them has a list of photos. I want to boil down the list of items to a list of the first photo of every item.

It would work, but I’m also going to do this in a workflow, where I wouldn’t be able to leverage the repeating group to iterate over the parent list (the list of items). In that case I would need to get the first elements in the sub-list by using an expression, which I can’t find how to craft.

I might have misunderstood what you wanted to do here. I’ll describe it with my words, just to see if I got it right:

You want a repeating group to show a list of all items, represented only by a photo. You want that photo to be the first photo of all photos added to that particular item.

If I’m guetting your case correctly, you can probably solve it by setting the Source of the repeating group to Search for items, instead of Item’s photos. Then, inside the repeating group cell, you place a group or image, and set that source to Current Cell’s Item’s Photo’s:first item. If you’re only showing one image, I see no reason to rest Repeating Group.

Let me know if that helps, or if I’m way off.

It’s exactly what I want to do.
The only gripe I have is that I have to do it through a repeating group, which is fine in this specific case, but in general I would like to be able to do this through a single expression.
If I were to do this transformation in an API Workflow or in a Custom Event, without being able to rely on a visual element (the repating group) to transform the item to a photo (its first photo), I would be stuck.

You can probably solve that through some creative searches, but I can’t help thinking it wouldn’t be very database-efficient. Is there a reason you can’t save the Item Photo in two data types: one being the first image only, and the second being the list of images?

I know this kind of double database entry hurts the soul a bit, as it contradicts db best practices, but in Bubble I’ve found tradeoffs like these to be necessary sometimes to keep the app running efficiently.

I think you’ll need to re-structure your database to make this work. @petter offered one route. Here’s another that also has you creating a new type:

If your Photo is saved in a type of its own and there is a 2-way reference between Photo and Item, you can retrieve the first item of each Item’s List of Photos with a single expression.

Item

  • Name (text)
  • Photos (list of Photos)

Photo

  • File (file or image)
  • Item (Item)

Search for Photos :filtered

where the filter is: Advanced: This Photo’s Item’s Photos :first item is This Photo

6 Likes

I understand what you and @petter are saying. I’ve come up with such scenarios already and the solution is to maintain copies of data.

@romanmg In your case, I would have to maintain the 2-way reference manually, remembering to update the other side. Specifically, if I add a photo to an item, I have to remember to add the item to the photo. Same for removing or changing.

@petter a similar observation could be made to your solution. I would have to remember to keep track of every addition, removal or change.

In each case, a thorough testing is required to check every case is covered, because the issue checker does not ensure data integrity as we don’t have constraints (please @Bubble can you add constraints? At least not null?).

In general one thing that baffles me about Bubble is exactly this: why isn’t it yet possible to access references from each side?
If I have a one-to-many relation, I can implement it as a list on a Thing (Thing1 has-many Thing2) or as a field on Thing2 (Thing2 belongs-to Thing1). It’s upsetting that Bubble doesn’t generate the other side of the relation automatically, given it has all the information it needs.
This creates a rigid link between how the data is stored and how we can query it, which reduces flexibility a lot.

@furnys could you give more context regarding the ‘constraints’?

You can make thing 1 a field in thing 2 thereby linking in both directions.

By "adding a not null constraint I mean that I would like the database to throw an error when I create a new thing in a workflow and a field which has been marked as “required” is missing.
At the moment is not possible to tell Bubble that some field must not be empty.

About linking in both directions, I’ve made a test app to show that 2-way relations have to be maintained.
In my app, a parent has many children. If I add a child to a parent but don’t add back the parent to the child, the child doesn’t know who the parent is, but the parent does. This happens because in Bubble there’s not (yet) the concept of referential integrity, which is one of the pillars of building data models.

@furnys Thanks for the context and feedback. Our engineering team will take it into consideration.

1 Like

Thanks @neerja!
Paging @NigelG @keith @laurence who might be interested in this topic

1 Like

Fixed that you :slight_smile:

Interested to find out what might come of this of course. I would be nervous of a “full” RI style constraint system, but the “not null” idea has a lot of value I think.

1 Like

What you’re talking about here is all app level stuff. If you create things and then those things must have certain fields that “must not be empty”, it’s of course up to you (by which I mean the app) to handle that.

Note that fields on custom data types – where the field is of a primitive (built-in) data type – can have a default value that will be set to that default upon creation of a new thing.

Complex (custom) data types cannot have a default value.

The following snap from the Data > Data Types tab illustrates:

I guess the question is: If a new object of “A Very Large Object (Demo Purposes Only)” is created, should we be able to set a non-null default for the complex fields (in this case, fields “Listing” and “User”).

I would argue such a feature is not wildly helpful. However, if such a capability were offered, the issue is: “How do we specify the default for a complex data type?”

Well, the only way to do this would be to use the unique ID of a prototype object of the correct complex type.

I don’t see how this would be any different than having the field be null upon creation. Neither option gives you what you want in most cases.

So, again, it’s up to you (your app) to add a User and Listing to “A Very Large Object” when such an object is created. I don’t see the value in the feature you’ve proposed.

I would just like an error to be thrown to catch it and execute a different sequence of events. For me there’s no need to be able to initialize a null complex object.

@furnys To close the loop on the ‘not null’ issue, our engineering team confirmed this is a bigger project as there isn’t a ‘required’ field concept currently within workflows. So the approach for now is to have a condition on the action or terminate action.

@neerja - so then what does checking “This input should not be empty” field currently do?

@furnys - I agree that 2-way relations/built-in referential integrity would be helpful. It would be great if Bubble had a check-box option to decide per app whether or not you want to automatically create the parent on the child type when you create the child on the parent type. It would save a lot of duplicate work.