Skip to content

Commit 4f447e8

Browse files
committed
[MODEL] Added Rails-compatible version of pagination support with Kaminari
# Controller: def search @articles = Article.search(params[:q]).page(params[:page]).records render :action => "index" end # View: <%= paginate @articles %> Added integration tests. TODO: Make the <Response> object lazy loaded.
1 parent dabe800 commit 4f447e8

File tree

7 files changed

+143
-10
lines changed

7 files changed

+143
-10
lines changed

elasticsearch-model/lib/elasticsearch/model/response.rb

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,14 @@ def initialize(klass, search, response)
3131
#
3232
def results
3333
@response ||= search.execute!
34-
@results ||= Results.new(klass, response)
34+
@results ||= Results.new(klass, response, nil, self)
3535
end
3636

3737
# Return the collection of records from the database
3838
#
3939
def records
4040
@response ||= search.execute!
41-
@records ||= Records.new(klass, response, results)
41+
@records ||= Records.new(klass, response, results, self)
4242
end
4343

4444
end

elasticsearch-model/lib/elasticsearch/model/response/base.rb

+3-2
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,16 @@ module Response
44
# Common funtionality for classes in the {Elasticsearch::Model::Response} module
55
#
66
module Base
7-
attr_reader :klass, :response,
7+
attr_reader :klass, :response, :response_object,
88
:total, :max_score
99

1010
# @param klass [Class] The name of the model class
1111
# @param response [Hash] The full response returned from Elasticsearch client
1212
# @param results [Results] The collection of results
1313
#
14-
def initialize(klass, response, results=nil)
14+
def initialize(klass, response, results=nil, response_object=nil)
1515
@klass = klass
16+
@response_object = response_object
1617
@response = response
1718
@total = response['hits']['total']
1819
@max_score = response['hits']['max_score']

elasticsearch-model/lib/elasticsearch/model/response/pagination.rb

+10-1
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,20 @@ module Pagination
99
#
1010
module Kaminari
1111
def self.included(base)
12-
# Include the Kaminari modules: Configuration and paging
12+
# Include the Kaminari configuration and paging method in response
1313
#
1414
base.__send__ :include, ::Kaminari::ConfigurationMethods::ClassMethods
1515
base.__send__ :include, ::Kaminari::PageScopeMethods
1616

17+
# Include the Kaminari paging methods in results and records
18+
#
19+
Elasticsearch::Model::Response::Results.__send__ :include, ::Kaminari::ConfigurationMethods::ClassMethods
20+
Elasticsearch::Model::Response::Results.__send__ :include, ::Kaminari::PageScopeMethods
21+
Elasticsearch::Model::Response::Records.__send__ :include, ::Kaminari::PageScopeMethods
22+
23+
Elasticsearch::Model::Response::Results.__send__ :forward, :response_object, :limit_value, :offset_value, :total_count
24+
Elasticsearch::Model::Response::Records.__send__ :forward, :response_object, :limit_value, :offset_value, :total_count
25+
1726
base.class_eval <<-RUBY, __FILE__, __LINE__ + 1
1827
# Define the `page` Kaminari method
1928
#

elasticsearch-model/lib/elasticsearch/model/response/records.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class Records
1717

1818
# @see Base#initialize
1919
#
20-
def initialize(klass, response, results)
20+
def initialize(klass, response, results=nil, response_object=nil)
2121
super
2222
@response = response
2323
@results = results

elasticsearch-model/lib/elasticsearch/model/response/results.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class Results
1818

