diff --git a/lib/fbe/iterate.rb b/lib/fbe/iterate.rb index d702a652..c43be277 100644 --- a/lib/fbe/iterate.rb +++ b/lib/fbe/iterate.rb @@ -318,12 +318,8 @@ def over # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metric if @sorting values[repo] ||= @fb.query(@query).each( @fb, before: before[repo], repository: repo - ).filter_map { _1[@sorting]&.first }.uniq.sort!.each - begin - values[repo].next - rescue StopIteration - nil - end + ).filter_map { _1[@sorting]&.first }.uniq.sort! + values[repo].bsearch { _1 > before[repo] } else @fb.query(@query).one(@fb, before: before[repo], repository: repo) end diff --git a/test/fbe/test_iterate.rb b/test/fbe/test_iterate.rb index 3abbdf5c..8bf83409 100644 --- a/test/fbe/test_iterate.rb +++ b/test/fbe/test_iterate.rb @@ -749,6 +749,93 @@ def test_continues_where_the_previous_run_stopped assert_equal([[1, 2], [3], []], runs) end + def test_sort_by_skips_values_up_to_the_one_block_returns + seed = Random.new_seed + base = Random.new(seed).rand(0..1_000) + opts = Judges::Options.new(['repositories=foo/bar', 'testing=true']) + fb = Fbe.fb(fb: Factbase.new, global: {}, options: opts, loog: Loog::NULL) + (base + 1).upto(base + 6) do |n| + f = fb.insert + f.what = 'issue' + f.n = n + end + seen = [] + Fbe.iterate(fb:, loog: Loog::NULL, options: opts, global: {}, epoch: Time.now, kickoff: Time.now) do + as('skipped') + by('(and (eq what "issue") (gt n $before))') + sort_by('n') + since!(base) + repeats(10) + over do |_repository, n| + seen << n + n + 2 + end + end + assert_equal([base + 1, base + 4], seen, "values below the returned one are visited, seed #{seed}") + end + + def test_sort_by_stores_the_value_block_returns + seed = Random.new_seed + base = Random.new(seed).rand(0..1_000) + opts = Judges::Options.new(['repositories=foo/bar', 'testing=true']) + fb = Fbe.fb(fb: Factbase.new, global: {}, options: opts, loog: Loog::NULL) + (base + 1).upto(base + 6) do |n| + f = fb.insert + f.what = 'issue' + f.n = n + end + Fbe.iterate(fb:, loog: Loog::NULL, options: opts, global: {}, epoch: Time.now, kickoff: Time.now) do + as('stored') + by('(and (eq what "issue") (gt n $before))') + sort_by('n') + since!(base) + repeats(10) + over do |_repository, n| + n + 2 + end + end + assert_equal( + base + 6, fb.query('(eq what "iterate")').each.first.stored, + "marker is not what was processed, seed #{seed}" + ) + end + + def test_sort_by_visits_what_a_plain_query_visits + seed = Random.new_seed + base = Random.new(seed).rand(0..1_000) + opts = Judges::Options.new(['repositories=foo/bar', 'testing=true']) + fb = Fbe.fb(fb: Factbase.new, global: {}, options: opts, loog: Loog::NULL) + (base + 1).upto(base + 9) do |n| + f = fb.insert + f.what = 'issue' + f.n = n + end + plain = [] + Fbe.iterate(fb:, loog: Loog::NULL, options: opts, global: {}, epoch: Time.now, kickoff: Time.now) do + as('plain') + by('(agg (and (eq what "issue") (gt n $before)) (min n))') + since!(base) + repeats(10) + over do |_repository, n| + plain << n + n + 3 + end + end + sorted = [] + Fbe.iterate(fb:, loog: Loog::NULL, options: opts, global: {}, epoch: Time.now, kickoff: Time.now) do + as('sorted') + by('(and (eq what "issue") (gt n $before))') + sort_by('n') + since!(base) + repeats(10) + over do |_repository, n| + sorted << n + n + 3 + end + end + assert_equal(plain, sorted, "sort_by visits other values than a plain query, seed #{seed}") + end + private def fresh_iterator