Skip to content

Commit 4fe670e

Browse files
committed
Merge pull request #752 from ParsePlatform/nlutsenko.adapter.collection
Add MongoCollection wrapper and move few basic uses of collection to it.
2 parents 63f7468 + 9538a7d commit 4fe670e

File tree

4 files changed

+99
-70
lines changed

4 files changed

+99
-70
lines changed
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
2+
let mongodb = require('mongodb');
3+
let Collection = mongodb.Collection;
4+
5+
export default class MongoCollection {
6+
_mongoCollection:Collection;
7+
8+
constructor(mongoCollection:Collection) {
9+
this._mongoCollection = mongoCollection;
10+
}
11+
12+
// Does a find with "smart indexing".
13+
// Currently this just means, if it needs a geoindex and there is
14+
// none, then build the geoindex.
15+
// This could be improved a lot but it's not clear if that's a good
16+
// idea. Or even if this behavior is a good idea.
17+
find(query, { skip, limit, sort } = {}) {
18+
return this._rawFind(query, { skip, limit, sort })
19+
.catch(error => {
20+
// Check for "no geoindex" error
21+
if (error.code != 17007 ||
22+
!error.message.match(/unable to find index for .geoNear/)) {
23+
throw error;
24+
}
25+
// Figure out what key needs an index
26+
let key = error.message.match(/field=([A-Za-z_0-9]+) /)[1];
27+
if (!key) {
28+
throw error;
29+
}
30+
31+
var index = {};
32+
index[key] = '2d';
33+
//TODO: condiser moving index creation logic into Schema.js
34+
return this._mongoCollection.createIndex(index)
35+
// Retry, but just once.
36+
.then(() => this._rawFind(query, { skip, limit, sort }));
37+
});
38+
}
39+
40+
_rawFind(query, { skip, limit, sort } = {}) {
41+
return this._mongoCollection
42+
.find(query, { skip, limit, sort })
43+
.toArray();
44+
}
45+
46+
count(query, { skip, limit, sort } = {}) {
47+
return this._mongoCollection.count(query, { skip, limit, sort });
48+
}
49+
50+
drop() {
51+
return this._mongoCollection.drop();
52+
}
53+
}

src/Adapters/Storage/Mongo/MongoStorageAdapter.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11

2+
import MongoCollection from './MongoCollection';
3+
24
let mongodb = require('mongodb');
35
let MongoClient = mongodb.MongoClient;
46

@@ -30,6 +32,12 @@ export class MongoStorageAdapter {
3032
});
3133
}
3234

