Class: Parse::Constraint::DoesNotEqualLinkedPointerConstraint

Inherits:
Parse::Constraint 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 Attribute Summary

Attributes inherited from Parse::Constraint

#operand, #operation, #operator, #value

Instance Method Summary collapse

Methods inherited from Parse::Constraint

#as_json, constraint_keyword, create, formatted_value, #formatted_value, #initialize, #key, #precedence, #regex_unicode_option, register, #to_s

Constructor Details

This class inherits a constructor from Parse::Constraint

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



3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
# File 'lib/parse/query/constraints.rb', line 3106

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