Skip to content

Commit

Permalink
fix compound indexes
Browse files Browse the repository at this point in the history
  • Loading branch information
mikekosulin committed Oct 23, 2023
1 parent 47ff69e commit 6013188
Show file tree
Hide file tree
Showing 6 changed files with 51 additions and 22 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ class Employee
end
```

Note: This approach creates multiple indexes, differing from single-field scoping, and impacting database performance and storage.
Note: this approach creates multiple indexes, differing from single-field scoping, and impacting database performance and storage.

### Slug Max Length

Expand Down
23 changes: 12 additions & 11 deletions lib/mongoid/slug.rb
Original file line number Diff line number Diff line change
Expand Up @@ -91,14 +91,7 @@ def slug(*fields, &block)

# Set indexes
if slug_index && !embedded?
# Even if the slug_scope is nil, we need to proceed.
slug_scopes = slug_scope.nil? ? [slug_scope_key] : Array(slug_scope)

# Here, build indexes for each scope in the array.
slug_scopes.each do |individual_scope|
Mongoid::Slug::IndexBuilder.build_indexes(self, individual_scope, slug_by_model_type,
options[:localize])
end
Mongoid::Slug::IndexBuilder.build_indexes(self, slug_scope_key, slug_by_model_type, options[:localize])
end

self.slug_url_builder = block_given? ? block : default_slug_url_builder
Expand All @@ -120,15 +113,23 @@ def look_like_slugs?(*args)
with_default_scope.look_like_slugs?(*args)
end

def slug_scopes
# If slug_scope is set (i.e., not nil), we convert it to an array to ensure we can handle it consistently.
# If it's not set, we use an array with a single nil element, signifying no specific scope.
slug_scope ? Array(slug_scope) : [nil]
end

# Returns the scope key for indexing, considering associations
#
# @return [ Array<Document>, Document ]
def slug_scope_key
keys = Array(slug_scope).map do |individual_scope|
return nil unless slug_scope

# If slug_scope is an array, we map over its elements to get each individual scope's key.
slug_scopes.map do |individual_scope|
# Attempt to find the association and get its key. If no association is found, use the scope as-is.
reflect_on_association(individual_scope).try(:key) || individual_scope
end

keys.empty? ? nil : keys
end

# Find documents by slugs.
Expand Down
5 changes: 3 additions & 2 deletions lib/mongoid/slug/index_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ module IndexBuilder
# Creates indexes on a document for a given slug scope
#
# @param [ Mongoid::Document ] doc The document on which to create the index(es)
# @param [ String or Symbol ] scope_key The optional scope key for the index(es)
# @param [ String or Symbol or Array<String, Symbol> ] scope_key The optional scope key for the index(es)
# @param [ Boolean ] by_model_type Whether or not to use single table inheritance
# @param [ Boolean or Array ] localize The locale for localized index field
#
Expand All @@ -28,7 +28,8 @@ def build_index(doc, scope_key = nil, by_model_type = false, locale = nil)
# See: http://docs.mongodb.org/manual/core/index-compound/
fields = {}
fields[:_type] = 1 if by_model_type
fields[scope_key] = 1 if scope_key

Array(scope_key).each { |key| fields[key] = 1 }

locale = ::I18n.default_locale if locale.is_a?(TrueClass)
if locale
Expand Down
12 changes: 5 additions & 7 deletions lib/mongoid/slug/unique_slug.rb
Original file line number Diff line number Diff line change
Expand Up @@ -100,14 +100,12 @@ def find_unique(attempt = nil)
where_hash[:_slugs.all] = [regex_for_slug]
where_hash[:_id.ne] = model._id

if (scope = slug_scope)
Array(scope).each do |individual_scope|
next unless reflect_on_association(individual_scope).nil?
Array(slug_scope).each do |individual_scope|
next unless reflect_on_association(individual_scope).nil?

# scope is not an association, so it's scoped to a local field
# (e.g. an association id in a denormalized db design)
where_hash[individual_scope] = model.try(:read_attribute, individual_scope)
end
# scope is not an association, so it's scoped to a local field
# (e.g. an association id in a denormalized db design)
where_hash[individual_scope] = model.try(:read_attribute, individual_scope)
end

where_hash[:_type] = model.try(:read_attribute, :_type) if slug_by_model_type
Expand Down
2 changes: 1 addition & 1 deletion lib/mongoid/slug/version.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

module Mongoid # :nodoc:
module Slug
VERSION = '7.0.0'
VERSION = '7.1.0'
end
end
29 changes: 29 additions & 0 deletions spec/mongoid/index_builder_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,35 @@
end
end

context 'when scope_key is set and is array' do
let(:doc) do
Class.new do
include Mongoid::Document
field :title, type: String
field :page_category, type: String
field :page_sub_category, type: String
end
end
let(:scope_key) { %i[page_category page_sub_category] }

before do
doc.field :page_category, type: String
doc.field :page_sub_category, type: String

Mongoid::Slug::IndexBuilder.build_indexes(doc, scope_key, by_model_type, locales)
end

context 'when by_model_type is true' do
let(:by_model_type) { true }

it { is_expected.to eq [[{ _slugs: 1, page_category: 1, page_sub_category: 1, _type: 1 }, {}]] }
end

context 'when by_model_type is false' do
it { is_expected.to eq [[{ _slugs: 1, page_category: 1, page_sub_category: 1 }, {}]] }
end
end

context 'when scope_key is not set' do
context 'when by_model_type is true' do
let(:by_model_type) { true }
Expand Down

0 comments on commit 6013188

Please sign in to comment.