35+
adaptiveCollection(name: string) {
36+
return this.connect()
37+
.then(() => this.database.collection(name))
38+
.then(rawCollection => new MongoCollection(rawCollection));
39+
}
40+
3341
collectionExists(name: string) {
3442
return this.connect().then(() => {
3543
return this.database.listCollections({ name: name }).toArray();

src/Controllers/DatabaseController.js

Lines changed: 20 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ DatabaseController.prototype.collection = function(className) {
3838
return this.rawCollection(className);
3939
};
4040

41+
DatabaseController.prototype.adaptiveCollection = function(className) {
42+
return this.adapter.adaptiveCollection(this.collectionPrefix + className);
43+
};
44+
4145
DatabaseController.prototype.collectionExists = function(className) {
4246
return this.adapter.collectionExists(this.collectionPrefix + className);
4347
};
@@ -340,9 +344,8 @@ DatabaseController.prototype.create = function(className, object, options) {
340344
// to avoid Mongo-format dependencies.
341345
// Returns a promise that resolves to a list of items.
342346
DatabaseController.prototype.mongoFind = function(className, query, options = {}) {
343-
return this.collection(className).then((coll) => {
344-
return coll.find(query, options).toArray();
345-
});
347+
return this.adaptiveCollection(className)
348+
.then(collection => collection.find(query, options));
346349
};
347350

348351
// Deletes everything in the database matching the current collectionPrefix
@@ -378,23 +381,17 @@ function keysForQuery(query) {
378381
// Returns a promise for a list of related ids given an owning id.
379382
// className here is the owning className.
380383
DatabaseController.prototype.relatedIds = function(className, key, owningId) {
381-
var joinTable = '_Join:' + key + ':' + className;
382-
return this.collection(joinTable).then((coll) => {
383-
return coll.find({owningId: owningId}).toArray();
384-
}).then((results) => {
385-
return results.map(r => r.relatedId);
386-
});
384+
return this.adaptiveCollection(joinTableName(className, key))
385+
.then(coll => coll.find({owningId : owningId}))
386+
.then(results => results.map(r => r.relatedId));
387387
};
388388

389389
// Returns a promise for a list of owning ids given some related ids.
390390
// className here is the owning className.
391391
DatabaseController.prototype.owningIds = function(className, key, relatedIds) {
392-
var joinTable = '_Join:' + key + ':' + className;
393-
return this.collection(joinTable).then((coll) => {
394-
return coll.find({relatedId: {'$in': relatedIds}}).toArray();
395-
}).then((results) => {
396-
return results.map(r => r.owningId);
397-
});
392+
return this.adaptiveCollection(joinTableName(className, key))
393+
.then(coll => coll.find({ relatedId: { '$in': relatedIds } }))
394+
.then(results => results.map(r => r.owningId));
398395
};
399396

400397
// Modifies query so that it no longer has $in on relation fields, or
@@ -443,38 +440,6 @@ DatabaseController.prototype.reduceRelationKeys = function(className, query) {
443440
}
444441
};
445442

446-
// Does a find with "smart indexing".
447-
// Currently this just means, if it needs a geoindex and there is
448-
// none, then build the geoindex.
449-
// This could be improved a lot but it's not clear if that's a good
450-
// idea. Or even if this behavior is a good idea.
451-
DatabaseController.prototype.smartFind = function(coll, where, options) {
452-
return coll.find(where, options).toArray()
453-
.then((result) => {
454-
return result;
455-
}, (error) => {
456-
// Check for "no geoindex" error
457-
if (!error.message.match(/unable to find index for .geoNear/) ||
458-
error.code != 17007) {
459-
throw error;
460-
}
461-
462-
// Figure out what key needs an index
463-
var key = error.message.match(/field=([A-Za-z_0-9]+) /)[1];
464-
if (!key) {
465-
throw error;
466-
}
467-
468-
var index = {};
469-
index[key] = '2d';
470-
//TODO: condiser moving index creation logic into Schema.js
471-
return coll.createIndex(index).then(() => {
472-
// Retry, but just once.
473-
return coll.find(where, options).toArray();
474-
});
475-
});
476-
};
477-
478443
// Runs a query on the database.
479444
// Returns a promise that resolves to a list of items.
480445
// Options:
@@ -528,8 +493,8 @@ DatabaseController.prototype.find = function(className, query, options = {}) {
528493
}).then(() => {
529494
return this.reduceInRelation(className, query, schema);
530495
}).then(() => {
531-
return this.collection(className);
532-
}).then((coll) => {
496+
return this.adaptiveCollection(className);
497+
}).then(collection => {
533498
var mongoWhere = transform.transformWhere(schema, className, query);
534499
if (!isMaster) {
535500
var orParts = [
@@ -542,9 +507,9 @@ DatabaseController.prototype.find = function(className, query, options = {}) {
542507
mongoWhere = {'$and': [mongoWhere, {'$or': orParts}]};
543508
}
544509
if (options.count) {
545-
return coll.count(mongoWhere, mongoOptions);
510+
return collection.count(mongoWhere, mongoOptions);
546511
} else {
547-
return this.smartFind(coll, mongoWhere, mongoOptions)
512+
return collection.find(mongoWhere, mongoOptions)
548513
.then((mongoResults) => {
549514
return mongoResults.map((r) => {
550515
return this.untransformObject(
@@ -555,4 +520,8 @@ DatabaseController.prototype.find = function(className, query, options = {}) {
555520
});
556521
};
557522

523+
function joinTableName(className, key) {
524+
return `_Join:${key}:${className}`;
525+
}
526+
558527
module.exports = DatabaseController;

src/Routers/SchemasRouter.js

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,10 @@ function mongoSchemaToSchemaAPIResponse(schema) {
3838
}
3939

4040
function getAllSchemas(req) {
41-
return req.config.database.collection('_SCHEMA')
42-
.then(coll => coll.find({}).toArray())
43-
.then(schemas => ({response: {
44-
results: schemas.map(mongoSchemaToSchemaAPIResponse)
45-
}}));
41+
return req.config.database.adaptiveCollection('_SCHEMA')
42+
.then(collection => collection.find({}))
43+
.then(schemas => schemas.map(mongoSchemaToSchemaAPIResponse))
44+
.then(schemas => ({ response: { results: schemas }}));
4645
}
4746

4847
function getOneSchema(req) {
@@ -152,7 +151,7 @@ function deleteSchema(req) {
152151
throw new Parse.Error(Parse.Error.INVALID_CLASS_NAME, Schema.invalidClassNameMessage(req.params.className));
153152
}
154153

155-
return req.config.database.collection(req.params.className)
154+
return req.config.database.adaptiveCollection(req.params.className)
156155
.then(collection => {
157156
return collection.count()
158157
.then(count => {
@@ -161,19 +160,19 @@ function deleteSchema(req) {
161160
}
162161
return collection.drop();
163162
})
164-
.then(() => {
165-
// We've dropped the collection now, so delete the item from _SCHEMA
166-
// and clear the _Join collections
167-
return req.config.database.collection('_SCHEMA')
168-
.then(coll => coll.findAndRemove({_id: req.params.className}, []))
169-
.then(doc => {
170-
if (doc.value === null) {
171-
//tried to delete non-existent class
172-
return Promise.resolve();
173-
}
174-
return removeJoinTables(req.config.database, doc.value);
175-
});
176-
})
163+
})
164+
.then(() => {
165+
// We've dropped the collection now, so delete the item from _SCHEMA
166+
// and clear the _Join collections
167+
return req.config.database.collection('_SCHEMA')
168+
.then(coll => coll.findAndRemove({_id: req.params.className}, []))
169+
.then(doc => {
170+
if (doc.value === null) {
171+
//tried to delete non-existent class
172+
return Promise.resolve();
173+
}
174+
return removeJoinTables(req.config.database, doc.value);
175+
});
177176
})
178177
.then(() => {
179178
// Success

0 commit comments

Comments
 (0)