Module: MongoScript::Multiquery::ClassMethods

Defined in:
lib/mongoscript/multiquery.rb

Instance Method Summary collapse

Instance Method Details

#mongoize_queries(queries) ⇒ Object

Prepare normalized queries for use in Mongo. Currently, this involves deleting parameters that can’t be turned into BSON. (We can’t act directly on the normalized queries, since they contain data used later to rehydrate models.)

Parameters:

  • queries

    previously normalized queries



142
143
144
145
146
147
148
149
# File 'lib/mongoscript/multiquery.rb', line 142

def mongoize_queries(queries)
  # delete any information not needed by/compatible with Mongoid execution
  mongoized_queries = queries.dup
  mongoized_queries.each_pair do |name, details|
    # have to dup the query details to avoid changing the original hashes
    mongoized_queries[name] = details.dup.tap {|t| t.delete(:klass) }
  end
end

#multiquery(queries) ⇒ Object

Runs multiple find queries at once, returning the results keyed to the original query names. If a query produces an error, its result will be a QueryFailedError object with the appropriate details; other queries will be unaffected.

Examples:

MongoScript.multiquery({
  # simplest form -- the name is used as the collection to be queried
  :cars => {:query => {:_id => {$in: my_ids}}},
  # the name can also be arbitrary if you explicitly specify a collection
  # (allowing you to query the same collection twice)
  :my_cool_query => {:collection => :books, :objects }
  # you can also pass in Mongoid criteria
  :planes => Plane.where(manufacturer: "Boeing").sort("created_at desc").only(:_id),
})
=> {
  # results get automatically turned into
  :cars => [#<Car: details>, #<Car: details>],
  :my_cool_query => [#<Book: details>],
  :planes => #<QueryFailedError>
}


56
57
58
59
60
61
62
63
64
65
# File 'lib/mongoscript/multiquery.rb', line 56

def multiquery(queries)
  # don't do anything if we don't get any queries
  return {} if queries == {}

  # resolve all the
  queries = normalize_queries(queries)
  validate_queries!(queries)
  results = MongoScript.execute_readonly_routine("multiquery", mongoize_queries(queries))
  process_results(results, queries)
end

#normalize_queries(queries) ⇒ Object

Standardize a set of queries into a form we can use. Specifically, ensure each query has a database collection and an ORM class, and resolve any ORM criteria into hashes.

Parameters:

  • queries

    a set of query_name => hash_or_orm_criteria pairs



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/mongoscript/multiquery.rb', line 77

def normalize_queries(queries)
  # need to also ensure we have details[:klass]
  queries.inject({}) do |normalized_queries, data|
    name, details = data

    if details.is_a?(Hash)
      # duplicate the details so we don't make changes to the original query data
      details = details.dup.with_indifferent_access

      # if no collection is specified, assume it's the same as the name
      details[:collection] ||= name

      # ensure that we know which class the collection maps to
      # so we can rehydrate the resulting data
      unless details[:klass]
        expected_class_name = details[:collection].to_s.singularize.titlecase
        # if the class doesn't exist, this'll be false (and we'll raise an error later)
        details[:klass] = Object.const_defined?(expected_class_name) && Object.const_get(expected_class_name)
      end
    elsif processable_into_parameters?(details)
      # process Mongo ORM selectors into JS-compatible hashes
      details = MongoScript.build_multiquery_parameters(details)
    else
      raise ArgumentError, "Invalid selector type provided to multiquery for #{name}, expected hash or Mongoid::Criteria, got #{data.class}"
    end

    normalized_queries[name] = details
    normalized_queries.with_indifferent_access
  end
end

#process_results(results, queries) ⇒ Object

If any results failed, create appropriate QueryFailedError objects.

Parameters:

  • results

    the results of the multiquery Javascript routine

  • queries

    the original queries



157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/mongoscript/multiquery.rb', line 157

def process_results(results, queries)
  processed_results = {}
  results.each_pair do |name, response|
    processed_results[name] = if response.is_a?(Hash) && response["error"]
      QueryFailedError.new(name, queries[name], response)
    elsif response
      # turn all the individual responses into real objects
      response.map {|data| MongoScript.rehydrate(queries[name][:klass], data)}
    end
  end
  processed_results
end

#validate_queries!(queries) ⇒ Object

Validate that all the queries are well formed. We could do this when building them, but doing it afterward allows us to present a single, comprehensive error message (in case multiple queries have problems).

Parameters:

  • queries

    a set of normalized queries



118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/mongoscript/multiquery.rb', line 118

def validate_queries!(queries)
  errors = {:collection => [], :klass => []}
  queries.each_pair do |name, details|
    errors[:collection] << name unless details[:collection]
    errors[:klass] << name unless details[:klass]
  end
  error_text = ""
  error_text += "Missing collection details: #{errors[:collection].join(", ")}." if errors[:collection].length > 0
  error_text += "Missing Ruby class details: #{errors[:klass].join(", ")}." if errors[:klass].length > 0
  if error_text.length > 0
    raise ArgumentError, "Unable to execute multiquery. #{error_text}"
  end
  true
end