Class: Mongory::QueryBuilder

Inherits:
Object
  • Object
show all
Includes:
Enumerable, Utils
Defined in:
lib/mongory/query_builder.rb

Overview

QueryBuilder provides a Mongo-like in-memory query interface.

It supports condition chaining (where, or, not), limiting, and plucking fields.

Internally it compiles all conditions and invokes QueryMatcher.

Examples:

Basic query

records.mongory
  .where(:age.gte => 18)
  .or({ :name => /J/ }, { :name.eq => 'Bob' })
  .limit(2)
  .to_a

Complex query

records.mongory
  .where(:status => 'active')
  .not(:age.lt => 18)
  .any_of({ :role => 'admin' }, { :role => 'moderator' })
  .pluck(:name, :email)

Direct Known Subclasses

CQueryBuilder

Instance Method Summary collapse

Methods included from Utils

included, included_classes, #is_blank?, #is_present?

Constructor Details

#initialize(records, context: Utils::Context.new) ⇒ QueryBuilder

Initializes a new query builder with the given record set.

Parameters:

  • records (Enumerable)

    the collection to query against



33
34
35
36
37
# File 'lib/mongory/query_builder.rb', line 33

def initialize(records, context: Utils::Context.new)
  @records = records
  @context = context
  set_matcher
end

Instance Method Details

#and(*conditions) ⇒ QueryBuilder

Adds one or more conditions combined with $and. All conditions must match for a record to be included.

Parameters:

  • conditions (Array<Hash>)

    the conditions to add

Returns:



118
119
120
121
122
# File 'lib/mongory/query_builder.rb', line 118

def and(*conditions)
  dup_instance_exec do
    add_conditions('$and', conditions)
  end
end

#any_of(*conditions) ⇒ QueryBuilder

Adds a $or query combined inside an $and block. This is a semantic alias for .and('$or' => [...])

Parameters:

  • conditions (Array<Hash>)

    the conditions to add

Returns:



145
146
147
# File 'lib/mongory/query_builder.rb', line 145

def any_of(*conditions)
  self.and('$or' => conditions)
end

#cObject



225
226
227
228
229
230
231
# File 'lib/mongory/query_builder.rb', line 225

def c
  return self unless defined?(Mongory::CMatcher)

  c_builder = CQueryBuilder.new(@records, context: @context)
  c_builder.send(:set_matcher, @matcher.condition)
  c_builder
end

#each {|record| ... } ⇒ Enumerator, void

Iterates through all records that match the current matcher. Uses the standard matcher implementation.

Yield Parameters:

  • record (Object)

    each matching record

Returns:

  • (Enumerator)

    if no block given

  • (void)

    if block given



45
46
47
48
49
50
51
52
53
# File 'lib/mongory/query_builder.rb', line 45

def each
  return to_enum(:each) unless block_given?

  @matcher.prepare_query
  @records.each do |record|
    @context.current_record = record
    yield record if @matcher.match?(record)
  end
end

#explainvoid

This method returns an undefined value.

Prints the internal matcher tree structure for the current query. Will output a human-readable visual tree of matchers. This is useful for debugging and visualizing complex conditions.



219
220
221
222
223
# File 'lib/mongory/query_builder.rb', line 219

def explain
  @matcher.match?(@records.first)
  @matcher.render_tree
  nil
end

#fast {|record| ... } ⇒ Enumerator, void

Deprecated.

Iterates through all records that match the current matcher. Uses a compiled Proc for faster matching.

Since C extension has implemented, the fast mode is no longer needed. Use C extension instead.

This method is deprecated and will be removed in future versions.

Yield Parameters:

  • record (Object)

    each matching record

Returns:

  • (Enumerator)

    if no block given

  • (void)

    if block given



66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/mongory/query_builder.rb', line 66

def fast
  warn('Deprecated: Since C extension has implemented, the fast mode is no longer needed. Use C extension instead.')
  return to_enum(:fast) unless block_given?

  @context.need_convert = false
  @matcher.prepare_query
  matcher_block = @matcher.to_proc
  @records.each do |record|
    @context.current_record = record
    yield record if matcher_block.call(record)
  end
end

#in(condition) ⇒ QueryBuilder

