Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(firestore): Add support for Query Partitions #11635

Merged
merged 23 commits into from
Jun 15, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
3530ec7
feat(firestore): Add support for Query Partitions
quartzmo May 4, 2021
d9747de
Add constructors to Query and CollectionReference
quartzmo May 18, 2021
2580944
Add QueryPartition and QueryPartition::List
quartzmo May 19, 2021
ce6aed9
Add order by to CollectionGroup#partitions
quartzmo May 21, 2021
3895e3f
Fix CollectionGroup#partitions and QueryPartition::List.from_grpc
quartzmo May 22, 2021
3f02954
Fix array support in Query#start_at, #start_after, #end_before and #e…
quartzmo May 24, 2021
9d566d0
feat(firestore): Add support for Query Partitions
quartzmo May 4, 2021
47425a7
Add constructors to Query and CollectionReference
quartzmo May 18, 2021
fb1c55e
Add QueryPartition and QueryPartition::List
quartzmo May 19, 2021
e3a9d9f
Add order by to CollectionGroup#partitions
quartzmo May 21, 2021
7317f9b
Fix CollectionGroup#partitions and QueryPartition::List.from_grpc
quartzmo May 22, 2021
058e306
Fix array support in Query#start_at, #start_after, #end_before and #e…
quartzmo May 24, 2021
b659ac2
Complete acceptance test
quartzmo May 25, 2021
f7f80c5
Update partitions samples
quartzmo May 25, 2021
a8388a2
Update documentation
quartzmo May 25, 2021
4e61da5
Update QueryPartition::List to add final QueryPartition only at end o…
quartzmo May 26, 2021
ab513c3
Update CollectionGroup#partitions to retrieve and sort all pages
quartzmo May 28, 2021
f9a8d5c
Add ResourcePath and update sort in CollectionGroup#partitions
quartzmo May 31, 2021
85c5ffa
Rename QueryPartition#create_query to #to_query
quartzmo Jun 1, 2021
345bfa9
Verify QueryPartition#start_at and #end_before can be used with a new…
quartzmo Jun 1, 2021
dc26349
Ensure an empty partition is returned when 1 is requested or when RPC…
quartzmo Jun 1, 2021
7463a03
Updates for dazuma comments
quartzmo Jun 1, 2021
cbbea36
Add Query#to_json and Query.from_json
quartzmo Jun 2, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions google-cloud-firestore/acceptance/firestore/query_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -284,4 +284,37 @@
_(results.map(&:document_id)).must_equal ["doc1", "doc2"]
_(results.map { |doc| doc[:foo] }).must_equal ["a", "b"]
end

it "has to_json method and from_json class method" do
rand_query_col = firestore.col "#{root_path}/query/#{SecureRandom.hex(4)}"
rand_query_col.doc("doc1").create({foo: "a"})
rand_query_col.doc("doc2").create({foo: "b"})
rand_query_col.doc("doc3").create({foo: "c"})

original_query = rand_query_col.order(:foo).limit_to_last 2

json = original_query.to_json
_(json).must_be_instance_of String

query = Google::Cloud::Firestore::Query.from_json json, firestore
quartzmo marked this conversation as resolved.
Show resolved Hide resolved
_(query).must_be_instance_of Google::Cloud::Firestore::Query

results_1 = []
query.get { |result| results_1 << result } # block directly to get, rpc
_(results_1.map(&:document_id)).must_equal ["doc2","doc3"]
_(results_1.map { |doc| doc[:foo] }).must_equal ["b","c"]

results_2 = []
query.get { |result| results_2 << result } # block directly to get, rpc
_(results_2.map(&:document_id)).must_equal ["doc2","doc3"]
_(results_2.map { |doc| doc[:foo] }).must_equal ["b","c"]

results_3 = query.get # enum_for :get
_(results_3.map(&:document_id)).must_equal ["doc2","doc3"] # rpc
_(results_3.map { |doc| doc[:foo] }).must_equal ["b","c"] # rpc

