Commit 1540a935 authored by Ohad Dahan's avatar Ohad Dahan

New API support, total replacement for pluck

parent 2e55e495
......@@ -22,16 +22,16 @@ Or install it yourself as:
## Usage
* If you want to use `nativepluck` as an `ActiveRecord` method, add `include Nativepluck` inside your model.<br>
In this usage mode (more examples in `test/dummy/test/models/genericmodel_test.rb`):<br>
`<Model>.nativepluck(:id, order: {id: :asc} , limit: limit)`<br>
Internally, the method call will generate a SQL query based on your arguments and use native PostgreSQL typecasting on the query results.
* If you have more complex queries that the above mixin method doesn't currently support (such as `joins`):<br>
You can take an existing ActiveRecord query method call chain, for example:<br>
* You can use `nativepluck` just like you use `pluck` , as in `<Model>.limit(10).nativepluck(:id,:name)`<br>
* You can also set in your `config/initializers/nativepluck` , `Nativepluck.set_override_pluck true` and we'll override `pluck` with `nativepluck`<br>
No changes needed in your side, completely transparent<br>
* You can take an existing ActiveRecord query method call chain, for example:<br>
`<Model>.where('id >= 21').limit(100).joins(:some_association_to_another_model).pluck(:id)`<br>
Change the `pluck` call to `select` and use it as the argument to the `nativepluck` module method: `Nativepluck.nativepluck(<Model>.where('id >= 21').limit(100).joins(:some_association_to_another_model).select(:id))`<br>
Change the `pluck` call to `select` and use it as the argument to the `nativepluck` module method:<br>
`Nativepluck.nativepluck(<Model>.where('id >= 21').limit(100).joins(:some_association_to_another_model).select(:id))`<br>
Internally, this method calls `to_sql` on the supplied `ActiveRecord::Relation` instance and submits the generated SQL query to the database. Again, native PostgreSQL typecasting is performed on the query results.<br>
* You can also use a raw SQL string argument in the module method, e.g.,<br>
`Nativepluck.nativepluck('SELECT models.id FROM models JOIN some_other_models ON some_other_models.model_id = models.id WHERE models.id > 21 LIMIT 100')`.
......@@ -40,82 +40,18 @@ Or install it yourself as:
To run the benchmarks use:
`BenchMarks::Memory::benchmark(write_to_file: true|false, row_limit: nil|integer)`<br>
* We're seeing up to ~650X less object allocations for integer columns<br>
### Memory allocation benchmark
* Benchmark code `test/dummy/test/benchmarks/memory_benchmarks.rb`
* Benchmark CSV `test/dummy/memory_report.csv`
Method | Attribute Name | Total Allocated | Total Retained
-------|-----------------|------------------|-----------------
pluck | id | 5125 | 3
nativepluck | id | 20 | 1
nativepluck_raw_to_sql | id | 68 | 5
nativepluck_raw_string | id | 7 | 1
pluck | integer_col | 5125 | 3
nativepluck | integer_col | 20 | 1
nativepluck_raw_to_sql | integer_col | 68 | 5
nativepluck_raw_string | integer_col | 7 | 1
pluck | string_col | 7125 | 1003
nativepluck | string_col | 1020 | 1001
nativepluck_raw_to_sql | string_col | 1068 | 1005
nativepluck_raw_string | string_col | 1007 | 1001
pluck | datetime_col | 22125 | 2003
nativepluck | datetime_col | 1020 | 1001
nativepluck_raw_to_sql | datetime_col | 1068 | 1005
nativepluck_raw_string | datetime_col | 1007 | 1001
pluck | float_col | 5125 | 3
nativepluck | float_col | 20 | 1
nativepluck_raw_to_sql | float_col | 68 | 5
nativepluck_raw_string | float_col | 7 | 1
pluck | json_col | 16125 | 3003
nativepluck | json_col | 11020 | 3001
nativepluck_raw_to_sql | json_col | 11068 | 3005
nativepluck_raw_string | json_col | 11007 | 3001
pluck | jsonb_col | 16125 | 3003
nativepluck | jsonb_col | 11020 | 3001
nativepluck_raw_to_sql | jsonb_col | 11068 | 3005
nativepluck_raw_string | jsonb_col | 11007 | 3001
pluck | created_at | 22125 | 2003
nativepluck | created_at | 1020 | 1001
nativepluck_raw_to_sql | created_at | 1068 | 1005
nativepluck_raw_string | created_at | 1007 | 1001
pluck | updated_at | 22125 | 2003
nativepluck | updated_at | 1020 | 1001
nativepluck_raw_to_sql | updated_at | 1068 | 1005
nativepluck_raw_string | updated_at | 1007 | 1001
### Allocated Objects by Class Benchmark
* Benchmark code `test/dummy/test/benchmarks/memory_benchmarks.rb`
* Benchmark CSV by objects `test/dummy/by_attributes_objects.csv`
* Benchmark CSV by memory `test/dummy/by_attributes_memory.csv`
Sample of the results, for full results visit the CSV files above.<br>
Attribute Name| Class|pluck|nativepluck|nativepluck_raw_to_sql|nativepluck_raw_string
--------------|------|-----|-----------|----------------------|-----------------------
id|Array|5073|7|42|5
id|String|48|8|4|0
id|Hash|16|3|6|0
id|Time|5|0|0|0
id|ActiveRecord::Relation|3|0|3|0
id|PG::Result|3|1|1|1
id|ActiveSupport::Notifications::Event|2|0|0|0
id|ActiveModel::Attribute::WithCastValue|1|0|1|0
id|ActiveRecord::Result|1|0|0|0
id|Arel::Attributes::Attribute|1|0|1|0
id|Arel::Collectors::Bind|1|0|0|0
id|Arel::Collectors::Composite|1|0|0|0
id|Arel::Collectors::SQLString|1|0|1|0
id|Arel::Nodes::BindParam|1|0|1|0
id|Arel::Nodes::JoinSource|1|0|1|0
id|Arel::Nodes::Limit|1|0|1|0
id|Arel::Nodes::SelectCore|1|0|1|0
id|Arel::Nodes::SelectStatement|1|0|1|0
id|Arel::Nodes::Top|1|0|1|0
id|Arel::SelectManager|1|0|1|0
## RunTime Benchmarks
......@@ -123,47 +59,10 @@ To run the benchmarks use:
`BenchMarks::RunTime::benchmark`<br>
* Benchmark code `test/dummy/test/benchmarks/runtime_benchmarks.rb`<br>
* We're seeing up to ~4X faster run-time when plucking DatTime columns
* Full results at `test/dummy/test/benchmarks/runtime.csv`
Method | Number of Rows | Attribute Name | Results
------------------------|----------------|----------------|---------------
nativepluck | 1000 | id | 1711.4 i/s
nativepluck_raw SQL_RAW | 1000 | id | 1645.6 i/s - same-ish: difference falls within error
nativepluck_raw :to_sql | 1000 | id | 980.0 i/s - 1.75x slower
pluck | 1000 | id | 711.7 i/s - 2.40x slower
nativepluck_raw SQL_RAW | 1000 | integer_col | 1339.6 i/s
nativepluck | 1000 | integer_col | 1092.5 i/s - 1.23x slower
nativepluck_raw :to_sql | 1000 | integer_col | 929.0 i/s - 1.44x slower
pluck | 1000 | integer_col | 585.1 i/s - 2.29x slower
nativepluck_raw SQL_RAW | 1000 | string_col | 1153.8 i/s
nativepluck | 1000 | string_col | 1078.1 i/s - same-ish: difference falls within error
nativepluck_raw :to_sql | 1000 | string_col | 863.3 i/s - 1.34x slower
pluck | 1000 | string_col | 484.1 i/s - 2.38x slower
nativepluck_raw SQL_RAW | 1000 | datetime_col | 217.7 i/s
nativepluck | 1000 | datetime_col | 207.0 i/s - same-ish: difference falls within error
nativepluck_raw :to_sql | 1000 | datetime_col | 198.8 i/s - 1.09x slower
pluck | 1000 | datetime_col | 43.3 i/s - 5.02x slower
nativepluck_raw SQL_RAW | 1000 | float_col | 618.4 i/s
nativepluck | 1000 | float_col | 578.9 i/s - same-ish: difference falls within error
nativepluck_raw :to_sql | 1000 | float_col | 539.7 i/s - 1.15x slower
pluck | 1000 | float_col | 304.0 i/s - 2.03x slower
nativepluck | 1000 | json_col | 125.3 i/s
nativepluck_raw SQL_RAW | 1000 | json_col | 124.6 i/s - same-ish: difference falls within error
nativepluck_raw :to_sql | 1000 | json_col | 116.7 i/s - same-ish: difference falls within error
pluck | 1000 | json_col | 106.5 i/s - 1.18x slower
nativepluck | 1000 | jsonb_col | 109.9 i/s
nativepluck_raw SQL_RAW | 1000 | jsonb_col | 109.7 i/s - same-ish: difference falls within error
nativepluck_raw :to_sql | 1000 | jsonb_col | 109.1 i/s - same-ish: difference falls within error
pluck | 1000 | jsonb_col | 95.9 i/s - 1.15x slower
nativepluck_raw SQL_RAW | 1000 | created_at | 215.3 i/s
nativepluck_raw :to_sql | 1000 | created_at | 204.9 i/s - same-ish: difference falls within error
nativepluck | 1000 | created_at | 204.6 i/s - same-ish: difference falls within error
pluck | 1000 | created_at | 47.0 i/s - 4.58x slower
nativepluck_raw SQL_RAW | 1000 | updated_at | 219.7 i/s
nativepluck_raw :to_sql | 1000 | updated_at | 205.3 i/s - 1.07x slower
nativepluck | 1000 | updated_at | 202.1 i/s - same-ish: difference falls within error
pluck | 1000 | updated_at | 43.7 i/s - 5.03x slower
## License
The gem is available as open source under the terms of the [Apache 2.0 License](https://opensource.org/licenses/Apache-2.0).
......
......@@ -15,31 +15,15 @@
require "nativepluck/version"
module Nativepluck
@nativepluck_init = false
@validate_args = false
@arguments_validator = {
offset: Integer,
limit: Integer,
order: Hash,
group: Array
}
@override_pluck = @nativepluck_init = false
class << self
attr_accessor :nativepluck_type_map_for_results, :nativepluck_type_map_for_queries
attr_accessor :original_type_map_for_results, :original_type_map_for_queries
attr_accessor :nativepluck_init, :arguments_validator
attr_accessor :validate_args
attr_accessor :nativepluck_init, :override_pluck
end
def self.set_validate_args(selection)
raise ArgumentError.new("Input should be true/false and not #{selection.class}") if !(selection.is_a?(TrueClass) || selection.is_a?(FalseClass))
Nativepluck.validate_args = selection
end
def self.included(klass)
klass.extend(ClassMethods)
init_nativepluck
end
def self.init_nativepluck
return if @nativepluck_init
......@@ -76,32 +60,17 @@ module Nativepluck
return out
end
def self.validate_nativepluck_args(**opts)
opts.each_pair do |k,v|
raise ArgumentError.new("Unknown argument #{k}") unless Nativepluck.arguments_validator[k]
raise ArgumentError.new("Expected #{k} to be #{Nativepluck.arguments_validator[k]} but it's #{v.class}") unless v.is_a?(Nativepluck.arguments_validator[k])
end
end
def self.format_order(order)
str = ''
return str if order.empty?
order.each_pair { |k, v| str << ", #{k} #{v}" }
str[0] = ''.freeze
str
end
module InstanceMethods
def nativepluck2(*column_names)
def nativepluck(*column_names)
# Extracted (before modifications) from:
# ruby-2.5.0/gems/activerecord-5.2.1/lib/active_record/relation/calculations.rb
if loaded? && (column_names.map(&:to_s) - @klass.attribute_names - @klass.attribute_aliases.keys).empty?
return records.pluck(*column_names)
return records.nativepluck(*column_names)
end
if has_include?(column_names.first)
relation = apply_join_dependency
relation.pluck(*column_names)
relation.nativepluck(*column_names)
else
enforce_raw_sql_whitelist(column_names)
relation = spawn
......@@ -113,37 +82,34 @@ module Nativepluck
end
end
module ClassMethods
def nativepluck(*columns, **opts)
raise ArgumentError.new('No columns to pluck were provided') if columns.size == 0
Nativepluck.validate_nativepluck_args(**opts) if Nativepluck.validate_args
begin
Nativepluck.set_pg_native_casters
sql = "
SELECT #{columns.join(',')}
FROM #{self.table_name}
#{"GROUP BY #{opts[:group].join(',')}" if opts[:group]}
#{"ORDER BY #{Nativepluck.format_order(opts[:order])}" if opts[:order]}
#{"LIMIT #{opts[:limit]}" if opts[:limit]}
#{"OFFSET #{opts[:offset]}" if opts[:offset]}
"
results = ActiveRecord::Base.connection.raw_connection.async_exec(sql)
puts "#{__method__} sql = #{sql}" if opts[:verbose]
results.nfields == 1 ? out = results.column_values(0) : out = results.values
ensure
results.try(:clear)
Nativepluck.return_original_casters
end
return out
end
end
module ::ActiveRecord
module Calculations
alias_method :orig_pluck, :pluck
include Nativepluck::InstanceMethods
end
module Querying
delegate :nativepluck2, to: :all
alias_method :orig_pluck, :pluck
delegate :nativepluck, to: :all
end
end
def self.set_override_pluck(selection)
raise ArgumentError.new("#{__method__}:: Input should be true/false and not #{selection.class}") unless (selection.is_a?(TrueClass) || selection.is_a?(FalseClass))
Nativepluck.override_pluck = selection
if selection
::ActiveRecord::Calculations.class_eval do
alias_method :pluck, :nativepluck
end
::ActiveRecord::Querying.class_eval do
alias_method :pluck, :nativepluck
end
else
::ActiveRecord::Calculations.class_eval do
alias_method :pluck, :orig_pluck
end
::ActiveRecord::Querying.class_eval do
alias_method :pluck, :orig_pluck
end
end
end
end
......@@ -13,5 +13,5 @@
# limitations under the License.
module Nativepluck
VERSION = "0.1.2"
VERSION = "0.1.3"
end
Number of rows : 1010
Class, Attribute Name, pluck, nativepluck, nativepluck_raw_to_sql, nativepluck_raw_string
Array, id,221408,10760,10480,8320
Array, integer_col,221408,10760,10480,8320
......@@ -25,7 +26,7 @@ String, float_col,2633,760,600,0
String, json_col,422844,420972,420812,420212
String, jsonb_col,429915,428042,427882,427282
String, created_at,391503,760,600,0
String, updated_at,391503,760,600,0
String, updated_at,391504,760,600,0
Hash, id,2120,968,968,0
Hash, integer_col,2120,968,968,0
Hash, string_col,2120,968,968,0
......
Number of rows : 1010
Class, Attribute Name, pluck, nativepluck, nativepluck_raw_to_sql, nativepluck_raw_string
Array, id,5130,67,60,6
Array, integer_col,5130,67,60,6
......
Number of rows : 1010
Attribute Name, Class,pluck,nativepluck,nativepluck_raw_to_sql,nativepluck_raw_string
id,Array,221408,10760,10480,8320
id,PG::Result,30976,30976,30976,30976
......@@ -195,7 +196,7 @@ created_at,Arel::Nodes::Top,40,40,40,0
created_at,Arel::SelectManager,40,40,40,0
created_at,PG::TypeMapByColumn,40,40,40,40
created_at,Arel::Collectors::SubstituteBinds,0,40,40,0
updated_at,String,391503,760,600,0
updated_at,String,391504,760,600,0
updated_at,Array,302208,10760,10480,8320
updated_at,MatchData,283080,0,0,0
updated_at,Time,246698,86860,86860,86860
......
Number of rows : 1010
Attribute Name, Class,pluck,nativepluck,nativepluck_raw_to_sql,nativepluck_raw_string
id,Array,5130,67,60,6
id,String,33,13,9,0
......
Number of rows : 1010
Method, Attribute Name, Total Allocated, Total Retained
pluck,id,5203,3
nativepluck,id,108,5
......
Attribute Name, pluck, nativepluck, nativepluck_raw_to_sql, nativepluck_raw_SQL
id,613.0,1044.0,1018.1,1545.3
integer_col,588.5,863.8,862.6,1143.4
string_col,443.1,687.3,705.1,926.4
datetime_col,45.3,190.2,195.1,207.9
float_col,309.8,423.1,503.2,566.8
json_col,91.2,111.1,112.7,115.4
jsonb_col,92.7,101.7,96.2,96.6
created_at,44.0,172.3,191.0,203.6
updated_at,46.1,192.0,192.0,169.3
......@@ -9,22 +9,22 @@ module BenchMarks
reports = {pluck: {}, nativepluck: {}, nativepluck_raw_to_sql: {}, nativepluck_raw_string: {}}
Genericmodel.attribute_names.each do |attribute_name|
##################################################################################################
Genericmodel.pluck(attribute_name)
Genericmodel.limit(limit).order(id: :desc).pluck(attribute_name)
reports[:pluck][attribute_name] = MemoryProfiler.report do
results = Genericmodel.limit(limit).pluck(attribute_name)
results = Genericmodel.limit(limit).order(id: :desc).pluck(attribute_name)
end
##################################################################################################
Genericmodel.nativepluck(attribute_name)
Genericmodel.limit(limit).order(id: :desc).nativepluck(attribute_name)
reports[:nativepluck][attribute_name] = MemoryProfiler.report do
results = Genericmodel.nativepluck(attribute_name, limit: limit)
results = Genericmodel.limit(limit).order(id: :desc).nativepluck(attribute_name)
end
##################################################################################################
Nativepluck.nativepluck(Genericmodel.limit(limit).select(attribute_name))
Nativepluck.nativepluck(Genericmodel.limit(limit).order(id: :desc).select(attribute_name))
reports[:nativepluck_raw_to_sql][attribute_name] = MemoryProfiler.report do
results = Nativepluck.nativepluck(Genericmodel.limit(limit).select(attribute_name))
results = Nativepluck.nativepluck(Genericmodel.limit(limit).order(id: :desc).select(attribute_name))
end
##################################################################################################
sql = Genericmodel.limit(limit).select(attribute_name).to_sql
sql = Genericmodel.limit(limit).order(id: :desc).select(attribute_name).to_sql
Nativepluck.nativepluck(sql)
reports[:nativepluck_raw_string][attribute_name] = MemoryProfiler.report do
results = Nativepluck.nativepluck(sql)
......
......@@ -10,18 +10,29 @@ module BenchMarks
loggers = [ActiveRecord::Base.logger, ActiveModelSerializers.logger]
ActiveRecord::Base.logger ,ActiveModelSerializers.logger = nil , Logger.new(nil)
limit = Genericmodel.count
results = {}
Genericmodel.attribute_names.each do |attribute_name|
sql_raw = Genericmodel.order(id: :asc).limit(limit).select(attribute_name).to_sql
Benchmark.ips do |x|
results[attribute_name] = Benchmark.ips do |x|
x.config(time: 5, warmup: 1)
x.report("pluck | #{limit} | #{attribute_name}") { Genericmodel.order(id: :asc).limit(limit).pluck(attribute_name) }
x.report("nativepluck | #{limit} | #{attribute_name}") { Genericmodel.nativepluck(attribute_name, order: {id: :asc}, limit: limit) }
x.report("nativepluck_raw :to_sql | #{limit} | #{attribute_name}") { Nativepluck.nativepluck(Genericmodel.order(id: :asc).limit(limit).select(attribute_name)) }
x.report("nativepluck_raw SQL_RAW | #{limit} | #{attribute_name}") { Nativepluck.nativepluck(sql_raw) }
x.report("pluck") { Genericmodel.order(id: :asc).limit(limit).pluck(attribute_name) }
x.report("nativepluck") { Genericmodel.order(id: :asc).limit(limit).nativepluck(attribute_name) }
x.report("nativepluck_raw_to_sql") { Nativepluck.nativepluck(Genericmodel.order(id: :asc).limit(limit).select(attribute_name)) }
x.report("nativepluck_raw_SQL") { Nativepluck.nativepluck(sql_raw) }
x.compare!
end
end
ActiveRecord::Base.logger , ActiveModelSerializers.logger = loggers ; nil
File.open('runtime.csv','w') do |fh|
fh.puts("Attribute Name, pluck, nativepluck, nativepluck_raw_to_sql, nativepluck_raw_SQL")
results.each_pair do |attribute_name, report|
str = "#{attribute_name}"
report.entries.each do |entry|
str << ",#{entry.stats.instance_variable_get(:@mean).to_f.round(1)}"
end
fh.puts(str)
end
end
end
end
end
......@@ -2,53 +2,52 @@ require 'test_helper'
#
# We use order in all tests to prevent tests from failing due to DB fetching rows in a different order
#
METHODS = [:pluck, :nativepluck]
class GenericmodelTest < ActiveSupport::TestCase
##########################################################
test 'pluck_each_attribute' do
failed = []
Genericmodel.attribute_names.each do |attr_name|
pluck = Genericmodel.order(id: :asc).pluck(attr_name)
nativepluck = Genericmodel.nativepluck(attr_name, order: {id: :asc} )
failed << attr_name if pluck != nativepluck
results = []
METHODS.each do |method_name|
results << Genericmodel.order(id: :asc).send(method_name, attr_name)
end
failed << attr_name if results[0] != results[1]
end
assert(failed.empty?, "#{__method__}:: pluck != nativepluck for attributes : #{failed.join(', ')}")
end
##########################################################
test 'pluck_nativepluck_2each_attribute' do
failed = []
Genericmodel.attribute_names.each do |attr_name|
pluck = Genericmodel.order(id: :asc).pluck(attr_name)
nativepluck2 = Genericmodel.order(id: :asc).nativepluck2(attr_name)
failed << attr_name if pluck != nativepluck2
end
assert(failed.empty?, "#{__method__}:: pluck != nativepluck2 for attributes : #{failed.join(', ')}")
end
##########################################################
test 'pluck_permutations' do
row_limit = 100
failed = []
Genericmodel.attribute_names.permutation(2).each do |attrs|
pluck = Genericmodel.limit(100).order(id: :asc).pluck(*attrs)
nativepluck = Genericmodel.nativepluck(*attrs, order: {id: :asc} , limit: row_limit)
failed << attrs if pluck != nativepluck
results = []
METHODS.each do |method_name|
results << Genericmodel.limit(row_limit).order(id: :asc).send(method_name, *attrs)
end
failed << attrs if results[0] != results[1]
end
assert(failed.empty?, "#{__method__}:: pluck != nativepluck for attributes : #{failed.join(', ')}")
end
##########################################################
test 'test_limit' do
Range.new(1,100).step(rand(5) + 1).each do |limit|
pluck = Genericmodel.limit(limit).order(id: :asc).pluck(:id)
nativepluck = Genericmodel.nativepluck(:id, order: {id: :asc} , limit: limit)
assert(pluck == nativepluck, "#{__method__}:: pluck != nativepluck for column :id and limit #{limit}")
results = []
METHODS.each do |method_name|
results << Genericmodel.limit(limit).order(id: :asc).send(method_name, :id)
end
assert(results[0] == results[1], "#{__method__}:: pluck != nativepluck for column :id and limit #{limit}")
end
end
##########################################################
test 'test_offset' do
row_limit = 100
Range.new(1,100).step(rand(5) + 1).each do |offset|
pluck = Genericmodel.limit(row_limit).offset(offset).order(id: :asc).pluck(:id)
nativepluck = Genericmodel.nativepluck(:id, order: {id: :asc} , limit: row_limit, offset: offset)
assert(pluck == nativepluck, "#{__method__}:: pluck != nativepluck for column :id and offset #{offset}")
results = []
METHODS.each do |method_name|
results << Genericmodel.limit(row_limit).offset(offset).order(id: :asc).send(method_name, :id)
end
assert(results[0] == results[1], "#{__method__}:: pluck != nativepluck for column :id and offset #{offset}")
end
end
##########################################################
......@@ -56,18 +55,22 @@ class GenericmodelTest < ActiveSupport::TestCase
failed = []
Genericmodel.attribute_names.each do |attr_name|
next if attr_name.match(/json/) # Cannot order by JSON(B) column
pluck = Genericmodel.order(attr_name => :asc, :id => :asc).pluck(attr_name)
nativepluck = Genericmodel.nativepluck(attr_name, order: {attr_name => :asc, :id => :asc} )
failed << attr_name if pluck != nativepluck
results = []
METHODS.each do |method_name|
results << Genericmodel.order(attr_name => :asc, :id => :asc).send(method_name, attr_name)
end
failed << attr_name if results[0] != results[1]
end
assert(failed.empty?, "#{__method__}:: pluck != nativepluck for attributes : #{failed.join(', ')}")
end
##########################################################
test 'test_group_by' do
row_limit = 100
pluck = Genericmodel.limit(row_limit).order(id: :asc).group(:id).pluck(:id)
nativepluck = Genericmodel.nativepluck(:id, order: {id: :asc} , group: [:id], limit: row_limit )
assert(pluck == nativepluck, "#{__method__}:: pluck != nativepluck")
results = []
METHODS.each do |method_name|
results << Genericmodel.limit(row_limit).order(id: :asc).group(:id).send(method_name, :id)
end
assert(results[0] == results[1], "#{__method__}:: pluck != nativepluck")
end
##########################################################
test 'Nativepluck_nativepluck_query' do
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment