Skip to content

Commit 89d1109

Browse files
Static helper
1 parent bbb186b commit 89d1109

File tree

2 files changed

+90
-42
lines changed

2 files changed

+90
-42
lines changed

firebase-firestore/src/main/java/com/google/firebase/firestore/local/SQLiteIndexManager.java

Lines changed: 6 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import static com.google.firebase.firestore.model.Values.isArray;
1818
import static com.google.firebase.firestore.util.Assert.fail;
1919
import static com.google.firebase.firestore.util.Assert.hardAssert;
20-
import static com.google.firebase.firestore.util.Util.advanceIterator;
20+
import static com.google.firebase.firestore.util.Util.diffCollections;
2121
import static com.google.firebase.firestore.util.Util.repeatSequence;
2222
import static java.lang.Math.max;
2323

@@ -53,8 +53,6 @@
5353
import java.util.List;
5454
import java.util.Map;
5555
import java.util.Set;
56-
import java.util.SortedSet;
57-
import java.util.TreeSet;
5856

5957
/** A persisted implementation of IndexManager. */
6058
final class SQLiteIndexManager implements IndexManager {
@@ -258,42 +256,11 @@ private void updateEntries(
258256
Logger.debug(TAG, "Updating index entries for document '%s'", document.getKey());
259257
}
260258

261-
SortedSet<IndexEntry> existingIndexes = new TreeSet<>(existingEntries);
262-
Iterator<IndexEntry> existingIt = existingIndexes.iterator();
263-
@Nullable IndexEntry existingValue = advanceIterator(existingIt);
264-
265-
List<IndexEntry> updatedIndexes = new ArrayList<>(updatedEntries);
266-
Iterator<IndexEntry> updatedIt = updatedIndexes.iterator();
267-
@Nullable IndexEntry updatedValue = advanceIterator(updatedIt);
268-
269-
while (existingValue != null || updatedValue != null) {
270-
boolean deleted = false;
271-
boolean updated = false;
272-
273-
if (existingValue != null && updatedValue != null) {
274-
int cmp = existingValue.compareTo(updatedValue);
275-
if (cmp < 0) {
276-
deleted = true;
277-
} else if (cmp > 0) {
278-
updated = true;
279-
}
280-
} else if (existingValue != null) {
281-
deleted = true;
282-
} else {
283-
updated = true;
284-
}
285-
286-
if (deleted) {
287-
deleteIndexEntry(document, existingValue);
288-
existingValue = advanceIterator(existingIt);
289-
} else if (updated) {
290-
addIndexEntry(document, updatedValue);
291-
updatedValue = advanceIterator(updatedIt);
292-
} else {
293-
existingValue = advanceIterator(existingIt);
294-
updatedValue = advanceIterator(updatedIt);
295-
}
296-
}
259+
diffCollections(
260+
existingEntries,
261+
updatedEntries,
262+
entry -> addIndexEntry(document, entry),
263+
entry -> deleteIndexEntry(document, entry));
297264
}
298265

299266
@Override

firebase-firestore/src/main/java/com/google/firebase/firestore/util/Util.java

