Skip to content

Commit 930dc3e

Browse files
authored
Merge pull request #342 from arjes/add_metadata_to_page_iterator
Adding metadata to page iteration
2 parents af8ccc0 + 9d2c13e commit 930dc3e

File tree

7 files changed

+144
-65
lines changed

7 files changed

+144
-65
lines changed

README.md

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -590,11 +590,30 @@ Access to the native DynamoDB pages can be obtained via the `find_by_pages`
590590
method, which yields arrays of records.
591591

592592
```ruby
593-
Address.find_by_pages do |addresses|
593+
Address.find_by_pages do |addresses, metadata|
594594
# have an array of pages
595595
end
596596
```
597597

598+
Each yielded pages returns metadata as the second argument, which is a hash
599+
including a key `:last_evaluated_key`. The value of this key can be used for
600+
the `start` method to fetch the next page of records.
601+
602+
```ruby
603+
class UserController < ApplicationController
604+
def index
605+
next_page = params[:next_page_token] ? JSON.parse(Base64.decode64(params[:next_page_token])) : ''
606+
607+
records, metadata = User.start(next_page).find_by_pages.first
608+
609+
render json: {
610+
records: records,
611+
next_page_token: Base64.encode64(metadata[:last_evaluated_key].to_json)
612+
}
613+
end
614+
end
615+
```
616+
598617
#### Sort Conditions and Filters
599618

600619
You are able to optimize query with condition for sort key. Following operators are available: `gt`, `lt`, `gte`, `lte`,

lib/dynamoid/adapter_plugin/aws_sdk_v3.rb

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -532,11 +532,15 @@ def put_item(table_name, object, options = {})
532532
#
533533
# @todo Provide support for various other options http://docs.aws.amazon.com/sdkforruby/api/Aws/DynamoDB/Client.html#query-instance_method
534534
def query(table_name, options = {})
535-
return enum_for(:query, table_name, options) unless block_given?
536-
table = describe_table(table_name)
535+
Enumerator.new do |yielder|
536+
table = describe_table(table_name)
537537

538-
Query.new(client, table, options).call.each do |page|
539-
yield page.items.map{ |row| result_item_to_hash(row) }
538+
Query.new(client, table, options).call.each do |page|
539+
yielder.yield(
540+
page.items.map { |row| result_item_to_hash(row) },
541+
last_evaluated_key: page.last_evaluated_key
542+
)
543+
end
540544
end
541545
end
542546

@@ -561,11 +565,15 @@ def query_count(table_name, options = {})
561565
#
562566
# @todo: Provide support for various options http://docs.aws.amazon.com/sdkforruby/api/Aws/DynamoDB/Client.html#scan-instance_method
563567
def scan(table_name, conditions = {}, options = {})
564-
return enum_for(:scan, table_name, conditions, options) unless block_given?
565-
table = describe_table(table_name)
568+
Enumerator.new do |yielder|
569+
table = describe_table(table_name)
566570

567-
Scan.new(client, table, conditions, options).call.each do |page|
568-
yield page.items.map{ |row| result_item_to_hash(row) }
571+
Scan.new(client, table, conditions, options).call.each do |page|
572+
yielder.yield(
573+
page.items.map { |row| result_item_to_hash(row) },
574+
last_evaluated_key: page.last_evaluated_key
575+
)
576+
end
569577
end
570578
end
571579

lib/dynamoid/criteria.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@ module ClassMethods
1313
# see Dynamoid::Criteria::Chain.
1414
#
1515
# @since 0.2.0
16-
define_method(meth) do |*args|
16+
define_method(meth) do |*args, &blk|
1717
chain = Dynamoid::Criteria::Chain.new(self)
1818
if args
19-
chain.send(meth, *args)
19+
chain.send(meth, *args, &blk)
2020
else
21-
chain.send(meth)
21+
chain.send(meth, &blk)
2222
end
2323
end
2424
end

lib/dynamoid/criteria/chain.rb

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -163,10 +163,10 @@ def pages
163163
#
164164
# @since 3.1.0
165165
def pages_via_query
166-
return enum_for(:pages_via_query) unless block_given?
167-
168-
Dynamoid.adapter.query(source.table_name, range_query).each do |items|
169-
yield items.map { |hash| source.from_database(hash) }
166+
Enumerator.new do |yielder|
167+
Dynamoid.adapter.query(source.table_name, range_query).each do |items, metadata|
168+
yielder.yield items.map { |hash| source.from_database(hash) }, metadata.slice(:last_evaluated_key)
169+
end
170170
end
171171
end
172172

