Module: Parse::Core::Indexing
- Included in:
- Object
- Defined in:
- lib/parse/model/core/indexing.rb
Overview
Model-declarative MongoDB index DSL. Mixed into Parse::Object so subclasses can declare the indexes they expect to exist on their collection. Declarations are inert at load time — they only land on MongoDB when Schema::IndexMigrator reads them and ‘apply_indexes!` is invoked through the writer connection.
SECURITY POSTURE — purely declarative. No network I/O, no class introspection that could leak data, no LLM-visible surface. The validation rules below run at declaration time so a typo surfaces as a load-time error, not a runtime surprise during ‘rake parse:mongo:indexes:apply` in prod.
Constant Summary collapse
- MAX_INDEXES_PER_COLLECTION =
MongoDB limits each collection to 64 indexes total (including the implicit ‘id` index). The migrator’s plan phase reports remaining capacity using this constant.
64- PARSE_MANAGED_ARRAY_FIELDS =
Parse-managed array columns we can know about without introspecting actual data. Used by #assert_at_most_one_array_field! to catch parallel-array compounds at declaration time even when the parallel field is the ‘_rperm`/`_wperm` ACL array.
%w[_rperm _wperm].to_set.freeze
- SENSITIVE_FIELDS =
Wire-format column names that hold Parse-internal secret material (password hashes, session tokens, verification tokens, auth provider blobs). The DSL refuses to declare an index on any of these because a queryable index on bcrypt hashes or session tokens turns ‘$indexStats` / collection-scan access into a credential-enumeration oracle. Parse Server already manages the legitimate indexes for these columns (see Schema::IndexMigrator::PARSE_MANAGED_INDEX_PATTERNS); this guard exists so a typo or malicious PR can’t add a new one.
%w[ _hashed_password _session_token _email_verify_token _perishable_token _password_history authData _auth_data ].freeze
Instance Method Summary collapse
-
#apply_indexes!(drop: false) ⇒ Hash
Apply additive index changes via the writer connection.
-
#indexes_plan ⇒ Hash{String=>Hash}
Dry-run reconciliation between declared indexes and what’s on the collection.
-
#mongo_geo_index(field, sparse: false, name: nil) ⇒ Object
Sugar for a 2dsphere geospatial index.
-
#mongo_index(*fields, unique: false, sparse: false, partial: nil, expire_after: nil, name: nil) ⇒ Hash
Declare a regular (B-tree) index on one or more fields.
-
#mongo_index_declarations ⇒ Array<Hash>
Storage for declared indexes.
-
#mongo_relation_index(field, bidirectional: false, unique: false) ⇒ Array<Hash>
Declare an index on a Parse Relation’s join collection.
Instance Method Details
#apply_indexes!(drop: false) ⇒ Hash
Apply additive index changes via the writer connection. Pass ‘drop: true` to also drop orphan indexes; each drop carries its own audit log and confirmation envelope.
175 176 177 |
# File 'lib/parse/model/core/indexing.rb', line 175 def apply_indexes!(drop: false) Parse::Schema::IndexMigrator.new(self).apply!(drop: drop) end |
#indexes_plan ⇒ Hash{String=>Hash}
Dry-run reconciliation between declared indexes and what’s on the collection. Delegates to Schema::IndexMigrator.
167 168 169 |
# File 'lib/parse/model/core/indexing.rb', line 167 def indexes_plan Parse::Schema::IndexMigrator.new(self).plan end |
#mongo_geo_index(field, sparse: false, name: nil) ⇒ Object
Sugar for a 2dsphere geospatial index. Geopoint columns are stored in Mongo as GeoJSON ‘{ type: “Point”, coordinates: [lng, lat] }` which `2dsphere` indexes natively.
95 96 97 98 |
# File 'lib/parse/model/core/indexing.rb', line 95 def mongo_geo_index(field, sparse: false, name: nil) register_index([field], key_value: "2dsphere", unique: false, sparse: sparse, partial: nil, expire_after: nil, name: name) end |
#mongo_index(*fields, unique: false, sparse: false, partial: nil, expire_after: nil, name: nil) ⇒ Hash
Declare a regular (B-tree) index on one or more fields. Symbols in ‘fields` are looked up against the class’s ‘references` table — pointers auto-rewrite to `p<field>` so callers think in property names. Use `mongo_geo_index` for 2dsphere indexes.
86 87 88 89 90 |
# File 'lib/parse/model/core/indexing.rb', line 86 def mongo_index(*fields, unique: false, sparse: false, partial: nil, expire_after: nil, name: nil) register_index(fields, key_value: 1, unique: unique, sparse: sparse, partial: partial, expire_after: expire_after, name: name) end |
#mongo_index_declarations ⇒ Array<Hash>
Storage for declared indexes. Each entry is a frozen Hash with the keys ‘:keys`, `:options`, `:declared_for` (the source-of-truth symbol list from the `mongo_index` call, for diagnostics).
63 64 65 |
# File 'lib/parse/model/core/indexing.rb', line 63 def mongo_index_declarations @mongo_index_declarations ||= [] end |
#mongo_relation_index(field, bidirectional: false, unique: false) ⇒ Array<Hash>
Declare an index on a Parse Relation’s join collection. Relations are stored in ‘_Join:<field>:<ParentClass>` collections — these have no Ruby model, so an `add_index :field` against the parent class would index the wrong collection. This method routes the declaration to the correct join-collection name, with the conventional column shape: `owningId` is the parent-side foreign key, `relatedId` is the related-side.
Default: single declaration on ‘1` — the forward lookup (“what’s related to this owner”), which is the dominant pattern for most Parse Relation queries.
‘bidirectional: true` adds a second declaration on `1` — the reverse lookup (“which owners contain this related object”). For high-traffic auth patterns like `Parse::Role.users`, the reverse direction is often the heavier-used index.
Uniqueness is NOT supported on ‘mongo_relation_index` — a unique single-direction index on a `has_many :through => :relation` field is semantically broken (it would say each owner can hold at most one related, contradicting `has_many`). If you want to enforce no-duplicate-pair membership, declare a compound unique index directly via `Parse::MongoDB.create_index` or a later extension to this DSL.
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
# File 'lib/parse/model/core/indexing.rb', line 141 def mongo_relation_index(field, bidirectional: false, unique: false) if unique raise ArgumentError, "#{self}.mongo_relation_index does not support unique: — uniqueness on " \ "a single-direction relation column breaks has_many semantics. For no-" \ "duplicate-pair membership, declare a compound unique index directly " \ "via Parse::MongoDB.create_index." end field = field.to_sym unless respond_to?(:relations) && relations.key?(field) raise ArgumentError, "#{self}.mongo_relation_index requires #{field.inspect} to be declared " \ "via `has_many :#{field}, through: :relation`. Got non-relation field." end join_collection = "_Join:#{field}:#{parse_class}" decls = [register_relation_index(join_collection, "owningId", source: field)] decls << register_relation_index(join_collection, "relatedId", source: field) if bidirectional decls end |