Skip to content

Commit b86dc68

Browse files
Review
1 parent f3e2da5 commit b86dc68

File tree

3 files changed

+134
-53
lines changed

3 files changed

+134
-53
lines changed

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

Lines changed: 7 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
package com.google.firebase.firestore.local;
1616

1717
import static com.google.firebase.firestore.util.Assert.hardAssert;
18-
import static com.google.firebase.firestore.util.Util.advanceIterator;
18+
import static com.google.firebase.firestore.util.Util.diffCollections;
1919
import static java.util.Arrays.asList;
2020

2121
import android.util.SparseArray;
@@ -51,10 +51,8 @@
5151
import com.google.protobuf.ByteString;
5252
import java.util.ArrayList;
5353
import java.util.Collection;
54-
import java.util.Collections;
5554
import java.util.HashMap;
5655
import java.util.HashSet;
57-
import java.util.Iterator;
5856
import java.util.List;
5957
import java.util.Map;
6058
import java.util.Map.Entry;
@@ -791,44 +789,12 @@ public void configureFieldIndexes(List<FieldIndex> newFieldIndexes) {
791789
persistence.runTransaction(
792790
"Configure indexes",
793791
() -> {
794-
List<FieldIndex> existingIndexes = new ArrayList<>(indexManager.getFieldIndexes());
795-
Collections.sort(existingIndexes, FieldIndex.SEMANTIC_COMPARATOR);
796-
Iterator<FieldIndex> existingIt = existingIndexes.iterator();
797-
@Nullable FieldIndex existingValue = advanceIterator(existingIt);
798-
799-
List<FieldIndex> updatedIndexes = new ArrayList<>(newFieldIndexes);
800-
Collections.sort(updatedIndexes, FieldIndex.SEMANTIC_COMPARATOR);
801-
Iterator<FieldIndex> updatedIt = updatedIndexes.iterator();
802-
@Nullable FieldIndex updatedValue = advanceIterator(updatedIt);
803-
804-
while (existingValue != null || updatedValue != null) {
805-
boolean deleted = false;
806-
boolean updated = false;
807-
808-
if (existingValue != null && updatedValue != null) {
809-
int cmp = FieldIndex.SEMANTIC_COMPARATOR.compare(existingValue, updatedValue);
810-
if (cmp < 0) {
811-
deleted = true;
812-
} else if (cmp > 0) {
813-
updated = true;
814-
}
815-
} else if (existingValue != null) {
816-
deleted = true;
817-
} else {
818-
updated = true;
819-
}
820-
821-
if (deleted) {
822-
indexManager.deleteFieldIndex(existingValue);
823-
existingValue = advanceIterator(existingIt);
824-
} else if (updated) {
825-
indexManager.addFieldIndex(updatedValue);
826-
updatedValue = advanceIterator(updatedIt);
827-
} else {
828-
existingValue = advanceIterator(existingIt);
829-
updatedValue = advanceIterator(updatedIt);
830-
}
831-
}
792+
diffCollections(
793+
indexManager.getFieldIndexes(),
794+
newFieldIndexes,
795+
FieldIndex.SEMANTIC_COMPARATOR,
796+
indexManager::addFieldIndex,
797+
indexManager::deleteFieldIndex);
832798
});
833799
}
834800

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

Lines changed: 88 additions & 12 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;
@@ -98,18 +99,8 @@ public static int compareMixed(double doubleValue, long longValue) {
9899
return NumberComparisonHelper.firestoreCompareDoubleWithLong(doubleValue, longValue);
99100
}
100101

101-
@SuppressWarnings("unchecked")
102-
private static final Comparator COMPARABLE_COMPARATOR =
103-
new Comparator<Comparable<?>>() {
104-
@Override
105-
public int compare(Comparable left, Comparable right) {
106-
return left.compareTo(right);
107-
}
108-
};
109-
110-
@SuppressWarnings("unchecked")
111102
public static <T extends Comparable<T>> Comparator<T> comparator() {
112-
return COMPARABLE_COMPARATOR;
103+
return Comparable::compareTo;
113104
}
114105

