|
1 | 1 | const _ = require('underscore'); |
| 2 | +const debug = require('debug')('leancloud:query'); |
| 3 | +const Promise = require('./promise'); |
2 | 4 | const AVError = require('./error'); |
3 | 5 | const AVRequest = require('./request').request; |
4 | 6 | const { ensureArray } = require('./utils'); |
@@ -263,27 +265,107 @@ module.exports = function(AV) { |
263 | 265 | return AVRequest('classes', this.className, null, "GET", params, options); |
264 | 266 | }, |
265 | 267 |
|
| 268 | + _parseResponse(response) { |
| 269 | + return _.map(response.results, (json) => { |
| 270 | + var obj = this._newObject(response); |
| 271 | + if (obj._finishFetch) { |
| 272 | + obj._finishFetch(this._processResult(json), true); |
| 273 | + } |
| 274 | + return obj; |
| 275 | + }); |
| 276 | + }, |
| 277 | + |
266 | 278 | /** |
267 | 279 | * Retrieves a list of AVObjects that satisfy this query. |
268 | 280 | * |
269 | 281 | * @param {AuthOptions} options |
270 | 282 | * @return {Promise} A promise that is resolved with the results when |
271 | 283 | * the query completes. |
272 | 284 | */ |
273 | | - find: function(options) { |
274 | | - var self = this; |
275 | | - |
276 | | - var request = this._createRequest(undefined, options); |
| 285 | + find(options) { |
| 286 | + const request = this._createRequest(undefined, options); |
| 287 | + return request.then(this._parseResponse.bind(this)); |
| 288 | + }, |
277 | 289 |
|
278 | | - return request.then(function(response) { |
279 | | - return _.map(response.results, function(json) { |
280 | | - var obj = self._newObject(response); |
281 | | - if (obj._finishFetch) { |
282 | | - obj._finishFetch(self._processResult(json), true); |
283 | | - } |
284 | | - return obj; |
285 | | - }); |
286 | | - }); |
| 290 | + /** |
| 291 | + * scan a Query. masterKey required. |
| 292 | + * |
| 293 | + * @param {String} orderedBy |
| 294 | + * @param {Number} batchSize |
| 295 | + * @return {AsyncIterator.<AV.Object>} |
| 296 | + * @example const scan = new AV.Query(TestClass).scan({ |
| 297 | + * orderedBy: 'objectId', |
| 298 | + * batchSize: 10, |
| 299 | + * }, { |
| 300 | + * useMasterKey: true, |
| 301 | + * }); |
| 302 | + * const getTen = () => Promise.all(new Array(10).fill(0).map(() => scan.next())); |
| 303 | + * getTen().then(results => { |
| 304 | + * // results are fisrt 10 instances of TestClass |
| 305 | + * return getTen(); |
| 306 | + * }).then(results => { |
| 307 | + * // 11 - 20 |
| 308 | + * }); |
| 309 | + */ |
| 310 | + scan({ |
| 311 | + orderedBy, |
| 312 | + batchSize, |
| 313 | + } = {}, authOptions) { |
| 314 | + const condition = this.toJSON(); |
| 315 | + debug('scan %O', condition); |
| 316 | + if (condition.order) { |
| 317 | + console.warn('The order of the query is ignored for Query#scan. Checkout the orderedBy option of Query#scan.'); |
| 318 | + delete condition.order; |
| 319 | + } |
| 320 | + if (condition.skip) { |
| 321 | + console.warn('The skip option of the query is ignored for Query#scan.'); |
| 322 | + delete condition.skip; |
| 323 | + } |
| 324 | + if (condition.limit) { |
| 325 | + console.warn('The limit option of the query is ignored for Query#scan.'); |
| 326 | + delete condition.limit; |
| 327 | + } |
| 328 | + if (orderedBy) condition.scan_key = orderedBy; |
| 329 | + if (batchSize) condition.limit = batchSize; |
| 330 | + let promise = Promise.resolve([]); |
| 331 | + let cursor; |
| 332 | + let done = false; |
| 333 | + return { |
| 334 | + next: () => { |
| 335 | + promise = promise.then((remainResults) => { |
| 336 | + if (done) return []; |
| 337 | + if (remainResults.length > 1) return remainResults; |
| 338 | + // no cursor means we have reached the end |
| 339 | + // except for the first time |
| 340 | + if (!cursor && remainResults.length !== 0) { |
| 341 | + done = true; |
| 342 | + return remainResults; |
| 343 | + } |
| 344 | + // when only 1 item left in queue |
| 345 | + // start the next request to see if it is the last one |
| 346 | + return AVRequest( |
| 347 | + 'scan/classes', |
| 348 | + this.className, |
| 349 | + null, |
| 350 | + 'GET', |
| 351 | + cursor ? _.extend({}, condition, { cursor }) : condition, |
| 352 | + authOptions |
| 353 | + ).then(response => { |
| 354 | + cursor = response.cursor; |
| 355 | + return this._parseResponse(response); |
| 356 | + }).then(results => { |
| 357 | + if (!results.length) done = true; |
| 358 | + return remainResults.concat(results); |
| 359 | + }); |
| 360 | + }); |
| 361 | + return promise |
| 362 | + .then(remainResults => remainResults.shift()) |
| 363 | + .then(result => ({ |
| 364 | + value: result, |
| 365 | + done, |
| 366 | + })); |
| 367 | + }, |
| 368 | + }; |
287 | 369 | }, |
288 | 370 |
|
289 | 371 | /** |
|
0 commit comments