Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add PostgreSQL support #7

Merged
merged 18 commits into from
Mar 22, 2017
8 changes: 7 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,18 @@ rvm:
- 2.3
sudo: required
dist: trusty
env:
- DB=mysql
- DB=pg
services:
- postgresql
before_install:
- gem install bundler
- gem update --system
- gem --version
before_script:
- mysql -u root -e 'CREATE DATABASE test;'
- mysql -u root -e 'CREATE DATABASE travis_ci_test;'
- psql -c 'create database travis_ci_test;' -U postgres
gemfile:
- gemfiles/3.2.gemfile
- gemfiles/4.2.gemfile
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ Find then sort by hash mapping 1.120000 0.010000 1.130000 ( 1.192891)

## Development

After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test DB=mysql` / `rake test DB=pg` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).

Expand Down
3 changes: 2 additions & 1 deletion find_with_order.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@ Gem::Specification.new do |spec|
spec.add_development_dependency "bundler", "~> 1.11"
spec.add_development_dependency "rake", "~> 12.0"
spec.add_development_dependency "minitest", "~> 5.0"
spec.add_development_dependency "mysql2", ">= 0.3"
spec.add_development_dependency "pg", "~> 0.18"

spec.add_dependency "rails", ">= 3"
spec.add_dependency "mysql2", ">= 0.3"

end
5 changes: 4 additions & 1 deletion gemfiles/3.2.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ source 'https://rubygems.org'
gem "activerecord", "~> 3.2"

group :test do
gem 'mysql2' , '0.3.21'
case ENV['DB']
when "mysql" ; gem 'mysql2' , '0.3.21'
when "postgres" ; gem 'pg', '~> 0.18'
end
gem "simplecov"
gem "codeclimate-test-reporter", "~> 1.0.0"
end
Expand Down
5 changes: 4 additions & 1 deletion gemfiles/4.2.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ source 'https://rubygems.org'
gem "activerecord", "~> 4.2"

group :test do
gem 'mysql2' , '0.3.21'
case ENV['DB']
when "mysql" ; gem 'mysql2' , '0.3.21'
when "postgres" ; gem 'pg', '~> 0.18'
end
gem "simplecov"
gem "codeclimate-test-reporter", "~> 1.0.0"
end
Expand Down
5 changes: 4 additions & 1 deletion gemfiles/5.0.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ source 'https://rubygems.org'
gem "activerecord", "~> 5.0"

group :test do
gem 'mysql2' , '0.3.21'
case ENV['DB']
when "mysql" ; gem 'mysql2' , '0.3.21'
when "postgres" ; gem 'pg', '~> 0.18'
end
gem "simplecov"
gem "codeclimate-test-reporter", "~> 1.0.0"
end
Expand Down
8 changes: 6 additions & 2 deletions lib/find_with_order.rb
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
require "find_with_order/version"
require "find_with_order/mysql_support"
require "find_with_order/pg_support"
require 'active_record'

class << ActiveRecord::Base
def find_with_order(ids)
return none if ids.blank?
ids = ids.uniq
return where(id: ids).order("field(#{table_name}.id, #{ids.join(',')})").to_a
return FindWithOrder::PGSupport.find_with_order(self, ids) if defined?(PG)
return FindWithOrder::MysqlSupport.find_with_order(self, ids)
end
def where_with_order(column, ids)
return none if ids.blank?
ids = ids.uniq
return where(column => ids).order("field(#{column}, #{ids.map(&:inspect).join(',')})")
return FindWithOrder::PGSupport.where_with_order(self, column, ids) if defined?(PG)
return FindWithOrder::MysqlSupport.where_with_order(self, column, ids)
end
end
unless ActiveRecord::Base.respond_to?(:none) # extend only if not implement yet
Expand Down
10 changes: 10 additions & 0 deletions lib/find_with_order/mysql_support.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module FindWithOrder
module MysqlSupport
def self.find_with_order(relation, ids)
return relation.where(id: ids).order("field(#{relation.table_name}.id, #{ids.join(',')})").to_a
end
def self.where_with_order(relation, column, ids)
return relation.where(column => ids).order("field(#{column}, #{ids.map(&:inspect).join(',')})")
end
end
end
26 changes: 26 additions & 0 deletions lib/find_with_order/pg_support.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
module FindWithOrder
module PGSupport
def self.find_with_order(relation, ids)
# return relation.where(id: ids).order("array_position(ARRAY[#{ids.join(',')}], #{relation.table_name}.id)").to_a #array_position is only support in PG >= 9.5
return relation.where(id: ids)
.joins("JOIN (SELECT id.val, row_number() over() FROM (VALUES(#{ids.join('),(')})) AS id(val)) AS id ON (#{relation.table_name}.id = id.val)")
.order('row_number')
end
def self.where_with_order(relation, column, ids)
relation = relation.where(column => ids)
case ids.first
when Numeric
# return relation.order("array_position(ARRAY[#{ids.join(',')}], #{column})") #array_position is only support in PG >= 9.5
return relation.joins("JOIN (SELECT id.val, row_number() over() FROM (VALUES(#{ids.join('),(')})) AS id(val)) AS id ON (#{column} = id.val)")
.order('row_number')
when String
ids.map!{|s| ActiveRecord::Base.connection.quote_string(s) }
# return relation.order("array_position(ARRAY['#{ids.join("','")}']::varchar[], #{column})") #array_position is only support in PG >= 9.5
return relation.joins("JOIN (SELECT id.val, row_number() over() FROM (VALUES('#{ids.join("'),('")}')) AS id(val)) AS id ON (#{column} = id.val)")
.order('row_number')
else
raise "not support type: #{ids.first.class}"
end
end
end
end
12 changes: 10 additions & 2 deletions test/find_with_order_test.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
require 'test_helper'
case ENV['DB']
when 'mysql'
require 'mysql2_connection'
when 'pg'
require 'postgresql_connection'
else
raise "no database"
end

class FindWithOrderTest < Minitest::Test
def setup
Expand Down Expand Up @@ -57,8 +65,8 @@ def test_association_find_with_order

def test_ambiguous_id_in_join
order = [2, 1, 3]
assert_equal order, User.joins(:posts).uniq.find_with_order(order).map(&:id)
assert_equal order, User.joins(:posts).uniq.where_with_order(:'users.id', order).pluck(:id)
assert_equal order, User.joins(:posts).find_with_order(order).map(&:id).uniq #postgresql doesn't support distinct + order
assert_equal order, User.joins(:posts).where_with_order(:'users.id', order).pluck(:id).uniq
end
end

Expand Down
5 changes: 5 additions & 0 deletions test/mysql2_connection.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
ActiveRecord::Base.establish_connection(
"adapter" => "mysql2",
"database" => "travis_ci_test",
)
require 'seeds'
5 changes: 5 additions & 0 deletions test/postgresql_connection.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
ActiveRecord::Base.establish_connection(
"adapter" => "postgresql",
"database" => "travis_ci_test",
)
require 'seeds'
6 changes: 0 additions & 6 deletions test/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,3 @@
require 'find_with_order'

require 'minitest/autorun'

ActiveRecord::Base.establish_connection(
"adapter" => "mysql2",
"database" => "test"
)
require 'seeds'