Class: Parse::Schema::SearchIndexMigrator
- Inherits:
-
Object
- Object
- Parse::Schema::SearchIndexMigrator
- Defined in:
- lib/parse/schema/search_index_migrator.rb
Overview
Reconciliation engine for Core::SearchIndexing declarations vs. the actual Atlas Search index state. Reads existing indexes via ‘$listSearchIndexes` (so `plan` works without writer config); applies via the writer connection through AtlasSearch::IndexManager.create_index / AtlasSearch::IndexManager.update_index / AtlasSearch::IndexManager.drop_index.
**Drift semantics are detect-and-refuse, not auto-update.** When a declared definition differs from what Atlas reports as the index’s ‘latestDefinition`, the migrator classifies the declaration as `drifted:` and leaves the index alone. The operator opts into the update with `apply!(update: true)`. This matches the spirit of the regular IndexMigrator’s ‘conflicts:` slot but with an explicit opt-in escape hatch, because Atlas Search rebuilds run asynchronously and an over-eager auto-update would silently rebuild production indexes on every deploy.
**Builds are async.** ‘apply!(wait: false)` (the default) submits commands and returns immediately. `apply!(wait: true)` blocks on AtlasSearch::IndexManager.wait_for_ready after each create / update to confirm the index transitions to `READY`. CI / deployment pipelines that need post-apply queryability should opt-in; default fire-and-forget keeps the common rake task fast.
Instance Attribute Summary collapse
-
#model_class ⇒ Object
readonly
Returns the value of attribute model_class.
Instance Method Summary collapse
-
#apply!(update: false, drop: false, wait: false, timeout: 600) ⇒ Hash
Apply the plan.
-
#collection_name ⇒ String
The model’s collection name (parse_class).
-
#initialize(model_class) ⇒ SearchIndexMigrator
constructor
A new instance of SearchIndexMigrator.
-
#plan ⇒ Hash
Compute the plan: what would change if ‘apply!` ran now.
Constructor Details
#initialize(model_class) ⇒ SearchIndexMigrator
Returns a new instance of SearchIndexMigrator.
33 34 35 36 37 38 39 |
# File 'lib/parse/schema/search_index_migrator.rb', line 33 def initialize(model_class) unless model_class.is_a?(Class) && model_class < Parse::Object raise ArgumentError, "SearchIndexMigrator expects a Parse::Object subclass; got #{model_class.inspect}" end @model_class = model_class end |
Instance Attribute Details
#model_class ⇒ Object (readonly)
Returns the value of attribute model_class.
31 32 33 |
# File 'lib/parse/schema/search_index_migrator.rb', line 31 def model_class @model_class end |
Instance Method Details
#apply!(update: false, drop: false, wait: false, timeout: 600) ⇒ Hash
Apply the plan. Additive by default — only ‘:to_create` is mutated. Pass `update: true` to also rebuild drifted indexes, `drop: true` to also drop orphans, `wait: true` to block on build completion after each mutation.
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 |
# File 'lib/parse/schema/search_index_migrator.rb', line 133 def apply!(update: false, drop: false, wait: false, timeout: 600) p = plan coll = p[:collection] wait_results = {} # Drops run BEFORE creates so any per-cluster cap (Atlas has a # cluster-wide search-index quota) doesn't reject a create that # would have fit after the orphan was removed. dropped = [] orphans_skipped = [] if drop p[:orphans].each do |name| confirm = "drop_search:#{coll}:#{name}" res = Parse::AtlasSearch::IndexManager.drop_index(coll, name, confirm: confirm) dropped << name if res == :dropped end else orphans_skipped = p[:orphans].dup end created = [] skipped_exists = [] p[:to_create].each do |decl| res = Parse::AtlasSearch::IndexManager.create_index(coll, decl[:name], decl[:definition]) if res == :exists skipped_exists << decl else created << decl wait_results[decl[:name]] = wait_for(coll, decl[:name], timeout) if wait end end updated = [] drifted_skipped = [] if update p[:drifted].each do |entry| decl = entry[:declared] Parse::AtlasSearch::IndexManager.update_index(coll, decl[:name], decl[:definition]) updated << decl[:name] wait_results[decl[:name]] = wait_for(coll, decl[:name], timeout) if wait end else drifted_skipped = p[:drifted].map { |e| e[:declared][:name] } end { created: created, skipped_exists: skipped_exists, in_sync: p[:in_sync], updated: updated, drifted_skipped: drifted_skipped, dropped: dropped, orphans_skipped: orphans_skipped, wait_results: wait_results, } end |
#collection_name ⇒ String
Returns the model’s collection name (parse_class).
42 43 44 |
# File 'lib/parse/schema/search_index_migrator.rb', line 42 def collection_name @model_class.parse_class end |
#plan ⇒ Hash
Compute the plan: what would change if ‘apply!` ran now.
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
# File 'lib/parse/schema/search_index_migrator.rb', line 67 def plan coll = collection_name existing, available = fetch_existing_indexes(coll) declared = @model_class.mongo_search_index_declarations existing_by_name = existing.each_with_object({}) do |idx, h| name = (idx["name"] || idx[:name]).to_s h[name] = idx unless name.empty? end to_create = [] in_sync = [] drifted = [] declared.each do |decl| target = existing_by_name[decl[:name]] if target.nil? to_create << decl elsif definition_matches?(target, decl[:definition]) in_sync << decl else drifted << { declared: decl, existing: serialize_existing(target) } end end declared_names = declared.map { |d| d[:name] }.to_set orphans = existing_by_name.keys.reject { |name| declared_names.include?(name) } { collection: coll, declared: declared, existing: existing, atlas_available: available, to_create: to_create, in_sync: in_sync, drifted: drifted, orphans: orphans, } end |