Lines changed: 84 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import io.grpc.StatusRuntimeException;
2929
import java.security.SecureRandom;
3030
import java.util.ArrayList;
31+
import java.util.Collection;
3132
import java.util.Collections;
3233
import java.util.Comparator;
3334
import java.util.Iterator;
@@ -164,8 +165,8 @@ public static Continuation<Void, Void> voidErrorTransformer() {
164165
* Converts varargs from an update call to a list of objects, ensuring that the arguments
165166
* alternate between String/FieldPath and Objects.
166167
*
167-
* @param fieldPathOffset The offset of the first field path in the original update API (used as
168-
* the index in error messages)
168+
* @param fieldPathOffset The offset of the first field path in the before update API (used as the
169+
* index in error messages)
169170
*/
170171
public static List<Object> collectUpdateArguments(
171172
int fieldPathOffset, Object field, Object val, Object... fieldsAndValues) {
@@ -277,9 +278,89 @@ public static StringBuilder repeatSequence(
277278
return sb;
278279
}
279280

281+
/**
282+
* Compares two collections for equality using the provided comparator. The method computes the
283+
* intersection and invokes `onAdd` for every element that is in `after` but not `before`.
284+
* `onDelete` is invoked for every element in `before` but missing from `after`.
285+
*
286+
* <p>The method creates a copy of both `before` and `after` and runs in O(n log n), where n is
287+
* the size of the two lists.
288+
*
289+
* @param before The elements that exist in the original set.
290+
* @param after The elements to diff against the original set.
291+
* @param comparator The comparator to use to define equality between elements.
292+
* @param onAdd A function to invoke for every element that is part of `after` but not `before`.
293+
* @param onDelete A function to invoke for every element that is part of `before` but not
294+
* `after`.
295+
*/
296+
public static <T> void diffCollections(
297+
Collection<T> before,
298+
Collection<T> after,
299+
Comparator<T> comparator,
300+
Consumer<T> onAdd,
301+
Consumer<T> onDelete) {
302+
List<T> beforeEntries = new ArrayList<>(before);
303+
Collections.sort(beforeEntries, comparator);
304+
Iterator<T> beforeIt = beforeEntries.iterator();
305+
@Nullable T beforeValue = advanceIterator(beforeIt);
306+
307+
List<T> afterEntries = new ArrayList<>(after);
308+
Collections.sort(afterEntries, comparator);
309+
Iterator<T> afterIt = afterEntries.iterator();
310+
@Nullable T afterValue = advanceIterator(afterIt);
311+
312+
while (beforeValue != null || afterValue != null) {
313+
boolean deleted = false;
314+
boolean added = false;
315+
316+
if (beforeValue != null && afterValue != null) {
317+
int cmp = comparator.compare(beforeValue, afterValue);
318+
if (cmp < 0) {
319+
deleted = true;
320+
} else if (cmp > 0) {
321+
added = true;
322+
}
323+
} else if (beforeValue != null) {
324+
deleted = true;
325+
} else {
326+
added = true;
327+
}
328+
329+
if (deleted) {
330+
onDelete.accept(beforeValue);
331+
beforeValue = advanceIterator(beforeIt);
332+
} else if (added) {
333+
onAdd.accept(afterValue);
334+
afterValue = advanceIterator(afterIt);
335+
} else {
336+
beforeValue = advanceIterator(beforeIt);
337+
afterValue = advanceIterator(afterIt);
338+
}
339+
}
340+
}
341+
342+
/**
343+
* Compares two collections for equality using their natural ordering. The method computes the
344+
* intersection and invokes `onAdd` for every element that is in `after` but not `before`.
345+
* `onDelete` is invoked for every element in `before` but missing from `after`.
346+
*
347+
* <p>The method creates a copy of both `before` and `after` and runs in O(n log n), where n is
348+
* the size of the two lists.
349+
*
350+
* @param before The elements that exist in the original set.
351+
* @param after The elements to diff against the original set.
352+
* @param onAdd A function to invoke for every element that is part of `after` but not `before`.
353+
* @param onDelete A function to invoke for every element that is part of `before` but not
354+
* `after`.
355+
*/
356+
public static <T extends Comparable<T>> void diffCollections(
357+
Collection<T> before, Collection<T> after, Consumer<T> onAdd, Consumer<T> onDelete) {
358+
diffCollections(before, after, Comparable::compareTo, onAdd, onDelete);
359+
}
360+
280361
/** Returns the next element from the iterator or `null` if none available. */
281362
@Nullable
282-
public static <T> T advanceIterator(Iterator<T> it) {
363+
private static <T> T advanceIterator(Iterator<T> it) {
283364
return it.hasNext() ? it.next() : null;
284365
}
285366
}

0 commit comments

Comments
 (0)