115106
public static FirebaseFirestoreException exceptionFromStatus(Status error) {
@@ -249,9 +240,94 @@ public static StringBuilder repeatSequence(
249240
return sb;
250241
}
251242

243+
/**
244+
* Compares two collections for equality using the provided comparator. The method computes the
245+
* intersection and invokes `onAdd` for every element that is in `after` but not `before`.
246+
* `onRemove` is invoked for every element in `before` but missing from `after`.
247+
*
248+
* <p>The method creates a copy of both `before` and `after` and runs in O(n log n), where n is
249+
* the size of the two lists.
250+
*
251+
* @param before The elements that exist in the original set.
252+
* @param after The elements to diff against the original set.
253+
* @param comparator The comparator to use to define equality between elements.
254+
* @param onAdd A function to invoke for every element that is part of `after` but not `before`.
255+
* @param onRemove A function to invoke for every element that is part of `before` but not
256+
* `after`.
257+
*/
258+
public static <T> void diffCollections(
259+
Collection<T> before,
260+
Collection<T> after,
261+
Comparator<T> comparator,
262+
Consumer<T> onAdd,
263+
Consumer<T> onRemove) {
264+
List<T> beforeEntries = new ArrayList<>(before);
265+
Collections.sort(beforeEntries, comparator);
266+
Iterator<T> beforeIt = beforeEntries.iterator();
267+
@Nullable T beforeValue = advanceIterator(beforeIt);
268+
269+
List<T> afterEntries = new ArrayList<>(after);
270+
Collections.sort(afterEntries, comparator);
271+
Iterator<T> afterIt = afterEntries.iterator();
272+
@Nullable T afterValue = advanceIterator(afterIt);
273+
274+
// Walk through the two sets at the same time, using the ordering defined by `comparator`.
275+
while (beforeValue != null || afterValue != null) {
276+
boolean added = false;
277+
boolean removed = false;
278+
279+
if (beforeValue != null && afterValue != null) {
280+
int cmp = comparator.compare(beforeValue, afterValue);
281+
if (cmp < 0) {
282+
// The element was removed if the next element in our ordered walkthrough is only in
283+
// `before`.
284+
removed = true;
285+
} else if (cmp > 0) {
286+
// The element was added if the next element in our ordered walkthrough is only in
287+
// `after`.
288+
added = true;
289+
}
290+
} else if (beforeValue != null) {
291+
removed = true;
292+
} else {
293+
added = true;
294+
}
295+
296+
if (added) {
297+
onAdd.accept(afterValue);
298+
afterValue = advanceIterator(afterIt);
299+
} else if (removed) {
300+
onRemove.accept(beforeValue);
301+
beforeValue = advanceIterator(beforeIt);
302+
} else {
303+
beforeValue = advanceIterator(beforeIt);
304+
afterValue = advanceIterator(afterIt);
305+
}
306+
}
307+
}
308+
309+
/**
310+
* Compares two collections for equality using their natural ordering. The method computes the
311+
* intersection and invokes `onAdd` for every element that is in `after` but not `before`.
312+
* `onRemove` is invoked for every element in `before` but missing from `after`.
313+
*
314+
* <p>The method creates a copy of both `before` and `after` and runs in O(n log n), where n is
315+
* the size of the two lists.
316+
*
317+
* @param before The elements that exist in the original set.
318+
* @param after The elements to diff against the original set.
319+
* @param onAdd A function to invoke for every element that is part of `after` but not `before`.
320+
* @param onRemove A function to invoke for every element that is part of `before` but not
321+
* `after`.
322+
*/
323+
public static <T extends Comparable<T>> void diffCollections(
324+
Collection<T> before, Collection<T> after, Consumer<T> onAdd, Consumer<T> onRemove) {
325+
diffCollections(before, after, Comparable::compareTo, onAdd, onRemove);
326+
}
327+
252328
/** Returns the next element from the iterator or `null` if none available. */
253329
@Nullable
254-
public static <T> T advanceIterator(Iterator<T> it) {
330+
private static <T> T advanceIterator(Iterator<T> it) {
255331
return it.hasNext() ? it.next() : null;
256332
}
257333
}

firebase-firestore/src/test/java/com/google/firebase/firestore/util/UtilTest.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,15 @@
1414

1515
package com.google.firebase.firestore.util;
1616

17+
import static com.google.common.truth.Truth.assertThat;
1718
import static org.junit.Assert.assertEquals;
1819

1920
import com.google.firebase.firestore.testutil.TestUtil;
2021
import com.google.protobuf.ByteString;
22+
import java.util.ArrayList;
23+
import java.util.Arrays;
24+
import java.util.Collections;
25+
import java.util.List;
2126
import org.junit.Test;
2227
import org.junit.runner.RunWith;
2328
import org.junit.runners.JUnit4;
@@ -35,4 +40,38 @@ public void testToDebugString() {
3540
Util.toDebugString(
3641
TestUtil.byteString(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF)));
3742
}
43+
44+
@Test
45+
public void testDiffCollectionsWithMissingElement() {
46+
List<String> before = Arrays.asList("a", "b", "c");
47+
List<String> after = Arrays.asList("a", "b");
48+
validateDiffCollection(before, after);
49+
}
50+
51+
@Test
52+
public void testDiffCollectionsWithAddedElement() {
53+
List<String> before = Arrays.asList("a", "b");
54+
List<String> after = Arrays.asList("a", "b", "c");
55+
validateDiffCollection(before, after);
56+
}
57+
58+
@Test
59+
public void testDiffCollectionsWithoutOrdering() {
60+
List<String> before = Arrays.asList("b", "a");
61+
List<String> after = Arrays.asList("a", "b");
62+
validateDiffCollection(before, after);
63+
}
64+
65+
@Test
66+
public void testDiffCollectionsWithEmptyLists() {
67+
validateDiffCollection(Collections.singletonList("a"), Collections.emptyList());
68+
validateDiffCollection(Collections.emptyList(), Collections.singletonList("a"));
69+
validateDiffCollection(Collections.emptyList(), Collections.emptyList());
70+
}
71+
72+
private void validateDiffCollection(List<String> before, List<String> after) {
73+
List<String> result = new ArrayList<>(before);
74+
Util.diffCollections(before, after, result::add, result::remove);
75+
assertThat(result).containsExactlyElementsIn(after);
76+
}
3877
}

0 commit comments

Comments
 (0)