Class: Google::Cloud::Firestore::Query

Inherits:
Object
  • Object
show all
Defined in:
lib/google/cloud/firestore/query.rb

Overview

Query

Represents a query to the Firestore API.

Instances of this class are immutable. All methods that refine the query return new instances.

Examples:

require "google/cloud/firestore"

firestore = Google::Cloud::Firestore.new

# Create a query
query = firestore.col(:cities).select(:population)

query.get do |city|
  puts "#{city.document_id} has #{city[:population]} residents."
end

Listen to a query for changes:

require "google/cloud/firestore"

firestore = Google::Cloud::Firestore.new

# Create a query
query = firestore.col(:cities).order(:population, :desc)

listener = query.listen do |snapshot|
  puts "The query snapshot has #{snapshot.docs.count} documents "
  puts "and has #{snapshot.changes.count} changes."
end

# When ready, stop the listen operation and close the stream.
listener.stop

Direct Known Subclasses

CollectionReference

Instance Method Summary collapse

Instance Method Details

#end_at(*values) ⇒ Query

Ends query results at a set of field values. The field values can be specified explicitly as arguments, or can be specified implicitly by providing a DocumentSnapshot object instead. The result set will include the document specified by values.

If the current query already has specified end_before or end_at, this will overwrite it.

The values are associated with the field paths that have been provided to order, and must match the same sort order. An ArgumentError will be raised if more explicit values are given than are present in order.

Examples:

Ending a query at a document reference id

require "google/cloud/firestore"

firestore = Google::Cloud::Firestore.new

# Get a collection reference
cities_col = firestore.col "cities"
nyc_doc_id = "NYC"

# Create a query
query = cities_col.order(firestore.document_id)
                  .end_at(nyc_doc_id)

query.get do |city|
  puts "#{city.document_id} has #{city[:population]} residents."
end

Ending a query at a document reference object

require "google/cloud/firestore"

firestore = Google::Cloud::Firestore.new

# Get a collection reference
cities_col = firestore.col "cities"
nyc_doc_id = "NYC"
nyc_ref = cities_col.doc nyc_doc_id

# Create a query
query = cities_col.order(firestore.document_id)
                  .end_at(nyc_ref)

query.get do |city|
  puts "#{city.document_id} has #{city[:population]} residents."
end

Ending a query at multiple explicit values

require "google/cloud/firestore"

firestore = Google::Cloud::Firestore.new

# Get a collection reference
cities_col = firestore.col "cities"

# Create a query
query = cities_col.order(:population, :desc)
                  .order(:name)
                  .end_at(1000000, "New York City")

query.get do |city|
  puts "#{city.document_id} has #{city[:population]} residents."
end

Ending a query at a DocumentSnapshot

require "google/cloud/firestore"

firestore = Google::Cloud::Firestore.new

# Get a collection reference
cities_col = firestore.col "cities"

# Get a document snapshot
nyc_snap = firestore.doc("cities/NYC").get

# Create a query
query = cities_col.order(:population, :desc)
                  .order(:name)
                  .end_at(nyc_snap)

query.get do |city|
  puts "#{city.document_id} has #{city[:population]} residents."
end

Parameters:

  • values (DocumentSnapshot, Object, Array<Object>)

    The field values to end the query at.

Returns:

  • (Query)

    New query with end_at called on it.

Raises:

  • (ArgumentError)


874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
# File 'lib/google/cloud/firestore/query.rb', line 874

def end_at *values
  raise ArgumentError, "must provide values" if values.empty?

  if limit_type == :last
    raise "cannot call end_at after calling limit_to_last"
  end


  new_query = @query.dup
  new_query ||= StructuredQuery.new

  cursor = values_to_cursor values, new_query
  cursor.before = false
  new_query.end_at = cursor

  Query.start new_query, parent_path, client, limit_type: limit_type
end

#end_before(*values) ⇒ Query

Ends query results before a set of field values. The field values can be specified explicitly as arguments, or can be specified implicitly by providing a DocumentSnapshot object instead. The result set will not include the document specified by values.

If the current query already has specified end_before or end_at, this will overwrite it.

The values are associated with the field paths that have been provided to order, and must match the same sort order. An ArgumentError will be raised if more explicit values are given than are present in order.

Examples:

Ending a query before a document reference id

require "google/cloud/firestore"

firestore = Google::Cloud::Firestore.new

# Get a collection reference
cities_col = firestore.col "cities"
nyc_doc_id = "NYC"

# Create a query
query = cities_col.order(firestore.document_id)
                  .end_before(nyc_doc_id)

