Class: Oplogjam::Operators::Set

Inherits:
Object
  • Object
show all
Defined in:
lib/oplogjam/operators/set.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(tree = {}) ⇒ Set

Returns a new instance of Set.



55
56
57
# File 'lib/oplogjam/operators/set.rb', line 55

def initialize(tree = {})
  @tree = tree
end

Instance Attribute Details

#treeObject (readonly)

Returns the value of attribute tree.



53
54
55
# File 'lib/oplogjam/operators/set.rb', line 53

def tree
  @tree
end

Class Method Details

.from(operation) ⇒ Object

Transform a MongoDB $set operation (e.g. $set: { ‘a.1.b.0’: ‘foo’ }) into a tree of nodes more amenable to transforming into SQL.

Specifically, parse nested field and index assignments into specific node types FieldAssignment, IndexAssignment (for setting the final value) and IntermediateField, IntermediateIndex (for any intermediate fields and indexes).

e.g.

$set: { a: 1 } will become Set(['a'] => FieldAssignment(['a'], 1))
$set: { a: 1, b: 2 } will become Set(['a'] => FieldAssignment(['a'], 1), ['b'] => FieldAssignment(['b'], 2))
$set: { 'a.b': 1 } will become Set(['a'] => IntermediateField(['a'], FieldAssignment(['a', 'b'], 1)))


21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/oplogjam/operators/set.rb', line 21

def self.from(operation)

  # Start with an empty Set and iterate over every key in the $set operation, mutating the Set as we go
  operation.each_with_object(new) do |(dotted_path, value), set|

    # Split the dotted path `a.b.c` into an array `['a', 'b', 'c']`
    path = dotted_path.split(FIELD_SEPARATOR)

    # Start with an empty path which will incrementally populate
    current_path = []

    # Starting with the set, go through the successive path segments, building up intermediate paths on the current
    # node, e.g. 'a.b' will iterate over ['a'], ['a', 'b']
    #
    # Note that we exclude the final path segment as that will be used below in a separate set phase
    populated_node = path[0...-1].inject(set) { |current_node, segment|

      # Extend the current path with the current segment appended.
      current_path << segment

      # Populate an empty intermediate with a copy of the current path.
      #
      # Note that this could either be a numeric index (which might be indexing into an array) or an object field
      # name.
      current_node.populate(current_path.dup)
    }

    # Set the final value on the full path
    populated_node.set(path, value)
  end
end

Instance Method Details

#nodesObject



95
96
97
# File 'lib/oplogjam/operators/set.rb', line 95

def nodes
  tree.values
end

#populate(path) ⇒ Object



59
60
61
62
63
64
65
# File 'lib/oplogjam/operators/set.rb', line 59

def populate(path)
  if path.last =~ NUMERIC_INDEX
    populate_index(path)
  else
    populate_field(path)
  end
end

#populate_field(path) ⇒ Object



75
76
77
# File 'lib/oplogjam/operators/set.rb', line 75

def populate_field(path)
  tree[path] ||= IntermediateField.new(path)
end

#populate_index(path) ⇒ Object



79
80
81
# File 'lib/oplogjam/operators/set.rb', line 79

def populate_index(path)
  tree[path] ||= IntermediateIndex.new(path)
end

#set(path, value) ⇒ Object



67
68
69
70
71
72
73
# File 'lib/oplogjam/operators/set.rb', line 67

def set(path, value)
  if path.last =~ NUMERIC_INDEX
    set_index(path, value)
  else
    set_field(path, value)
  end
end

#set_field(path, value) ⇒ Object



83
84
85
# File 'lib/oplogjam/operators/set.rb', line 83

def set_field(path, value)
  tree[path] = FieldAssignment.new(path, value)
end

#set_index(path, value) ⇒ Object



87
88
89
# File 'lib/oplogjam/operators/set.rb', line 87

def set_index(path, value)
  tree[path] = IndexAssignment.new(path, value)
end

#update(column) ⇒ Object



91
92
93
# File 'lib/oplogjam/operators/set.rb', line 91

def update(column)
  nodes.inject(column, &UPDATE_COLUMN)
end