Skip to content

Commit 30e6b55

Browse files
committed
feat: Add ParseQuery.watch to trigger LiveQuery only on update of specific fields
1 parent a59ce65 commit 30e6b55

File tree

4 files changed

+102
-11
lines changed

4 files changed

+102
-11
lines changed

integration/test/ParseLiveQueryTest.js

+40-2
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ describe('Parse LiveQuery', () => {
7676
query.equalTo('objectId', object.id);
7777
const subscription = await client.subscribe(query);
7878
const promise = resolvingPromise();
79-
subscription.on('update', async (object) => {
79+
subscription.on('update', async object => {
8080
assert.equal(object.get('foo'), 'bar');
8181
await client.close();
8282
promise.resolve();
@@ -206,7 +206,7 @@ describe('Parse LiveQuery', () => {
206206
subscription.on('update', async object => {
207207
assert.equal(object.get('foo'), 'bar');
208208
await Parse.User.logOut();
209-
promise.resolve()
209+
promise.resolve();
210210
});
211211
await object.save({ foo: 'bar' });
212212
await promise;
@@ -276,6 +276,44 @@ describe('Parse LiveQuery', () => {
276276
await promise;
277277
});
278278

279+
it('can subscribe to query with watch', async () => {
280+
const query = new Parse.Query(TestObject);
281+
query.watch('yolo');
282+
const subscription = await query.subscribe();
283+
const spy = {
284+
create(obj) {
285+
if (!obj.get('yolo')) {
286+
fail('create should not have been called');
287+
}
288+
},
289+
update(object, original) {
290+
if (object.get('yolo') === original.get('yolo')) {
291+
fail('create should not have been called');
292+
}
293+
},
294+
};
295+
const createSpy = spyOn(spy, 'create').and.callThrough();
296+
const updateSpy = spyOn(spy, 'update').and.callThrough();
297+
subscription.on('create', spy.create);
298+
subscription.on('update', spy.update);
299+
const obj = new TestObject();
300+
obj.set('foo', 'bar');
301+
await obj.save();
302+
obj.set('foo', 'xyz');
303+
obj.set('yolo', 'xyz');
304+
await obj.save();
305+
const obj2 = new TestObject();
306+
obj2.set('foo', 'bar');
307+
obj2.set('yolo', 'bar');
308+
await obj2.save();
309+
obj2.set('foo', 'bart');
310+
await obj2.save();
311+
await sleep(1000);
312+
await subscription.unsubscribe();
313+
expect(createSpy).toHaveBeenCalledTimes(1);
314+
expect(updateSpy).toHaveBeenCalledTimes(1);
315+
});
316+
279317
it('live query can handle beforeConnect and beforeSubscribe errors', async () => {
280318
await reconfigureServer({
281319
cloud({ Cloud }) {

src/LiveQueryClient.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -191,14 +191,16 @@ class LiveQueryClient extends EventEmitter {
191191
const className = query.className;
192192
const queryJSON = query.toJSON();
193193
const where = queryJSON.where;
194-
const fields = queryJSON.keys ? queryJSON.keys.split(',') : undefined;
194+
const fields = queryJSON.keys?.split(',');
195+
const watch = queryJSON.watch?.split(',');
195196
const subscribeRequest = {
196197
op: OP_TYPES.SUBSCRIBE,
197198
requestId: this.requestId,
198199
query: {
199200
className,
200201
where,
201202
fields,
203+
watch,
202204
},
203205
};
204206

src/ParseQuery.js

+29
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export type WhereClause = {
2222

2323
export type QueryJSON = {
2424
where: WhereClause,
25+
watch?: string,
2526
include?: string,
2627
excludeKeys?: string,
2728
keys?: string,
@@ -224,6 +225,7 @@ class ParseQuery {
224225
*/
225226
className: string;
226227
_where: any;
228+
_watch: Array<string>;
227229
_include: Array<string>;
228230
_exclude: Array<string>;
229231
_select: Array<string>;
@@ -265,6 +267,7 @@ class ParseQuery {
265267
}
266268

267269
this._where = {};
270+
this._watch = [];
268271
this._include = [];
269272
this._exclude = [];
270273
this._count = false;
@@ -426,6 +429,9 @@ class ParseQuery {
426429
where: this._where,
427430
};
428431

432+
if (this._watch.length) {
433+
params.watch = this._watch.join(',');
434+
}
429435
if (this._include.length) {
430436
params.include = this._include.join(',');
431437
}
@@ -495,6 +501,10 @@ class ParseQuery {
495501
this._where = json.where;
496502
}
497503

504+
if (json.watch) {
505+
this._watch = json.watch.split(',');
506+
}
507+
498508
if (json.include) {
499509
this._include = json.include.split(',');
500510
}
@@ -1921,6 +1931,25 @@ class ParseQuery {
19211931
return this;
19221932
}
19231933

1934+
/**
1935+
* Restricts live query to trigger only for watched fields.
1936+
*
1937+
* Requires Parse Server 6.0.0+
1938+
*
1939+
* @param {...string|Array<string>} keys The name(s) of the key(s) to watch.
1940+
* @returns {Parse.Query} Returns the query, so you can chain this call.
1941+
*/
1942+
watch(...keys: Array<string | Array<string>>): ParseQuery {
1943+
keys.forEach(key => {
1944+
if (Array.isArray(key)) {
1945+
this._watch = this._watch.concat(key);
1946+
} else {
1947+
this._watch.push(key);
1948+
}
1949+
});
1950+
return this;
1951+
}
1952+
19241953
/**
19251954
* Changes the read preference that the backend will use when performing the query to the database.
19261955
*

src/__tests__/ParseQuery-test.js

+30-8
Original file line numberDiff line numberDiff line change
@@ -1078,6 +1078,32 @@ describe('ParseQuery', () => {
10781078
expect(q2._exclude).toEqual(['foo', 'bar']);
10791079
});
10801080

1081+
it('can watch keys', () => {
1082+
const q = new ParseQuery('Item');
1083+
q.watch('foo');
1084+
const json = q.toJSON();
1085+
expect(json).toEqual({
1086+
where: {},
1087+
watch: 'foo',
1088+
});
1089+
const q2 = new ParseQuery('Item');
1090+
q2.withJSON(json);
1091+
expect(q2._watch).toEqual(['foo']);
1092+
});
1093+
1094+
it('can watch multiple keys', () => {
1095+
const q = new ParseQuery('Item');
1096+
q.watch(['foo', 'bar']);
1097+
const json = q.toJSON();
1098+
expect(json).toEqual({
1099+
where: {},
1100+
watch: 'foo,bar',
1101+
});
1102+
const q2 = new ParseQuery('Item');
1103+
q2.withJSON(json);
1104+
expect(q2._watch).toEqual(['foo', 'bar']);
1105+
});
1106+
10811107
it('can use extraOptions', () => {
10821108
const q = new ParseQuery('Item');
10831109
q._extraOptions.randomOption = 'test';
@@ -2334,8 +2360,7 @@ describe('ParseQuery', () => {
23342360

23352361
const q = new ParseQuery('Thing');
23362362
let testObject;
2337-
q
2338-
.find()
2363+
q.find()
23392364
.then(results => {
23402365
testObject = results[0];
23412366

@@ -2460,8 +2485,7 @@ describe('ParseQuery', () => {
24602485

24612486
const q = new ParseQuery('Thing');
24622487
let testObject;
2463-
q
2464-
.first()
2488+
q.first()
24652489
.then(result => {
24662490
testObject = result;
24672491

@@ -2875,8 +2899,7 @@ describe('ParseQuery', () => {
28752899
const q = new ParseQuery('Thing');
28762900
q.select('other', 'tbd', 'subObject.key1');
28772901
let testObject;
2878-
q
2879-
.find()
2902+
q.find()
28802903
.then(results => {
28812904
testObject = results[0];
28822905

@@ -2926,8 +2949,7 @@ describe('ParseQuery', () => {
29262949

29272950
const q = new ParseQuery('Thing');
29282951
let testObject;
2929-
q
2930-
.find()
2952+
q.find()
29312953
.then(results => {
29322954
testObject = results[0];
29332955

0 commit comments

Comments
 (0)