簡體   English   中英

Java Reactive stream how to map an object when the object being mapped is also needed on the next step of the stream

[英]Java Reactive stream how to map an object when the object being mapped is also needed on the next step of the stream

我正在使用 Java 11 和項目 Reactor(來自 Spring)。 我需要對 rest api 進行 http 調用(我只能在整個流程中調用一次)。 有了響應,我需要計算兩件事:

  1. 檢查數據庫(mongodb)中是否存在文檔。 如果它不存在,則創建它並返回它。 否則就退貨。
  2. 計算一些關於響應的邏輯,我們就完成了。

在偽代碼中是這樣的:

public void computeData(String id) {
    httpClient.getData(id) // Returns a Mono<Data>
        .flatMap(data -> getDocument(data.getDocumenId()))
         // Issue here is we need access to the data object consumed in the previous flatMap but at the same time we also need the document object we get from the previous flatMap
        .flatMap(document -> calculateValue(document, data)) 
        .subscribe();
}

public Mono<Document> getDocument(String id) {
    // Check if document exists
    // If not create document

    return document;
}

public Mono<Value> calculateValue(Document doc, Data data) {
    // Do something...
    return value;
}

問題是,calculateValue 需要 http.getData 的返回值,但這已經在第一個 flatMap 上使用了,但我們還需要我們從之前的 flatMap 獲得的文檔 object。

我嘗試使用Mono.zip解決此問題,如下所示:

public void computeData(String id) {
    final Mono<Data> dataMono = httpClient.getData(id);

    Mono.zip(
        new Mono<Mono<Document>>() {
            @Override
            public void subscribe(CoreSubscriber<? super Mono<Document>> actual) {
                final Mono<Document> documentMono = dataMono.flatMap(data -> getDocument(data.getDocumentId()))
                actual.onNext(documentMono);
            }
        },
        new Mono<Mono<Value>>() {
            @Override
            public void subscribe(CoreSubscriber<? super Mono<Value>> actual) {
                actual.onNext(dataMono);
            }
        }
    )
    .flatMap(objects -> {
        final Mono<Document> documentMono = objects.getT1();
        final Mono<Data> dataMono = objects.getT2();

        return Mono.zip(documentMono, dataMono, (document, data) -> calculateValue(document, data))
    })
}

但這是執行httpClient.getData(id)兩次,這違背了我只調用一次的約束。 我明白為什么要執行兩次(我訂閱了兩次)。

也許我的解決方案設計可以在某個地方改進,但我看不到在哪里。 對我來說,這在設計響應式代碼時聽起來像是一個“正常”問題,但到目前為止我找不到合適的解決方案。

我的問題是,如何以反應性和非阻塞方式完成此流程,並且只調用一次 rest api?

附言; 我可以在一個 map 中添加所有邏輯,但這將迫使我訂閱 map 內的 Mono 之一,這是不推薦的,我想避免采用這種方法。

關於@caco3 評論的編輯我需要在 map 中訂閱,因為getDocumentcalculateValue方法都返回Mono

所以,如果我想把所有的邏輯放在一個單一的 map 中,它會是這樣的:

public void computeData(String id) {
    httpClient.getData(id)
        .map(data -> getDocument(data).subscribe(s -> calculateValue(s, data)))
        .subscribe();
}

您不必在map內訂閱,只需繼續在flatMap內構建反應鏈:

getData(id) // Mono<Data>
        .flatMap(data -> getDocument(data.getDocumentId()) // Mono<Document>
                .switchIfEmpty(createDocument(data.getDocumentId())) // Mono<Document>
                .flatMap(document -> calculateValue(document, data)) // Mono<Value>
         )
        .subscribe()

歸結起來,您的問題類似於:

Mono.just(1)
        .flatMap(original -> process(original))
        .flatMap(processed -> I need access to the original value and the processed value!
                System.out.println(original); //Won't work
        );


private static Mono<String> process(int in) {
    return Mono.just(in + " is an integer").delayElement(Duration.ofSeconds(2));
}

(愚蠢的例子,我知道。)

問題是map() (以及擴展的flatMap() )是轉換- 您可以訪問值,而舊值消失。 因此,在您的第二個flatMap()調用中,您可以訪問1 is an integer ,但不是原始值( 1 。)

這里的解決方案是將 map 映射到包含原始值和新值的某種合並結果,而不是映射到新值。 Reactor 為此提供了一個內置類型 - Tuple 因此,編輯我們的原始示例,我們將擁有:

Mono.just(1)
        .flatMap(original -> operation(original))
        .flatMap(processed -> //Help - I need access to the original value and the processed value!
                System.out.println(processed.getT1()); //Original
                System.out.println(processed.getT2()); //Processed

                ///etc.
        );


private static Mono<Tuple2<Integer, String>> operation(int in) {
    return Mono.just(in + " is an integer").delayElement(Duration.ofSeconds(2))
            .map(newValue -> Tuples.of(in, newValue));
}

您可以使用相同的策略來“保留” documentdata - 無需內部訂閱或任何類似的東西:-)

如何轉換列表<object>至 Map<k, v> 使用 java stream<div id="text_translate"><p> 我想將List&lt;ObjectInList&gt;轉換為Map&lt;K, V&gt;</p><pre> class ObjectInList { List&lt;Long&gt; listWithLong; Map&lt;String, Object&gt; dataMap; // there is 'id' key, and i want to use this id as key in map to be converted }</pre><p> 新的 map 格式如下</p><pre>String type; // this type is value of dataMap. List&lt;Long&gt; contents</pre><p> List&lt;Object&gt;中的每個 Object 都可以有重復的類型</p><p>例如</p><pre>///////// before converted //////////// [ { list: [1,2,3], dataMap: { type: "a", } }, { list: [4,5,6], dataMap: { type: "b", } }, { list: [7,8], dataMap: { type: "a", } }, ] ///////////// after converted ////////// { "a": [1,2,3,7,8], "b": [4,5,6] }</pre></div></k,></object>

[英]How to convert LIst<Object> to Map<K, V> with using java stream

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM