diff --git a/3rdParty/iresearch/core/search/disjunction.hpp b/3rdParty/iresearch/core/search/disjunction.hpp index ac58e3629f87..9d6e9af1f20a 100644 --- a/3rdParty/iresearch/core/search/disjunction.hpp +++ b/3rdParty/iresearch/core/search/disjunction.hpp @@ -766,6 +766,9 @@ class disjunction final virtual void visit(void* ctx, bool (*visitor)(void*, Adapter&)) override { assert(ctx); assert(visitor); + if (heap_.empty()) { + return; + } hitch_all_iterators(); auto& lead = itrs_[heap_.back()]; auto cont = visitor(ctx, lead); @@ -908,6 +911,7 @@ class disjunction final } std::pair hitch_all_iterators() { + assert(!heap_.empty()); // hitch all iterators in head to the lead (current doc_) auto begin = heap_.begin(), end = heap_.end()-1; diff --git a/CHANGELOG b/CHANGELOG index 0e7fd0096d31..463decb5e115 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,7 @@ devel ----- +* Fix DEVSUP-753: now it is safe to call visit on exhausted disjunction + iterator. * Slightly improve specific warning messages for better readability. diff --git a/tests/js/common/aql/aql-view-arangosearch-noncluster.js b/tests/js/common/aql/aql-view-arangosearch-noncluster.js index 7fc757f604b5..30be33a3558b 100644 --- a/tests/js/common/aql/aql-view-arangosearch-noncluster.js +++ b/tests/js/common/aql/aql-view-arangosearch-noncluster.js @@ -2567,7 +2567,55 @@ function iResearchAqlTestSuite () { db._dropView(queryView); analyzers.remove(queryAnalyzer, true); } + }, + + testDisjunctionVisit : function() { + let queryColl = "DisjunctionCollection"; + let queryView = "DisjunctionView"; + try { + db._drop(queryColl); + db._dropView(queryView); + let coll = db._create(queryColl); + let view = db._createView(queryView, "arangosearch", { + links: { + [queryColl] : { + fields: { + value: { analyzers:['identity', 'text_en'] } + } + } + } + }); + let documents = [ + {"value":"test"}, + // these docs will make STARTS_WITH more "costly" than PHRASE and conjunction will use PHRASE as lead! + {"value":"test1234"}, + {"value":"test21321312312"}, + {"value":"test213213123122"}, + {"value":"test2132131231222"}, + {"value":"test21321312312222"}, + {"value":"test2132131231222322"}, + {"value":"test2132131231231231222322"}, + // this will make PHRASE iterator not use small_disjunction (more than 5 candidates for LEVENSHTEIN) + {"value":"rest"}, + {"value":"arest"}, + {"value":"brest"}, + {"value":"zest"}, + {"value":"qest"}]; + db[queryColl].save(documents); + let res = db._query("FOR d IN " + queryView +" SEARCH " + + " (ANALYZER(PHRASE(d.value, {LEVENSHTEIN_MATCH : ['test', 2, true]}), 'text_en') " + + " && ANALYZER(STARTS_WITH(d.value, 'test'),'text_en')) OR " + + " BOOST(PHRASE(d.value, 'test144', 'identity'), 10) " + + " OPTIONS {waitForSync:true} SORT BM25(d) DESC LIMIT 20 " + + " RETURN {'Score':BM25(d), 'Id' : d.value, 'Entity' : d }").toArray(); + assertEqual(1, res.length); + + } finally { + db._drop(queryColl); + db._dropView(queryView); + } } + }; }