diff --git a/.github/workflows/run_test_suite.yml b/.github/workflows/run_test_suite.yml index 1042c083..205db90c 100644 --- a/.github/workflows/run_test_suite.yml +++ b/.github/workflows/run_test_suite.yml @@ -83,7 +83,6 @@ jobs: - name: run pg tests env: DB: pg - COLLATE_SYMBOLS: false run: | COUNT=1 while ! pg_isready ; do diff --git a/README.md b/README.md index ac855724..1370dc43 100644 --- a/README.md +++ b/README.md @@ -48,24 +48,32 @@ $ rails g migration add_ancestry_to_[table] ancestry:string:index # rails g migration add_name_to_[people] name:string:index ``` +You will have best results to set the collation of the field. It works without +the collation, but without collation, it will not use the ancestry index. Alternatively +adding oppset will fix this. + +Postgres on ubuntu in particular has been troublesome because by default, +it sorts ignoring slashes. + + +For postgres use `collate: :default`, and for mysql and sqllite use `collate: :binary`. + +```ruby +class AddAncestryToTable < ActiveRecord::Migration[6.1] + def change + add_column :table, :ancestry, :string, collation: :default # alt: :binary + add_index :table, :ancestry + end +end +``` + * Migrate your database: ```bash $ rake db:migrate ``` -Depending upon your comfort with databases, you may want to create the column -with `C` or `POSIX` encoding. This is a more primitive encoding and just compares -bytes. Since this column will just contain numbers and slashes, it works much -better. It also works better for the uuid case as well. - -Alternatively, if you create a [`text_pattern_ops`](https://www.postgresql.org/docs/current/indexes-opclass.html) index for your postgresql column, subtree selection will use an efficient index for you regardless of whether you created the column with `POSIX` encoding. - -If you opt out of this, and are trying to run tests on postgres, you may need to -set the environment variable `COLLATE_SYMBOLS=false`. Sorry to say that a discussion -on this topic is out of scope. The important take away is postgres sort order is -not consistent across operating systems but other databases do not have this same -issue. +NOTE: If you decide to add collation to this column at a later time, please remember to drop and recreate this index. NOTE: A Btree index (as is recommended) has a limitaton of 2704 characters for the ancestry column. This means you can't have an tree with a depth that is too great (~> 900 items at most). diff --git a/test/concerns/scopes_test.rb b/test/concerns/scopes_test.rb index 7901afe8..006b26b5 100644 --- a/test/concerns/scopes_test.rb +++ b/test/concerns/scopes_test.rb @@ -52,12 +52,8 @@ def test_chained_scopes def test_order_by AncestryTestDatabase.with_model :depth => 3, :width => 3 do |model, _roots| # Some pg databases do not use symbols in sorting - # if this is failing, try running the test via DB=pg COLLATE_SYMBOLS=false rake test - if ENV["COLLATE_SYMBOLS"].to_s =~ /false/i - expected = model.all.sort_by { |m| [m.ancestor_ids.map(&:to_s).join, m.id.to_i] } - else - expected = model.all.sort_by { |m| [m.ancestor_ids.map(&:to_s), m.id.to_i] } - end + # if this is failing, try tweaking the collation of your ancestry columns + expected = model.all.sort_by { |m| [m.ancestor_ids.map(&:to_s), m.id.to_i] } actual = model.ordered_by_ancestry_and(:id) assert_equal (expected.map { |r| [r.ancestor_ids, r.id.to_s] }), (actual.map { |r| [r.ancestor_ids, r.id.to_s] }) end diff --git a/test/environment.rb b/test/environment.rb index addb8ce9..33d87408 100644 --- a/test/environment.rb +++ b/test/environment.rb @@ -56,7 +56,7 @@ def self.setup # This only affects postgres # the :ruby code path will get tested in mysql and sqlite3 - Ancestry.default_update_strategy = :sql if db_type == "pg" + Ancestry.default_update_strategy = :sql if postgres? rescue => err if ENV["CI"] @@ -69,6 +69,18 @@ def self.setup end end + # pass ANCESTRY_LOCALE=default to not override locale on ancestry + def self.ancestry_collation + env = ENV["ANCESTRY_LOCALE"].presence + if env + env + elsif postgres? + "C" + else + "binary" + end + end + def self.with_model options = {} depth = options.delete(:depth) || 0 width = options.delete(:width) || 0 @@ -79,8 +91,11 @@ def self.with_model options = {} table_options={} table_options[:id] = options.delete(:id) if options.key?(:id) + column_options = {:collation => ancestry_collation} + column_options = {} if column_options[:collation] == "default" + ActiveRecord::Base.connection.create_table 'test_nodes', **table_options do |table| - table.string options[:ancestry_column] || :ancestry + table.string options[:ancestry_column] || :ancestry, **column_options table.integer options[:depth_cache_column] || :ancestry_depth if options[:cache_depth] if options[:counter_cache] counter_cache_column = options[:counter_cache] == true ? :children_count : options[:counter_cache] @@ -128,6 +143,9 @@ def self.create_test_nodes model, depth, width, parent = nil else; []; end end + def self.postgres? + db_type == "pg" + end private def self.db_type