results_4 = query.get # enum_for :get
_(results_4.map(&:document_id)).must_equal ["doc2","doc3"] # rpc
_(results_4.map { |doc| doc[:foo] }).must_equal ["b","c"] # rpc
end
end
62 changes: 62 additions & 0 deletions google-cloud-firestore/lib/google/cloud/firestore/query.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
require "google/cloud/firestore/document_snapshot"
require "google/cloud/firestore/query_listener"
require "google/cloud/firestore/convert"
require "json"

module Google
module Cloud
Expand Down Expand Up @@ -972,6 +973,67 @@ def listen &callback
end
alias on_snapshot listen

##
# Serializes the instance to a JSON text string. See also {Query.from_json}.
#
# @return [String] A JSON text string.
#
# @example
# 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
#
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

##
# Deserializes a JSON text string serialized from this class and returns it as a new instance. See also
# {#to_json}.
#
# @param [String] json A JSON text string serialized using {#to_json}.
# @param [Google::Cloud::Firestore::Client] client A connected client instance.
#
# @return [Query] A new query equal to the original query used to create the JSON text string.
#
# @example
# 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
#
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

##
# @private Start a new Query.
def self.start query, parent_path, client, limit_type: nil
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def initialize query, start_at, end_before
end

##
# Creates a new query that only returns the documents for this partition, using the cursor valuess from
# Creates a new query that only returns the documents for this partition, using the cursor values from
# {#start_at} and {#end_before}.
#
# @return [Query] The query for the partition.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,43 @@
assert_results_enum results_enum
end

it "gets a complex query after serialization and deserialization" do
expected_query = Google::Cloud::Firestore::V1::StructuredQuery.new(
select: Google::Cloud::Firestore::V1::StructuredQuery::Projection.new(
fields: [Google::Cloud::Firestore::V1::StructuredQuery::FieldReference.new(field_path: "name")]),
from: [Google::Cloud::Firestore::V1::StructuredQuery::CollectionSelector.new(collection_id: "users", all_descendants: false)],
offset: 3,
limit: Google::Protobuf::Int32Value.new(value: 42),
order_by: [
Google::Cloud::Firestore::V1::StructuredQuery::Order.new(
field: Google::Cloud::Firestore::V1::StructuredQuery::FieldReference.new(field_path: "name"),
direction: :ASCENDING),
Google::Cloud::Firestore::V1::StructuredQuery::Order.new(
field: Google::Cloud::Firestore::V1::StructuredQuery::FieldReference.new(field_path: "__name__"),
direction: :DESCENDING)],
start_at: Google::Cloud::Firestore::V1::Cursor.new(values: [Google::Cloud::Firestore::Convert.raw_to_value("foo")], before: false),
end_at: Google::Cloud::Firestore::V1::Cursor.new(values: [Google::Cloud::Firestore::Convert.raw_to_value("bar")], before: true)
)
firestore_mock.expect :run_query, query_results_enum, run_query_args(expected_query)

original_query = firestore.col(:users).select(:name).offset(3).limit(42).order(:name).order(firestore.document_id, :desc).start_after(:foo).end_before(:bar)

json = original_query.to_json
_(json).must_be_instance_of String

deserialized_query = Google::Cloud::Firestore::Query.from_json json, firestore
_(deserialized_query).must_be_instance_of Google::Cloud::Firestore::Query

_(deserialized_query.query).must_equal expected_query # Private field
_(deserialized_query.parent_path).must_equal original_query.parent_path # Private field
_(deserialized_query.limit_type).must_equal original_query.limit_type # Private field
_(deserialized_query.client).must_equal original_query.client # Private field

results_enum = deserialized_query.get

assert_results_enum results_enum
end

def assert_results_enum enum
_(enum).must_be_kind_of Enumerator

Expand Down