Skip to content

Commit c813929

Browse files
committed
initial commit
0 parents  commit c813929

File tree

6 files changed

+273
-0
lines changed

6 files changed

+273
-0
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
node_modules

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2016 Hustle, Inc.
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
## StitchQuery - "for getting all your stuff"™
2+
3+
StitchQuery allows you to query for more than 1000 objects by making multiple requests and stitching
4+
the results together.
5+
6+
If you need more than 10,000 results (unlikely), you can use a SuperStitchQuery.
7+
8+
#### How does it work?
9+
10+
StitchQuery sets the maximum hosted Parse limit of 1000 on the provided query and makes an initial
11+
request for the data. If 1000 objects are returned (the maximum batch size) we make additional
12+
requests, taking care to increment the query's skip by the batch size of 1000 until we reach the
13+
maximum skip (10k). This allows you to fetch up to 10,000 results using StitchQuery.
14+
15+
If desired, a SuperStitchQuery can be performed which will fetch unlimited results. It does this by
16+
applying a sort on `createdAt` to the provided query and, upon reaching the maximum skip limit, gets
17+
the date of the final item retrieved. To fetch the next batch it adds an additional query predicate
18+
requesting only items that have `createdAt` greater than that of the last item returned and resets
19+
the skip to zero. This process is repeated until all results are retrieved (receiving a batch with
20+
less than the requested limit of 1000 documents).
21+
22+
- Note: SuperStitchQuery should **not** be used on queries that already have a sort applied!
23+
24+
#### Installation
25+
```sh
26+
npm install --save parse-stitch-query
27+
```
28+
29+
#### Usage w/ Parse
30+
```js
31+
var Parse = require('parse-shim');
32+
var StitchQuery = require('parse-stitch-query');
33+
34+
var query = new Parse.Query(Model);
35+
query.equalTo('blah', 'cool');
36+
37+
// Get up to 10,000 results
38+
StitchQuery(query).then((results) => {
39+
// all the results are here!
40+
}, (error) => {
41+
// oh noes! errors
42+
})
43+
44+
// SuperStitchQuery, for unlimited results
45+
StitchQuery(query, true).then((results) => {
46+
// all the results are here!
47+
}, (error) => {
48+
// oh noes! errors
49+
})
50+
```

index.js

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
'use strict';
2+
3+
const Parse = require('parse-shim');
4+
5+
//
6+
// Run the specified parse query multiple times, modifying skip/limit each
7+
// time to download all available objects. Return a promise with the stitched
8+
// results from all individual queries. This works around the limit of 1000
9+
// results per parse query.
10+
//
11+
// For safety, unless superStitch is specified, this limits the total number
12+
// of queries to 10, yielding an effective max of 10,000 results.
13+
//
14+
// If 'superStitch' is specified, results are ordered and additionally paged
15+
// by the 'createdAt' date field after exhausting the 10k skip. This effectively
16+
// allows a query to yield unlimited results.
17+
//
18+
// @param query {Parse.Query} The query to stitch.
19+
// @param superStitch {Boolean} If you want more than 10k rows you can superStitch.
20+
//
21+
// NOTE: superStitch is not suitable for use with a sort (as it applies its own sort on createdAt)
22+
// NOTE: This will modify the specified query, do not reuse the query after calling this function.
23+
//
24+
function StitchQuery(query, superStitch) {
25+
const pageSize = 1000;
26+
const maxQueries = 10;
27+
28+
query.limit(pageSize);
29+
30+
if (superStitch) {
31+
query.ascending('createdAt');
32+
}
33+
34+
const stitchedPromise = new Parse.Promise();
35+
const stitchedResults = [];
36+
function getNextPage(curPage, startDate) {
37+
query.skip(curPage * pageSize);
38+
39+
if (startDate) {
40+
query.greaterThan('createdAt', startDate);
41+
}
42+
43+
const pagePromise = query.find();
44+
45+
pagePromise.done((pageResults) => {
46+
pageResults.forEach(result => { stitchedResults.push(result); });
47+
const maxBatchSize = pageResults.length === pageSize;
48+
const lastResult = stitchedResults[stitchedResults.length - 1];
49+
50+
if (maxBatchSize) {
51+
if (curPage + 1 < maxQueries) {
52+
getNextPage(curPage + 1, startDate);
53+
} else if (superStitch) {
54+
getNextPage(0, lastResult.createdAt);
55+
} else {
56+
stitchedPromise.resolve(stitchedResults);
57+
}
58+
} else {
59+
stitchedPromise.resolve(stitchedResults);
60+
}
61+
62+
});
63+
pagePromise.fail(stitchedPromise.reject);
64+
};
65+
66+
getNextPage(0);
67+
return stitchedPromise;
68+
};
69+
70+
module.exports = StitchQuery;