1919
# @see Base#initialize
2020
#
21-
def initialize(klass, response)
21+
def initialize(klass, response, results=nil, response_object=nil)
2222
super
2323
# TODO: Configurable custom wrapper
2424
@results = response['hits']['hits'].map { |hit| Result.new(hit) }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
require 'test_helper'
2+
3+
module Elasticsearch
4+
module Model
5+
class ActiveRecordPaginationTest < Elasticsearch::Test::IntegrationTestCase
6+
7+
class ::Article < ActiveRecord::Base
8+
include Elasticsearch::Model
9+
10+
settings index: { number_of_shards: 1, number_of_replicas: 0 } do
11+
mapping do
12+
indexes :title, type: 'string', analyzer: 'snowball'
13+
indexes :created_at, type: 'date'
14+
end
15+
end
16+
end
17+
18+
Kaminari::Hooks.init
19+
20+
context "ActiveRecord pagination" do
21+
setup do
22+
ActiveRecord::Schema.define(:version => 1) do
23+
create_table :articles do |t|
24+
t.string :title
25+
t.datetime :created_at, :default => 'NOW()'
26+
end
27+
end
28+
29+
Article.delete_all
30+
Article.__elasticsearch__.create_index! force: true
31+
32+
68.times do |i| ::Article.create! title: "Test #{i}" end
33+
34+
Article.import
35+
Article.__elasticsearch__.refresh_index!
36+
end
37+
38+
should "be on the first page by default" do
39+
records = Article.search('title:test').page(1).records
40+
41+
assert_equal 25, records.size
42+
assert_equal 1, records.current_page
43+
assert_equal nil, records.prev_page
44+
assert_equal 2, records.next_page
45+
assert_equal 3, records.total_pages
46+
47+
assert records.first_page?, "Should be the first page"
48+
assert ! records.last_page?, "Should NOT be the last page"
49+
assert ! records.out_of_range?, "Should NOT be out of range"
50+
end
51+
52+
should "load next page" do
53+
records = Article.search('title:test').page(2).records
54+
55+
assert_equal 25, records.size
56+
assert_equal 2, records.current_page
57+
assert_equal 1, records.prev_page
58+
assert_equal 3, records.next_page
59+
assert_equal 3, records.total_pages
60+
61+
assert ! records.first_page?, "Should NOT be the first page"
62+
assert ! records.last_page?, "Should NOT be the last page"
63+
assert ! records.out_of_range?, "Should NOT be out of range"
64+
end
65+
66+
should "load last page" do
67+
records = Article.search('title:test').page(3).records
68+
69+
assert_equal 18, records.size
70+
assert_equal 3, records.current_page
71+
assert_equal 2, records.prev_page
72+
assert_equal nil, records.next_page
73+
assert_equal 3, records.total_pages
74+
75+
assert ! records.first_page?, "Should NOT be the first page"
76+
assert records.last_page?, "Should be the last page"
77+
assert ! records.out_of_range?, "Should NOT be out of range"
78+
end
79+
80+
should "not load invalid page" do
81+
records = Article.search('title:test').page(6).records
82+
83+
assert_equal 0, records.size
84+
assert_equal 6, records.current_page
85+
assert_equal 5, records.prev_page
86+
assert_equal nil, records.next_page
87+
assert_equal 3, records.total_pages
88+
89+
assert ! records.first_page?, "Should NOT be the first page"
90+
assert records.last_page?, "Should be the last page"
91+
assert records.out_of_range?, "Should be out of range"
92+
end
93+
end
94+
95+
end
96+
end
97+
end

elasticsearch-model/test/unit/response_pagination_test.rb

+29-3
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ def self.document_type; 'bar'; end
1010
end
1111

1212
RESPONSE = { 'took' => '5', 'timed_out' => false, '_shards' => {'one' => 'OK'},
13-
'hits' => { 'total' => 100, 'hits' => [ {'_id' => 1} ] } }
13+
'hits' => { 'total' => 100, 'hits' => (1..100).to_a.map { |i| { _id: i } } } }
1414

1515
setup do
16-
search = Elasticsearch::Model::Searching::SearchRequest.new ModelClass, '*'
17-
@response = Elasticsearch::Model::Response::Response.new ModelClass, search, RESPONSE
16+
@search = Elasticsearch::Model::Searching::SearchRequest.new ModelClass, '*'
17+
@response = Elasticsearch::Model::Response::Response.new ModelClass, @search, RESPONSE
1818
@response.klass.stubs(:client).returns mock('client')
1919
end
2020

@@ -136,5 +136,31 @@ def self.document_type; 'bar'; end
136136
assert_equal 100, @response.total_count
137137
end
138138
end
139+
140+
context "results" do
141+
setup do
142+
@search.stubs(:execute!).returns RESPONSE
143+
end
144+
145+
should "return current page and total count" do
146+
assert_equal 1, @response.page(1).results.current_page
147+
assert_equal 100, @response.results.total_count
148+
149+
assert_equal 5, @response.page(5).results.current_page
150+
end
151+
end
152+
153+
context "records" do
154+
setup do
155+
@search.stubs(:execute!).returns RESPONSE
156+
end
157+
158+
should "return current page and total count" do
159+
assert_equal 1, @response.page(1).records.current_page
160+
assert_equal 100, @response.records.total_count
161+
162+
assert_equal 5, @response.page(5).records.current_page
163+
end
164+
end
139165
end
140166
end

0 commit comments

Comments
 (0)