query.get do |city|
  puts "#{city.document_id} has #{city[:population]} residents."
end

Ending a query before a document reference object

require "google/cloud/firestore"

firestore = Google::Cloud::Firestore.new

# Get a collection reference
cities_col = firestore.col "cities"
nyc_doc_id = "NYC"
nyc_ref = cities_col.doc nyc_doc_id

# Create a query
query = cities_col.order(firestore.document_id)
                  .end_before(nyc_ref)

query.get do |city|
  puts "#{city.document_id} has #{city[:population]} residents."
end

Ending a query before multiple explicit values

require "google/cloud/firestore"

firestore = Google::Cloud::Firestore.new

# Get a collection reference
cities_col = firestore.col "cities"

# Create a query
query = cities_col.order(:population, :desc)
                  .order(:name)
                  .end_before(1000000, "New York City")

query.get do |city|
  puts "#{city.document_id} has #{city[:population]} residents."
end

Ending a query before a DocumentSnapshot

require "google/cloud/firestore"

firestore = Google::Cloud::Firestore.new

# Get a collection reference
cities_col = firestore.col "cities"

# Get a document snapshot
nyc_snap = firestore.doc("cities/NYC").get

# Create a query
query = cities_col.order(:population, :desc)
                  .order(:name)
                  .end_before(nyc_snap)

query.get do |city|
  puts "#{city.document_id} has #{city[:population]} residents."
end

Parameters:

  • values (DocumentSnapshot, Object, Array<Object>)

    The field values to end the query before.

Returns:

  • (Query)

    New query with end_before called on it.

Raises:

  • (ArgumentError)


765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
# File 'lib/google/cloud/firestore/query.rb', line 765

def end_before *values
  raise ArgumentError, "must provide values" if values.empty?

  if limit_type == :last
    raise "cannot call end_before after calling limit_to_last"
  end


  new_query = @query.dup
  new_query ||= StructuredQuery.new

  cursor = values_to_cursor values, new_query
  cursor.before = true
  new_query.end_at = cursor

  Query.start new_query, parent_path, client, limit_type: limit_type
end

#get {|documents| ... } ⇒ Enumerator<DocumentSnapshot> Also known as: run

Retrieves document snapshots for the query.

Examples:

require "google/cloud/firestore"

firestore = Google::Cloud::Firestore.new

# Get a collection reference
cities_col = firestore.col "cities"

# Create a query
query = cities_col.select(:population)

query.get do |city|
  puts "#{city.document_id} has #{city[:population]} residents."
end

Yields:

  • (documents)

    The block for accessing the document snapshots.

Yield Parameters:

Returns:



915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
# File 'lib/google/cloud/firestore/query.rb', line 915

def get
  ensure_service!

  return enum_for :get unless block_given?

  results = service.run_query parent_path, @query

  # Reverse the results for Query#limit_to_last queries since that method reversed the order_by directions.
  results = results.to_a.reverse if limit_type == :last

  results.each do |result|
    next if result.document.nil?
    yield DocumentSnapshot.from_query_result result, client
  end
end

#limit(num) ⇒ Query

Limits a query to return only the first matching documents.

If the current query already has a limit set, this will overwrite it.

Examples:

require "google/cloud/firestore"

firestore = Google::Cloud::Firestore.new

# Get a collection reference
cities_col = firestore.col "cities"

# Create a query
query = cities_col.order(:name, :desc).offset(10).limit(5)

query.get do |city|
  puts "#{city.document_id} has #{city[:population]} residents."
end

Parameters:

  • num (Integer)

    The maximum number of results to return.

Returns:

  • (Query)

    New query with limit called on it.



384
385
386
387
388
389
390
391
392
393
394
395
# File 'lib/google/cloud/firestore/query.rb', line 384

def limit num
  if limit_type == :last
    raise "cannot call limit after calling limit_to_last"
  end

  new_query = @query.dup
  new_query ||= StructuredQuery.new

  new_query.limit = Google::Protobuf::Int32Value.new value: num

  Query.start new_query, parent_path, client, limit_type: :first
end

#limit_to_last(num) ⇒ Query

Limits a query to return only the last matching documents.

