“Reactor – 如何将找到的实体和未找到的实体收集到一个以ID为键的映射中?”

huangapple go评论88阅读模式
英文:

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 &lt;true&gt;
     but: was &lt;false&gt;
java.lang.AssertionError: 
Expected: is &lt;true&gt;
     but: was &lt;false&gt;

I am doing:

return Flux.fromIterable(ids)
            .concatMap(repository::findById) // &lt;--------------- this step only gets found entities
            .collectMap(Customer::getId); // &lt;------------------ 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(&quot;id1&quot;).build();
        Customer customer2 = Customer.builder().id(&quot;id2&quot;).build();
        // given mocked
        when(cache.getAllPresent(ArgumentMatchers.any())).thenReturn(Collections.emptyMap());
        when(repository.findById(&quot;id1&quot;)).thenReturn(Mono.just(customer1));
        when(repository.findById(&quot;id2&quot;)).thenReturn(Mono.just(customer2));
        when(repository.findById(&quot;id3&quot;)).thenReturn(Mono.empty());

        // when
        Map&lt;String, Customer&gt; actual = service.findAllByIdAndMissing(Set.of(&quot;id3&quot;, &quot;id2&quot;, &quot;id1&quot;)).block();

        // then
        assertThat(actual.keySet().contains(&quot;id1&quot;), is(true));
        assertThat(actual.keySet().contains(&quot;id2&quot;), is(true));
        assertThat(actual.keySet().contains(&quot;id3&quot;), 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 -&gt; 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);
    }
}

huangapple
  • 本文由 发表于 2020年8月27日 19:00:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/63614589.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定