如何将来自不同可观察量的rx.Observable搜索结果合并到一个Observable中?

时间:2015-08-07 15:12:40

标签: java android system.reactive rx-java

对于我的Android应用程序,我需要一个Observable,它聚合来自7个不同搜索的结果并作为单个集合发出。

  • 对于最终发布,我选择了ListMultimap<Content, SearchResult>,其中Content是搜索结果类型的枚举,SearchResult是用于显示搜索结果的视图模型界面。

这是我如何做到的,但有哪些我不知道的RxJava操作符可以做得更干净吗?

public Observable<ListMultimap<Content, SearchResult>> getSearchResults(final CharSequence charSequence) {
        return Observable.just(charSequence)
                .map(new Func1<CharSequence, ListMultimap<Content, SearchResult>>() {
                    @Override
                    public ListMultimap<Content, SearchResult> call(CharSequence charSequence) {
                        final String searchTerm = charSequence.toString();

                        final ListMultimap<Content, SearchResult> data =
                                MultimapBuilder.enumKeys(Content.class)
                                        .arrayListValues()
                                        .build();

                        for (SearchResult searchResult : SearchBookmarkableRepoImpl.this.getSectionPageSearchResults(searchTerm).toBlocking().first())
                            data.put(Content.SECTION_PAGE, searchResult);

                        for (SearchResult searchResult : SearchBookmarkableRepoImpl.this.getAppendixPageSearchResults(searchTerm).toBlocking().first())
                            data.put(Content.APPENDIX_PAGE, searchResult);

                        for (SearchResult searchResult : SearchBookmarkableRepoImpl.this.getImageSearchResults(searchTerm, ImageType.FIGURE).toBlocking().first())
                            data.put(Content.FIGURE, searchResult);

                        for (SearchResult searchResult : SearchBookmarkableRepoImpl.this.getImageSearchResults(searchTerm, ImageType.TABLE).toBlocking().first())
                            data.put(Content.TABLE, searchResult);

                        for (SearchResult searchResult : SearchBookmarkableRepoImpl.this.getImageSearchResults(searchTerm, ImageType.FORM).toBlocking().first())
                            data.put(Content.FORM, searchResult);

                        for (SearchResult searchResult : SearchBookmarkableRepoImpl.this.getGlossarySearchResults(searchTerm, GlossaryType.ACRONYMS).toBlocking().first())
                            data.put(Content.ACRONYM, searchResult);

                        for (SearchResult searchResult : SearchBookmarkableRepoImpl.this.getGlossarySearchResults(searchTerm, GlossaryType.DEFINITIONS).toBlocking().first())
                            data.put(Content.DEFINITION, searchResult);

                        return data;
                    }
                });
    }

以下是7种搜索方法之一的示例方法签名。每个返回Observable<List<SearchResult>>如果RxJava有更好的方法来执行此操作,我可以将它们作为单独的SearchResult而不是List<>

发出
Observable<List<SearchResult>> getSectionPageSearchResults(final String searchWord)

2 个答案:

答案 0 :(得分:1)

让我们一步一步。

为了使它更通用,假设你有一个名为anEnum的对象(= SearchResult)和enum(= Content)。

我已经将ArrayListMultimap用于具体的实现,我之前没有使用它,所以如果有更好的方法来添加对象请做。

第一步:map Observable<List<Object>>Observable<ArrayListMultimap<anEnum, Object>>

    private Observable<ArrayListMultimap<anEnum, Object>> getMultiListSectionPageSearchResults(final String searchWord, anEnum anEnum) {
    return getSectionPageSearchResults(searchWord).map(objects -> {
        ArrayListMultimap<anEnum, Object> newList = ArrayListMultimap.create();
        for (Object o : objects) {
            newList.put(anEnum, o);
        }
        return newList;
    });
    }

现在您有一个所需格式的列表。

第二步:为所有数据创建observable,假设你有4个

    Observable<ArrayListMultimap<anEnum, Object>> multiList1 = getMultiListSectionPageSearchResults("one", anEnum.ONE);
    Observable<ArrayListMultimap<anEnum, Object>> multiList2 = getMultiListSectionPageSearchResults("two", anEnum.TWO);
    Observable<ArrayListMultimap<anEnum, Object>> multiList3 = getMultiListSectionPageSearchResults("three", anEnum.THREE);
    Observable<ArrayListMultimap<anEnum, Object>> multiList4 = getMultiListSectionPageSearchResults("four", anEnum.FOUR);

第三步:merge将它们转换为一个流(如果订单对您很重要,您还可以使用concat

        Observable<ArrayListMultimap<anEnum, Object>> mergedLists = Observable.merge(
            multiList1, multiList2, multiList3, multiList4);

第四步:reduce将它们放到一个列表中,我们将使用第一个列表作为将保留所有列表的列表

        Observable<ArrayListMultimap<anEnum, Object>> oneList = mergedLists.reduce((l1, l2) -> {
        for (anEnum c : l2.keySet()) {
            for (Object o : l2.get(c)) {
                l1.put(c, o);
            }
        }
        return l1;
        });

最后一步:使用数据,single()只是为了确保我们实际获得一个列表

oneList.single().subscribe(this::doSomething);

doSomething:private void doSomething(ArrayListMultimap<anEnum, Object> list)

如果您使用常规列表,转换将会更容易,因为已经有一些方法可以为您执行此操作,例如l1.addAll(l2) for reduce

答案 1 :(得分:0)

假设您将单个Observable拆分为每个搜索类型的单独Observable,您可以使用combining operatorsaggregate operators之一来收集所有搜索结果再次成为一个Observable。这将使您能够并行运行搜索,并且如果您愿意,也可以运行单个搜索。

例如,您可以连接两个Observable,如下所示:

Observable o1 = Observable.just(1, 2, 3);
Observable o2 = Observable.just(4, 5, 6);
Observable result = o1.concatWith(o2);

result观察者会发出1, 2, 3, 4, 5, 6