You must specify at least one "order by" clause for limitToLast queries. (See #order.)

Results for limit_to_last queries are only available once all documents are received. Hence, limit_to_last queries cannot be streamed using #listen.

Examples:

require "google/cloud/firestore"

firestore = Google::Cloud::Firestore.new

# Get a collection reference
cities_col = firestore.col "cities"

# Create a query
query = cities_col.order(:name, :desc).limit_to_last(5)

query.get do |city|
  puts "#{city.document_id} has #{city[:population]} residents."
end

Parameters:

  • num (Integer)

    The maximum number of results to return.

Returns:

  • (Query)

    New query with limit_to_last called on it.



426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
# File 'lib/google/cloud/firestore/query.rb', line 426

def limit_to_last num
  new_query = @query.dup

  if new_query.nil? || new_query.order_by.nil? || new_query.order_by.empty?
    raise "specify at least one order clause before calling limit_to_last"
  end

  if limit_type != :last # Don't reverse order_by more than once.
    # Reverse the order_by directions since we want the last results.
    new_query.order_by.each do |order|
      order.direction = order.direction.to_sym == :DESCENDING ? :ASCENDING : :DESCENDING
    end

    # Swap the cursors to match the reversed query ordering.
    new_end_at = new_query.start_at.dup
    new_start_at = new_query.end_at.dup
    if new_end_at
      new_end_at.before = !new_end_at.before
      new_query.end_at = new_end_at
    end
    if new_start_at
      new_start_at.before = !new_start_at.before
      new_query.start_at = new_start_at
    end
  end

  new_query.limit = Google::Protobuf::Int32Value.new value: num

  Query.start new_query, parent_path, client, limit_type: :last
end

#listen {|callback| ... } ⇒ QueryListener Also known as: on_snapshot

Listen to this query for changes.

Examples:

require "google/cloud/firestore"

firestore = Google::Cloud::Firestore.new

# Create a query
query = firestore.col(:cities).order(:population, :desc)

listener = query.listen do |snapshot|
  puts "The query snapshot has #{snapshot.docs.count} documents "
  puts "and has #{snapshot.changes.count} changes."
end

# When ready, stop the listen operation and close the stream.
listener.stop

Yields:

  • (callback)

    The block for accessing the query snapshot.

Yield Parameters:

Returns:

Raises:

  • (ArgumentError)


956
957
958
959
960
961
962
# File 'lib/google/cloud/firestore/query.rb', line 956

def listen &callback
  raise ArgumentError, "callback required" if callback.nil?

  ensure_service!

  QueryListener.new(self, &callback).start
end

#offset(num) ⇒ Query

Skips to an offset in a query. If the current query already has specified an offset, this will overwrite it.

Examples:

require "google/cloud/firestore"

firestore = Google::Cloud::Firestore.new

# Get a collection reference
cities_col = firestore.col "cities"

# Create a query
query = cities_col.limit(5).offset(10)

query.get do |city|
  puts "#{city.document_id} has #{city[:population]} residents."
end

Parameters:

  • num (Integer)

    The number of results to skip.

Returns:

  • (Query)

    New query with offset called on it.



351
352
353
354
355
356
357
358
# File 'lib/google/cloud/firestore/query.rb', line 351

def offset num
  new_query = @query.dup
  new_query ||= StructuredQuery.new

  new_query.offset = num

  Query.start new_query, parent_path, client, limit_type: limit_type
end

#order(field, direction = :asc) ⇒ Query Also known as: order_by

Specifies an "order by" clause on a field.

Examples:

require "google/cloud/firestore"

firestore = Google::Cloud::Firestore.new

# Get a collection reference
cities_col = firestore.col "cities"

# Create a query
query = cities_col.order(:name)

query.get do |city|
  puts "#{city.document_id} has #{city[:population]} residents."
end

Order by name descending:

require "google/cloud/firestore"

firestore = Google::Cloud::Firestore.new

# Get a collection reference
cities_col = firestore.col "cities"

# Create a query
query = cities_col.order(:name, :desc)

query.get do |city|
  puts "#{city.document_id} has #{city[:population]} residents."
end

Parameters:

  • field (FieldPath, String, Symbol)

    A field path to order results with.

    If a FieldPath object is not provided then the field will be treated as a dotted string, meaning the string represents individual fields joined by ".". Fields containing ~, *, /, [, ], and . cannot be in a dotted string, and should provided using a FieldPath object instead.

  • direction (String, Symbol) (defaults to: :asc)

    The direction to order the results by. Values that start with "a" are considered ascending. Values that start with "d" are considered descending. Default is ascending. Optional.

Returns:

  • (Query)

    New query with order called on it.



307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
# File 'lib/google/cloud/firestore/query.rb', line 307

def order field, direction = :asc
  if query_has_cursors? || limit_type == :last
    raise "cannot call order after calling limit_to_last, start_at, start_after, end_before, or end_at"
  end

  new_query = @query.dup
  new_query ||= StructuredQuery.new

  field = FieldPath.parse field unless field.is_a? FieldPath

  new_query.order_by << StructuredQuery::Order.new(
    field:     StructuredQuery::FieldReference.new(
      field_path: field.formatted_string
    ),
    direction: order_direction(direction)
  )

  Query.start new_query, parent_path, client, limit_type: limit_type
end

#select(*fields) ⇒ Query

Restricts documents matching the query to return only data for the provided fields.

Examples:

require "google/cloud/firestore"

firestore = Google::Cloud::Firestore.new

# Get a collection reference
cities_col = firestore.col "cities"

# Create a query
query = cities_col.select(:population)

query.get do |city|
  puts "#{city.document_id} has #{city[:population]} residents."
end

Parameters:

  • fields (FieldPath, String, Symbol, Array<FieldPath|String|Symbol>)

    A field path to filter results with and return only the specified fields. One or more field paths can be specified.

    If a FieldPath object is not provided then the field will be treated as a dotted string, meaning the string represents individual fields joined by ".". Fields containing ~, *, /, [, ], and . cannot be in a dotted string, and should provided using a FieldPath object instead.

Returns:

  • (Query)

    New query with select called on it.



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/google/cloud/firestore/query.rb', line 108

def select *fields
  new_query = @query.dup
  new_query ||= StructuredQuery.new

  fields = Array(fields).flatten.compact
  fields = [FieldPath.document_id] if fields.empty?
  field_refs = fields.flatten.compact.map do |field|
    field = FieldPath.parse field unless field.is_a? FieldPath
    StructuredQuery::FieldReference.new \
      field_path: field.formatted_string
  end

  new_query.select = StructuredQuery::Projection.new
  field_refs.each do |field_ref|
    new_query.select.fields << field_ref
  end

  Query.start new_query, parent_path, client, limit_type: limit_type
end

#start_after(*values) ⇒ Query

Starts query results after a set of field values. The field values can be specified explicitly as arguments, or can be specified implicitly by providing a DocumentSnapshot object instead. The result set will not include the document specified by values.

If the current query already has specified start_at or start_after, this will overwrite it.

The values are associated with the field paths that have been provided to order, and must match the same sort order. An ArgumentError will be raised if more explicit values are given than are present in order.

Examples:

Starting a query after a document reference id

require "google/cloud/firestore"

firestore = Google::Cloud::Firestore.new

# Get a collection reference
cities_col = firestore.col "cities"
nyc_doc_id = "NYC"

# Create a query
query = cities_col.order(firestore.document_id)
                  .start_after(nyc_doc_id)

query.get do |city|
  puts "#{city.document_id} has #{city[:population]} residents."
end

Starting a query after a document reference object

require "google/cloud/firestore"

firestore = Google::Cloud::Firestore.new

# Get a collection reference
cities_col = firestore.col "cities"
nyc_doc_id = "NYC"
nyc_ref = cities_col.doc nyc_doc_id

# Create a query
query = cities_col.order(firestore.document_id)
                  .start_after(nyc_ref)

query.get do |city|
  puts "#{city.document_id} has #{city[:population]} residents."
end

Starting a query after multiple explicit values

require "google/cloud/firestore"

firestore = Google::Cloud::Firestore.new

# Get a collection reference
cities_col = firestore.col "cities"

# Create a query
query = cities_col.order(:population, :desc)
                  .order(:name)
                  .start_after(1000000, "New York City")

query.get do |city|
  puts "#{city.document_id} has #{city[:population]} residents."
end

Starting a query after a DocumentSnapshot

require "google/cloud/firestore"

firestore = Google::Cloud::Firestore.new

# Get a collection reference
cities_col = firestore.col "cities"

# Get a document snapshot
nyc_snap = firestore.doc("cities/NYC").get

# Create a query
query = cities_col.order(:population, :desc)
                  .order(:name)
                  .start_after(nyc_snap)

query.get do |city|
  puts "#{city.document_id} has #{city[:population]} residents."
end

Parameters:

  • values (DocumentSnapshot, Object, Array<Object>)

    The field values to start the query after.

Returns:

  • (Query)

    New query with start_after called on it.

Raises:

  • (ArgumentError)


656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
# File 'lib/google/cloud/firestore/query.rb', line 656

def start_after *values
  raise ArgumentError, "must provide values" if values.empty?

  if limit_type == :last
    raise "cannot call start_after after calling limit_to_last"
  end


  new_query = @query.dup
  new_query ||= StructuredQuery.new

  cursor = values_to_cursor values, new_query
  cursor.before = false
  new_query.start_at = cursor

  Query.start new_query, parent_path, client, limit_type: limit_type
end

#start_at(*values) ⇒ Query

Starts query results at a set of field values. The field values can be specified explicitly as arguments, or can be specified implicitly by providing a DocumentSnapshot object instead. The result set will include the document specified by values.

If the current query already has specified start_at or start_after, this will overwrite it.

The values are associated with the field paths that have been provided to order, and must match the same sort order. An ArgumentError will be raised if more explicit values are given than are present in order.

Examples:

Starting a query at a document reference id

require "google/cloud/firestore"

firestore = Google::Cloud::Firestore.new

# Get a collection reference
cities_col = firestore.col "cities"
nyc_doc_id = "NYC"

# Create a query
query = cities_col.order(firestore.document_id)
                  .start_at(nyc_doc_id)

query.get do |city|
  puts "#{city.document_id} has #{city[:population]} residents."
end

Starting a query at a document reference object

require "google/cloud/firestore"

firestore = Google::Cloud::Firestore.new

# Get a collection reference
cities_col = firestore.col "cities"
nyc_doc_id = "NYC"
nyc_ref = cities_col.doc nyc_doc_id

# Create a query
query = cities_col.order(firestore.document_id)
                  .start_at(nyc_ref)

query.get do |city|
  puts "#{city.document_id} has #{city[:population]} residents."
end

Starting a query at multiple explicit values

require "google/cloud/firestore"

firestore = Google::Cloud::Firestore.new

# Get a collection reference
cities_col = firestore.col "cities"

# Create a query
query = cities_col.order(:population, :desc)
                  .order(:name)
                  .start_at(1000000, "New York City")

query.get do |city|
  puts "#{city.document_id} has #{city[:population]} residents."
end

Starting a query at a DocumentSnapshot

require "google/cloud/firestore"

firestore = Google::Cloud::Firestore.new

# Get a collection reference
cities_col = firestore.col "cities"

# Get a document snapshot
nyc_snap = firestore.doc("cities/NYC").get

# Create a query
query = cities_col.order(:population, :desc)
                  .order(:name)
                  .start_at(nyc_snap)

query.get do |city|
  puts "#{city.document_id} has #{city[:population]} residents."
end

Parameters:

  • values (DocumentSnapshot, Object, Array<Object>)

    The field values to start the query at.

Returns:

  • (Query)

    New query with start_at called on it.

Raises:

  • (ArgumentError)


548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
# File 'lib/google/cloud/firestore/query.rb', line 548

def start_at *values
  raise ArgumentError, "must provide values" if values.empty?

  if limit_type == :last
    raise "cannot call start_at after calling limit_to_last"
  end

  new_query = @query.dup
  new_query ||= StructuredQuery.new

  cursor = values_to_cursor values, new_query
  cursor.before = true
  new_query.start_at = cursor

  Query.start new_query, parent_path, client, limit_type: limit_type
end

#where(field, operator, value) ⇒ Query

Filters the query on a field.

Examples:

require "google/cloud/firestore"

firestore = Google::Cloud::Firestore.new

# Get a collection reference
cities_col = firestore.col "cities"

# Create a query
query = cities_col.where(:population, :>=, 1000000)

query.get do |city|
  puts "#{city.document_id} has #{city[:population]} residents."
end

Parameters:

  • field (FieldPath, String, Symbol)

    A field path to filter results with.

    If a FieldPath object is not provided then the field will be treated as a dotted string, meaning the string represents individual fields joined by ".". Fields containing ~, *, /, [, ], and . cannot be in a dotted string, and should provided using a FieldPath object instead.

  • operator (String, Symbol)

    The operation to compare the field to. Acceptable values include:

    • less than: <, lt
    • less than or equal: <=, lte
    • greater than: >, gt
    • greater than or equal: >=, gte
    • equal: =, ==, eq, eql, is
    • not equal: !=
    • in: in
    • not in: not-in, not_in
    • array contains: array-contains, array_contains
  • value (Object)

    A value the field is compared to.

Returns:

  • (Query)

    New query with where called on it.



242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
# File 'lib/google/cloud/firestore/query.rb', line 242

def where field, operator, value
  if query_has_cursors?
    raise "cannot call where after calling " \
          "start_at, start_after, end_before, or end_at"
  end

  new_query = @query.dup
  new_query ||= StructuredQuery.new

  field = FieldPath.parse field unless field.is_a? FieldPath

  new_filter = filter field.formatted_string, operator, value
  add_filters_to_query new_query, new_filter

  Query.start new_query, parent_path, client, limit_type: limit_type
end