ParseQuery.js 71 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.default = void 0;
  6. var _CoreManager = _interopRequireDefault(require("./CoreManager"));
  7. var _encode = _interopRequireDefault(require("./encode"));
  8. var _promiseUtils = require("./promiseUtils");
  9. var _ParseError = _interopRequireDefault(require("./ParseError"));
  10. var _ParseGeoPoint = _interopRequireDefault(require("./ParseGeoPoint"));
  11. var _ParseObject = _interopRequireDefault(require("./ParseObject"));
  12. var _OfflineQuery = _interopRequireDefault(require("./OfflineQuery"));
  13. var _LocalDatastoreUtils = require("./LocalDatastoreUtils");
  14. function _interopRequireDefault(obj) {
  15. return obj && obj.__esModule ? obj : {
  16. default: obj
  17. };
  18. }
  19. function _defineProperty(obj, key, value) {
  20. if (key in obj) {
  21. Object.defineProperty(obj, key, {
  22. value: value,
  23. enumerable: true,
  24. configurable: true,
  25. writable: true
  26. });
  27. } else {
  28. obj[key] = value;
  29. }
  30. return obj;
  31. }
  32. /**
  33. * Converts a string into a regex that matches it.
  34. * Surrounding with \Q .. \E does this, we just need to escape any \E's in
  35. * the text separately.
  36. *
  37. * @param s
  38. * @private
  39. * @returns {string}
  40. */
  41. function quote(s
  42. /*: string*/
  43. )
  44. /*: string*/
  45. {
  46. return `\\Q${s.replace('\\E', '\\E\\\\E\\Q')}\\E`;
  47. }
  48. /**
  49. * Extracts the class name from queries. If not all queries have the same
  50. * class name an error will be thrown.
  51. *
  52. * @param queries
  53. * @private
  54. * @returns {string}
  55. */
  56. function _getClassNameFromQueries(queries
  57. /*: Array<ParseQuery>*/
  58. )
  59. /*: ?string*/
  60. {
  61. let className = null;
  62. queries.forEach(q => {
  63. if (!className) {
  64. // eslint-disable-next-line prefer-destructuring
  65. className = q.className;
  66. }
  67. if (className !== q.className) {
  68. throw new Error('All queries must be for the same class.');
  69. }
  70. });
  71. return className;
  72. }
  73. /*
  74. * Handles pre-populating the result data of a query with select fields,
  75. * making sure that the data object contains keys for all objects that have
  76. * been requested with a select, so that our cached state updates correctly.
  77. */
  78. function handleSelectResult(data
  79. /*: any*/
  80. , select
  81. /*: Array<string>*/
  82. ) {
  83. const serverDataMask = {};
  84. select.forEach(field => {
  85. const hasSubObjectSelect = field.indexOf('.') !== -1;
  86. if (!hasSubObjectSelect && !data.hasOwnProperty(field)) {
  87. // this field was selected, but is missing from the retrieved data
  88. data[field] = undefined;
  89. } else if (hasSubObjectSelect) {
  90. // this field references a sub-object,
  91. // so we need to walk down the path components
  92. const pathComponents = field.split('.');
  93. let obj = data;
  94. let serverMask = serverDataMask;
  95. pathComponents.forEach((component, index, arr) => {
  96. // add keys if the expected data is missing
  97. if (obj && !obj.hasOwnProperty(component)) {
  98. obj[component] = undefined;
  99. }
  100. if (obj && typeof obj === 'object') {
  101. obj = obj[component];
  102. } // add this path component to the server mask so we can fill it in later if needed
  103. if (index < arr.length - 1) {
  104. if (!serverMask[component]) {
  105. serverMask[component] = {};
  106. }
  107. serverMask = serverMask[component];
  108. }
  109. });
  110. }
  111. });
  112. if (Object.keys(serverDataMask).length > 0) {
  113. // When selecting from sub-objects, we don't want to blow away the missing
  114. // information that we may have retrieved before. We've already added any
  115. // missing selected keys to sub-objects, but we still need to add in the
  116. // data for any previously retrieved sub-objects that were not selected.
  117. const serverData = _CoreManager.default.getObjectStateController().getServerData({
  118. id: data.objectId,
  119. className: data.className
  120. });
  121. copyMissingDataWithMask(serverData, data, serverDataMask, false);
  122. }
  123. }
  124. function copyMissingDataWithMask(src, dest, mask, copyThisLevel) {
  125. // copy missing elements at this level
  126. if (copyThisLevel) {
  127. for (const key in src) {
  128. if (src.hasOwnProperty(key) && !dest.hasOwnProperty(key)) {
  129. dest[key] = src[key];
  130. }
  131. }
  132. }
  133. for (const key in mask) {
  134. if (dest[key] !== undefined && dest[key] !== null && src !== undefined && src !== null) {
  135. // traverse into objects as needed
  136. copyMissingDataWithMask(src[key], dest[key], mask[key], true);
  137. }
  138. }
  139. }
  140. function handleOfflineSort(a, b, sorts) {
  141. let order = sorts[0];
  142. const operator = order.slice(0, 1);
  143. const isDescending = operator === '-';
  144. if (isDescending) {
  145. order = order.substring(1);
  146. }
  147. if (order === '_created_at') {
  148. order = 'createdAt';
  149. }
  150. if (order === '_updated_at') {
  151. order = 'updatedAt';
  152. }
  153. if (!/^[A-Za-z][0-9A-Za-z_]*$/.test(order) || order === 'password') {
  154. throw new _ParseError.default(_ParseError.default.INVALID_KEY_NAME, `Invalid Key: ${order}`);
  155. }
  156. const field1 = a.get(order);
  157. const field2 = b.get(order);
  158. if (field1 < field2) {
  159. return isDescending ? 1 : -1;
  160. }
  161. if (field1 > field2) {
  162. return isDescending ? -1 : 1;
  163. }
  164. if (sorts.length > 1) {
  165. const remainingSorts = sorts.slice(1);
  166. return handleOfflineSort(a, b, remainingSorts);
  167. }
  168. return 0;
  169. }
  170. /**
  171. * Creates a new parse Parse.Query for the given Parse.Object subclass.
  172. *
  173. * <p>Parse.Query defines a query that is used to fetch Parse.Objects. The
  174. * most common use case is finding all objects that match a query through the
  175. * <code>find</code> method. for example, this sample code fetches all objects
  176. * of class <code>myclass</code>. it calls a different function depending on
  177. * whether the fetch succeeded or not.
  178. *
  179. * <pre>
  180. * var query = new Parse.Query(myclass);
  181. * query.find().then((results) => {
  182. * // results is an array of parse.object.
  183. * }).catch((error) => {
  184. * // error is an instance of parse.error.
  185. * });</pre></p>
  186. *
  187. * <p>a Parse.Query can also be used to retrieve a single object whose id is
  188. * known, through the get method. for example, this sample code fetches an
  189. * object of class <code>myclass</code> and id <code>myid</code>. it calls a
  190. * different function depending on whether the fetch succeeded or not.
  191. *
  192. * <pre>
  193. * var query = new Parse.Query(myclass);
  194. * query.get(myid).then((object) => {
  195. * // object is an instance of parse.object.
  196. * }).catch((error) => {
  197. * // error is an instance of parse.error.
  198. * });</pre></p>
  199. *
  200. * <p>a Parse.Query can also be used to count the number of objects that match
  201. * the query without retrieving all of those objects. for example, this
  202. * sample code counts the number of objects of the class <code>myclass</code>
  203. * <pre>
  204. * var query = new Parse.Query(myclass);
  205. * query.count().then((number) => {
  206. * // there are number instances of myclass.
  207. * }).catch((error) => {
  208. * // error is an instance of Parse.Error.
  209. * });</pre></p>
  210. *
  211. * @alias Parse.Query
  212. */
  213. class ParseQuery {
  214. /**
  215. * @property {string} className
  216. */
  217. /**
  218. * @param {(string | Parse.Object)} objectClass An instance of a subclass of Parse.Object, or a Parse className string.
  219. */
  220. constructor(objectClass
  221. /*: string | ParseObject*/
  222. ) {
  223. _defineProperty(this, "className", void 0);
  224. _defineProperty(this, "_where", void 0);
  225. _defineProperty(this, "_include", void 0);
  226. _defineProperty(this, "_exclude", void 0);
  227. _defineProperty(this, "_select", void 0);
  228. _defineProperty(this, "_limit", void 0);
  229. _defineProperty(this, "_skip", void 0);
  230. _defineProperty(this, "_count", void 0);
  231. _defineProperty(this, "_order", void 0);
  232. _defineProperty(this, "_readPreference", void 0);
  233. _defineProperty(this, "_includeReadPreference", void 0);
  234. _defineProperty(this, "_subqueryReadPreference", void 0);
  235. _defineProperty(this, "_queriesLocalDatastore", void 0);
  236. _defineProperty(this, "_localDatastorePinName", void 0);
  237. _defineProperty(this, "_extraOptions", void 0);
  238. _defineProperty(this, "_hint", void 0);
  239. _defineProperty(this, "_explain", void 0);
  240. _defineProperty(this, "_xhrRequest", void 0);
  241. if (typeof objectClass === 'string') {
  242. if (objectClass === 'User' && _CoreManager.default.get('PERFORM_USER_REWRITE')) {
  243. this.className = '_User';
  244. } else {
  245. this.className = objectClass;
  246. }
  247. } else if (objectClass instanceof _ParseObject.default) {
  248. this.className = objectClass.className;
  249. } else if (typeof objectClass === 'function') {
  250. if (typeof objectClass.className === 'string') {
  251. this.className = objectClass.className;
  252. } else {
  253. const obj = new objectClass();
  254. this.className = obj.className;
  255. }
  256. } else {
  257. throw new TypeError('A ParseQuery must be constructed with a ParseObject or class name.');
  258. }
  259. this._where = {};
  260. this._include = [];
  261. this._exclude = [];
  262. this._count = false; // negative limit is not sent in the server request
  263. this._limit = -1;
  264. this._skip = 0;
  265. this._readPreference = null;
  266. this._includeReadPreference = null;
  267. this._subqueryReadPreference = null;
  268. this._queriesLocalDatastore = false;
  269. this._localDatastorePinName = null;
  270. this._extraOptions = {};
  271. this._xhrRequest = {
  272. task: null,
  273. onchange: () => {}
  274. };
  275. }
  276. /**
  277. * Adds constraint that at least one of the passed in queries matches.
  278. *
  279. * @param {Array} queries
  280. * @returns {Parse.Query} Returns the query, so you can chain this call.
  281. */
  282. _orQuery(queries
  283. /*: Array<ParseQuery>*/
  284. )
  285. /*: ParseQuery*/
  286. {
  287. const queryJSON = queries.map(q => {
  288. return q.toJSON().where;
  289. });
  290. this._where.$or = queryJSON;
  291. return this;
  292. }
  293. /**
  294. * Adds constraint that all of the passed in queries match.
  295. *
  296. * @param {Array} queries
  297. * @returns {Parse.Query} Returns the query, so you can chain this call.
  298. */
  299. _andQuery(queries
  300. /*: Array<ParseQuery>*/
  301. )
  302. /*: ParseQuery*/
  303. {
  304. const queryJSON = queries.map(q => {
  305. return q.toJSON().where;
  306. });
  307. this._where.$and = queryJSON;
  308. return this;
  309. }
  310. /**
  311. * Adds constraint that none of the passed in queries match.
  312. *
  313. * @param {Array} queries
  314. * @returns {Parse.Query} Returns the query, so you can chain this call.
  315. */
  316. _norQuery(queries
  317. /*: Array<ParseQuery>*/
  318. )
  319. /*: ParseQuery*/
  320. {
  321. const queryJSON = queries.map(q => {
  322. return q.toJSON().where;
  323. });
  324. this._where.$nor = queryJSON;
  325. return this;
  326. }
  327. /**
  328. * Helper for condition queries
  329. *
  330. * @param key
  331. * @param condition
  332. * @param value
  333. * @returns {Parse.Query}
  334. */
  335. _addCondition(key
  336. /*: string*/
  337. , condition
  338. /*: string*/
  339. , value
  340. /*: mixed*/
  341. )
  342. /*: ParseQuery*/
  343. {
  344. if (!this._where[key] || typeof this._where[key] === 'string') {
  345. this._where[key] = {};
  346. }
  347. this._where[key][condition] = (0, _encode.default)(value, false, true);
  348. return this;
  349. }
  350. /**
  351. * Converts string for regular expression at the beginning
  352. *
  353. * @param string
  354. * @returns {string}
  355. */
  356. _regexStartWith(string
  357. /*: string*/
  358. )
  359. /*: string*/
  360. {
  361. return `^${quote(string)}`;
  362. }
  363. async _handleOfflineQuery(params
  364. /*: any*/
  365. ) {
  366. _OfflineQuery.default.validateQuery(this);
  367. const localDatastore = _CoreManager.default.getLocalDatastore();
  368. const objects = await localDatastore._serializeObjectsFromPinName(this._localDatastorePinName);
  369. let results = objects.map((json, index, arr) => {
  370. const object = _ParseObject.default.fromJSON(json, false);
  371. if (json._localId && !json.objectId) {
  372. object._localId = json._localId;
  373. }
  374. if (!_OfflineQuery.default.matchesQuery(this.className, object, arr, this)) {
  375. return null;
  376. }
  377. return object;
  378. }).filter(object => object !== null);
  379. if (params.keys) {
  380. let keys = params.keys.split(',');
  381. keys = keys.concat(['className', 'objectId', 'createdAt', 'updatedAt', 'ACL']);
  382. results = results.map(object => {
  383. const json = object._toFullJSON();
  384. Object.keys(json).forEach(key => {
  385. if (!keys.includes(key)) {
  386. delete json[key];
  387. }
  388. });
  389. return _ParseObject.default.fromJSON(json, false);
  390. });
  391. }
  392. if (params.order) {
  393. const sorts = params.order.split(',');
  394. results.sort((a, b) => {
  395. return handleOfflineSort(a, b, sorts);
  396. });
  397. } // count total before applying limit/skip
  398. let count;
  399. if (params.count) {
  400. // total count from response
  401. count = results.length;
  402. }
  403. if (params.skip) {
  404. if (params.skip >= results.length) {
  405. results = [];
  406. } else {
  407. results = results.splice(params.skip, results.length);
  408. }
  409. }
  410. let limit = results.length;
  411. if (params.limit !== 0 && params.limit < results.length) {
  412. // eslint-disable-next-line prefer-destructuring
  413. limit = params.limit;
  414. }
  415. results = results.splice(0, limit);
  416. if (typeof count === 'number') {
  417. return {
  418. results,
  419. count
  420. };
  421. }
  422. return results;
  423. }
  424. /**
  425. * Returns a JSON representation of this query.
  426. *
  427. * @returns {object} The JSON representation of the query.
  428. */
  429. toJSON()
  430. /*: QueryJSON*/
  431. {
  432. const params
  433. /*: QueryJSON*/
  434. = {
  435. where: this._where
  436. };
  437. if (this._include.length) {
  438. params.include = this._include.join(',');
  439. }
  440. if (this._exclude.length) {
  441. params.excludeKeys = this._exclude.join(',');
  442. }
  443. if (this._select) {
  444. params.keys = this._select.join(',');
  445. }
  446. if (this._count) {
  447. params.count = 1;
  448. }
  449. if (this._limit >= 0) {
  450. params.limit = this._limit;
  451. }
  452. if (this._skip > 0) {
  453. params.skip = this._skip;
  454. }
  455. if (this._order) {
  456. params.order = this._order.join(',');
  457. }
  458. if (this._readPreference) {
  459. params.readPreference = this._readPreference;
  460. }
  461. if (this._includeReadPreference) {
  462. params.includeReadPreference = this._includeReadPreference;
  463. }
  464. if (this._subqueryReadPreference) {
  465. params.subqueryReadPreference = this._subqueryReadPreference;
  466. }
  467. if (this._hint) {
  468. params.hint = this._hint;
  469. }
  470. if (this._explain) {
  471. params.explain = true;
  472. }
  473. for (const key in this._extraOptions) {
  474. params[key] = this._extraOptions[key];
  475. }
  476. return params;
  477. }
  478. /**
  479. * Return a query with conditions from json, can be useful to send query from server side to client
  480. * Not static, all query conditions was set before calling this method will be deleted.
  481. * For example on the server side we have
  482. * var query = new Parse.Query("className");
  483. * query.equalTo(key: value);
  484. * query.limit(100);
  485. * ... (others queries)
  486. * Create JSON representation of Query Object
  487. * var jsonFromServer = query.fromJSON();
  488. *
  489. * On client side getting query:
  490. * var query = new Parse.Query("className");
  491. * query.fromJSON(jsonFromServer);
  492. *
  493. * and continue to query...
  494. * query.skip(100).find().then(...);
  495. *
  496. * @param {QueryJSON} json from Parse.Query.toJSON() method
  497. * @returns {Parse.Query} Returns the query, so you can chain this call.
  498. */
  499. withJSON(json
  500. /*: QueryJSON*/
  501. )
  502. /*: ParseQuery*/
  503. {
  504. if (json.where) {
  505. this._where = json.where;
  506. }
  507. if (json.include) {
  508. this._include = json.include.split(',');
  509. }
  510. if (json.keys) {
  511. this._select = json.keys.split(',');
  512. }
  513. if (json.excludeKeys) {
  514. this._exclude = json.excludeKeys.split(',');
  515. }
  516. if (json.count) {
  517. this._count = json.count === 1;
  518. }
  519. if (json.limit) {
  520. this._limit = json.limit;
  521. }
  522. if (json.skip) {
  523. this._skip = json.skip;
  524. }
  525. if (json.order) {
  526. this._order = json.order.split(',');
  527. }
  528. if (json.readPreference) {
  529. this._readPreference = json.readPreference;
  530. }
  531. if (json.includeReadPreference) {
  532. this._includeReadPreference = json.includeReadPreference;
  533. }
  534. if (json.subqueryReadPreference) {
  535. this._subqueryReadPreference = json.subqueryReadPreference;
  536. }
  537. if (json.hint) {
  538. this._hint = json.hint;
  539. }
  540. if (json.explain) {
  541. this._explain = !!json.explain;
  542. }
  543. for (const key in json) {
  544. if (json.hasOwnProperty(key)) {
  545. if (['where', 'include', 'keys', 'count', 'limit', 'skip', 'order', 'readPreference', 'includeReadPreference', 'subqueryReadPreference', 'hint', 'explain'].indexOf(key) === -1) {
  546. this._extraOptions[key] = json[key];
  547. }
  548. }
  549. }
  550. return this;
  551. }
  552. /**
  553. * Static method to restore Parse.Query by json representation
  554. * Internally calling Parse.Query.withJSON
  555. *
  556. * @param {string} className
  557. * @param {QueryJSON} json from Parse.Query.toJSON() method
  558. * @returns {Parse.Query} new created query
  559. */
  560. static fromJSON(className
  561. /*: string*/
  562. , json
  563. /*: QueryJSON*/
  564. )
  565. /*: ParseQuery*/
  566. {
  567. const query = new ParseQuery(className);
  568. return query.withJSON(json);
  569. }
  570. /**
  571. * Constructs a Parse.Object whose id is already known by fetching data from
  572. * the server. Unlike the <code>first</code> method, it never returns undefined.
  573. *
  574. * @param {string} objectId The id of the object to be fetched.
  575. * @param {object} options
  576. * Valid options are:<ul>
  577. * <li>useMasterKey: In Cloud Code and Node only, causes the Master Key to
  578. * be used for this request.
  579. * <li>sessionToken: A valid session token, used for making a request on
  580. * behalf of a specific user.
  581. * <li>context: A dictionary that is accessible in Cloud Code `beforeFind` trigger.
  582. * </ul>
  583. *
  584. * @returns {Promise} A promise that is resolved with the result when
  585. * the query completes.
  586. */
  587. get(objectId
  588. /*: string*/
  589. , options
  590. /*:: ?: FullOptions*/
  591. )
  592. /*: Promise<ParseObject>*/
  593. {
  594. this.equalTo('objectId', objectId);
  595. const firstOptions = {};
  596. if (options && options.hasOwnProperty('useMasterKey')) {
  597. firstOptions.useMasterKey = options.useMasterKey;
  598. }
  599. if (options && options.hasOwnProperty('sessionToken')) {
  600. firstOptions.sessionToken = options.sessionToken;
  601. }
  602. if (options && options.hasOwnProperty('context') && typeof options.context === 'object') {
  603. firstOptions.context = options.context;
  604. }
  605. return this.first(firstOptions).then(response => {
  606. if (response) {
  607. return response;
  608. }
  609. const errorObject = new _ParseError.default(_ParseError.default.OBJECT_NOT_FOUND, 'Object not found.');
  610. return Promise.reject(errorObject);
  611. });
  612. }
  613. /**
  614. * Retrieves a list of ParseObjects that satisfy this query.
  615. *
  616. * @param {object} options Valid options
  617. * are:<ul>
  618. * <li>useMasterKey: In Cloud Code and Node only, causes the Master Key to
  619. * be used for this request.
  620. * <li>sessionToken: A valid session token, used for making a request on
  621. * behalf of a specific user.
  622. * <li>context: A dictionary that is accessible in Cloud Code `beforeFind` trigger.
  623. * </ul>
  624. *
  625. * @returns {Promise} A promise that is resolved with the results when
  626. * the query completes.
  627. */
  628. find(options
  629. /*:: ?: FullOptions*/
  630. )
  631. /*: Promise<Array<ParseObject>>*/
  632. {
  633. options = options || {};
  634. const findOptions = {};
  635. if (options.hasOwnProperty('useMasterKey')) {
  636. findOptions.useMasterKey = options.useMasterKey;
  637. }
  638. if (options.hasOwnProperty('sessionToken')) {
  639. findOptions.sessionToken = options.sessionToken;
  640. }
  641. if (options.hasOwnProperty('context') && typeof options.context === 'object') {
  642. findOptions.context = options.context;
  643. }
  644. this._setRequestTask(findOptions);
  645. const controller = _CoreManager.default.getQueryController();
  646. const select = this._select;
  647. if (this._queriesLocalDatastore) {
  648. return this._handleOfflineQuery(this.toJSON());
  649. }
  650. return controller.find(this.className, this.toJSON(), findOptions).then(response => {
  651. // Return generic object when explain is used
  652. if (this._explain) {
  653. return response.results;
  654. }
  655. const results = response.results.map(data => {
  656. // In cases of relations, the server may send back a className
  657. // on the top level of the payload
  658. const override = response.className || this.className;
  659. if (!data.className) {
  660. data.className = override;
  661. } // Make sure the data object contains keys for all objects that
  662. // have been requested with a select, so that our cached state
  663. // updates correctly.
  664. if (select) {
  665. handleSelectResult(data, select);
  666. }
  667. return _ParseObject.default.fromJSON(data, !select);
  668. });
  669. const {
  670. count
  671. } = response;
  672. if (typeof count === 'number') {
  673. return {
  674. results,
  675. count
  676. };
  677. }
  678. return results;
  679. });
  680. }
  681. /**
  682. * Retrieves a complete list of ParseObjects that satisfy this query.
  683. * Using `eachBatch` under the hood to fetch all the valid objects.
  684. *
  685. * @param {object} options Valid options are:<ul>
  686. * <li>batchSize: How many objects to yield in each batch (default: 100)
  687. * <li>useMasterKey: In Cloud Code and Node only, causes the Master Key to
  688. * be used for this request.
  689. * <li>sessionToken: A valid session token, used for making a request on
  690. * behalf of a specific user.
  691. * </ul>
  692. * @returns {Promise} A promise that is resolved with the results when
  693. * the query completes.
  694. */
  695. async findAll(options
  696. /*:: ?: BatchOptions*/
  697. )
  698. /*: Promise<Array<ParseObject>>*/
  699. {
  700. let result
  701. /*: ParseObject[]*/
  702. = [];
  703. await this.eachBatch((objects
  704. /*: ParseObject[]*/
  705. ) => {
  706. result = [...result, ...objects];
  707. }, options);
  708. return result;
  709. }
  710. /**
  711. * Counts the number of objects that match this query.
  712. *
  713. * @param {object} options
  714. * Valid options are:<ul>
  715. * <li>useMasterKey: In Cloud Code and Node only, causes the Master Key to
  716. * be used for this request.
  717. * <li>sessionToken: A valid session token, used for making a request on
  718. * behalf of a specific user.
  719. * </ul>
  720. *
  721. * @returns {Promise} A promise that is resolved with the count when
  722. * the query completes.
  723. */
  724. count(options
  725. /*:: ?: FullOptions*/
  726. )
  727. /*: Promise<number>*/
  728. {
  729. options = options || {};
  730. const findOptions = {};
  731. if (options.hasOwnProperty('useMasterKey')) {
  732. findOptions.useMasterKey = options.useMasterKey;
  733. }
  734. if (options.hasOwnProperty('sessionToken')) {
  735. findOptions.sessionToken = options.sessionToken;
  736. }
  737. this._setRequestTask(findOptions);
  738. const controller = _CoreManager.default.getQueryController();
  739. const params = this.toJSON();
  740. params.limit = 0;
  741. params.count = 1;
  742. return controller.find(this.className, params, findOptions).then(result => {
  743. return result.count;
  744. });
  745. }
  746. /**
  747. * Executes a distinct query and returns unique values
  748. *
  749. * @param {string} key A field to find distinct values
  750. * @param {object} options
  751. * Valid options are:<ul>
  752. * <li>sessionToken: A valid session token, used for making a request on
  753. * behalf of a specific user.
  754. * </ul>
  755. *
  756. * @returns {Promise} A promise that is resolved with the query completes.
  757. */
  758. distinct(key
  759. /*: string*/
  760. , options
  761. /*:: ?: FullOptions*/
  762. )
  763. /*: Promise<Array<mixed>>*/
  764. {
  765. options = options || {};
  766. const distinctOptions = {};
  767. distinctOptions.useMasterKey = true;
  768. if (options.hasOwnProperty('sessionToken')) {
  769. distinctOptions.sessionToken = options.sessionToken;
  770. }
  771. this._setRequestTask(distinctOptions);
  772. const controller = _CoreManager.default.getQueryController();
  773. const params = {
  774. distinct: key,
  775. where: this._where,
  776. hint: this._hint
  777. };
  778. return controller.aggregate(this.className, params, distinctOptions).then(results => {
  779. return results.results;
  780. });
  781. }
  782. /**
  783. * Executes an aggregate query and returns aggregate results
  784. *
  785. * @param {(Array|object)} pipeline Array or Object of stages to process query
  786. * @param {object} options Valid options are:<ul>
  787. * <li>sessionToken: A valid session token, used for making a request on
  788. * behalf of a specific user.
  789. * </ul>
  790. *
  791. * @returns {Promise} A promise that is resolved with the query completes.
  792. */
  793. aggregate(pipeline
  794. /*: mixed*/
  795. , options
  796. /*:: ?: FullOptions*/
  797. )
  798. /*: Promise<Array<mixed>>*/
  799. {
  800. options = options || {};
  801. const aggregateOptions = {};
  802. aggregateOptions.useMasterKey = true;
  803. if (options.hasOwnProperty('sessionToken')) {
  804. aggregateOptions.sessionToken = options.sessionToken;
  805. }
  806. this._setRequestTask(aggregateOptions);
  807. const controller = _CoreManager.default.getQueryController();
  808. if (!Array.isArray(pipeline) && typeof pipeline !== 'object') {
  809. throw new Error('Invalid pipeline must be Array or Object');
  810. }
  811. if (Object.keys(this._where || {}).length) {
  812. if (!Array.isArray(pipeline)) {
  813. pipeline = [pipeline];
  814. }
  815. pipeline.unshift({
  816. match: this._where
  817. });
  818. }
  819. const params = {
  820. pipeline,
  821. hint: this._hint,
  822. explain: this._explain,
  823. readPreference: this._readPreference
  824. };
  825. return controller.aggregate(this.className, params, aggregateOptions).then(results => {
  826. return results.results;
  827. });
  828. }
  829. /**
  830. * Retrieves at most one Parse.Object that satisfies this query.
  831. *
  832. * Returns the object if there is one, otherwise undefined.
  833. *
  834. * @param {object} options Valid options are:<ul>
  835. * <li>useMasterKey: In Cloud Code and Node only, causes the Master Key to
  836. * be used for this request.
  837. * <li>sessionToken: A valid session token, used for making a request on
  838. * behalf of a specific user.
  839. * <li>context: A dictionary that is accessible in Cloud Code `beforeFind` trigger.
  840. * </ul>
  841. *
  842. * @returns {Promise} A promise that is resolved with the object when
  843. * the query completes.
  844. */
  845. first(options
  846. /*:: ?: FullOptions*/
  847. )
  848. /*: Promise<ParseObject | void>*/
  849. {
  850. options = options || {};
  851. const findOptions = {};
  852. if (options.hasOwnProperty('useMasterKey')) {
  853. findOptions.useMasterKey = options.useMasterKey;
  854. }
  855. if (options.hasOwnProperty('sessionToken')) {
  856. findOptions.sessionToken = options.sessionToken;
  857. }
  858. if (options.hasOwnProperty('context') && typeof options.context === 'object') {
  859. findOptions.context = options.context;
  860. }
  861. this._setRequestTask(findOptions);
  862. const controller = _CoreManager.default.getQueryController();
  863. const params = this.toJSON();
  864. params.limit = 1;
  865. const select = this._select;
  866. if (this._queriesLocalDatastore) {
  867. return this._handleOfflineQuery(params).then(objects => {
  868. if (!objects[0]) {
  869. return undefined;
  870. }
  871. return objects[0];
  872. });
  873. }
  874. return controller.find(this.className, params, findOptions).then(response => {
  875. const objects = response.results;
  876. if (!objects[0]) {
  877. return undefined;
  878. }
  879. if (!objects[0].className) {
  880. objects[0].className = this.className;
  881. } // Make sure the data object contains keys for all objects that
  882. // have been requested with a select, so that our cached state
  883. // updates correctly.
  884. if (select) {
  885. handleSelectResult(objects[0], select);
  886. }
  887. return _ParseObject.default.fromJSON(objects[0], !select);
  888. });
  889. }
  890. /**
  891. * Iterates over objects matching a query, calling a callback for each batch.
  892. * If the callback returns a promise, the iteration will not continue until
  893. * that promise has been fulfilled. If the callback returns a rejected
  894. * promise, then iteration will stop with that error. The items are processed
  895. * in an unspecified order. The query may not have any sort order, and may
  896. * not use limit or skip.
  897. *
  898. * @param {Function} callback Callback that will be called with each result
  899. * of the query.
  900. * @param {object} options Valid options are:<ul>
  901. * <li>batchSize: How many objects to yield in each batch (default: 100)
  902. * <li>useMasterKey: In Cloud Code and Node only, causes the Master Key to
  903. * be used for this request.
  904. * <li>sessionToken: A valid session token, used for making a request on
  905. * behalf of a specific user.
  906. * <li>context: A dictionary that is accessible in Cloud Code `beforeFind` trigger.
  907. * </ul>
  908. * @returns {Promise} A promise that will be fulfilled once the
  909. * iteration has completed.
  910. */
  911. eachBatch(callback
  912. /*: (objs: Array<ParseObject>) => Promise<*>*/
  913. , options
  914. /*:: ?: BatchOptions*/
  915. )
  916. /*: Promise<void>*/
  917. {
  918. options = options || {};
  919. if (this._order || this._skip || this._limit >= 0) {
  920. return Promise.reject('Cannot iterate on a query with sort, skip, or limit.');
  921. }
  922. const query = new ParseQuery(this.className);
  923. query._limit = options.batchSize || 100;
  924. query._include = this._include.map(i => {
  925. return i;
  926. });
  927. if (this._select) {
  928. query._select = this._select.map(s => {
  929. return s;
  930. });
  931. }
  932. query._hint = this._hint;
  933. query._where = {};
  934. for (const attr in this._where) {
  935. const val = this._where[attr];
  936. if (Array.isArray(val)) {
  937. query._where[attr] = val.map(v => {
  938. return v;
  939. });
  940. } else if (val && typeof val === 'object') {
  941. const conditionMap = {};
  942. query._where[attr] = conditionMap;
  943. for (const cond in val) {
  944. conditionMap[cond] = val[cond];
  945. }
  946. } else {
  947. query._where[attr] = val;
  948. }
  949. }
  950. query.ascending('objectId');
  951. const findOptions = {};
  952. if (options.hasOwnProperty('useMasterKey')) {
  953. findOptions.useMasterKey = options.useMasterKey;
  954. }
  955. if (options.hasOwnProperty('sessionToken')) {
  956. findOptions.sessionToken = options.sessionToken;
  957. }
  958. if (options.hasOwnProperty('context') && typeof options.context === 'object') {
  959. findOptions.context = options.context;
  960. }
  961. let finished = false;
  962. let previousResults = [];
  963. return (0, _promiseUtils.continueWhile)(() => {
  964. return !finished;
  965. }, async () => {
  966. const [results] = await Promise.all([query.find(findOptions), Promise.resolve(previousResults.length > 0 && callback(previousResults))]);
  967. if (results.length >= query._limit) {
  968. query.greaterThan('objectId', results[results.length - 1].id);
  969. previousResults = results;
  970. } else if (results.length > 0) {
  971. await Promise.resolve(callback(results));
  972. finished = true;
  973. } else {
  974. finished = true;
  975. }
  976. });
  977. }
  978. /**
  979. * Iterates over each result of a query, calling a callback for each one. If
  980. * the callback returns a promise, the iteration will not continue until
  981. * that promise has been fulfilled. If the callback returns a rejected
  982. * promise, then iteration will stop with that error. The items are
  983. * processed in an unspecified order. The query may not have any sort order,
  984. * and may not use limit or skip.
  985. *
  986. * @param {Function} callback Callback that will be called with each result
  987. * of the query.
  988. * @param {object} options Valid options are:<ul>
  989. * <li>useMasterKey: In Cloud Code and Node only, causes the Master Key to
  990. * be used for this request.
  991. * <li>sessionToken: A valid session token, used for making a request on
  992. * behalf of a specific user.
  993. * </ul>
  994. * @returns {Promise} A promise that will be fulfilled once the
  995. * iteration has completed.
  996. */
  997. each(callback
  998. /*: (obj: ParseObject) => any*/
  999. , options
  1000. /*:: ?: BatchOptions*/
  1001. )
  1002. /*: Promise<void>*/
  1003. {
  1004. return this.eachBatch(results => {
  1005. let callbacksDone = Promise.resolve();
  1006. results.forEach(result => {
  1007. callbacksDone = callbacksDone.then(() => {
  1008. return callback(result);
  1009. });
  1010. });
  1011. return callbacksDone;
  1012. }, options);
  1013. }
  1014. /**
  1015. * Adds a hint to force index selection. (https://docs.mongodb.com/manual/reference/operator/meta/hint/)
  1016. *
  1017. * @param {(string|object)} value String or Object of index that should be used when executing query
  1018. * @returns {Parse.Query} Returns the query, so you can chain this call.
  1019. */
  1020. hint(value
  1021. /*: mixed*/
  1022. )
  1023. /*: ParseQuery*/
  1024. {
  1025. if (typeof value === 'undefined') {
  1026. delete this._hint;
  1027. }
  1028. this._hint = value;
  1029. return this;
  1030. }
  1031. /**
  1032. * Investigates the query execution plan. Useful for optimizing queries. (https://docs.mongodb.com/manual/reference/operator/meta/explain/)
  1033. *
  1034. * @param {boolean} explain Used to toggle the information on the query plan.
  1035. * @returns {Parse.Query} Returns the query, so you can chain this call.
  1036. */
  1037. explain(explain
  1038. /*: boolean*/
  1039. = true)
  1040. /*: ParseQuery*/
  1041. {
  1042. if (typeof explain !== 'boolean') {
  1043. throw new Error('You can only set explain to a boolean value');
  1044. }
  1045. this._explain = explain;
  1046. return this;
  1047. }
  1048. /**
  1049. * Iterates over each result of a query, calling a callback for each one. If
  1050. * the callback returns a promise, the iteration will not continue until
  1051. * that promise has been fulfilled. If the callback returns a rejected
  1052. * promise, then iteration will stop with that error. The items are
  1053. * processed in an unspecified order. The query may not have any sort order,
  1054. * and may not use limit or skip.
  1055. *
  1056. * @param {Function} callback Callback <ul>
  1057. * <li>currentObject: The current Parse.Object being processed in the array.</li>
  1058. * <li>index: The index of the current Parse.Object being processed in the array.</li>
  1059. * <li>query: The query map was called upon.</li>
  1060. * </ul>
  1061. *
  1062. * @param {object} options Valid options are:<ul>
  1063. * <li>useMasterKey: In Cloud Code and Node only, causes the Master Key to
  1064. * be used for this request.
  1065. * <li>sessionToken: A valid session token, used for making a request on
  1066. * behalf of a specific user.
  1067. * </ul>
  1068. * @returns {Promise} A promise that will be fulfilled once the
  1069. * iteration has completed.
  1070. */
  1071. async map(callback
  1072. /*: (currentObject: ParseObject, index: number, query: ParseQuery) => any*/
  1073. , options
  1074. /*:: ?: BatchOptions*/
  1075. )
  1076. /*: Promise<Array<any>>*/
  1077. {
  1078. const array = [];
  1079. let index = 0;
  1080. await this.each(object => {
  1081. return Promise.resolve(callback(object, index, this)).then(result => {
  1082. array.push(result);
  1083. index += 1;
  1084. });
  1085. }, options);
  1086. return array;
  1087. }
  1088. /**
  1089. * Iterates over each result of a query, calling a callback for each one. If
  1090. * the callback returns a promise, the iteration will not continue until
  1091. * that promise has been fulfilled. If the callback returns a rejected
  1092. * promise, then iteration will stop with that error. The items are
  1093. * processed in an unspecified order. The query may not have any sort order,
  1094. * and may not use limit or skip.
  1095. *
  1096. * @param {Function} callback Callback <ul>
  1097. * <li>accumulator: The accumulator accumulates the callback's return values. It is the accumulated value previously returned in the last invocation of the callback.</li>
  1098. * <li>currentObject: The current Parse.Object being processed in the array.</li>
  1099. * <li>index: The index of the current Parse.Object being processed in the array.</li>
  1100. * </ul>
  1101. * @param {*} initialValue A value to use as the first argument to the first call of the callback. If no initialValue is supplied, the first object in the query will be used and skipped.
  1102. * @param {object} options Valid options are:<ul>
  1103. * <li>useMasterKey: In Cloud Code and Node only, causes the Master Key to
  1104. * be used for this request.
  1105. * <li>sessionToken: A valid session token, used for making a request on
  1106. * behalf of a specific user.
  1107. * </ul>
  1108. * @returns {Promise} A promise that will be fulfilled once the
  1109. * iteration has completed.
  1110. */
  1111. async reduce(callback
  1112. /*: (accumulator: any, currentObject: ParseObject, index: number) => any*/
  1113. , initialValue
  1114. /*: any*/
  1115. , options
  1116. /*:: ?: BatchOptions*/
  1117. )
  1118. /*: Promise<Array<any>>*/
  1119. {
  1120. let accumulator = initialValue;
  1121. let index = 0;
  1122. await this.each(object => {
  1123. // If no initial value was given, we take the first object from the query
  1124. // as the initial value and don't call the callback with it.
  1125. if (index === 0 && initialValue === undefined) {
  1126. accumulator = object;
  1127. index += 1;
  1128. return;
  1129. }
  1130. return Promise.resolve(callback(accumulator, object, index)).then(result => {
  1131. accumulator = result;
  1132. index += 1;
  1133. });
  1134. }, options);
  1135. if (index === 0 && initialValue === undefined) {
  1136. // Match Array.reduce behavior: "Calling reduce() on an empty array
  1137. // without an initialValue will throw a TypeError".
  1138. throw new TypeError('Reducing empty query result set with no initial value');
  1139. }
  1140. return accumulator;
  1141. }
  1142. /**
  1143. * Iterates over each result of a query, calling a callback for each one. If
  1144. * the callback returns a promise, the iteration will not continue until
  1145. * that promise has been fulfilled. If the callback returns a rejected
  1146. * promise, then iteration will stop with that error. The items are
  1147. * processed in an unspecified order. The query may not have any sort order,
  1148. * and may not use limit or skip.
  1149. *
  1150. * @param {Function} callback Callback <ul>
  1151. * <li>currentObject: The current Parse.Object being processed in the array.</li>
  1152. * <li>index: The index of the current Parse.Object being processed in the array.</li>
  1153. * <li>query: The query filter was called upon.</li>
  1154. * </ul>
  1155. *
  1156. * @param {object} options Valid options are:<ul>
  1157. * <li>useMasterKey: In Cloud Code and Node only, causes the Master Key to
  1158. * be used for this request.
  1159. * <li>sessionToken: A valid session token, used for making a request on
  1160. * behalf of a specific user.
  1161. * </ul>
  1162. * @returns {Promise} A promise that will be fulfilled once the
  1163. * iteration has completed.
  1164. */
  1165. async filter(callback
  1166. /*: (currentObject: ParseObject, index: number, query: ParseQuery) => boolean*/
  1167. , options
  1168. /*:: ?: BatchOptions*/
  1169. )
  1170. /*: Promise<Array<ParseObject>>*/
  1171. {
  1172. const array = [];
  1173. let index = 0;
  1174. await this.each(object => {
  1175. return Promise.resolve(callback(object, index, this)).then(flag => {
  1176. if (flag) {
  1177. array.push(object);
  1178. }
  1179. index += 1;
  1180. });
  1181. }, options);
  1182. return array;
  1183. }
  1184. /** Query Conditions * */
  1185. /**
  1186. * Adds a constraint to the query that requires a particular key's value to
  1187. * be equal to the provided value.
  1188. *
  1189. * @param {string} key The key to check.
  1190. * @param value The value that the Parse.Object must contain.
  1191. * @returns {Parse.Query} Returns the query, so you can chain this call.
  1192. */
  1193. equalTo(key
  1194. /*: string | { [key: string]: any }*/
  1195. , value
  1196. /*: ?mixed*/
  1197. )
  1198. /*: ParseQuery*/
  1199. {
  1200. if (key && typeof key === 'object') {
  1201. Object.entries(key).forEach(([k, val]) => this.equalTo(k, val));
  1202. return this;
  1203. }
  1204. if (typeof value === 'undefined') {
  1205. return this.doesNotExist(key);
  1206. }
  1207. this._where[key] = (0, _encode.default)(value, false, true);
  1208. return this;
  1209. }
  1210. /**
  1211. * Adds a constraint to the query that requires a particular key's value to
  1212. * be not equal to the provided value.
  1213. *
  1214. * @param {string} key The key to check.
  1215. * @param value The value that must not be equalled.
  1216. * @returns {Parse.Query} Returns the query, so you can chain this call.
  1217. */
  1218. notEqualTo(key
  1219. /*: string | { [key: string]: any }*/
  1220. , value
  1221. /*: ?mixed*/
  1222. )
  1223. /*: ParseQuery*/
  1224. {
  1225. if (key && typeof key === 'object') {
  1226. Object.entries(key).forEach(([k, val]) => this.notEqualTo(k, val));
  1227. return this;
  1228. }
  1229. return this._addCondition(key, '$ne', value);
  1230. }
  1231. /**
  1232. * Adds a constraint to the query that requires a particular key's value to
  1233. * be less than the provided value.
  1234. *
  1235. * @param {string} key The key to check.
  1236. * @param value The value that provides an upper bound.
  1237. * @returns {Parse.Query} Returns the query, so you can chain this call.
  1238. */
  1239. lessThan(key
  1240. /*: string*/
  1241. , value
  1242. /*: mixed*/
  1243. )
  1244. /*: ParseQuery*/
  1245. {
  1246. return this._addCondition(key, '$lt', value);
  1247. }
  1248. /**
  1249. * Adds a constraint to the query that requires a particular key's value to
  1250. * be greater than the provided value.
  1251. *
  1252. * @param {string} key The key to check.
  1253. * @param value The value that provides an lower bound.
  1254. * @returns {Parse.Query} Returns the query, so you can chain this call.
  1255. */
  1256. greaterThan(key
  1257. /*: string*/
  1258. , value
  1259. /*: mixed*/
  1260. )
  1261. /*: ParseQuery*/
  1262. {
  1263. return this._addCondition(key, '$gt', value);
  1264. }
  1265. /**
  1266. * Adds a constraint to the query that requires a particular key's value to
  1267. * be less than or equal to the provided value.
  1268. *
  1269. * @param {string} key The key to check.
  1270. * @param value The value that provides an upper bound.
  1271. * @returns {Parse.Query} Returns the query, so you can chain this call.
  1272. */
  1273. lessThanOrEqualTo(key
  1274. /*: string*/
  1275. , value
  1276. /*: mixed*/
  1277. )
  1278. /*: ParseQuery*/
  1279. {
  1280. return this._addCondition(key, '$lte', value);
  1281. }
  1282. /**
  1283. * Adds a constraint to the query that requires a particular key's value to
  1284. * be greater than or equal to the provided value.
  1285. *
  1286. * @param {string} key The key to check.
  1287. * @param {*} value The value that provides an lower bound.
  1288. * @returns {Parse.Query} Returns the query, so you can chain this call.
  1289. */
  1290. greaterThanOrEqualTo(key
  1291. /*: string*/
  1292. , value
  1293. /*: mixed*/
  1294. )
  1295. /*: ParseQuery*/
  1296. {
  1297. return this._addCondition(key, '$gte', value);
  1298. }
  1299. /**
  1300. * Adds a constraint to the query that requires a particular key's value to
  1301. * be contained in the provided list of values.
  1302. *
  1303. * @param {string} key The key to check.
  1304. * @param {*} value The values that will match.
  1305. * @returns {Parse.Query} Returns the query, so you can chain this call.
  1306. */
  1307. containedIn(key
  1308. /*: string*/
  1309. , value
  1310. /*: mixed*/
  1311. )
  1312. /*: ParseQuery*/
  1313. {
  1314. return this._addCondition(key, '$in', value);
  1315. }
  1316. /**
  1317. * Adds a constraint to the query that requires a particular key's value to
  1318. * not be contained in the provided list of values.
  1319. *
  1320. * @param {string} key The key to check.
  1321. * @param {*} value The values that will not match.
  1322. * @returns {Parse.Query} Returns the query, so you can chain this call.
  1323. */
  1324. notContainedIn(key
  1325. /*: string*/
  1326. , value
  1327. /*: mixed*/
  1328. )
  1329. /*: ParseQuery*/
  1330. {
  1331. return this._addCondition(key, '$nin', value);
  1332. }
  1333. /**
  1334. * Adds a constraint to the query that requires a particular key's value to
  1335. * be contained by the provided list of values. Get objects where all array elements match.
  1336. *
  1337. * @param {string} key The key to check.
  1338. * @param {Array} values The values that will match.
  1339. * @returns {Parse.Query} Returns the query, so you can chain this call.
  1340. */
  1341. containedBy(key
  1342. /*: string*/
  1343. , values
  1344. /*: Array<mixed>*/
  1345. )
  1346. /*: ParseQuery*/
  1347. {
  1348. return this._addCondition(key, '$containedBy', values);
  1349. }
  1350. /**
  1351. * Adds a constraint to the query that requires a particular key's value to
  1352. * contain each one of the provided list of values.
  1353. *
  1354. * @param {string} key The key to check. This key's value must be an array.
  1355. * @param {Array} values The values that will match.
  1356. * @returns {Parse.Query} Returns the query, so you can chain this call.
  1357. */
  1358. containsAll(key
  1359. /*: string*/
  1360. , values
  1361. /*: Array<mixed>*/
  1362. )
  1363. /*: ParseQuery*/
  1364. {
  1365. return this._addCondition(key, '$all', values);
  1366. }
  1367. /**
  1368. * Adds a constraint to the query that requires a particular key's value to
  1369. * contain each one of the provided list of values starting with given strings.
  1370. *
  1371. * @param {string} key The key to check. This key's value must be an array.
  1372. * @param {Array<string>} values The string values that will match as starting string.
  1373. * @returns {Parse.Query} Returns the query, so you can chain this call.
  1374. */
  1375. containsAllStartingWith(key
  1376. /*: string*/
  1377. , values
  1378. /*: Array<string>*/
  1379. )
  1380. /*: ParseQuery*/
  1381. {
  1382. const _this = this;
  1383. if (!Array.isArray(values)) {
  1384. values = [values];
  1385. }
  1386. const regexObject = values.map(value => {
  1387. return {
  1388. $regex: _this._regexStartWith(value)
  1389. };
  1390. });
  1391. return this.containsAll(key, regexObject);
  1392. }
  1393. /**
  1394. * Adds a constraint for finding objects that contain the given key.
  1395. *
  1396. * @param {string} key The key that should exist.
  1397. * @returns {Parse.Query} Returns the query, so you can chain this call.
  1398. */
  1399. exists(key
  1400. /*: string*/
  1401. )
  1402. /*: ParseQuery*/
  1403. {
  1404. return this._addCondition(key, '$exists', true);
  1405. }
  1406. /**
  1407. * Adds a constraint for finding objects that do not contain a given key.
  1408. *
  1409. * @param {string} key The key that should not exist
  1410. * @returns {Parse.Query} Returns the query, so you can chain this call.
  1411. */
  1412. doesNotExist(key
  1413. /*: string*/
  1414. )
  1415. /*: ParseQuery*/
  1416. {
  1417. return this._addCondition(key, '$exists', false);
  1418. }
  1419. /**
  1420. * Adds a regular expression constraint for finding string values that match
  1421. * the provided regular expression.
  1422. * This may be slow for large datasets.
  1423. *
  1424. * @param {string} key The key that the string to match is stored in.
  1425. * @param {RegExp} regex The regular expression pattern to match.
  1426. * @param {string} modifiers The regular expression mode.
  1427. * @returns {Parse.Query} Returns the query, so you can chain this call.
  1428. */
  1429. matches(key
  1430. /*: string*/
  1431. , regex
  1432. /*: RegExp*/
  1433. , modifiers
  1434. /*: string*/
  1435. )
  1436. /*: ParseQuery*/
  1437. {
  1438. this._addCondition(key, '$regex', regex);
  1439. if (!modifiers) {
  1440. modifiers = '';
  1441. }
  1442. if (regex.ignoreCase) {
  1443. modifiers += 'i';
  1444. }
  1445. if (regex.multiline) {
  1446. modifiers += 'm';
  1447. }
  1448. if (modifiers.length) {
  1449. this._addCondition(key, '$options', modifiers);
  1450. }
  1451. return this;
  1452. }
  1453. /**
  1454. * Adds a constraint that requires that a key's value matches a Parse.Query
  1455. * constraint.
  1456. *
  1457. * @param {string} key The key that the contains the object to match the
  1458. * query.
  1459. * @param {Parse.Query} query The query that should match.
  1460. * @returns {Parse.Query} Returns the query, so you can chain this call.
  1461. */
  1462. matchesQuery(key
  1463. /*: string*/
  1464. , query
  1465. /*: ParseQuery*/
  1466. )
  1467. /*: ParseQuery*/
  1468. {
  1469. const queryJSON = query.toJSON();
  1470. queryJSON.className = query.className;
  1471. return this._addCondition(key, '$inQuery', queryJSON);
  1472. }
  1473. /**
  1474. * Adds a constraint that requires that a key's value not matches a
  1475. * Parse.Query constraint.
  1476. *
  1477. * @param {string} key The key that the contains the object to match the
  1478. * query.
  1479. * @param {Parse.Query} query The query that should not match.
  1480. * @returns {Parse.Query} Returns the query, so you can chain this call.
  1481. */
  1482. doesNotMatchQuery(key
  1483. /*: string*/
  1484. , query
  1485. /*: ParseQuery*/
  1486. )
  1487. /*: ParseQuery*/
  1488. {
  1489. const queryJSON = query.toJSON();
  1490. queryJSON.className = query.className;
  1491. return this._addCondition(key, '$notInQuery', queryJSON);
  1492. }
  1493. /**
  1494. * Adds a constraint that requires that a key's value matches a value in
  1495. * an object returned by a different Parse.Query.
  1496. *
  1497. * @param {string} key The key that contains the value that is being
  1498. * matched.
  1499. * @param {string} queryKey The key in the objects returned by the query to
  1500. * match against.
  1501. * @param {Parse.Query} query The query to run.
  1502. * @returns {Parse.Query} Returns the query, so you can chain this call.
  1503. */
  1504. matchesKeyInQuery(key
  1505. /*: string*/
  1506. , queryKey
  1507. /*: string*/
  1508. , query
  1509. /*: ParseQuery*/
  1510. )
  1511. /*: ParseQuery*/
  1512. {
  1513. const queryJSON = query.toJSON();
  1514. queryJSON.className = query.className;
  1515. return this._addCondition(key, '$select', {
  1516. key: queryKey,
  1517. query: queryJSON
  1518. });
  1519. }
  1520. /**
  1521. * Adds a constraint that requires that a key's value not match a value in
  1522. * an object returned by a different Parse.Query.
  1523. *
  1524. * @param {string} key The key that contains the value that is being
  1525. * excluded.
  1526. * @param {string} queryKey The key in the objects returned by the query to
  1527. * match against.
  1528. * @param {Parse.Query} query The query to run.
  1529. * @returns {Parse.Query} Returns the query, so you can chain this call.
  1530. */
  1531. doesNotMatchKeyInQuery(key
  1532. /*: string*/
  1533. , queryKey
  1534. /*: string*/
  1535. , query
  1536. /*: ParseQuery*/
  1537. )
  1538. /*: ParseQuery*/
  1539. {
  1540. const queryJSON = query.toJSON();
  1541. queryJSON.className = query.className;
  1542. return this._addCondition(key, '$dontSelect', {
  1543. key: queryKey,
  1544. query: queryJSON
  1545. });
  1546. }
  1547. /**
  1548. * Adds a constraint for finding string values that contain a provided
  1549. * string. This may be slow for large datasets.
  1550. *
  1551. * @param {string} key The key that the string to match is stored in.
  1552. * @param {string} substring The substring that the value must contain.
  1553. * @returns {Parse.Query} Returns the query, so you can chain this call.
  1554. */
  1555. contains(key
  1556. /*: string*/
  1557. , substring
  1558. /*: string*/
  1559. )
  1560. /*: ParseQuery*/
  1561. {
  1562. if (typeof substring !== 'string') {
  1563. throw new Error('The value being searched for must be a string.');
  1564. }
  1565. return this._addCondition(key, '$regex', quote(substring));
  1566. }
  1567. /**
  1568. * Adds a constraint for finding string values that contain a provided
  1569. * string. This may be slow for large datasets. Requires Parse-Server > 2.5.0
  1570. *
  1571. * In order to sort you must use select and ascending ($score is required)
  1572. * <pre>
  1573. * query.fullText('field', 'term');
  1574. * query.ascending('$score');
  1575. * query.select('$score');
  1576. * </pre>
  1577. *
  1578. * To retrieve the weight / rank
  1579. * <pre>
  1580. * object->get('score');
  1581. * </pre>
  1582. *
  1583. * You can define optionals by providing an object as a third parameter
  1584. * <pre>
  1585. * query.fullText('field', 'term', { language: 'es', diacriticSensitive: true });
  1586. * </pre>
  1587. *
  1588. * @param {string} key The key that the string to match is stored in.
  1589. * @param {string} value The string to search
  1590. * @param {object} options (Optional)
  1591. * @param {string} options.language The language that determines the list of stop words for the search and the rules for the stemmer and tokenizer.
  1592. * @param {boolean} options.caseSensitive A boolean flag to enable or disable case sensitive search.
  1593. * @param {boolean} options.diacriticSensitive A boolean flag to enable or disable diacritic sensitive search.
  1594. * @returns {Parse.Query} Returns the query, so you can chain this call.
  1595. */
  1596. fullText(key
  1597. /*: string*/
  1598. , value
  1599. /*: string*/
  1600. , options
  1601. /*: ?Object*/
  1602. )
  1603. /*: ParseQuery*/
  1604. {
  1605. options = options || {};
  1606. if (!key) {
  1607. throw new Error('A key is required.');
  1608. }
  1609. if (!value) {
  1610. throw new Error('A search term is required');
  1611. }
  1612. if (typeof value !== 'string') {
  1613. throw new Error('The value being searched for must be a string.');
  1614. }
  1615. const fullOptions = {};
  1616. fullOptions.$term = value;
  1617. for (const option in options) {
  1618. switch (option) {
  1619. case 'language':
  1620. fullOptions.$language = options[option];
  1621. break;
  1622. case 'caseSensitive':
  1623. fullOptions.$caseSensitive = options[option];
  1624. break;
  1625. case 'diacriticSensitive':
  1626. fullOptions.$diacriticSensitive = options[option];
  1627. break;
  1628. default:
  1629. throw new Error(`Unknown option: ${option}`);
  1630. }
  1631. }
  1632. return this._addCondition(key, '$text', {
  1633. $search: fullOptions
  1634. });
  1635. }
  1636. /**
  1637. * Method to sort the full text search by text score
  1638. *
  1639. * @returns {Parse.Query} Returns the query, so you can chain this call.
  1640. */
  1641. sortByTextScore() {
  1642. this.ascending('$score');
  1643. this.select(['$score']);
  1644. return this;
  1645. }
  1646. /**
  1647. * Adds a constraint for finding string values that start with a provided
  1648. * string. This query will use the backend index, so it will be fast even
  1649. * for large datasets.
  1650. *
  1651. * @param {string} key The key that the string to match is stored in.
  1652. * @param {string} prefix The substring that the value must start with.
  1653. * @returns {Parse.Query} Returns the query, so you can chain this call.
  1654. */
  1655. startsWith(key
  1656. /*: string*/
  1657. , prefix
  1658. /*: string*/
  1659. )
  1660. /*: ParseQuery*/
  1661. {
  1662. if (typeof prefix !== 'string') {
  1663. throw new Error('The value being searched for must be a string.');
  1664. }
  1665. return this._addCondition(key, '$regex', this._regexStartWith(prefix));
  1666. }
  1667. /**
  1668. * Adds a constraint for finding string values that end with a provided
  1669. * string. This will be slow for large datasets.
  1670. *
  1671. * @param {string} key The key that the string to match is stored in.
  1672. * @param {string} suffix The substring that the value must end with.
  1673. * @returns {Parse.Query} Returns the query, so you can chain this call.
  1674. */
  1675. endsWith(key
  1676. /*: string*/
  1677. , suffix
  1678. /*: string*/
  1679. )
  1680. /*: ParseQuery*/
  1681. {
  1682. if (typeof suffix !== 'string') {
  1683. throw new Error('The value being searched for must be a string.');
  1684. }
  1685. return this._addCondition(key, '$regex', `${quote(suffix)}$`);
  1686. }
  1687. /**
  1688. * Adds a proximity based constraint for finding objects with key point
  1689. * values near the point given.
  1690. *
  1691. * @param {string} key The key that the Parse.GeoPoint is stored in.
  1692. * @param {Parse.GeoPoint} point The reference Parse.GeoPoint that is used.
  1693. * @returns {Parse.Query} Returns the query, so you can chain this call.
  1694. */
  1695. near(key
  1696. /*: string*/
  1697. , point
  1698. /*: ParseGeoPoint*/
  1699. )
  1700. /*: ParseQuery*/
  1701. {
  1702. if (!(point instanceof _ParseGeoPoint.default)) {
  1703. // Try to cast it as a GeoPoint
  1704. point = new _ParseGeoPoint.default(point);
  1705. }
  1706. return this._addCondition(key, '$nearSphere', point);
  1707. }
  1708. /**
  1709. * Adds a proximity based constraint for finding objects with key point
  1710. * values near the point given and within the maximum distance given.
  1711. *
  1712. * @param {string} key The key that the Parse.GeoPoint is stored in.
  1713. * @param {Parse.GeoPoint} point The reference Parse.GeoPoint that is used.
  1714. * @param {number} maxDistance Maximum distance (in radians) of results to return.
  1715. * @param {boolean} sorted A Bool value that is true if results should be
  1716. * sorted by distance ascending, false is no sorting is required,
  1717. * defaults to true.
  1718. * @returns {Parse.Query} Returns the query, so you can chain this call.
  1719. */
  1720. withinRadians(key
  1721. /*: string*/
  1722. , point
  1723. /*: ParseGeoPoint*/
  1724. , maxDistance
  1725. /*: number*/
  1726. , sorted
  1727. /*: boolean*/
  1728. )
  1729. /*: ParseQuery*/
  1730. {
  1731. if (sorted || sorted === undefined) {
  1732. this.near(key, point);
  1733. return this._addCondition(key, '$maxDistance', maxDistance);
  1734. }
  1735. return this._addCondition(key, '$geoWithin', {
  1736. $centerSphere: [[point.longitude, point.latitude], maxDistance]
  1737. });
  1738. }
  1739. /**
  1740. * Adds a proximity based constraint for finding objects with key point
  1741. * values near the point given and within the maximum distance given.
  1742. * Radius of earth used is 3958.8 miles.
  1743. *
  1744. * @param {string} key The key that the Parse.GeoPoint is stored in.
  1745. * @param {Parse.GeoPoint} point The reference Parse.GeoPoint that is used.
  1746. * @param {number} maxDistance Maximum distance (in miles) of results to return.
  1747. * @param {boolean} sorted A Bool value that is true if results should be
  1748. * sorted by distance ascending, false is no sorting is required,
  1749. * defaults to true.
  1750. * @returns {Parse.Query} Returns the query, so you can chain this call.
  1751. */
  1752. withinMiles(key
  1753. /*: string*/
  1754. , point
  1755. /*: ParseGeoPoint*/
  1756. , maxDistance
  1757. /*: number*/
  1758. , sorted
  1759. /*: boolean*/
  1760. )
  1761. /*: ParseQuery*/
  1762. {
  1763. return this.withinRadians(key, point, maxDistance / 3958.8, sorted);
  1764. }
  1765. /**
  1766. * Adds a proximity based constraint for finding objects with key point
  1767. * values near the point given and within the maximum distance given.
  1768. * Radius of earth used is 6371.0 kilometers.
  1769. *
  1770. * @param {string} key The key that the Parse.GeoPoint is stored in.
  1771. * @param {Parse.GeoPoint} point The reference Parse.GeoPoint that is used.
  1772. * @param {number} maxDistance Maximum distance (in kilometers) of results to return.
  1773. * @param {boolean} sorted A Bool value that is true if results should be
  1774. * sorted by distance ascending, false is no sorting is required,
  1775. * defaults to true.
  1776. * @returns {Parse.Query} Returns the query, so you can chain this call.
  1777. */
  1778. withinKilometers(key
  1779. /*: string*/
  1780. , point
  1781. /*: ParseGeoPoint*/
  1782. , maxDistance
  1783. /*: number*/
  1784. , sorted
  1785. /*: boolean*/
  1786. )
  1787. /*: ParseQuery*/
  1788. {
  1789. return this.withinRadians(key, point, maxDistance / 6371.0, sorted);
  1790. }
  1791. /**
  1792. * Adds a constraint to the query that requires a particular key's
  1793. * coordinates be contained within a given rectangular geographic bounding
  1794. * box.
  1795. *
  1796. * @param {string} key The key to be constrained.
  1797. * @param {Parse.GeoPoint} southwest
  1798. * The lower-left inclusive corner of the box.
  1799. * @param {Parse.GeoPoint} northeast
  1800. * The upper-right inclusive corner of the box.
  1801. * @returns {Parse.Query} Returns the query, so you can chain this call.
  1802. */
  1803. withinGeoBox(key
  1804. /*: string*/
  1805. , southwest
  1806. /*: ParseGeoPoint*/
  1807. , northeast
  1808. /*: ParseGeoPoint*/
  1809. )
  1810. /*: ParseQuery*/
  1811. {
  1812. if (!(southwest instanceof _ParseGeoPoint.default)) {
  1813. southwest = new _ParseGeoPoint.default(southwest);
  1814. }
  1815. if (!(northeast instanceof _ParseGeoPoint.default)) {
  1816. northeast = new _ParseGeoPoint.default(northeast);
  1817. }
  1818. this._addCondition(key, '$within', {
  1819. $box: [southwest, northeast]
  1820. });
  1821. return this;
  1822. }
  1823. /**
  1824. * Adds a constraint to the query that requires a particular key's
  1825. * coordinates be contained within and on the bounds of a given polygon.
  1826. * Supports closed and open (last point is connected to first) paths
  1827. *
  1828. * Polygon must have at least 3 points
  1829. *
  1830. * @param {string} key The key to be constrained.
  1831. * @param {Array} points Array of Coordinates / GeoPoints
  1832. * @returns {Parse.Query} Returns the query, so you can chain this call.
  1833. */
  1834. withinPolygon(key
  1835. /*: string*/
  1836. , points
  1837. /*: Array<Array<number>>*/
  1838. )
  1839. /*: ParseQuery*/
  1840. {
  1841. return this._addCondition(key, '$geoWithin', {
  1842. $polygon: points
  1843. });
  1844. }
  1845. /**
  1846. * Add a constraint to the query that requires a particular key's
  1847. * coordinates that contains a ParseGeoPoint
  1848. *
  1849. * @param {string} key The key to be constrained.
  1850. * @param {Parse.GeoPoint} point
  1851. * @returns {Parse.Query} Returns the query, so you can chain this call.
  1852. */
  1853. polygonContains(key
  1854. /*: string*/
  1855. , point
  1856. /*: ParseGeoPoint*/
  1857. )
  1858. /*: ParseQuery*/
  1859. {
  1860. return this._addCondition(key, '$geoIntersects', {
  1861. $point: point
  1862. });
  1863. }
  1864. /** Query Orderings * */
  1865. /**
  1866. * Sorts the results in ascending order by the given key.
  1867. *
  1868. * @param {(string|string[])} keys The key to order by, which is a
  1869. * string of comma separated values, or an Array of keys, or multiple keys.
  1870. * @returns {Parse.Query} Returns the query, so you can chain this call.
  1871. */
  1872. ascending(...keys)
  1873. /*: ParseQuery*/
  1874. {
  1875. this._order = [];
  1876. return this.addAscending.apply(this, keys);
  1877. }
  1878. /**
  1879. * Sorts the results in ascending order by the given key,
  1880. * but can also add secondary sort descriptors without overwriting _order.
  1881. *
  1882. * @param {(string|string[])} keys The key to order by, which is a
  1883. * string of comma separated values, or an Array of keys, or multiple keys.
  1884. * @returns {Parse.Query} Returns the query, so you can chain this call.
  1885. */
  1886. addAscending(...keys)
  1887. /*: ParseQuery*/
  1888. {
  1889. if (!this._order) {
  1890. this._order = [];
  1891. }
  1892. keys.forEach(key => {
  1893. if (Array.isArray(key)) {
  1894. key = key.join();
  1895. }
  1896. this._order = this._order.concat(key.replace(/\s/g, '').split(','));
  1897. });
  1898. return this;
  1899. }
  1900. /**
  1901. * Sorts the results in descending order by the given key.
  1902. *
  1903. * @param {(string|string[])} keys The key to order by, which is a
  1904. * string of comma separated values, or an Array of keys, or multiple keys.
  1905. * @returns {Parse.Query} Returns the query, so you can chain this call.
  1906. */
  1907. descending(...keys)
  1908. /*: ParseQuery*/
  1909. {
  1910. this._order = [];
  1911. return this.addDescending.apply(this, keys);
  1912. }
  1913. /**
  1914. * Sorts the results in descending order by the given key,
  1915. * but can also add secondary sort descriptors without overwriting _order.
  1916. *
  1917. * @param {(string|string[])} keys The key to order by, which is a
  1918. * string of comma separated values, or an Array of keys, or multiple keys.
  1919. * @returns {Parse.Query} Returns the query, so you can chain this call.
  1920. */
  1921. addDescending(...keys)
  1922. /*: ParseQuery*/
  1923. {
  1924. if (!this._order) {
  1925. this._order = [];
  1926. }
  1927. keys.forEach(key => {
  1928. if (Array.isArray(key)) {
  1929. key = key.join();
  1930. }
  1931. this._order = this._order.concat(key.replace(/\s/g, '').split(',').map(k => {
  1932. return `-${k}`;
  1933. }));
  1934. });
  1935. return this;
  1936. }
  1937. /** Query Options * */
  1938. /**
  1939. * Sets the number of results to skip before returning any results.
  1940. * This is useful for pagination.
  1941. * Default is to skip zero results.
  1942. *
  1943. * @param {number} n the number of results to skip.
  1944. * @returns {Parse.Query} Returns the query, so you can chain this call.
  1945. */
  1946. skip(n
  1947. /*: number*/
  1948. )
  1949. /*: ParseQuery*/
  1950. {
  1951. if (typeof n !== 'number' || n < 0) {
  1952. throw new Error('You can only skip by a positive number');
  1953. }
  1954. this._skip = n;
  1955. return this;
  1956. }
  1957. /**
  1958. * Sets the limit of the number of results to return. The default limit is 100.
  1959. *
  1960. * @param {number} n the number of results to limit to.
  1961. * @returns {Parse.Query} Returns the query, so you can chain this call.
  1962. */
  1963. limit(n
  1964. /*: number*/
  1965. )
  1966. /*: ParseQuery*/
  1967. {
  1968. if (typeof n !== 'number') {
  1969. throw new Error('You can only set the limit to a numeric value');
  1970. }
  1971. this._limit = n;
  1972. return this;
  1973. }
  1974. /**
  1975. * Sets the flag to include with response the total number of objects satisfying this query,
  1976. * despite limits/skip. Might be useful for pagination.
  1977. * Note that result of this query will be wrapped as an object with
  1978. * `results`: holding {ParseObject} array and `count`: integer holding total number
  1979. *
  1980. * @param {boolean} includeCount false - disable, true - enable.
  1981. * @returns {Parse.Query} Returns the query, so you can chain this call.
  1982. */
  1983. withCount(includeCount
  1984. /*: boolean*/
  1985. = true)
  1986. /*: ParseQuery*/
  1987. {
  1988. if (typeof includeCount !== 'boolean') {
  1989. throw new Error('You can only set withCount to a boolean value');
  1990. }
  1991. this._count = includeCount;
  1992. return this;
  1993. }
  1994. /**
  1995. * Includes nested Parse.Objects for the provided key. You can use dot
  1996. * notation to specify which fields in the included object are also fetched.
  1997. *
  1998. * You can include all nested Parse.Objects by passing in '*'.
  1999. * Requires Parse Server 3.0.0+
  2000. * <pre>query.include('*');</pre>
  2001. *
  2002. * @param {...string|Array<string>} keys The name(s) of the key(s) to include.
  2003. * @returns {Parse.Query} Returns the query, so you can chain this call.
  2004. */
  2005. include(...keys)
  2006. /*: ParseQuery*/
  2007. {
  2008. keys.forEach(key => {
  2009. if (Array.isArray(key)) {
  2010. this._include = this._include.concat(key);
  2011. } else {
  2012. this._include.push(key);
  2013. }
  2014. });
  2015. return this;
  2016. }
  2017. /**
  2018. * Includes all nested Parse.Objects.
  2019. *
  2020. * Requires Parse Server 3.0.0+
  2021. *
  2022. * @returns {Parse.Query} Returns the query, so you can chain this call.
  2023. */
  2024. includeAll()
  2025. /*: ParseQuery*/
  2026. {
  2027. return this.include('*');
  2028. }
  2029. /**
  2030. * Restricts the fields of the returned Parse.Objects to include only the
  2031. * provided keys. If this is called multiple times, then all of the keys
  2032. * specified in each of the calls will be included.
  2033. *
  2034. * @param {...string|Array<string>} keys The name(s) of the key(s) to include.
  2035. * @returns {Parse.Query} Returns the query, so you can chain this call.
  2036. */
  2037. select(...keys)
  2038. /*: ParseQuery*/
  2039. {
  2040. if (!this._select) {
  2041. this._select = [];
  2042. }
  2043. keys.forEach(key => {
  2044. if (Array.isArray(key)) {
  2045. this._select = this._select.concat(key);
  2046. } else {
  2047. this._select.push(key);
  2048. }
  2049. });
  2050. return this;
  2051. }
  2052. /**
  2053. * Restricts the fields of the returned Parse.Objects to all keys except the
  2054. * provided keys. Exclude takes precedence over select and include.
  2055. *
  2056. * Requires Parse Server 3.6.0+
  2057. *
  2058. * @param {...string|Array<string>} keys The name(s) of the key(s) to exclude.
  2059. * @returns {Parse.Query} Returns the query, so you can chain this call.
  2060. */
  2061. exclude(...keys)
  2062. /*: ParseQuery*/
  2063. {
  2064. keys.forEach(key => {
  2065. if (Array.isArray(key)) {
  2066. this._exclude = this._exclude.concat(key);
  2067. } else {
  2068. this._exclude.push(key);
  2069. }
  2070. });
  2071. return this;
  2072. }
  2073. /**
  2074. * Changes the read preference that the backend will use when performing the query to the database.
  2075. *
  2076. * @param {string} readPreference The read preference for the main query.
  2077. * @param {string} includeReadPreference The read preference for the queries to include pointers.
  2078. * @param {string} subqueryReadPreference The read preference for the sub queries.
  2079. * @returns {Parse.Query} Returns the query, so you can chain this call.
  2080. */
  2081. readPreference(readPreference
  2082. /*: string*/
  2083. , includeReadPreference
  2084. /*:: ?: string*/
  2085. , subqueryReadPreference
  2086. /*:: ?: string*/
  2087. )
  2088. /*: ParseQuery*/
  2089. {
  2090. this._readPreference = readPreference;
  2091. this._includeReadPreference = includeReadPreference;
  2092. this._subqueryReadPreference = subqueryReadPreference;
  2093. return this;
  2094. }
  2095. onChange(onUpdate
  2096. /*: any*/
  2097. , onError
  2098. /*:: ?: any*/
  2099. /*:: ?: string*/
  2100. )
  2101. /*: Promise<LiveQuerySubscription>*/
  2102. {
  2103. let sub = null;
  2104. this.subscribe().then(subscription => {
  2105. sub = subscription;
  2106. subscription.on('create', object => {
  2107. onUpdate(object);
  2108. });
  2109. subscription.on('update', object => {
  2110. onUpdate(object);
  2111. });
  2112. subscription.on('error', err => {
  2113. if (onError) {
  2114. onError(err);
  2115. } else {
  2116. // eslint-disable-next-line no-console
  2117. console.warn('Subscription error', err);
  2118. }
  2119. });
  2120. }).catch(err => {
  2121. if (onError) {
  2122. onError(err);
  2123. } else {
  2124. // eslint-disable-next-line no-console
  2125. console.warn('Subscription connection error', err);
  2126. }
  2127. });
  2128. return function () {
  2129. if (sub) {
  2130. sub.unsubscribe();
  2131. }
  2132. };
  2133. }
  2134. /**
  2135. * Subscribe this query to get liveQuery updates
  2136. *
  2137. * @param {string} sessionToken (optional) Defaults to the currentUser
  2138. * @returns {Promise<LiveQuerySubscription>} Returns the liveQuerySubscription, it's an event emitter
  2139. * which can be used to get liveQuery updates.
  2140. */
  2141. async subscribe(sessionToken
  2142. /*:: ?: string*/
  2143. )
  2144. /*: Promise<LiveQuerySubscription>*/
  2145. {
  2146. const currentUser = await _CoreManager.default.getUserController().currentUserAsync();
  2147. if (!sessionToken) {
  2148. sessionToken = currentUser ? currentUser.getSessionToken() : undefined;
  2149. }
  2150. const liveQueryClient = await _CoreManager.default.getLiveQueryController().getDefaultLiveQueryClient();
  2151. if (liveQueryClient.shouldOpen()) {
  2152. liveQueryClient.open();
  2153. }
  2154. const subscription = liveQueryClient.subscribe(this, sessionToken);
  2155. return subscription.subscribePromise.then(() => {
  2156. return subscription;
  2157. });
  2158. }
  2159. /**
  2160. * Constructs a Parse.Query that is the OR of the passed in queries. For
  2161. * example:
  2162. * <pre>var compoundQuery = Parse.Query.or(query1, query2, query3);</pre>
  2163. *
  2164. * will create a compoundQuery that is an or of the query1, query2, and
  2165. * query3.
  2166. *
  2167. * @param {...Parse.Query} queries The list of queries to OR.
  2168. * @static
  2169. * @returns {Parse.Query} The query that is the OR of the passed in queries.
  2170. */
  2171. static or(...queries)
  2172. /*: ParseQuery*/
  2173. {
  2174. const className = _getClassNameFromQueries(queries);
  2175. const query = new ParseQuery(className);
  2176. query._orQuery(queries);
  2177. return query;
  2178. }
  2179. /**
  2180. * Constructs a Parse.Query that is the AND of the passed in queries. For
  2181. * example:
  2182. * <pre>var compoundQuery = Parse.Query.and(query1, query2, query3);</pre>
  2183. *
  2184. * will create a compoundQuery that is an and of the query1, query2, and
  2185. * query3.
  2186. *
  2187. * @param {...Parse.Query} queries The list of queries to AND.
  2188. * @static
  2189. * @returns {Parse.Query} The query that is the AND of the passed in queries.
  2190. */
  2191. static and(...queries)
  2192. /*: ParseQuery*/
  2193. {
  2194. const className = _getClassNameFromQueries(queries);
  2195. const query = new ParseQuery(className);
  2196. query._andQuery(queries);
  2197. return query;
  2198. }
  2199. /**
  2200. * Constructs a Parse.Query that is the NOR of the passed in queries. For
  2201. * example:
  2202. * <pre>const compoundQuery = Parse.Query.nor(query1, query2, query3);</pre>
  2203. *
  2204. * will create a compoundQuery that is a nor of the query1, query2, and
  2205. * query3.
  2206. *
  2207. * @param {...Parse.Query} queries The list of queries to NOR.
  2208. * @static
  2209. * @returns {Parse.Query} The query that is the NOR of the passed in queries.
  2210. */
  2211. static nor(...queries)
  2212. /*: ParseQuery*/
  2213. {
  2214. const className = _getClassNameFromQueries(queries);
  2215. const query = new ParseQuery(className);
  2216. query._norQuery(queries);
  2217. return query;
  2218. }
  2219. /**
  2220. * Change the source of this query to the server.
  2221. *
  2222. * @returns {Parse.Query} Returns the query, so you can chain this call.
  2223. */
  2224. fromNetwork()
  2225. /*: ParseQuery*/
  2226. {
  2227. this._queriesLocalDatastore = false;
  2228. this._localDatastorePinName = null;
  2229. return this;
  2230. }
  2231. /**
  2232. * Changes the source of this query to all pinned objects.
  2233. *
  2234. * @returns {Parse.Query} Returns the query, so you can chain this call.
  2235. */
  2236. fromLocalDatastore()
  2237. /*: ParseQuery*/
  2238. {
  2239. return this.fromPinWithName(null);
  2240. }
  2241. /**
  2242. * Changes the source of this query to the default group of pinned objects.
  2243. *
  2244. * @returns {Parse.Query} Returns the query, so you can chain this call.
  2245. */
  2246. fromPin()
  2247. /*: ParseQuery*/
  2248. {
  2249. return this.fromPinWithName(_LocalDatastoreUtils.DEFAULT_PIN);
  2250. }
  2251. /**
  2252. * Changes the source of this query to a specific group of pinned objects.
  2253. *
  2254. * @param {string} name The name of query source.
  2255. * @returns {Parse.Query} Returns the query, so you can chain this call.
  2256. */
  2257. fromPinWithName(name
  2258. /*:: ?: string*/
  2259. )
  2260. /*: ParseQuery*/
  2261. {
  2262. const localDatastore = _CoreManager.default.getLocalDatastore();
  2263. if (localDatastore.checkIfEnabled()) {
  2264. this._queriesLocalDatastore = true;
  2265. this._localDatastorePinName = name;
  2266. }
  2267. return this;
  2268. }
  2269. /**
  2270. * Cancels the current network request (if any is running).
  2271. *
  2272. * @returns {Parse.Query} Returns the query, so you can chain this call.
  2273. */
  2274. cancel()
  2275. /*: ParseQuery*/
  2276. {
  2277. if (this._xhrRequest.task && typeof this._xhrRequest.task.abort === 'function') {
  2278. this._xhrRequest.task._aborted = true;
  2279. this._xhrRequest.task.abort();
  2280. this._xhrRequest.task = null;
  2281. this._xhrRequest.onchange = () => {};
  2282. return this;
  2283. }
  2284. return this._xhrRequest.onchange = () => this.cancel();
  2285. }
  2286. _setRequestTask(options) {
  2287. options.requestTask = task => {
  2288. this._xhrRequest.task = task;
  2289. this._xhrRequest.onchange();
  2290. };
  2291. }
  2292. }
  2293. const DefaultController = {
  2294. find(className
  2295. /*: string*/
  2296. , params
  2297. /*: QueryJSON*/
  2298. , options
  2299. /*: RequestOptions*/
  2300. )
  2301. /*: Promise<Array<ParseObject>>*/
  2302. {
  2303. const RESTController = _CoreManager.default.getRESTController();
  2304. return RESTController.request('GET', `classes/${className}`, params, options);
  2305. },
  2306. aggregate(className
  2307. /*: string*/
  2308. , params
  2309. /*: any*/
  2310. , options
  2311. /*: RequestOptions*/
  2312. )
  2313. /*: Promise<Array<mixed>>*/
  2314. {
  2315. const RESTController = _CoreManager.default.getRESTController();
  2316. return RESTController.request('GET', `aggregate/${className}`, params, options);
  2317. }
  2318. };
  2319. _CoreManager.default.setQueryController(DefaultController);
  2320. var _default = ParseQuery;
  2321. exports.default = _default;