@@ -176,10 +176,10 @@ def pages_via_query
176176
#
177177
# @since 3.1.0
178178
def pages_via_scan
179-
return enum_for(:pages_via_scan) unless block_given?
180-
181-
Dynamoid.adapter.scan(source.table_name, scan_query, scan_opts).each do |items|
182-
yield items.map { |hash| source.from_database(hash) }
179+
Enumerator.new do |yielder|
180+
Dynamoid.adapter.scan(source.table_name, scan_query, scan_opts).each do |items, metadata|
181+
yielder.yield(items.map { |hash| source.from_database(hash) }, metadata.slice(:last_evaluated_key))
182+
end
183183
end
184184
end
185185

spec/dynamoid/adapter_plugin/aws_sdk_v3_spec.rb

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -258,27 +258,27 @@ def dynamo_request(table_name, scan_hash = {}, select_opts = {})
258258
end
259259

260260
it 'performs query on a table with a range and selects items in a range' do
261-
expect(Dynamoid.adapter.query(test_table3, hash_value: '1', range_between: [0.0, 3.0]).to_a).to eq [[{ id: '1', range: BigDecimal(1) }, { id: '1', range: BigDecimal(3) }]]
261+
expect(Dynamoid.adapter.query(test_table3, hash_value: '1', range_between: [0.0, 3.0]).to_a).to eq [[[{ id: '1', range: BigDecimal(1) }, { id: '1', range: BigDecimal(3) }], {last_evaluated_key: nil}]]
262262
end
263263

264264
it 'performs query on a table with a range and selects items in a range with :select option' do
265-
expect(Dynamoid.adapter.query(test_table3, hash_value: '1', range_between: [0.0, 3.0], select: 'ALL_ATTRIBUTES').to_a).to eq [[{ id: '1', range: BigDecimal(1) }, { id: '1', range: BigDecimal(3) }]]
265+
expect(Dynamoid.adapter.query(test_table3, hash_value: '1', range_between: [0.0, 3.0], select: 'ALL_ATTRIBUTES').to_a).to eq [[[{ id: '1', range: BigDecimal(1) }, { id: '1', range: BigDecimal(3) }], {last_evaluated_key: nil}]]
266266
end
267267

268268
it 'performs query on a table with a range and selects items greater than' do
269-
expect(Dynamoid.adapter.query(test_table3, hash_value: '1', range_greater_than: 1.0).to_a).to eq [[{ id: '1', range: BigDecimal(3) }]]
269+
expect(Dynamoid.adapter.query(test_table3, hash_value: '1', range_greater_than: 1.0).to_a).to eq [[[{ id: '1', range: BigDecimal(3) }], {last_evaluated_key: nil}]]
270270
end
271271

272272
it 'performs query on a table with a range and selects items less than' do
273-
expect(Dynamoid.adapter.query(test_table3, hash_value: '1', range_less_than: 2.0).to_a).to eq [[{ id: '1', range: BigDecimal(1) }]]
273+
expect(Dynamoid.adapter.query(test_table3, hash_value: '1', range_less_than: 2.0).to_a).to eq [[[{ id: '1', range: BigDecimal(1) }], {last_evaluated_key: nil}]]
274274
end
275275

276276
it 'performs query on a table with a range and selects items gte' do
277-
expect(Dynamoid.adapter.query(test_table3, hash_value: '1', range_gte: 1.0).to_a).to eq [[{ id: '1', range: BigDecimal(1) }, { id: '1', range: BigDecimal(3) }]]
277+
expect(Dynamoid.adapter.query(test_table3, hash_value: '1', range_gte: 1.0).to_a).to eq [[[{ id: '1', range: BigDecimal(1) }, { id: '1', range: BigDecimal(3) }], {last_evaluated_key: nil}]]
278278
end
279279

280280
it 'performs query on a table with a range and selects items lte' do
281-
expect(Dynamoid.adapter.query(test_table3, hash_value: '1', range_lte: 3.0).to_a).to eq [[{ id: '1', range: BigDecimal(1) }, { id: '1', range: BigDecimal(3) }]]
281+
expect(Dynamoid.adapter.query(test_table3, hash_value: '1', range_lte: 3.0).to_a).to eq [[[{ id: '1', range: BigDecimal(1) }, { id: '1', range: BigDecimal(3) }], {last_evaluated_key: nil}]]
282282
end
283283

