Class: Parse::Constraint::DoesNotEqualLinkedPointerConstraint

Inherits:
Constraint
  • Object
show all
Defined in:
lib/parse/query/constraints.rb

Overview

Constraint for comparing pointer fields where they do NOT equal through linked objects. Uses MongoDB's $lookup to join collections and $expr with $ne to compare fields.

Usage:

Document.where(:project.does_not_equal_linked_pointer => { through: :post, field: :project })

This generates a MongoDB aggregation pipeline that:

  1. Uses $lookup to join the linked collection
  2. Uses $match with $expr and $ne to find records where fields do NOT match

Examples:

Find assets where the project does not equal the post's project

Document.where(:project.does_not_equal_linked_pointer => {
  through: :post,
  field: :project
})

Instance Method Summary collapse

Instance Method Details

#buildHash

Builds the MongoDB aggregation pipeline for the does-not-equal-linked-pointer constraint

Returns:

  • (Hash)

    Hash containing the aggregation pipeline

Raises:

  • (ArgumentError)

    if required parameters are missing or invalid



3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
# File 'lib/parse/query/constraints.rb', line 3162

def build
  # Validate that value is a hash with required keys
  unless @value.is_a?(Hash) && @value[:through] && @value[:field]
    raise ArgumentError, "DoesNotEqualLinkedPointerConstraint requires a hash with :through and :field keys"
  end

  through_field = @value[:through]
  target_field = @value[:field]

  # Convert field names to Parse format (snake_case to camelCase) with _p_ prefix for pointers
  local_field_name = format_field_name(@operation.operand, is_pointer: true)
  through_field_name = format_field_name(through_field, is_pointer: true)
  target_field_name = format_field_name(target_field, is_pointer: true)

  # Determine the collection name for the lookup (Rails pluralization)
  through_class_name = through_field.to_s.classify
  lookup_collection = through_class_name

  # Generate unique alias name for the joined data (use clean name without _p_ prefix)
  lookup_alias = "#{through_field.to_s.camelize(:lower)}_data"

  # Build the MongoDB aggregation pipeline
  pipeline = []

  # Parse stores pointers as "ClassName$objectId" strings
  # We need to extract just the objectId part after the $
  # Stage 1: Add field with extracted objectId
  add_fields_stage = {
    "$addFields" => {
      "#{through_field_name}_id" => {
        "$substr" => [
          "$#{through_field_name}",
          lookup_collection.length + 1,  # Skip "ClassName$"
          -1,  # Rest of string
        ],
      },
    },
  }
  pipeline << add_fields_stage

  # Stage 2: $lookup to join the linked collection
  lookup_stage = {
    "$lookup" => {
      "from" => lookup_collection,
      "localField" => through_field_name,
      "foreignField" => "_id",
      "as" => lookup_alias,
    },
  }
  pipeline << lookup_stage

  # Stage 2: $match with $expr to compare the fields using $ne (not equal)
  match_stage = {
    "$match" => {
      "$expr" => {
        "$ne" => [
          { "$arrayElemAt" => ["$#{lookup_alias}.#{target_field_name}", 0] },
          "$#{local_field_name}",
        ],
      },
    },
  }
  pipeline << match_stage

  # Return a special marker that indicates this needs aggregation pipeline processing
  { "__aggregation_pipeline" => pipeline }
end