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

CollectionGroup, CollectionReference

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.from_json(json, client) ⇒ Query

Deserializes a JSON text string serialized from this class and returns it as a new instance. See also #to_json.

Examples:

require "google/cloud/firestore"

firestore = Google::Cloud::Firestore.new
query = firestore.col(:cities).select(:population)

json = query.to_json

new_query = Google::Cloud::Firestore::Query.from_json json, firestore

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

Parameters:

Returns:

  • (Query)

    A new query equal to the original query used to create the JSON text string.

Raises:

  • (ArgumentError)


1027
1028
1029
1030
1031
1032
1033
1034
1035
# File 'lib/google/cloud/firestore/query.rb', line 1027

def self.from_json json, client
  raise ArgumentError, "client is required" unless client

  json = JSON.parse json
  query_json = json["query"]
  raise ArgumentError, "Field 'query' is required" unless query_json
  query = Google::Cloud::Firestore::V1::StructuredQuery.decode_json query_json.to_json
  start query, json["parent_path"], client, limit_type: json["limit_type"]&.to_sym
end

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)


885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
# File 'lib/google/cloud/firestore/query.rb', line 885

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)


776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
# File 'lib/google/cloud/firestore/query.rb', line 776

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:



926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
# File 'lib/google/cloud/firestore/query.rb', line 926

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.



395
396
397
398
399
400
401
402
403
404
405
406
# File 'lib/google/cloud/firestore/query.rb', line 395

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.



437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
# File 'lib/google/cloud/firestore/query.rb', line 437

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)


967
968
969
970
971
972
973
# File 'lib/google/cloud/firestore/query.rb', line 967

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.



362
363
364
365
366
367
368
369
# File 'lib/google/cloud/firestore/query.rb', line 362

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.



318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
# File 'lib/google/cloud/firestore/query.rb', line 318

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.



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/google/cloud/firestore/query.rb', line 119

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)


667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
# File 'lib/google/cloud/firestore/query.rb', line 667

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)


559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
# File 'lib/google/cloud/firestore/query.rb', line 559

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

#to_json(options = nil) ⇒ String

Serializes the instance to a JSON text string. See also from_json.

Examples:

require "google/cloud/firestore"

firestore = Google::Cloud::Firestore.new
query = firestore.col(:cities).select(:population)

json = query.to_json

new_query = Google::Cloud::Firestore::Query.from_json json, firestore

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

Returns:

  • (String)

    A JSON text string.



995
996
997
998
999
1000
1001
1002
# File 'lib/google/cloud/firestore/query.rb', line 995

def to_json options = nil
  query_json = Google::Cloud::Firestore::V1::StructuredQuery.encode_json query
  {
    "query" => JSON.parse(query_json),
    "parent_path" => parent_path,
    "limit_type" => limit_type
  }.to_json options
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.



253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
# File 'lib/google/cloud/firestore/query.rb', line 253

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