284284
it 'performs query on a table and returns items based on returns correct limit' do
@@ -677,11 +677,14 @@ def dynamo_request(table_name, scan_hash = {}, select_opts = {})
677677
])
678678

679679
results = Dynamoid.adapter.scan(test_table3)
680-
expect(results.to_a.first).to contain_exactly(
681-
{ id: '1', range: 1.0 },
682-
{ id: '2', range: 2.0 },
683-
{ id: '3', range: 3.0 }
684-
)
680+
expect(results.to_a.first).to match [
681+
contain_exactly(
682+
{ id: '1', range: 1.0 },
683+
{ id: '2', range: 2.0 },
684+
{ id: '3', range: 3.0 }
685+
),
686+
{ last_evaluated_key: nil }
687+
]
685688
end
686689

687690
it 'performs BatchDeleteItem with more than 25 items' do
@@ -790,14 +793,14 @@ def dynamo_request(table_name, scan_hash = {}, select_opts = {})
790793
it 'performs query on a table and returns items' do
791794
Dynamoid.adapter.put_item(test_table1, id: '1', name: 'Josh')
792795

793-
expect(Dynamoid.adapter.query(test_table1, hash_value: '1').first).to eq([id: '1', name: 'Josh'])
796+
expect(Dynamoid.adapter.query(test_table1, hash_value: '1').first).to eq([[id: '1', name: 'Josh'], {last_evaluated_key: nil}])
794797
end
795798

796799
it 'performs query on a table and returns items if there are multiple items' do
797800
Dynamoid.adapter.put_item(test_table1, id: '1', name: 'Josh')
798801
Dynamoid.adapter.put_item(test_table1, id: '2', name: 'Justin')
799802

800-
expect(Dynamoid.adapter.query(test_table1, hash_value: '1').first).to eq([id: '1', name: 'Josh'])
803+
expect(Dynamoid.adapter.query(test_table1, hash_value: '1').first).to eq([[id: '1', name: 'Josh'], {last_evaluated_key: nil}])
801804
end
802805

803806
context 'backoff is specified' do
@@ -834,21 +837,28 @@ def dynamo_request(table_name, scan_hash = {}, select_opts = {})
834837
it 'performs scan on a table and returns items' do
835838
Dynamoid.adapter.put_item(test_table1, id: '1', name: 'Josh')
836839

837-
expect(Dynamoid.adapter.scan(test_table1, name: { eq: 'Josh' }).to_a).to eq [[{ id: '1', name: 'Josh' }]]
840+
expect(Dynamoid.adapter.scan(test_table1, name: { eq: 'Josh' }).to_a).to eq [[[{ id: '1', name: 'Josh' }], {last_evaluated_key: nil}]]
838841
end
839842

840843
it 'performs scan on a table and returns items if there are multiple items but only one match' do
841844
Dynamoid.adapter.put_item(test_table1, id: '1', name: 'Josh')
842845
Dynamoid.adapter.put_item(test_table1, id: '2', name: 'Justin')
843846

844-
expect(Dynamoid.adapter.scan(test_table1, name: { eq: 'Josh' }).to_a).to eq [[{ id: '1', name: 'Josh' }]]
847+
expect(Dynamoid.adapter.scan(test_table1, name: { eq: 'Josh' }).to_a).to eq [[[{ id: '1', name: 'Josh' }], {last_evaluated_key: nil}]]
845848
end
846849

847850
it 'performs scan on a table and returns multiple items if there are multiple matches' do
848851
Dynamoid.adapter.put_item(test_table1, id: '1', name: 'Josh')
849852
Dynamoid.adapter.put_item(test_table1, id: '2', name: 'Josh')
850853

851-
expect(Dynamoid.adapter.scan(test_table1, name: { eq: 'Josh' })).to match_array(include({ name: 'Josh', id: '2' }, name: 'Josh', id: '1'))
854+
expect(
855+
Dynamoid.adapter.scan(test_table1, name: { eq: 'Josh' }).to_a
856+
).to match([
857+
[
858+
match_array([{ name: 'Josh', id: '2' }, { name: 'Josh', id: '1' }]),
859+
{last_evaluated_key: nil}
860+
]
861+
])
852862
end
853863

854864
it 'performs scan on a table and returns all items if no criteria are specified' do

0 commit comments

Comments
 (0)