Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(117)

Side by Side Diff: pkg/gcloud/lib/src/db/db.dart

Issue 804973002: Add appengine/gcloud/mustache dependencies. (Closed) Base URL: git@github.com:dart-lang/pub-dartlang-dart.git@master
Patch Set: Added AUTHORS/LICENSE/PATENTS files Created 6 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « pkg/gcloud/lib/src/db/annotations.dart ('k') | pkg/gcloud/lib/src/db/model_db.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 part of gcloud.db;
6
7 /**
8 * A function definition for transactional functions.
9 *
10 * The function will be given a [Transaction] object which can be used to make
11 * lookups/queries and queue modifications (inserts/updates/deletes).
12 */
13 typedef Future TransactionHandler(Transaction transaction);
14
15 /**
16 * A datastore transaction.
17 *
18 * It can be used for making lookups/queries and queue modifcations
19 * (inserts/updates/deletes). Finally the transaction can be either committed
20 * or rolled back.
21 */
22 class Transaction {
23 static const int _TRANSACTION_STARTED = 0;
24 static const int _TRANSACTION_ROLLED_BACK = 1;
25 static const int _TRANSACTION_COMMITTED = 2;
26
27 final DatastoreDB db;
28 final datastore.Transaction _datastoreTransaction;
29
30 final List<Model> _inserts = [];
31 final List<Key> _deletes = [];
32
33 int _transactionState = _TRANSACTION_STARTED;
34
35 Transaction(this.db, this._datastoreTransaction);
36
37 /**
38 * Looks up [keys] within this transaction.
39 */
40 Future<List<Model>> lookup(List<Key> keys) {
41 return _lookupHelper(db, keys, datastoreTransaction: _datastoreTransaction);
42 }
43
44 /**
45 * Enqueues [inserts] and [deletes] which should be commited at commit time.
46 */
47 void queueMutations({List<Model> inserts, List<Key> deletes}) {
48 _checkSealed();
49 if (inserts != null) {
50 _inserts.addAll(inserts);
51 }
52 if (deletes != null) {
53 _deletes.addAll(deletes);
54 }
55 }
56
57 /**
58 * Query for [kind] models with [ancestorKey].
59 *
60 * Note that [ancestorKey] is required, since a transaction is not allowed to
61 * touch/look at an arbitrary number of rows.
62 */
63 Query query(Type kind, Key ancestorKey, {Partition partition}) {
64 _checkSealed();
65 return new Query(db,
66 kind,
67 partition: partition,
68 ancestorKey: ancestorKey,
69 datastoreTransaction: _datastoreTransaction);
70 }
71
72 /**
73 * Rolls this transaction back.
74 */
75 Future rollback() {
76 _checkSealed(changeState: _TRANSACTION_ROLLED_BACK);
77 return db.datastore.rollback(_datastoreTransaction);
78 }
79
80 /**
81 * Commits this transaction including all of the queued mutations.
82 */
83 Future commit() {
84 _checkSealed(changeState: _TRANSACTION_COMMITTED);
85 return _commitHelper(db,
86 inserts: _inserts,
87 deletes: _deletes,
88 datastoreTransaction: _datastoreTransaction);
89 }
90
91 _checkSealed({int changeState}) {
92 if (_transactionState == _TRANSACTION_COMMITTED) {
93 throw new StateError(
94 'The transaction has already been committed.');
95 } else if (_transactionState == _TRANSACTION_ROLLED_BACK) {
96 throw new StateError(
97 'The transaction has already been rolled back.');
98 }
99 if (changeState != null) {
100 _transactionState = changeState;
101 }
102 }
103 }
104
105 class Query {
106 final _relationMapping = const <String, datastore.FilterRelation> {
107 '<': datastore.FilterRelation.LessThan,
108 '<=': datastore.FilterRelation.LessThanOrEqual,
109 '>': datastore.FilterRelation.GreatherThan,
110 '>=': datastore.FilterRelation.GreatherThanOrEqual,
111 '=': datastore.FilterRelation.Equal,
112 'IN': datastore.FilterRelation.In,
113 };
114
115 final DatastoreDB _db;
116 final datastore.Transaction _transaction;
117 final String _kind;
118
119 final Partition _partition;
120 final Key _ancestorKey;
121
122 final List<datastore.Filter> _filters = [];
123 final List<datastore.Order> _orders = [];
124 int _offset;
125 int _limit;
126
127 Query(DatastoreDB dbImpl, Type kind,
128 {Partition partition, Key ancestorKey,
129 datastore.Transaction datastoreTransaction})
130 : _db = dbImpl,
131 _kind = dbImpl.modelDB.kindName(kind),
132 _partition = partition,
133 _ancestorKey = ancestorKey, _transaction = datastoreTransaction;
134
135 /**
136 * Adds a filter to this [Query].
137 *
138 * [filterString] has form "name OP" where 'name' is a fieldName of the
139 * model and OP is an operator. The following operators are supported:
140 *
141 * * '<' (less than)
142 * * '<=' (less than or equal)
143 * * '>' (greater than)
144 * * '>=' (greater than or equal)
145 * * '=' (equal)
146 * * 'IN' (in - `comparisonObject` must be a list)
147 *
148 * [comparisonObject] is the object for comparison.
149 */
150 void filter(String filterString, Object comparisonObject) {
151 var parts = filterString.split(' ');
152 if (parts.length != 2 || !_relationMapping.containsKey(parts[1])) {
153 throw new ArgumentError(
154 "Invalid filter string '$filterString'.");
155 }
156
157 // TODO: do value transformation on [comparisionObject]
158
159 var propertyName = _convertToDatastoreName(parts[0]);
160 _filters.add(new datastore.Filter(
161 _relationMapping[parts[1]], propertyName, comparisonObject));
162 }
163
164 /**
165 * Adds an order to this [Query].
166 *
167 * [orderString] has the form "-name" where 'name' is a fieldName of the model
168 * and the optional '-' says whether the order is decending or ascending.
169 */
170 void order(String orderString) {
171 // TODO: validate [orderString] (e.g. is name valid)
172 if (orderString.startsWith('-')) {
173 _orders.add(new datastore.Order(
174 datastore.OrderDirection.Decending,
175 _convertToDatastoreName(orderString.substring(1))));
176 } else {
177 _orders.add(new datastore.Order(
178 datastore.OrderDirection.Ascending,
179 _convertToDatastoreName(orderString)));
180 }
181 }
182
183 /**
184 * Sets the [offset] of this [Query].
185 *
186 * When running this query, [offset] results will be skipped.
187 */
188 void offset(int offset) {
189 _offset = offset;
190 }
191
192 /**
193 * Sets the [limit] of this [Query].
194 *
195 * When running this query, a maximum of [limit] results will be returned.
196 */
197 void limit(int limit) {
198 _limit = limit;
199 }
200
201 /**
202 * Execute this [Query] on the datastore.
203 *
204 * Outside of transactions this method might return stale data or may not
205 * return the newest updates performed on the datastore since updates
206 * will be reflected in the indices in an eventual consistent way.
207 */
208 Stream<Model> run() {
209 var ancestorKey;
210 if (_ancestorKey != null) {
211 ancestorKey = _db.modelDB.toDatastoreKey(_ancestorKey);
212 }
213 var query = new datastore.Query(
214 ancestorKey: ancestorKey, kind: _kind,
215 filters: _filters, orders: _orders,
216 offset: _offset, limit: _limit);
217
218 var partition;
219 if (_partition != null) {
220 partition = new datastore.Partition(_partition.namespace);
221 }
222
223 return new StreamFromPages((int pageSize) {
224 return _db.datastore.query(
225 query, transaction: _transaction, partition: partition);
226 }).stream.map(_db.modelDB.fromDatastoreEntity);
227 }
228
229 // TODO:
230 // - add runPaged() returning Page<Model>
231 // - add run*() method once we have EntityResult{Entity,Cursor} in low-level
232 // API.
233
234 String _convertToDatastoreName(String name) {
235 var propertyName =
236 _db.modelDB.fieldNameToPropertyName(_kind, name);
237 if (propertyName == null) {
238 throw new ArgumentError(
239 "Field $name is not available for kind $_kind");
240 }
241 return propertyName;
242 }
243 }
244
245 class DatastoreDB {
246 final datastore.Datastore datastore;
247 final ModelDB _modelDB;
248 Partition _defaultPartition;
249
250 DatastoreDB(this.datastore, {ModelDB modelDB})
251 : _modelDB = modelDB != null ? modelDB : new ModelDBImpl() {
252 _defaultPartition = new Partition(null);
253 }
254
255 /**
256 * The [ModelDB] used to serialize/deserialize objects.
257 */
258 ModelDB get modelDB => _modelDB;
259
260 /**
261 * Gets the empty key using the default [Partition].
262 *
263 * Model keys with parent set to [emptyKey] will create their own entity
264 * groups.
265 */
266 Key get emptyKey => defaultPartition.emptyKey;
267
268 /**
269 * Gets the default [Partition].
270 */
271 Partition get defaultPartition => _defaultPartition;
272
273 /**
274 * Creates a new [Partition] with namespace [namespace].
275 */
276 Partition newPartition(String namespace) {
277 return new Partition(namespace);
278 }
279
280 /**
281 * Begins a new a new transaction.
282 *
283 * A transaction can touch only a limited number of entity groups. This limit
284 * is currently 5.
285 */
286 // TODO: Add retries and/or auto commit/rollback.
287 Future withTransaction(TransactionHandler transactionHandler) {
288 return datastore.beginTransaction(crossEntityGroup: true)
289 .then((datastoreTransaction) {
290 var transaction = new Transaction(this, datastoreTransaction);
291 return transactionHandler(transaction);
292 });
293 }
294
295 /**
296 * Build a query for [kind] models.
297 */
298 Query query(Type kind, {Partition partition, Key ancestorKey}) {
299 return new Query(this,
300 kind,
301 partition: partition,
302 ancestorKey: ancestorKey);
303 }
304
305 /**
306 * Looks up [keys] in the datastore and returns a list of [Model] objects.
307 *
308 * For transactions, please use [beginTransaction] and call the [lookup]
309 * method on it's returned [Transaction] object.
310 */
311 Future<List<Model>> lookup(List<Key> keys) {
312 return _lookupHelper(this, keys);
313 }
314
315 /**
316 * Add [inserts] to the datastore and remove [deletes] from it.
317 *
318 * The order of inserts and deletes is not specified. When the commit is done
319 * direct lookups will see the effect but non-ancestor queries will see the
320 * change in an eventual consistent way.
321 *
322 * For transactions, please use `beginTransaction` and it's returned
323 * [Transaction] object.
324 */
325 Future commit({List<Model> inserts, List<Key> deletes}) {
326 return _commitHelper(this, inserts: inserts, deletes: deletes);
327 }
328 }
329
330 Future _commitHelper(DatastoreDB db,
331 {List<Model> inserts,
332 List<Key> deletes,
333 datastore.Transaction datastoreTransaction}) {
334 var entityInserts, entityAutoIdInserts, entityDeletes;
335 var autoIdModelInserts;
336 if (inserts != null) {
337 entityInserts = [];
338 entityAutoIdInserts = [];
339 autoIdModelInserts = [];
340
341 for (var model in inserts) {
342 // If parent was not explicity set, we assume this model will map to
343 // it's own entity group.
344 if (model.parentKey == null) {
345 model.parentKey = db.defaultPartition.emptyKey;
346 }
347 if (model.id == null) {
348 autoIdModelInserts.add(model);
349 entityAutoIdInserts.add(db.modelDB.toDatastoreEntity(model));
350 } else {
351 entityInserts.add(db.modelDB.toDatastoreEntity(model));
352 }
353 }
354 }
355 if (deletes != null) {
356 entityDeletes = deletes.map(db.modelDB.toDatastoreKey).toList();
357 }
358
359 return db.datastore.commit(inserts: entityInserts,
360 autoIdInserts: entityAutoIdInserts,
361 deletes: entityDeletes,
362 transaction: datastoreTransaction)
363 .then((datastore.CommitResult result) {
364 if (entityAutoIdInserts != null && entityAutoIdInserts.length > 0) {
365 for (var i = 0; i < result.autoIdInsertKeys.length; i++) {
366 var key = db.modelDB.fromDatastoreKey(result.autoIdInsertKeys[i]);
367 autoIdModelInserts[i].parentKey = key.parent;
368 autoIdModelInserts[i].id = key.id;
369 }
370 }
371 });
372 }
373
374 Future<List<Model>> _lookupHelper(
375 DatastoreDB db, List<Key> keys,
376 {datastore.Transaction datastoreTransaction}) {
377 var entityKeys = keys.map(db.modelDB.toDatastoreKey).toList();
378 return db.datastore.lookup(entityKeys, transaction: datastoreTransaction)
379 .then((List<datastore.Entity> entities) {
380 return entities.map(db.modelDB.fromDatastoreEntity).toList();
381 });
382 }
OLDNEW
« no previous file with comments | « pkg/gcloud/lib/src/db/annotations.dart ('k') | pkg/gcloud/lib/src/db/model_db.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698