google-cloud-datastore 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/google-cloud-datastore.rb +141 -0
- data/lib/google/cloud/datastore.rb +532 -0
- data/lib/google/cloud/datastore/commit.rb +150 -0
- data/lib/google/cloud/datastore/credentials.rb +38 -0
- data/lib/google/cloud/datastore/cursor.rb +79 -0
- data/lib/google/cloud/datastore/dataset.rb +667 -0
- data/lib/google/cloud/datastore/dataset/lookup_results.rb +222 -0
- data/lib/google/cloud/datastore/dataset/query_results.rb +389 -0
- data/lib/google/cloud/datastore/entity.rb +454 -0
- data/lib/google/cloud/datastore/errors.rb +43 -0
- data/lib/google/cloud/datastore/gql_query.rb +216 -0
- data/lib/google/cloud/datastore/grpc_utils.rb +140 -0
- data/lib/google/cloud/datastore/key.rb +289 -0
- data/lib/google/cloud/datastore/properties.rb +133 -0
- data/lib/google/cloud/datastore/query.rb +351 -0
- data/lib/google/cloud/datastore/service.rb +171 -0
- data/lib/google/cloud/datastore/transaction.rb +365 -0
- data/lib/google/cloud/datastore/version.rb +22 -0
- data/lib/google/datastore/v1/datastore_pb.rb +120 -0
- data/lib/google/datastore/v1/datastore_services_pb.rb +61 -0
- data/lib/google/datastore/v1/entity_pb.rb +63 -0
- data/lib/google/datastore/v1/query_pb.rb +131 -0
- metadata +236 -0
@@ -0,0 +1,150 @@
|
|
1
|
+
# Copyright 2016 Google Inc. All rights reserved.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
|
16
|
+
module Google
|
17
|
+
module Cloud
|
18
|
+
module Datastore
|
19
|
+
##
|
20
|
+
# # Commit
|
21
|
+
#
|
22
|
+
# Object yielded from `commit` methods to allow multiple changes to be
|
23
|
+
# made in a single commit.
|
24
|
+
#
|
25
|
+
# @example
|
26
|
+
# gcloud = Google::Cloud.new
|
27
|
+
# datastore = gcloud.datastore
|
28
|
+
# datastore.commit do |c|
|
29
|
+
# c.save task1, task2
|
30
|
+
# c.delete entity1, entity2
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# See {Google::Cloud::Datastore::Dataset#commit} and
|
34
|
+
# {Google::Cloud::Datastore::Transaction#commit}.
|
35
|
+
class Commit
|
36
|
+
##
|
37
|
+
# @private Create a new Commit object.
|
38
|
+
def initialize
|
39
|
+
@shared_upserts = []
|
40
|
+
@shared_inserts = []
|
41
|
+
@shared_updates = []
|
42
|
+
@shared_deletes = []
|
43
|
+
end
|
44
|
+
|
45
|
+
##
|
46
|
+
# Saves entities to the Datastore.
|
47
|
+
#
|
48
|
+
# @param [Entity] entities One or more Entity objects to save.
|
49
|
+
#
|
50
|
+
# @example
|
51
|
+
# gcloud = Google::Cloud.new
|
52
|
+
# datastore = gcloud.datastore
|
53
|
+
# datastore.commit do |c|
|
54
|
+
# c.save task1, task2
|
55
|
+
# end
|
56
|
+
#
|
57
|
+
def save *entities
|
58
|
+
entities = Array(entities).flatten
|
59
|
+
@shared_upserts += entities unless entities.empty?
|
60
|
+
# Do not save yet
|
61
|
+
entities
|
62
|
+
end
|
63
|
+
alias_method :upsert, :save
|
64
|
+
|
65
|
+
##
|
66
|
+
# Inserts entities to the Datastore.
|
67
|
+
#
|
68
|
+
# @param [Entity] entities One or more Entity objects to insert.
|
69
|
+
#
|
70
|
+
# @example
|
71
|
+
# gcloud = Google::Cloud.new
|
72
|
+
# datastore = gcloud.datastore
|
73
|
+
# datastore.commit do |c|
|
74
|
+
# c.insert task1, task2
|
75
|
+
# end
|
76
|
+
#
|
77
|
+
def insert *entities
|
78
|
+
entities = Array(entities).flatten
|
79
|
+
@shared_inserts += entities unless entities.empty?
|
80
|
+
# Do not insert yet
|
81
|
+
entities
|
82
|
+
end
|
83
|
+
|
84
|
+
##
|
85
|
+
# Updates entities to the Datastore.
|
86
|
+
#
|
87
|
+
# @param [Entity] entities One or more Entity objects to update.
|
88
|
+
#
|
89
|
+
# @example
|
90
|
+
# gcloud = Google::Cloud.new
|
91
|
+
# datastore = gcloud.datastore
|
92
|
+
# datastore.commit do |c|
|
93
|
+
# c.update task1, task2
|
94
|
+
# end
|
95
|
+
#
|
96
|
+
def update *entities
|
97
|
+
entities = Array(entities).flatten
|
98
|
+
@shared_updates += entities unless entities.empty?
|
99
|
+
# Do not update yet
|
100
|
+
entities
|
101
|
+
end
|
102
|
+
|
103
|
+
##
|
104
|
+
# Remove entities from the Datastore.
|
105
|
+
#
|
106
|
+
# @param [Entity, Key] entities_or_keys One or more Entity or Key
|
107
|
+
# objects to remove.
|
108
|
+
#
|
109
|
+
# @example
|
110
|
+
# gcloud = Google::Cloud.new
|
111
|
+
# datastore = gcloud.datastore
|
112
|
+
# datastore.commit do |c|
|
113
|
+
# c.delete task1, task2
|
114
|
+
# end
|
115
|
+
#
|
116
|
+
def delete *entities_or_keys
|
117
|
+
keys = Array(entities_or_keys).flatten.map do |e_or_k|
|
118
|
+
e_or_k.respond_to?(:key) ? e_or_k.key : e_or_k
|
119
|
+
end
|
120
|
+
@shared_deletes += keys unless keys.empty?
|
121
|
+
# Do not delete yet
|
122
|
+
true
|
123
|
+
end
|
124
|
+
|
125
|
+
# @private Mutations object to be committed.
|
126
|
+
def mutations
|
127
|
+
mutations = []
|
128
|
+
mutations += @shared_upserts.map do |entity|
|
129
|
+
Google::Datastore::V1::Mutation.new upsert: entity.to_grpc
|
130
|
+
end
|
131
|
+
mutations += @shared_inserts.map do |entity|
|
132
|
+
Google::Datastore::V1::Mutation.new insert: entity.to_grpc
|
133
|
+
end
|
134
|
+
mutations += @shared_updates.map do |entity|
|
135
|
+
Google::Datastore::V1::Mutation.new update: entity.to_grpc
|
136
|
+
end
|
137
|
+
mutations += @shared_deletes.map do |key|
|
138
|
+
Google::Datastore::V1::Mutation.new delete: key.to_grpc
|
139
|
+
end
|
140
|
+
mutations
|
141
|
+
end
|
142
|
+
|
143
|
+
# @private All entities saved in the commit.
|
144
|
+
def entities
|
145
|
+
@shared_upserts + @shared_inserts + @shared_updates
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# Copyright 2014 Google Inc. All rights reserved.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
|
16
|
+
require "google/cloud/credentials"
|
17
|
+
|
18
|
+
module Google
|
19
|
+
module Cloud
|
20
|
+
module Datastore
|
21
|
+
##
|
22
|
+
# @private
|
23
|
+
#
|
24
|
+
# Authentication credentials to Google Cloud.
|
25
|
+
# The most common way to create this object is to provide the path
|
26
|
+
# to the JSON keyfile downloaded from Google Cloud.
|
27
|
+
#
|
28
|
+
# @see https://developers.google.com/accounts/docs/application-default-credentials
|
29
|
+
class Credentials < Google::Cloud::Credentials
|
30
|
+
SCOPE = ["https://www.googleapis.com/auth/datastore"]
|
31
|
+
PATH_ENV_VARS = %w(DATASTORE_KEYFILE GOOGLE_CLOUD_KEYFILE
|
32
|
+
GCLOUD_KEYFILE)
|
33
|
+
JSON_ENV_VARS = %w(DATASTORE_KEYFILE_JSON GOOGLE_CLOUD_KEYFILE_JSON
|
34
|
+
GCLOUD_KEYFILE_JSON)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# Copyright 2016 Google Inc. All rights reserved.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
|
16
|
+
module Google
|
17
|
+
module Cloud
|
18
|
+
module Datastore
|
19
|
+
##
|
20
|
+
# # Cursor
|
21
|
+
#
|
22
|
+
# Cursor is a point in query results. Cursors are returned in
|
23
|
+
# QueryResults.
|
24
|
+
#
|
25
|
+
# @example
|
26
|
+
# require "google/cloud"
|
27
|
+
#
|
28
|
+
# gcloud = Google::Cloud.new
|
29
|
+
# datastore = gcloud.datastore
|
30
|
+
#
|
31
|
+
# query = datastore.query("Task").
|
32
|
+
# where("done", "=", false)
|
33
|
+
#
|
34
|
+
# tasks = datastore.run query
|
35
|
+
# tasks.cursor #=> Cursor
|
36
|
+
#
|
37
|
+
class Cursor
|
38
|
+
# Base64 encoded array of bytes
|
39
|
+
def initialize cursor
|
40
|
+
@cursor = cursor
|
41
|
+
end
|
42
|
+
|
43
|
+
# Base64 encoded array of bytes
|
44
|
+
def to_s
|
45
|
+
@cursor
|
46
|
+
end
|
47
|
+
|
48
|
+
# @private
|
49
|
+
def inspect
|
50
|
+
"#{self.class}(#{@cursor})"
|
51
|
+
end
|
52
|
+
|
53
|
+
# @private
|
54
|
+
def == other
|
55
|
+
return false unless other.is_a? Cursor
|
56
|
+
@cursor == other.to_s
|
57
|
+
end
|
58
|
+
|
59
|
+
# @private
|
60
|
+
def <=> other
|
61
|
+
return -1 unless other.is_a? Cursor
|
62
|
+
@cursor <=> other.to_s
|
63
|
+
end
|
64
|
+
|
65
|
+
# @private byte array as a string
|
66
|
+
def to_grpc
|
67
|
+
Core::GRPCUtils.decode_bytes(@cursor)
|
68
|
+
end
|
69
|
+
|
70
|
+
# @private byte array as a string
|
71
|
+
def self.from_grpc grpc
|
72
|
+
grpc = String grpc
|
73
|
+
return nil if grpc.empty?
|
74
|
+
new Core::GRPCUtils.encode_bytes(grpc)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,667 @@
|
|
1
|
+
# Copyright 2014 Google Inc. All rights reserved.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
|
16
|
+
require "google/cloud/core/gce"
|
17
|
+
require "google/cloud/datastore/grpc_utils"
|
18
|
+
require "google/cloud/datastore/credentials"
|
19
|
+
require "google/cloud/datastore/service"
|
20
|
+
require "google/cloud/datastore/commit"
|
21
|
+
require "google/cloud/datastore/entity"
|
22
|
+
require "google/cloud/datastore/key"
|
23
|
+
require "google/cloud/datastore/query"
|
24
|
+
require "google/cloud/datastore/gql_query"
|
25
|
+
require "google/cloud/datastore/cursor"
|
26
|
+
require "google/cloud/datastore/dataset/lookup_results"
|
27
|
+
require "google/cloud/datastore/dataset/query_results"
|
28
|
+
|
29
|
+
module Google
|
30
|
+
module Cloud
|
31
|
+
module Datastore
|
32
|
+
##
|
33
|
+
# # Dataset
|
34
|
+
#
|
35
|
+
# Dataset is the data saved in a project's Datastore.
|
36
|
+
# Dataset is analogous to a database in relational database world.
|
37
|
+
#
|
38
|
+
# Google::Cloud::Datastore::Dataset is the main object for interacting
|
39
|
+
# with Google Datastore. {Google::Cloud::Datastore::Entity} objects are
|
40
|
+
# created, read, updated, and deleted by
|
41
|
+
# Google::Cloud::Datastore::Dataset.
|
42
|
+
#
|
43
|
+
# See {Google::Cloud#datastore}
|
44
|
+
#
|
45
|
+
# @example
|
46
|
+
# require "google/cloud"
|
47
|
+
#
|
48
|
+
# gcloud = Google::Cloud.new
|
49
|
+
# datastore = gcloud.datastore
|
50
|
+
#
|
51
|
+
# query = datastore.query("Task").
|
52
|
+
# where("done", "=", false)
|
53
|
+
#
|
54
|
+
# tasks = datastore.run query
|
55
|
+
#
|
56
|
+
class Dataset
|
57
|
+
##
|
58
|
+
# @private The gRPC Service object.
|
59
|
+
attr_accessor :service
|
60
|
+
|
61
|
+
##
|
62
|
+
# @private Creates a new Dataset instance.
|
63
|
+
#
|
64
|
+
# See {Google::Cloud#datastore}
|
65
|
+
def initialize service
|
66
|
+
@service = service
|
67
|
+
end
|
68
|
+
|
69
|
+
##
|
70
|
+
# The Datastore project connected to.
|
71
|
+
#
|
72
|
+
# @example
|
73
|
+
# require "google/cloud"
|
74
|
+
#
|
75
|
+
# gcloud = Google::Cloud.new "my-todo-project",
|
76
|
+
# "/path/to/keyfile.json"
|
77
|
+
#
|
78
|
+
# datastore = gcloud.datastore
|
79
|
+
# datastore.project #=> "my-todo-project"
|
80
|
+
#
|
81
|
+
def project
|
82
|
+
service.project
|
83
|
+
end
|
84
|
+
|
85
|
+
##
|
86
|
+
# @private Default project.
|
87
|
+
def self.default_project
|
88
|
+
ENV["DATASTORE_DATASET"] ||
|
89
|
+
ENV["DATASTORE_PROJECT"] ||
|
90
|
+
ENV["GOOGLE_CLOUD_PROJECT"] ||
|
91
|
+
ENV["GCLOUD_PROJECT"] ||
|
92
|
+
Google::Cloud::Core::GCE.project_id
|
93
|
+
end
|
94
|
+
|
95
|
+
##
|
96
|
+
# Generate IDs for a Key before creating an entity.
|
97
|
+
#
|
98
|
+
# @param [Key] incomplete_key A Key without `id` or `name` set.
|
99
|
+
# @param [String] count The number of new key IDs to create.
|
100
|
+
#
|
101
|
+
# @return [Array<Google::Cloud::Datastore::Key>]
|
102
|
+
#
|
103
|
+
# @example
|
104
|
+
# task_key = datastore.key "Task"
|
105
|
+
# task_keys = datastore.allocate_ids task_key, 5
|
106
|
+
#
|
107
|
+
def allocate_ids incomplete_key, count = 1
|
108
|
+
if incomplete_key.complete?
|
109
|
+
fail Google::Cloud::Datastore::KeyError,
|
110
|
+
"An incomplete key must be provided."
|
111
|
+
end
|
112
|
+
|
113
|
+
ensure_service!
|
114
|
+
incomplete_keys = count.times.map { incomplete_key.to_grpc }
|
115
|
+
allocate_res = service.allocate_ids(*incomplete_keys)
|
116
|
+
allocate_res.keys.map { |key| Key.from_grpc key }
|
117
|
+
end
|
118
|
+
|
119
|
+
##
|
120
|
+
# Persist one or more entities to the Datastore.
|
121
|
+
#
|
122
|
+
# @param [Entity] entities One or more entity objects to be saved.
|
123
|
+
#
|
124
|
+
# @return [Array<Google::Cloud::Datastore::Entity>]
|
125
|
+
#
|
126
|
+
# @example Insert a new entity:
|
127
|
+
# task = datastore.entity "Task" do |t|
|
128
|
+
# t["type"] = "Personal"
|
129
|
+
# t["done"] = false
|
130
|
+
# t["priority"] = 4
|
131
|
+
# t["description"] = "Learn Cloud Datastore"
|
132
|
+
# end
|
133
|
+
# task.key.id #=> nil
|
134
|
+
# datastore.save task
|
135
|
+
# task.key.id #=> 123456
|
136
|
+
#
|
137
|
+
# @example Insert multiple new entities in a batch:
|
138
|
+
# task1 = datastore.entity "Task" do |t|
|
139
|
+
# t["type"] = "Personal"
|
140
|
+
# t["done"] = false
|
141
|
+
# t["priority"] = 4
|
142
|
+
# t["description"] = "Learn Cloud Datastore"
|
143
|
+
# end
|
144
|
+
#
|
145
|
+
# task2 = datastore.entity "Task" do |t|
|
146
|
+
# t["type"] = "Personal"
|
147
|
+
# t["done"] = false
|
148
|
+
# t["priority"] = 5
|
149
|
+
# t["description"] = "Integrate Cloud Datastore"
|
150
|
+
# end
|
151
|
+
#
|
152
|
+
# task_key1, task_key2 = datastore.save(task1, task2).map(&:key)
|
153
|
+
#
|
154
|
+
# @example Update an existing entity:
|
155
|
+
# task = datastore.find "Task", "sampleTask"
|
156
|
+
# task["priority"] = 5
|
157
|
+
# datastore.save task
|
158
|
+
#
|
159
|
+
def save *entities
|
160
|
+
commit { |c| c.save(*entities) }
|
161
|
+
end
|
162
|
+
alias_method :upsert, :save
|
163
|
+
|
164
|
+
##
|
165
|
+
# Insert one or more entities to the Datastore. An InvalidArgumentError
|
166
|
+
# will raised if the entities cannot be inserted.
|
167
|
+
#
|
168
|
+
# @param [Entity] entities One or more entity objects to be inserted.
|
169
|
+
#
|
170
|
+
# @return [Array<Google::Cloud::Datastore::Entity>]
|
171
|
+
#
|
172
|
+
# @example Insert a new entity:
|
173
|
+
# task = datastore.entity "Task" do |t|
|
174
|
+
# t["type"] = "Personal"
|
175
|
+
# t["done"] = false
|
176
|
+
# t["priority"] = 4
|
177
|
+
# t["description"] = "Learn Cloud Datastore"
|
178
|
+
# end
|
179
|
+
# task.key.id #=> nil
|
180
|
+
# datastore.insert task
|
181
|
+
# task.key.id #=> 123456
|
182
|
+
#
|
183
|
+
# @example Insert multiple new entities in a batch:
|
184
|
+
# task1 = datastore.entity "Task" do |t|
|
185
|
+
# t["type"] = "Personal"
|
186
|
+
# t["done"] = false
|
187
|
+
# t["priority"] = 4
|
188
|
+
# t["description"] = "Learn Cloud Datastore"
|
189
|
+
# end
|
190
|
+
#
|
191
|
+
# task2 = datastore.entity "Task" do |t|
|
192
|
+
# t["type"] = "Personal"
|
193
|
+
# t["done"] = false
|
194
|
+
# t["priority"] = 5
|
195
|
+
# t["description"] = "Integrate Cloud Datastore"
|
196
|
+
# end
|
197
|
+
#
|
198
|
+
# task_key1, task_key2 = datastore.insert(task1, task2).map(&:key)
|
199
|
+
#
|
200
|
+
def insert *entities
|
201
|
+
commit { |c| c.insert(*entities) }
|
202
|
+
end
|
203
|
+
|
204
|
+
##
|
205
|
+
# Update one or more entities to the Datastore. An InvalidArgumentError
|
206
|
+
# will raised if the entities cannot be updated.
|
207
|
+
#
|
208
|
+
# @param [Entity] entities One or more entity objects to be updated.
|
209
|
+
#
|
210
|
+
# @return [Array<Google::Cloud::Datastore::Entity>]
|
211
|
+
#
|
212
|
+
# @example Update an existing entity:
|
213
|
+
# task = datastore.find "Task", "sampleTask"
|
214
|
+
# task["done"] = true
|
215
|
+
# datastore.save task
|
216
|
+
#
|
217
|
+
# @example update multiple new entities in a batch:
|
218
|
+
# query = datastore.query("Task").where("done", "=", false)
|
219
|
+
# tasks = datastore.run query
|
220
|
+
# tasks.each { |t| t["done"] = true }
|
221
|
+
# datastore.update tasks
|
222
|
+
#
|
223
|
+
def update *entities
|
224
|
+
commit { |c| c.update(*entities) }
|
225
|
+
end
|
226
|
+
|
227
|
+
##
|
228
|
+
# Remove entities from the Datastore.
|
229
|
+
#
|
230
|
+
# @param [Entity, Key] entities_or_keys One or more Entity or Key
|
231
|
+
# objects to remove.
|
232
|
+
#
|
233
|
+
# @return [Boolean] Returns `true` if successful
|
234
|
+
#
|
235
|
+
# @example
|
236
|
+
# gcloud = Google::Cloud.new
|
237
|
+
# datastore = gcloud.datastore
|
238
|
+
# datastore.delete task1, task2
|
239
|
+
#
|
240
|
+
def delete *entities_or_keys
|
241
|
+
commit { |c| c.delete(*entities_or_keys) }
|
242
|
+
true
|
243
|
+
end
|
244
|
+
|
245
|
+
##
|
246
|
+
# Make multiple changes in a single commit.
|
247
|
+
#
|
248
|
+
# @yield [commit] a block for making changes
|
249
|
+
# @yieldparam [Commit] commit The object that changes are made on
|
250
|
+
#
|
251
|
+
# @return [Array<Google::Cloud::Datastore::Entity>] The entities that
|
252
|
+
# were persisted.
|
253
|
+
#
|
254
|
+
# @example
|
255
|
+
# gcloud = Google::Cloud.new
|
256
|
+
# datastore = gcloud.datastore
|
257
|
+
# datastore.commit do |c|
|
258
|
+
# c.save task3, task4
|
259
|
+
# c.delete task1, task2
|
260
|
+
# end
|
261
|
+
#
|
262
|
+
def commit
|
263
|
+
return unless block_given?
|
264
|
+
c = Commit.new
|
265
|
+
yield c
|
266
|
+
|
267
|
+
ensure_service!
|
268
|
+
commit_res = service.commit c.mutations
|
269
|
+
entities = c.entities
|
270
|
+
returned_keys = commit_res.mutation_results.map(&:key)
|
271
|
+
returned_keys.each_with_index do |key, index|
|
272
|
+
next if entities[index].nil?
|
273
|
+
entities[index].key = Key.from_grpc(key) unless key.nil?
|
274
|
+
end
|
275
|
+
entities.each { |e| e.key.freeze unless e.persisted? }
|
276
|
+
entities
|
277
|
+
end
|
278
|
+
|
279
|
+
##
|
280
|
+
# Retrieve an entity by key.
|
281
|
+
#
|
282
|
+
# @param [Key, String] key_or_kind A Key object or `kind` string value.
|
283
|
+
# @param [Integer, String, nil] id_or_name The Key's `id` or `name`
|
284
|
+
# value if a `kind` was provided in the first parameter.
|
285
|
+
# @param [Symbol] consistency The non-transactional read consistency to
|
286
|
+
# use. Cannot be set to `:strong` for global queries. Accepted values
|
287
|
+
# are `:eventual` and `:strong`.
|
288
|
+
#
|
289
|
+
# The default consistency depends on the type of lookup used. See
|
290
|
+
# [Eventual Consistency in Google Cloud
|
291
|
+
# Datastore](https://cloud.google.com/datastore/docs/articles/balancing-strong-and-eventual-consistency-with-google-cloud-datastore/#h.tf76fya5nqk8)
|
292
|
+
# for more information.
|
293
|
+
#
|
294
|
+
# @return [Google::Cloud::Datastore::Entity, nil]
|
295
|
+
#
|
296
|
+
# @example Finding an entity with a key:
|
297
|
+
# task_key = datastore.key "Task", "sampleTask"
|
298
|
+
# task = datastore.find task_key
|
299
|
+
#
|
300
|
+
# @example Finding an entity with a `kind` and `id`/`name`:
|
301
|
+
# task = datastore.find "Task", "sampleTask"
|
302
|
+
#
|
303
|
+
def find key_or_kind, id_or_name = nil, consistency: nil
|
304
|
+
key = key_or_kind
|
305
|
+
unless key.is_a? Google::Cloud::Datastore::Key
|
306
|
+
key = Key.new key_or_kind, id_or_name
|
307
|
+
end
|
308
|
+
find_all(key, consistency: consistency).first
|
309
|
+
end
|
310
|
+
alias_method :get, :find
|
311
|
+
|
312
|
+
##
|
313
|
+
# Retrieve the entities for the provided keys. The order of results is
|
314
|
+
# undefined and has no relation to the order of `keys` arguments.
|
315
|
+
#
|
316
|
+
# @param [Key] keys One or more Key objects to find records for.
|
317
|
+
# @param [Symbol] consistency The non-transactional read consistency to
|
318
|
+
# use. Cannot be set to `:strong` for global queries. Accepted values
|
319
|
+
# are `:eventual` and `:strong`.
|
320
|
+
#
|
321
|
+
# The default consistency depends on the type of lookup used. See
|
322
|
+
# [Eventual Consistency in Google Cloud
|
323
|
+
# Datastore](https://cloud.google.com/datastore/docs/articles/balancing-strong-and-eventual-consistency-with-google-cloud-datastore/#h.tf76fya5nqk8)
|
324
|
+
# for more information.
|
325
|
+
#
|
326
|
+
# @return [Google::Cloud::Datastore::Dataset::LookupResults]
|
327
|
+
#
|
328
|
+
# @example
|
329
|
+
# gcloud = Google::Cloud.new
|
330
|
+
# datastore = gcloud.datastore
|
331
|
+
#
|
332
|
+
# task_key1 = datastore.key "Task", "sampleTask1"
|
333
|
+
# task_key2 = datastore.key "Task", "sampleTask2"
|
334
|
+
# tasks = datastore.find_all task_key1, task_key2
|
335
|
+
#
|
336
|
+
def find_all *keys, consistency: nil
|
337
|
+
ensure_service!
|
338
|
+
check_consistency! consistency
|
339
|
+
lookup_res = service.lookup(*Array(keys).flatten.map(&:to_grpc),
|
340
|
+
consistency: consistency)
|
341
|
+
LookupResults.from_grpc lookup_res, service, consistency
|
342
|
+
end
|
343
|
+
alias_method :lookup, :find_all
|
344
|
+
|
345
|
+
##
|
346
|
+
# Retrieve entities specified by a Query.
|
347
|
+
#
|
348
|
+
# @param [Query, GqlQuery] query The object with the search criteria.
|
349
|
+
# @param [String] namespace The namespace the query is to run within.
|
350
|
+
# @param [Symbol] consistency The non-transactional read consistency to
|
351
|
+
# use. Cannot be set to `:strong` for global queries. Accepted values
|
352
|
+
# are `:eventual` and `:strong`.
|
353
|
+
#
|
354
|
+
# The default consistency depends on the type of query used. See
|
355
|
+
# [Eventual Consistency in Google Cloud
|
356
|
+
# Datastore](https://cloud.google.com/datastore/docs/articles/balancing-strong-and-eventual-consistency-with-google-cloud-datastore/#h.tf76fya5nqk8)
|
357
|
+
# for more information.
|
358
|
+
#
|
359
|
+
# @return [Google::Cloud::Datastore::Dataset::QueryResults]
|
360
|
+
#
|
361
|
+
# @example
|
362
|
+
# query = datastore.query("Task").
|
363
|
+
# where("done", "=", false)
|
364
|
+
# tasks = datastore.run query
|
365
|
+
#
|
366
|
+
# @example Run an ancestor query with eventual consistency:
|
367
|
+
# task_list_key = datastore.key "TaskList", "default"
|
368
|
+
# query.kind("Task").
|
369
|
+
# ancestor(task_list_key)
|
370
|
+
#
|
371
|
+
# tasks = datastore.run query, consistency: :eventual
|
372
|
+
#
|
373
|
+
# @example Run the query within a namespace with the `namespace` option:
|
374
|
+
# query = datastore.query("Task").
|
375
|
+
# where("done", "=", false)
|
376
|
+
# tasks = datastore.run query, namespace: "ns~todo-project"
|
377
|
+
#
|
378
|
+
# @example Run the query with a GQL string.
|
379
|
+
# gql_query = datastore.gql "SELECT * FROM Task WHERE done = @done",
|
380
|
+
# done: false
|
381
|
+
# tasks = datastore.run gql_query
|
382
|
+
#
|
383
|
+
# @example Run the GQL query within a namespace with `namespace` option:
|
384
|
+
# gql_query = datastore.gql "SELECT * FROM Task WHERE done = @done",
|
385
|
+
# done: false
|
386
|
+
# tasks = datastore.run gql_query, namespace: "ns~todo-project"
|
387
|
+
#
|
388
|
+
def run query, namespace: nil, consistency: nil
|
389
|
+
ensure_service!
|
390
|
+
unless query.is_a?(Query) || query.is_a?(GqlQuery)
|
391
|
+
fail ArgumentError, "Cannot run a #{query.class} object."
|
392
|
+
end
|
393
|
+
check_consistency! consistency
|
394
|
+
query_res = service.run_query query.to_grpc, namespace,
|
395
|
+
consistency: consistency
|
396
|
+
QueryResults.from_grpc query_res, service, namespace,
|
397
|
+
query.to_grpc.dup
|
398
|
+
end
|
399
|
+
alias_method :run_query, :run
|
400
|
+
|
401
|
+
##
|
402
|
+
# Creates a Datastore Transaction.
|
403
|
+
#
|
404
|
+
# @yield [tx] a block yielding a new transaction
|
405
|
+
# @yieldparam [Transaction] tx the transaction object
|
406
|
+
#
|
407
|
+
# @example Runs the given block in a database transaction:
|
408
|
+
# require "google/cloud"
|
409
|
+
#
|
410
|
+
# gcloud = Google::Cloud.new
|
411
|
+
# datastore = gcloud.datastore
|
412
|
+
#
|
413
|
+
# task = datastore.entity "Task", "sampleTask" do |t|
|
414
|
+
# t["type"] = "Personal"
|
415
|
+
# t["done"] = false
|
416
|
+
# t["priority"] = 4
|
417
|
+
# t["description"] = "Learn Cloud Datastore"
|
418
|
+
# end
|
419
|
+
#
|
420
|
+
# datastore.transaction do |tx|
|
421
|
+
# if tx.find(task.key).nil?
|
422
|
+
# tx.save task
|
423
|
+
# end
|
424
|
+
# end
|
425
|
+
#
|
426
|
+
# @example If no block is given, a Transaction object is returned:
|
427
|
+
# require "google/cloud"
|
428
|
+
#
|
429
|
+
# gcloud = Google::Cloud.new
|
430
|
+
# datastore = gcloud.datastore
|
431
|
+
#
|
432
|
+
# task = datastore.entity "Task", "sampleTask" do |t|
|
433
|
+
# t["type"] = "Personal"
|
434
|
+
# t["done"] = false
|
435
|
+
# t["priority"] = 4
|
436
|
+
# t["description"] = "Learn Cloud Datastore"
|
437
|
+
# end
|
438
|
+
#
|
439
|
+
# tx = datastore.transaction
|
440
|
+
# begin
|
441
|
+
# if tx.find(task.key).nil?
|
442
|
+
# tx.save task
|
443
|
+
# end
|
444
|
+
# tx.commit
|
445
|
+
# rescue
|
446
|
+
# tx.rollback
|
447
|
+
# end
|
448
|
+
#
|
449
|
+
def transaction
|
450
|
+
tx = Transaction.new service
|
451
|
+
return tx unless block_given?
|
452
|
+
|
453
|
+
begin
|
454
|
+
yield tx
|
455
|
+
tx.commit
|
456
|
+
rescue
|
457
|
+
begin
|
458
|
+
tx.rollback
|
459
|
+
rescue
|
460
|
+
raise TransactionError,
|
461
|
+
"Transaction failed to commit and rollback."
|
462
|
+
end
|
463
|
+
raise TransactionError, "Transaction failed to commit."
|
464
|
+
end
|
465
|
+
end
|
466
|
+
|
467
|
+
##
|
468
|
+
# Create a new Query instance. This is a convenience method to make the
|
469
|
+
# creation of Query objects easier.
|
470
|
+
#
|
471
|
+
# @param [String] kinds The kind of entities to query. This is optional.
|
472
|
+
#
|
473
|
+
# @return [Google::Cloud::Datastore::Query]
|
474
|
+
#
|
475
|
+
# @example
|
476
|
+
# query = datastore.query("Task").
|
477
|
+
# where("done", "=", false)
|
478
|
+
# tasks = datastore.run query
|
479
|
+
#
|
480
|
+
# @example The previous example is equivalent to:
|
481
|
+
# query = Google::Cloud::Datastore::Query.new.
|
482
|
+
# kind("Task").
|
483
|
+
# where("done", "=", false)
|
484
|
+
# tasks = datastore.run query
|
485
|
+
#
|
486
|
+
def query *kinds
|
487
|
+
query = Query.new
|
488
|
+
query.kind(*kinds) unless kinds.empty?
|
489
|
+
query
|
490
|
+
end
|
491
|
+
|
492
|
+
##
|
493
|
+
# Create a new GqlQuery instance. This is a convenience method to make
|
494
|
+
# the creation of GqlQuery objects easier.
|
495
|
+
#
|
496
|
+
# @param [String] query The GQL query string.
|
497
|
+
# @param [Hash] bindings Named bindings for the GQL query string, each
|
498
|
+
# key must match regex `[A-Za-z_$][A-Za-z_$0-9]*`, must not match
|
499
|
+
# regex `__.*__`, and must not be `""`. The value must be an `Object`
|
500
|
+
# that can be stored as an Entity property value, or a `Cursor`.
|
501
|
+
#
|
502
|
+
# @return [Google::Cloud::Datastore::GqlQuery]
|
503
|
+
#
|
504
|
+
# @example
|
505
|
+
# gql_query = datastore.gql "SELECT * FROM Task WHERE done = @done",
|
506
|
+
# done: false
|
507
|
+
# tasks = datastore.run gql_query
|
508
|
+
#
|
509
|
+
# @example The previous example is equivalent to:
|
510
|
+
# gql_query = Google::Cloud::Datastore::GqlQuery.new
|
511
|
+
# gql_query.query_string = "SELECT * FROM Task WHERE done = @done"
|
512
|
+
# gql_query.named_bindings = {done: false}
|
513
|
+
# tasks = datastore.run gql_query
|
514
|
+
#
|
515
|
+
def gql query, bindings = {}
|
516
|
+
gql = GqlQuery.new
|
517
|
+
gql.query_string = query
|
518
|
+
gql.named_bindings = bindings unless bindings.empty?
|
519
|
+
gql
|
520
|
+
end
|
521
|
+
|
522
|
+
##
|
523
|
+
# Create a new Key instance. This is a convenience method to make the
|
524
|
+
# creation of Key objects easier.
|
525
|
+
#
|
526
|
+
# @param [Array<Array(String,(String|Integer|nil))>] path An optional
|
527
|
+
# list of pairs for the key's path. Each pair may include the key's
|
528
|
+
# kind (String) and an id (Integer) or name (String). This is
|
529
|
+
# optional.
|
530
|
+
# @param [String] project The project of the Key. This is optional.
|
531
|
+
# @param [String] namespace namespace kind of the Key. This is optional.
|
532
|
+
#
|
533
|
+
# @return [Google::Cloud::Datastore::Key]
|
534
|
+
#
|
535
|
+
# @example
|
536
|
+
# task_key = datastore.key "Task", "sampleTask"
|
537
|
+
#
|
538
|
+
# @example The previous example is equivalent to:
|
539
|
+
# task_key = Google::Cloud::Datastore::Key.new "Task", "sampleTask"
|
540
|
+
#
|
541
|
+
# @example Create an empty key:
|
542
|
+
# key = datastore.key
|
543
|
+
#
|
544
|
+
# @example Create an incomplete key:
|
545
|
+
# key = datastore.key "User"
|
546
|
+
#
|
547
|
+
# @example Create a key with a parent:
|
548
|
+
# key = datastore.key [["TaskList", "default"],
|
549
|
+
# ["Task", "sampleTask"]]
|
550
|
+
# key.path #=> [["TaskList", "default"], ["Task", "sampleTask"]]
|
551
|
+
#
|
552
|
+
# @example Create a key with multi-level ancestry:
|
553
|
+
# key = datastore.key([
|
554
|
+
# ["User", "alice"],
|
555
|
+
# ["TaskList", "default"],
|
556
|
+
# ["Task", "sampleTask"]
|
557
|
+
# ])
|
558
|
+
# key.path #=> [["User", "alice"], ["TaskList", "default"], [ ... ]]
|
559
|
+
#
|
560
|
+
# @example Create an incomplete key with a parent:
|
561
|
+
# key = datastore.key "TaskList", "default", "Task"
|
562
|
+
# key.path #=> [["TaskList", "default"], ["Task", nil]]
|
563
|
+
#
|
564
|
+
# @example Create a key with a project and namespace:
|
565
|
+
# key = datastore.key ["TaskList", "default"], ["Task", "sampleTask"],
|
566
|
+
# project: "my-todo-project",
|
567
|
+
# namespace: "ns~todo-project"
|
568
|
+
# key.path #=> [["TaskList", "default"], ["Task", "sampleTask"]]
|
569
|
+
# key.project #=> "my-todo-project",
|
570
|
+
# key.namespace #=> "ns~todo-project"
|
571
|
+
#
|
572
|
+
def key *path, project: nil, namespace: nil
|
573
|
+
path = path.flatten.each_slice(2).to_a # group in pairs
|
574
|
+
kind, id_or_name = path.pop
|
575
|
+
Key.new(kind, id_or_name).tap do |k|
|
576
|
+
k.project = project
|
577
|
+
k.namespace = namespace
|
578
|
+
unless path.empty?
|
579
|
+
k.parent = key path, project: project, namespace: namespace
|
580
|
+
end
|
581
|
+
end
|
582
|
+
end
|
583
|
+
|
584
|
+
##
|
585
|
+
# Create a new empty Entity instance. This is a convenience method to
|
586
|
+
# make the creation of Entity objects easier.
|
587
|
+
#
|
588
|
+
# @param [Key, Array<Array(String,(String|Integer|nil))>] key_or_path An
|
589
|
+
# optional list of pairs for the key's path. Each pair may include the
|
590
|
+
# key's kind (String) and an id (Integer) or name (String). This is
|
591
|
+
# optional.
|
592
|
+
# @param [String] project The project of the Key. This is optional.
|
593
|
+
# @param [String] namespace namespace kind of the Key. This is optional.
|
594
|
+
# @yield [entity] a block yielding a new entity
|
595
|
+
# @yieldparam [Entity] entity the newly created entity object
|
596
|
+
#
|
597
|
+
# @return [Google::Cloud::Datastore::Entity]
|
598
|
+
#
|
599
|
+
# @example
|
600
|
+
# task = datastore.entity
|
601
|
+
#
|
602
|
+
# @example The previous example is equivalent to:
|
603
|
+
# task = Google::Cloud::Datastore::Entity.new
|
604
|
+
#
|
605
|
+
# @example The key can also be passed in as an object:
|
606
|
+
# task_key = datastore.key "Task", "sampleTask"
|
607
|
+
# task = datastore.entity task_key
|
608
|
+
#
|
609
|
+
# @example Or the key values can be passed in as parameters:
|
610
|
+
# task = datastore.entity "Task", "sampleTask"
|
611
|
+
#
|
612
|
+
# @example The previous example is equivalent to:
|
613
|
+
# task_key = Google::Cloud::Datastore::Key.new "Task", "sampleTask"
|
614
|
+
# task = Google::Cloud::Datastore::Entity.new
|
615
|
+
# task.key = task_key
|
616
|
+
#
|
617
|
+
# @example The newly created entity can also be configured using block:
|
618
|
+
# task = datastore.entity "Task", "sampleTask" do |t|
|
619
|
+
# t["type"] = "Personal"
|
620
|
+
# t["done"] = false
|
621
|
+
# t["priority"] = 4
|
622
|
+
# t["description"] = "Learn Cloud Datastore"
|
623
|
+
# end
|
624
|
+
#
|
625
|
+
# @example The previous example is equivalent to:
|
626
|
+
# task_key = Google::Cloud::Datastore::Key.new "Task", "sampleTask"
|
627
|
+
# task = Google::Cloud::Datastore::Entity.new
|
628
|
+
# task.key = task_key
|
629
|
+
# task["type"] = "Personal"
|
630
|
+
# task["done"] = false
|
631
|
+
# task["priority"] = 4
|
632
|
+
# task["description"] = "Learn Cloud Datastore"
|
633
|
+
#
|
634
|
+
def entity *key_or_path, project: nil, namespace: nil
|
635
|
+
entity = Entity.new
|
636
|
+
|
637
|
+
# Set the key
|
638
|
+
if key_or_path.flatten.first.is_a? Google::Cloud::Datastore::Key
|
639
|
+
entity.key = key_or_path.flatten.first
|
640
|
+
else
|
641
|
+
entity.key = key key_or_path, project: project, namespace: namespace
|
642
|
+
end
|
643
|
+
|
644
|
+
yield entity if block_given?
|
645
|
+
|
646
|
+
entity
|
647
|
+
end
|
648
|
+
|
649
|
+
protected
|
650
|
+
|
651
|
+
##
|
652
|
+
# @private Raise an error unless an active connection to the service is
|
653
|
+
# available.
|
654
|
+
def ensure_service!
|
655
|
+
fail "Must have active connection to service" unless service
|
656
|
+
end
|
657
|
+
|
658
|
+
def check_consistency! consistency
|
659
|
+
fail(ArgumentError,
|
660
|
+
format("Consistency must be :eventual or :strong, not %s.",
|
661
|
+
consistency.inspect)
|
662
|
+
) unless [:eventual, :strong, nil].include? consistency
|
663
|
+
end
|
664
|
+
end
|
665
|
+
end
|
666
|
+
end
|
667
|
+
end
|