package.json

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"name": "parse-stitch-query",
3+
"version": "1.0.0",
4+
"description": "Query helper for retrieving a large number of objects from Parse",
5+
"main": "index.js",
6+
"scripts": {
7+
"test": "mocha test.js"
8+
},
9+
"repository": {
10+
"type": "git",
11+
"url": "git+https://github.com/HustleInc/parse-stitch-query.git"
12+
},
13+
"keywords": [
14+
"Parse",
15+
"query",
16+
"limit"
17+
],
18+
"author": "Tyler Brock, Mike Mintz",
19+
"license": "ISC",
20+
"bugs": {
21+
"url": "https://github.com/HustleInc/parse-stitch-query/issues"
22+
},
23+
"homepage": "https://github.com/HustleInc/parse-stitch-query#readme",
24+
"dependencies": {
25+
"mocha": "2.4.5",
26+
"parse": "^1.7.0",
27+
"parse-shim": "1.0.6"
28+
},
29+
"devDependencies": {
30+
"chai": "3.5.0",
31+
"parse-mockdb": "0.1.12"
32+
},
33+
"engines": {
34+
"node": ">=4.0.0"
35+
}
36+
}

test.js

+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
'use strict';
2+
3+
const Parse = require('parse-shim');
4+
5+
const ParseMockDB = require('parse-mockdb');
6+
const StitchQueryP = require('./index');
7+
const expect = require('chai').expect;
8+
9+
class Obj extends Parse.Object {
10+
constructor() {
11+
super('Obj');
12+
}
13+
};
14+
15+
function saveObjsP(count) {
16+
const objsToSave = [];
17+
for (let i=0; i<count; i++) {
18+
const obj = new Obj();
19+
objsToSave.push(obj);
20+
}
21+
return Parse.Object.saveAll(objsToSave);
22+
}
23+
24+
describe('StitchQueryP', function() {
25+
beforeEach(function() {
26+
Parse.MockDB.mockDB();
27+
});
28+
29+
afterEach(function() {
30+
Parse.MockDB.cleanUp();
31+
});
32+
33+
context('when we have exactly 100 objects in collection', function() {
34+
it('returns all of the expected results', function() {
35+
return saveObjsP(100).then((objs) => {
36+
return StitchQueryP(new Parse.Query(Obj))
37+
}).then((results) => {
38+
expect(results).to.have.lengthOf(100);
39+
})
40+
});
41+
});
42+
43+
context('when we have exactly 1000 objects in collection', function() {
44+
it('returns all of the expected results', function() {
45+
return saveObjsP(1000).then((objs) => {
46+
return StitchQueryP(new Parse.Query(Obj))
47+
}).then((results) => {
48+
expect(results).to.have.lengthOf(1000);
49+
})
50+
});
51+
});
52+
53+
context('when we have more than 1000 objects in collection', function() {
54+
it('returns all of the expected results', function() {
55+
return saveObjsP(1001).then((objs) => {
56+
return StitchQueryP(new Parse.Query(Obj))
57+
}).then((results) => {
58+
expect(results).to.have.lengthOf(1001);
59+
})
60+
});
61+
});
62+
63+
context('when we have exactly 10000 objects in collection', function() {
64+
it('returns all of the expected results @slow', function() {
65+
this.timeout(30000);
66+
return saveObjsP(10000).then((objs) => {
67+
return StitchQueryP(new Parse.Query(Obj))
68+
}).then((results) => {
69+
expect(results).to.have.lengthOf(10000);
70+
})
71+
});
72+
});
73+
74+
context('when we have greater than 10000 objects in collection', function() {
75+
it('returns returns only 10k of the expected results @slow', function() {
76+
this.timeout(30000);
77+
return saveObjsP(10100).then((objs) => {
78+
return StitchQueryP(new Parse.Query(Obj))
79+
}).then((results) => {
80+
expect(results).to.have.lengthOf(10000);
81+
})
82+
});
83+
84+
context('using super stitch query', function() {
85+
it('returns all of the expected results @slow', function() {
86+
this.timeout(30000);
87+
return saveObjsP(10100).then((objs) => {
88+
return StitchQueryP(new Parse.Query(Obj), true)
89+
}).then((results) => {
90+
expect(results).to.have.lengthOf(10100);
91+
})
92+
});
93+
});
94+
})
95+
});

0 commit comments

Comments
 (0)