rucko24

Hi guys,

I’m using project reactor and Vaadin Flow 23.3.2

I have a doubt, right now I know how to fill a Grid with few data, but the detail is when there are many data, since I don’t know very well how to do lazy loading as the existing JPA examples…

If there is a lot of data I need to use delayElements after doing a findAll with Mongo. then the Grid becomes very slow. do you know how I could improve it?

And in addition to slow it does not manage to fill it complete, but in the console it continues invoking data.

final List<AccountsDto> list = new CopyOnWriteArrayList<>();
this.service.findAll()
                .delayElements(Duration.ofMillis(100))
                .doOnError(error -> {
                    ui.access(() -> {
                        Notification.show("Error");
                        this.progressBar.setVisible(false);
                    });
                    log.error("Error retreive account from db {}", error);
                })
                .doOnComplete(() -> {
                    ui.access(() -> this.progressBar.setVisible(false));
                })
                .subscribe(accountsDto -> {
                    ui.access(() -> {
                        list.add(accountsDto);
                        this.gridListDataView = this.grid.setItems(list);
                        this.gridListDataView.addFilter(dtoFilter -> {
                            final String name = searchField.getValue().trim();
                            if (name.isEmpty()) {
                                return true;
                            }
                            return StringUtils.containsIgnoreCase(dtoFilter.getUserName(), name);
                        });
                        this.grid.getDataCommunicator().enablePushUpdates(Executors.newCachedThreadPool());             
                    });
                });

Since they ask that in the setItems(query → return a Stream() something that with project reactor I do not achieve well, and also the stream is not fired …

Well I don’t know if it’s the best way to do it but it’s working much better, and even the UI doesn’t crash on me.

But I think it’s because I’m not updating any component while filling the list.

this.grid.setPageSize(50);
final List<TwitterAccountsDto> list = new CopyOnWriteArrayList<>();
this.progressBar.setVisible(true);
this.twitterAccountService.findAll()
//.delayElements(Duration.ofMillis(500))
  .doOnError(error -> {
       ui.access(() -> {
           Notification.show("Error");
           this.progressBar.setVisible(false);
       });
   log.error("Error retreive account from db {}", error);
   })
   .doOnComplete(() -> {
        ui.access(() -> {
            this.progressBar.setVisible(false);
            this.grid.getDataProvider().refreshAll();
        });
   })
   .subscribe(twitterAccountsDto -> {
        ui.access(() -> {
            list.add(twitterAccountsDto);
        });
   });

this.grid.setItems(new CallbackDataProvider<>(
                query -> {
                    Notification.show("Offset=" + query.getOffset() + " Limit=" + query.getLimit());
                    return list.stream()
                            .skip(query.getOffset())
                            .limit(query.getLimit());
                },
                query -> list.size()));

You shouldn’t need to delay elements. The setItems(query->{}) API is indeed what you want to use. This video I recorded recently might be helpful https://youtu.be/9jhB9vL7KMM

That’s right Marcus, thanks as always helping.

Well before I posted here, I managed to watch your video.

But I still have those doubts.

It’s a bit more experienced and apparently the reactor collectList helps a bit, but I’m not quite convinced.

I wish you could do a PoC, as was the very interesting reactive chat…

What type of PoC are you thinking?

Is it possible to show us an example filling a grid with project reactor?

But with many items, I tried it with 100000 rows.

What is the use case that you’re building?

In my work we make intensive use of project reactor for the backend.

What I’m trying to understand is: what type of data are you receiving, what do you need to do with it? Understanding the use case would help to understand what the solution should look like

Hi Markus,

  • Data from a NoSQL database with MongoDB
  • A lot of data, 100mil or more.
  • We are using reactive Mongo with ReactiveMongoRepository and project reactor.
  • Those data that we read from our mongo databases we reflect them in a grid by doing push

Do they just scroll through the data? Do they sort, filter, etc?

As is, for the moment, just that, All that you have mentioned.

Only, we can’t find an efficient way to pass that reactive Stream to a Stream as such to set it to the setItems with the skip and limit…

We use collectList at the moment but it seems to add a slowness penalty.

In the service

public Mono<List<AccountsDto>> findAll() {
    return Flux.defer(this.accountRepository::findAll)
            .publishOn(Schedulers.boundedElastic())
            .map(AccountMapper.INSTANCE::entityToDto)
            .collectList()
            //.take(20000)
            .doOnNext(onNext -> log.info("List size: {}", onNext.size()));
}

In the view

this.accountService.findAll()
        //.delayElements(Duration.ofMillis(500))
        .doOnError(error -> {
            ui.access(() -> {
                Notification.show("Error");
                this.progressBar.setVisible(false);
            });
            log.error("Error retreive account from db {}", error);
        })
        .doOnTerminate(() -> {
            ui.access(() -> {
                this.progressBar.setVisible(false);
                this.grid.getDataProvider().refreshAll();
            });
        })
        .subscribe(accountsDto -> {
            ui.access(() -> {
                this.gridLazyDataView = this.grid.setItems(query -> {
                            Notification.show("Offset=" + query.getOffset() + " Limit=" + query.getLimit());
                            return accountsDto.stream()
                                    .skip(query.getOffset())
                                    .limit(query.getLimit());
                        },
                        query -> accountsDto.size());
                if (accountsDto.size() == accountsDto.size() / 2) {
                    this.grid.getDataProvider().refreshAll();
                }
            });
        });