英文:
Reactor - how to collect found and not found entities into a map, with id as key?
问题
以下是您要翻译的内容:
想象一下,我将一系列用户标识上传到服务器以查找它们;我想知道哪些标识在数据库中找不到,这样我就会意识到我的本地数据库缓存或实际数据已损坏。缺失的标识在用户界面和日志中作为反馈是有用的。
我想构建一个映射,以输入标识作为键,以在缓存/数据库中找到的实体用户作为值。如果未找到,值为 null
。然而,如果 findById(id)
没有返回任何内容,ReactiveCrudRepository
将返回 Mono.empty()
,并且随后的映射将不包括未找到的实体。在测试中,我得到了错误:
期望值:is <true>
但实际值:was <false>
java.lang.AssertionError:
Expected: is <true>
but: was <false>
我正在进行以下操作:
return Flux.fromIterable(ids)
.concatMap(repository::findById) // <--------------- 这一步只会获取到找到的实体
.collectMap(Customer::getId); // <------------------ 所以在这里我只得到了存在的元素(在测试中,是一个包含2个元素的映射)
关键是要知道哪些标识被找到了,哪些没有被找到。在 Reactor 3 中,我该如何用另一种方式来实现这个?
测试代码:
@Test
void findAllByIdAndMissing() {
// given
Customer customer1 = Customer.builder().id("id1").build();
Customer customer2 = Customer.builder().id("id2").build();
// 给定模拟
when(cache.getAllPresent(ArgumentMatchers.any())).thenReturn(Collections.emptyMap());
when(repository.findById("id1")).thenReturn(Mono.just(customer1));
when(repository.findById("id2")).thenReturn(Mono.just(customer2));
when(repository.findById("id3")).thenReturn(Mono.empty());
// when
Map<String, Customer> actual = service.findAllByIdAndMissing(Set.of("id3", "id2", "id1")).block();
// then
assertThat(actual.keySet().contains("id1"), is(true));
assertThat(actual.keySet().contains("id2"), is(true));
assertThat(actual.keySet().contains("id3"), is(true));
}
如果我添加 log()
:
13:08:03.072 [Test worker] INFO reactor.Flux.Iterable.1 - | onSubscribe([Synchronous Fuseable] FluxIterable.IterableSubscription)
13:08:03.078 [Test worker] INFO reactor.Flux.FlatMap.2 - onSubscribe(FluxFlatMap.FlatMapMain)
13:08:03.080 [Test worker] INFO reactor.Flux.FlatMap.2 - request(unbounded)
13:08:03.081 [Test worker] INFO reactor.Flux.Iterable.1 - | request(256)
13:08:03.082 [Test worker] INFO reactor.Flux.Iterable.1 - | onNext(id1)
13:08:03.084 [Test worker] INFO reactor.Flux.FlatMap.2 - onNext(Customer(super=Auditable(super=Customer@196b1, createdAt=null, createdBy=null, modifiedAt=null, modifiedBy=null), schemaVersion=1, internalVersion=null, id=id1))
13:08:03.145 [Test worker] INFO reactor.Flux.Iterable.1 - | onNext(id2)
13:08:03.146 [Test worker] INFO reactor.Flux.FlatMap.2 - onNext(Customer(super=Auditable(super=Customer@196b2, createdAt=null, createdBy=null, modifiedAt=null, modifiedBy=null), schemaVersion=1, internalVersion=null, id=id2))
13:08:03.147 [Test worker] INFO reactor.Flux.Iterable.1 - | onNext(id3)
13:08:03.148 [Test worker] INFO reactor.Flux.Iterable.1 - | onComplete()
13:08:03.149 [Test worker] INFO reactor.Flux.FlatMap.2 - onComplete()
英文:
Imagine that I upload a list of user ids into server to find them; I want to know which are the id not found in database, so I will be aware of that my local cache of db data, or my real data is broken. Missing ids are useful as feedback in UI and in logs.
I want to construct a map with key as input id, and value as found entity user in cache/database. If not found, the value is null
. But, ReactiveCrudRepository
will return Mono.empty() if findById(id)
returns nothing, and consequent mapping will not include not found entity. In test I get error:
Expected: is <true>
but: was <false>
java.lang.AssertionError:
Expected: is <true>
but: was <false>
I am doing:
return Flux.fromIterable(ids)
.concatMap(repository::findById) // <--------------- this step only gets found entities
.collectMap(Customer::getId); // <------------------ so here I get only existent elements(in test, a map with 2 elements)
The key is to know what id is found and what is not. How can I do this in another way in Reactor 3?
The test:
@Test
void findAllByIdAndMissing() {
// given
Customer customer1 = Customer.builder().id("id1").build();
Customer customer2 = Customer.builder().id("id2").build();
// given mocked
when(cache.getAllPresent(ArgumentMatchers.any())).thenReturn(Collections.emptyMap());
when(repository.findById("id1")).thenReturn(Mono.just(customer1));
when(repository.findById("id2")).thenReturn(Mono.just(customer2));
when(repository.findById("id3")).thenReturn(Mono.empty());
// when
Map<String, Customer> actual = service.findAllByIdAndMissing(Set.of("id3", "id2", "id1")).block();
// then
assertThat(actual.keySet().contains("id1"), is(true));
assertThat(actual.keySet().contains("id2"), is(true));
assertThat(actual.keySet().contains("id3"), is(true));
}
If I add log()
:
:08:03.072 [Test worker] INFO reactor.Flux.Iterable.1 - | onSubscribe([Synchronous Fuseable] FluxIterable.IterableSubscription)
13:08:03.078 [Test worker] INFO reactor.Flux.FlatMap.2 - onSubscribe(FluxFlatMap.FlatMapMain)
13:08:03.080 [Test worker] INFO reactor.Flux.FlatMap.2 - request(unbounded)
13:08:03.081 [Test worker] INFO reactor.Flux.Iterable.1 - | request(256)
13:08:03.082 [Test worker] INFO reactor.Flux.Iterable.1 - | onNext(id1)
13:08:03.084 [Test worker] INFO reactor.Flux.FlatMap.2 - onNext(Customer(super=Auditable(super=Customer@196b1, createdAt=null, createdBy=null, modifiedAt=null, modifiedBy=null), schemaVersion=1, internalVersion=null, id=id1))
13:08:03.145 [Test worker] INFO reactor.Flux.Iterable.1 - | onNext(id2)
13:08:03.146 [Test worker] INFO reactor.Flux.FlatMap.2 - onNext(Customer(super=Auditable(super=Customer@196b2, createdAt=null, createdBy=null, modifiedAt=null, modifiedBy=null), schemaVersion=1, internalVersion=null, id=id2))
13:08:03.147 [Test worker] INFO reactor.Flux.Iterable.1 - | onNext(id3)
13:08:03.148 [Test worker] INFO reactor.Flux.Iterable.1 - | onComplete()
13:08:03.149 [Test worker] INFO reactor.Flux.FlatMap.2 - onComplete()
答案1
得分: 1
不确定是否有更惯用的方法来实现这个,但你可以通过一个辅助类轻松实现:
Flux.fromIterable(ids)
.concatMap(id -> repository.findById(id).map(Result::found).defaultIfEmpty(Result.notFound(id)))
.collectMap(Result::getId, Result::getCustomer);
@Data
@AllArgsConstructor(access = AccessLevel.PRIVATE)
class Result {
private final String id;
private final Customer customer;
public static Result found(Customer customer) {
return new Result(customer.getId(), customer);
}
public static Result notFound(String id) {
return new Result(id, null);
}
}
英文:
Not sure if there is a more idiomatic way to do this but you can easily achieve this with a helper class:
Flux.fromIterable(ids)
.concatMap(id -> repository.findById(id).map(Result::found).defaultIfEmpty(Result.notFound(id)))
.collectMap(Result::getId, Result::getCustomer);
@Data
@AllArgsConstructor(access = AccessLevel.PRIVATE)
class Result {
private final String id;
private final Customer customer;
public static Result found(Customer customer) {
return new Result(customer.getId(), customer);
}
public static Result notFound(String id) {
return new Result(id, null);
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论