Adds an $in condition to the query. Matches records where the field value is in the given array.

Parameters:

  • condition (Hash)

    the field and values to match

Returns:



154
155
156
# File 'lib/mongory/query_builder.rb', line 154

def in(condition)
  self.and(wrap_values_with_key(condition, '$in'))
end

#limit(count) ⇒ QueryBuilder

Limits the number of records returned by the query.

Parameters:

  • count (Integer)

    the maximum number of records to return

Returns:



171
172
173
174
175
# File 'lib/mongory/query_builder.rb', line 171

def limit(count)
  dup_instance_exec do
    @records = take(count)
  end
end

#nin(condition) ⇒ QueryBuilder

Adds a $nin condition to the query. Matches records where the field value is not in the given array.

Parameters:

  • condition (Hash)

    the field and values to exclude

Returns:



163
164
165
# File 'lib/mongory/query_builder.rb', line 163

def nin(condition)
  self.and(wrap_values_with_key(condition, '$nin'))
end

#not(condition) ⇒ QueryBuilder

Adds a negated condition to the current query. Wraps the condition in a $not operator.

Parameters:

  • condition (Hash)

    the condition to negate

Returns:



109
110
111
# File 'lib/mongory/query_builder.rb', line 109

def not(condition)
  self.and('$not' => condition)
end

#or(*conditions) ⇒ QueryBuilder

Adds one or more conditions combined with $or. Any condition can match for a record to be included.

Parameters:

  • conditions (Array<Hash>)

    the conditions to add

Returns:



129
130
131
132
133
134
135
136
137
138
# File 'lib/mongory/query_builder.rb', line 129

def or(*conditions)
  operator = '$or'
  dup_instance_exec do
    if @matcher.condition.each_key.all? { |k| k == operator }
      add_conditions(operator, conditions)
    else
      set_matcher(operator => [@matcher.condition.dup, *conditions])
    end
  end
end

#pluck(field, *fields) ⇒ Array<Object>+

Extracts selected fields from matching records.

Parameters:

  • field (Symbol, String)

    the first field to extract

  • fields (Array<Symbol, String>)

    additional fields to extract

Returns:

  • (Array<Object>)

    array of single field values if one field given

  • (Array<Array<Object>>)

    array of field value arrays if multiple fields given



183
184
185
186
187
188
189
190
# File 'lib/mongory/query_builder.rb', line 183

def pluck(field, *fields)
  if fields.empty?
    map { |record| record[field] }
  else
    fields.unshift(field)
    map { |record| fields.map { |key| record[key] } }
  end
end

#raw_conditionHash

Returns the raw parsed condition for this query.

Returns:

  • (Hash)

    the raw compiled condition



195
196
197
# File 'lib/mongory/query_builder.rb', line 195

def raw_condition
  @matcher.condition
end

#traceObject



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/mongory/query_builder.rb', line 79

def trace
  return to_enum(:trace) unless block_given?

  Mongory.debugger.enable
  @matcher.prepare_query
  @records.each do |record|
    @context.current_record = record
    matched = @matcher.match?(record)
    Mongory.debugger.display
    yield record if matched
    Mongory.debugger.clear
  end
ensure
  Mongory.debugger.disable
end

#where(condition) ⇒ QueryBuilder

Adds a condition to filter records using the given condition. This is an alias for and.

Parameters:

  • condition (Hash)

    the condition to add

Returns:



100
101
102
# File 'lib/mongory/query_builder.rb', line 100

def where(condition)
  self.and(condition)
end

#with_context(addon_context = {}) ⇒ QueryBuilder

Note:

Creates a new query builder with the current matcher's condition and merged context

Creates a new query builder with additional context configuration.

Examples:

query.with_context(need_convert: false) #=> Returns a new query builder with conversion disabled

Parameters:

  • addon_context (Hash) (defaults to: {})

    Additional context configuration to merge

Returns:

  • (QueryBuilder)

    A new query builder instance with merged context



206
207
208
209
210
211
212
# File 'lib/mongory/query_builder.rb', line 206

def with_context(addon_context = {})
  dup_instance_exec do
    @context = @context.dup
    @context.config.merge!(addon_context)
    set_matcher(@matcher.condition)
  end
end