Skip to content

Commit 70c7d91

Browse files
Optimzing QuerySnapshot equality.
1 parent de0b3a1 commit 70c7d91

File tree

2 files changed

+65
-40
lines changed

2 files changed

+65
-40
lines changed

src/reference.js

Lines changed: 63 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -501,7 +501,7 @@ class DocumentReference {
501501

502502
let watch = Watch.forDocument(this);
503503

504-
return watch.onSnapshot((readTime, docs) => {
504+
return watch.onSnapshot((readTime, size, docs) => {
505505
for (let document of docs()) {
506506
if (document.ref.path === this.path) {
507507
onNext(document);
@@ -852,17 +852,20 @@ class QuerySnapshot {
852852
* @param {Query} query - The originating query.
853853
* @param {string} readTime - The ISO 8601 time when this query snapshot was
854854
* current.
855-
*
855+
* @param {number} size - The number of documents in the result set.
856856
* @param {function} docs - A callback returning a sorted array of documents
857857
* matching this query
858858
* @param {function} changes - A callback returning a sorted array of
859859
* document change events for this snapshot.
860860
*/
861-
constructor(query, readTime, docs, changes) {
861+
constructor(query, readTime, size, docs, changes) {
862862
this._query = query;
863863
this._readTime = readTime;
864+
this._size = size;
864865
this._docs = docs;
866+
this._materializedDocs = null;
865867
this._changes = changes;
868+
this._materializedChanges = null;
866869
}
867870

868871
/**
@@ -910,6 +913,7 @@ class QuerySnapshot {
910913
return this._materializedDocs;
911914
}
912915
this._materializedDocs = this._docs();
916+
this._docs = null;
913917
return this._materializedDocs;
914918
}
915919

@@ -925,6 +929,7 @@ class QuerySnapshot {
925929
return this._materializedChanges;
926930
}
927931
this._materializedChanges = this._changes();
932+
this._changes = null;
928933
return this._materializedChanges;
929934
}
930935

@@ -945,7 +950,7 @@ class QuerySnapshot {
945950
* });
946951
*/
947952
get empty() {
948-
return this.docs.length === 0;
953+
return this._size === 0;
949954
}
950955

951956
/**
@@ -963,7 +968,7 @@ class QuerySnapshot {
963968
* });
964969
*/
965970
get size() {
966-
return this.docs.length;
971+
return this._size;
967972
}
968973

969974
/**
@@ -1025,37 +1030,27 @@ class QuerySnapshot {
10251030
return false;
10261031
}
10271032

1028-
if (!this._query.isEqual(other._query)) {
1029-
return false;
1030-
}
1031-
1032-
const thisChanges = this.docChanges;
1033-
const otherChanges = other.docChanges;
1034-
1035-
if (thisChanges.length !== otherChanges.length) {
1033+
if (this._size !== other._size) {
10361034
return false;
10371035
}
10381036

1039-
for (let i = 0; i < thisChanges.length; ++i) {
1040-
if (!thisChanges[i].isEqual(otherChanges[i])) {
1041-
return false;
1042-
}
1043-
}
1044-
1045-
const thisDocs = this.docs;
1046-
const otherDocs = other.docs;
1047-
1048-
if (thisDocs.length !== otherDocs.length) {
1037+
if (!this._query.isEqual(other._query)) {
10491038
return false;
10501039
}
10511040

1052-
for (let i = 0; i < thisDocs.length; ++i) {
1053-
if (!thisDocs[i].isEqual(otherDocs[i])) {
1054-
return false;
1055-
}
1041+
if (this._materializedChanges) {
1042+
// If we have already materialized the document changes, we compare
1043+
// them first as they are expected to be much smaller.
1044+
return (
1045+
isArrayEqual(this.docChanges, other.docChanges) &&
1046+
isArrayEqual(this.docs, other.docs)
1047+
);
10561048
}
10571049

1058-
return true;
1050+
return (
1051+
isArrayEqual(this.docs, other.docs) &&
1052+
isArrayEqual(this.docChanges, other.docChanges)
1053+
);
10591054
}
10601055
}
10611056

@@ -1756,7 +1751,6 @@ class Query {
17561751
_get(queryOptions) {
17571752
let self = this;
17581753
let docs = [];
1759-
let changes = [];
17601754

17611755
return new Promise((resolve, reject) => {
17621756
let readTime;
@@ -1770,19 +1764,27 @@ class Query {
17701764
readTime = result.readTime;
17711765
if (result.document) {
17721766
let document = result.document;
1773-
changes.push(
1774-
new DocumentChange(
1775-
DocumentChange.ADDED,
1776-
document,
1777-
-1,
1778-
docs.length
1779-
)
1780-
);
17811767
docs.push(document);
17821768
}
17831769
})
17841770
.on('end', () => {
1785-
resolve(new QuerySnapshot(this, readTime, () => docs, () => changes));
1771+
resolve(
1772+
new QuerySnapshot(
1773+
this,
1774+
readTime,
1775+
docs.length,
1776+
() => docs,
1777+
() => {
1778+
let changes = [];
1779+
for (let i = 0; i < docs.length; ++i) {
1780+
changes.push(
1781+
new DocumentChange(DocumentChange.ADDED, docs[i], -1, i)
1782+
);
1783+
}
1784+
return changes;
1785+
}
1786+
)
1787+
);
17861788
});
17871789
});
17881790
}
@@ -1972,8 +1974,8 @@ class Query {
19721974

19731975
let watch = Watch.forQuery(this);
19741976

1975-
return watch.onSnapshot((readTime, docs, changes) => {
1976-
onNext(new QuerySnapshot(this, readTime, docs, changes));
1977+
return watch.onSnapshot((readTime, size, docs, changes) => {
1978+
onNext(new QuerySnapshot(this, readTime, docs.length, docs, changes));
19771979
}, onError);
19781980
}
19791981

@@ -2236,6 +2238,27 @@ function validateDocumentReference(value) {
22362238
throw validate.customObjectError(value);
22372239
}
22382240

2241+
/**
2242+
* Verifies euqality for an array of objects using the `isEqual` interface.
2243+
*
2244+
* @param {Array.<Object>} left Array of objects supporting `isEqual`.
2245+
* @param {Array.<Object>} right Array of objects supporting `isEqual`.
2246+
* @return {boolean} True if arrays are equal.
2247+
*/
2248+
function isArrayEqual(left, right) {
2249+
if (left.length !== right.length) {
2250+
return false;
2251+
}
2252+
2253+
for (let i = 0; i < left.length; ++i) {
2254+
if (!left[i].isEqual(right[i])) {
2255+
return false;
2256+
}
2257+
}
2258+
2259+
return true;
2260+
}
2261+
22392262
module.exports = FirestoreType => {
22402263
Firestore = FirestoreType;
22412264
let document = require('./document')(DocumentReference);

src/watch.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,7 @@ function isResourceExhaustedError(error) {
268268
*
269269
* @param {string} readTime - The ISO 8601 time at which this snapshot was
270270
* obtained.
271+
* @param {number} size - The number of documents in the result set.
271272
* @param {docsCallback} docs - A callback that returns the ordered list of
272273
* documents stored in this snapshot.
273274
* @param {changeCallback} changes - A callback that returns the list of
@@ -667,6 +668,7 @@ class Watch {
667668
);
668669
onNext(
669670
readTime,
671+
diff.updatedTree.length,
670672
() => diff.updatedTree.keys,
671673
() => diff.appliedChanges
672674
);

0 commit comments

Comments
 (0)