弹性搜索滚动API的rxjs流产生空结果集

时间:2018-06-12 07:37:16

标签: elasticsearch rxjs

我的目标是将elasticsearch结果转换为rxjs流,并考虑使用scroll API在每次调用时获取1个数据点。但是,我的rxjs流似乎没有返回第二个弹性查询(searchElastic)的结果。

以下是我的代码示例:

import * as Rx from 'rxjs';
import {elasticClient} from '../Helpers.ts';

function searchElastic({query, sort}) {
    const body: any = {
        size: 1,
        query,
        _source: { excludes: ['logbookType', 'editable', 'availabilityTag'] },
        sort
    };
    // keep the search results "scrollable" for 30 secs
    const scroll = '30s';

    return Rx.Observable
        .fromPromise(elasticClient.search({ index: 'data', body, scroll }))
        .concatMap(({_scroll_id, hits: {hits}}) => {
            const subject = new Rx.Subject();

            if(hits.length) {
                // initial data
                subject.onNext(hits[0]._source as ElasticDoc);
                console.log(hits[0]._id);

                const handleDoc = (err, res) => {
                    if(err) {
                        subject.onError(err);
                        return;
                    }

                    const {_scroll_id, hits: {hits}} = res;

                    if(!hits.length) {
                        subject.onCompleted();
                    } else {
                        subject.onNext(hits[0]._source as ElasticDoc);
                        console.log(hits[0]._id);
                        setImmediate(() =>
                            elasticClient.scroll({scroll, scrollId: _scroll_id},
                                handleDoc));
                    }
                };

                setImmediate(() =>
                    elasticClient.scroll({scroll, scrollId: _scroll_id},
                        handleDoc));
            } else {
                subject.onCompleted();
            }

            return subject.asObservable();
        });
}

function getEntries() {
    const entriesQuery = {
        query: {
            filtered: {
                filter: {
                    bool: {
                        must: [{
                            range: {
                                creationTimestamp: {
                                    gte: "2018-04-01T07:55:59.915Z",
                                    lte: "2018-04-01T07:57:08.915Z"
                                }
                            }
                        }, {
                            query: {
                                query_string: {
                                    query: "+type:*scan*"
                                }
                            }
                        }]
                    }
                }
            }
        },
        sort: [{
            creationTimestamp: {
                order: "asc"
            },
            id: {
                order: "asc"
            }
        }]
    };
    return searchElastic(entriesQuery)
        .concatMap(entry => {
            // all entries are logged correctly
            console.log(entry.id);
            // array that contains MongoDB _ids as strings
            const ancestors = entry.ancestors || [];

            // if no parents => doesn't match
            if(!ancestors.length) {
                return Rx.Observable.empty();
            }

            const parentsQuery = {
                query: {
                    filtered: {
                        filter: {
                            bool: {
                                must: [{
                                    range: {
                                        creationTimestamp: {
                                            gte: "2018-04-01T07:55:59.915Z",
                                            lte: "2018-04-01T07:57:08.915Z"
                                        }
                                    }
                                }, {
                                    query: {
                                        query_string: {
                                            query: "+type:*block* +name:*Report*"
                                        }
                                    }
                                }]
                            }
                        }
                    }
                },
                sort: [{
                    creationTimestamp: {
                        order: "asc"
                    },
                    id: {
                        order: "asc"
                    }
                }]
            };
            parentsQuery.query.filtered.filter.bool.must.push({
                terms: {
                    id: ancestors
                }
            });

            // fetch parent entries
            return searchElastic(parentsQuery)
                .count()
                .concatMap(count => {
                    // count is always 0 even though entries are logged
                    // in the searchElastic console.logs
                    console.log(entry.id, count);
                    return Rx.Observable.just(entry);
                });
        });
}

function executeQuery() {
    try {
       getEntries()
            .subscribe(
                (x) => console.log(x.id),
                err => console.error(err),
                () => {}
            )
    } catch(e) {
        console.error(e);
    }
}

看起来它是rxjs问题,因为所有祖先条目都会被记录,但count始终返回0.

P.S。使用elasticsearch v1.7

1 个答案:

答案 0 :(得分:0)

在玩了几个rxjs个主题的例子之后,在观察者订阅它之前,似乎主题正在完成(onCompleted)。

工作示例



var subject = new Rx.Subject();

var subscription = subject.subscribe(
  function(x) {
    console.log('onNext: ' + x);
  },
  function(e) {
    console.log('onError: ' + e.message);
  },
  function() {
    console.log('onCompleted');
  });

subject.onNext(1);
// => onNext: 1

subject.onNext(2);
// => onNext: 2

subject.onCompleted();
// => onCompleted

<!DOCTYPE html>
<html>

<head>
  <script src="//code.jquery.com/jquery-2.1.0.min.js"></script>
  <title>JS Bin</title>
  <script src="//cdn.jsdelivr.net/rsvp/3.0.6/rsvp.js"></script>
  <script src="//cdnjs.cloudflare.com/ajax/libs/rxjs/2.2.28/rx.all.min.js"></script>
</head>

</html>
&#13;
&#13;
&#13;

破碎的例子

&#13;
&#13;
var subject = new Rx.Subject();

subject.onNext(1);
// => onNext: 1

subject.onNext(2);
// => onNext: 2

subject.onCompleted();
// => onCompleted

var subscription = subject.subscribe(
  function(x) {
    console.log('onNext: ' + x);
  },
  function(e) {
    console.log('onError: ' + e.message);
  },
  function() {
    console.log('onCompleted');
  });
&#13;
<!DOCTYPE html>
<html>

<head>
  <script src="//code.jquery.com/jquery-2.1.0.min.js"></script>
  <title>JS Bin</title>
  <script src="//cdn.jsdelivr.net/rsvp/3.0.6/rsvp.js"></script>
  <script src="//cdnjs.cloudflare.com/ajax/libs/rxjs/2.2.28/rx.all.min.js"></script>
</head>

</html>
&#13;
&#13;
&#13;

所以我通过将searchElastic更改为以下内容来修复它:

function searchElasticStream({query, sort}) {
    const body: any = {
        size: 1,
        query,
        _source: { excludes: ['logbookType', 'editable', 'availabilityTag'] },
        sort
    };
    // keep the search results "scrollable" for 30 secs
    const scroll = '30s';

    return Rx.Observable
        .fromPromise(elasticClient.search({ index: 'data', body, scroll }))
        .flatMap(({_scroll_id, hits: {hits}}) => {
            const subject = new Rx.Subject();

            // this made the difference
            setImmediate(() => {
                if(hits.length) {
                    // initial data
                    subject.onNext(hits[0]._source as ElasticDoc);

                    const handleDoc = (err, res) => {
                        if(err) {
                            subject.onError(err);
                            return;
                        }

                        const {_scroll_id, hits: {hits}} = res;

                        if(!hits.length) {
                            subject.onCompleted();
                        } else {
                            subject.onNext(hits[0]._source as ElasticDoc);
                            setImmediate(() =>
                                elasticClient.scroll({scroll, scrollId: _scroll_id},
                                    handleDoc));
                        }
                    };

                    setImmediate(() =>
                        elasticClient.scroll({scroll, scrollId: _scroll_id},
                            handleDoc));
                } else {
                    subject.onCompleted();
                }
            });

            return subject.asObservable();
        });
}