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:

Asset.where(:project.does_not_equal_linked_pointer => { through: :capture, 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 capture’s project

Asset.where(:project.does_not_equal_linked_pointer => {
  through: :capture,
  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, 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



2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
# File 'lib/parse/query/constraints.rb', line 2894

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