Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion firebase-firestore/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Unreleased
- [feature] Added support for `minimum` and `maximum` FieldValue operations.

# 26.3.0

- [feature] Added search stage support for `languageCode`, `offset`, `limit`, and `retrievalDepth`.
- [feature] Added support for Pipeline expressions `arraySlice`, `arraySliceToEnd`, `arrayFilter`, `arrayTransform` and `arrayTransformWithIndex`.
[#7989](https://github.com/firebase/firebase-android-sdk/pull/7989)
Expand Down
4 changes: 4 additions & 0 deletions firebase-firestore/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,10 @@ package com.google.firebase.firestore {
method public static com.google.firebase.firestore.FieldValue delete();
method public static com.google.firebase.firestore.FieldValue increment(double);
method public static com.google.firebase.firestore.FieldValue increment(long);
method public static com.google.firebase.firestore.FieldValue maximum(double);
method public static com.google.firebase.firestore.FieldValue maximum(long);
method public static com.google.firebase.firestore.FieldValue minimum(double);
method public static com.google.firebase.firestore.FieldValue minimum(long);
method public static com.google.firebase.firestore.FieldValue serverTimestamp();
method public static com.google.firebase.firestore.VectorValue vector(double[]);
}
Expand Down
2 changes: 1 addition & 1 deletion firebase-firestore/gradle.properties
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
version=26.3.1
version=26.4.0
latestReleasedVersion=26.3.0
Original file line number Diff line number Diff line change
Expand Up @@ -71,18 +71,31 @@ private void writeInitialData(Map<String, Object> initialData) {

private void expectLocalAndRemoteValue(double expectedSum) {
DocumentSnapshot snap = accumulator.awaitLocalEvent();
org.junit.Assert.assertTrue(snap.get("sum") instanceof Double);
assertEquals(expectedSum, snap.getDouble("sum"), DOUBLE_EPSILON);
snap = accumulator.awaitRemoteEvent();
org.junit.Assert.assertTrue(snap.get("sum") instanceof Double);
assertEquals(expectedSum, snap.getDouble("sum"), DOUBLE_EPSILON);
}

private void expectLocalAndRemoteValue(long expectedSum) {
DocumentSnapshot snap = accumulator.awaitLocalEvent();
org.junit.Assert.assertTrue(snap.get("sum") instanceof Long);
assertEquals(expectedSum, (long) snap.getLong("sum"));
snap = accumulator.awaitRemoteEvent();
org.junit.Assert.assertTrue(snap.get("sum") instanceof Long);
assertEquals(expectedSum, (long) snap.getLong("sum"));
}

private void expectLocalAndRemoteNaN() {
DocumentSnapshot snap = accumulator.awaitLocalEvent();
org.junit.Assert.assertTrue(snap.get("sum") instanceof Double);
org.junit.Assert.assertTrue(Double.isNaN(snap.getDouble("sum")));
snap = accumulator.awaitRemoteEvent();
org.junit.Assert.assertTrue(snap.get("sum") instanceof Double);
org.junit.Assert.assertTrue(Double.isNaN(snap.getDouble("sum")));
}

@Test
public void createDocumentWithIncrement() {
waitFor(docRef.set(map("sum", FieldValue.increment(1337))));
Expand Down Expand Up @@ -218,4 +231,128 @@ public void serverTimestampAndIncrement() throws ExecutionException, Interrupted
snap = accumulator.awaitRemoteEvent();
assertEquals(1, (long) snap.getLong("val"));
}

@Test
public void createDocumentWithMinimum() {
waitFor(docRef.set(map("sum", FieldValue.minimum(1337))));
expectLocalAndRemoteValue(1337L);
}

@Test
public void createDocumentWithMaximum() {
waitFor(docRef.set(map("sum", FieldValue.maximum(1337))));
expectLocalAndRemoteValue(1337L);
}

@Test
public void minimumWithExistingInteger() {
writeInitialData(map("sum", 10L));
waitFor(docRef.update("sum", FieldValue.minimum(5L)));
expectLocalAndRemoteValue(5L);

waitFor(docRef.update("sum", FieldValue.minimum(20L)));
expectLocalAndRemoteValue(5L);
}

@Test
public void maximumWithExistingInteger() {
writeInitialData(map("sum", 10L));
waitFor(docRef.update("sum", FieldValue.maximum(5L)));
expectLocalAndRemoteValue(10L);

waitFor(docRef.update("sum", FieldValue.maximum(20L)));
expectLocalAndRemoteValue(20L);
}

@Test
public void minimumWithExistingDouble() {
writeInitialData(map("sum", 10.5D));
waitFor(docRef.update("sum", FieldValue.minimum(5.5D)));
expectLocalAndRemoteValue(5.5D);

waitFor(docRef.update("sum", FieldValue.minimum(20.5D)));
expectLocalAndRemoteValue(5.5D);
}

@Test
public void maximumWithExistingDouble() {
writeInitialData(map("sum", 10.5D));
waitFor(docRef.update("sum", FieldValue.maximum(5.5D)));
expectLocalAndRemoteValue(10.5D);

waitFor(docRef.update("sum", FieldValue.maximum(20.5D)));
expectLocalAndRemoteValue(20.5D);
}

@Test
public void mixedTypesPreserveOperandTypeForMinimum() {
// field and input value of mixed types: field takes on type of smaller operand
writeInitialData(map("sum", 10L));
waitFor(docRef.update("sum", FieldValue.minimum(5.5D)));
expectLocalAndRemoteValue(5.5D);

writeInitialData(map("sum", 10.5D));
waitFor(docRef.update("sum", FieldValue.minimum(5L)));
expectLocalAndRemoteValue(5L);
}

@Test
public void mixedTypesPreserveOperandTypeForMaximum() {
// field and input value of mixed types: field takes on type of larger operand
writeInitialData(map("sum", 10L));
waitFor(docRef.update("sum", FieldValue.maximum(20.5D)));
expectLocalAndRemoteValue(20.5D);

writeInitialData(map("sum", 10.5D));
waitFor(docRef.update("sum", FieldValue.maximum(20L)));
expectLocalAndRemoteValue(20L);
}

@Test
public void equivalentValuesDoNotChangeTypeForMinimum() {
// equivalent (e.g. 3 and 3.0), field does not change type
writeInitialData(map("sum", 3L));
waitFor(docRef.update("sum", FieldValue.minimum(3.0D)));
expectLocalAndRemoteValue(3L);

writeInitialData(map("sum", 3.0D));
waitFor(docRef.update("sum", FieldValue.minimum(3L)));
expectLocalAndRemoteValue(3.0D);
}

@Test
public void equivalentValuesDoNotChangeTypeForMaximum() {
// equivalent (e.g. 3 and 3.0), field does not change type
writeInitialData(map("sum", 3L));
waitFor(docRef.update("sum", FieldValue.maximum(3.0D)));
expectLocalAndRemoteValue(3L);

writeInitialData(map("sum", 3.0D));
waitFor(docRef.update("sum", FieldValue.maximum(3L)));
expectLocalAndRemoteValue(3.0D);
}

@Test
public void minimumWithNaN() {
// If one of the values is NaN, minimum is NaN
writeInitialData(map("sum", Double.NaN));
waitFor(docRef.update("sum", FieldValue.minimum(5L)));
expectLocalAndRemoteNaN();

writeInitialData(map("sum", 5L));
waitFor(docRef.update("sum", FieldValue.minimum(Double.NaN)));
expectLocalAndRemoteNaN();
}

@Test
public void maximumWithNaN() {
// If one of the values is NaN, maximum is NaN
writeInitialData(map("sum", Double.NaN));
waitFor(docRef.update("sum", FieldValue.maximum(5L)));
expectLocalAndRemoteNaN();

writeInitialData(map("sum", 5L));
waitFor(docRef.update("sum", FieldValue.maximum(Double.NaN)));
expectLocalAndRemoteNaN();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,42 @@ Number getOperand() {
}
}

/** {@code FieldValue} class for {@link #minimum()} transforms. */
static class NumericMinimumFieldValue extends FieldValue {
private final Number operand;

NumericMinimumFieldValue(Number operand) {
this.operand = operand;
}

@Override
String getMethodName() {
return "FieldValue.minimum";
}

Number getOperand() {
return operand;
}
}

/** {@code FieldValue} class for {@link #maximum()} transforms. */
static class NumericMaximumFieldValue extends FieldValue {
private final Number operand;

NumericMaximumFieldValue(Number operand) {
this.operand = operand;
}

@Override
String getMethodName() {
return "FieldValue.maximum";
}

Number getOperand() {
return operand;
}
}

private static final DeleteFieldValue DELETE_INSTANCE = new DeleteFieldValue();
private static final ServerTimestampFieldValue SERVER_TIMESTAMP_INSTANCE =
new ServerTimestampFieldValue();
Expand Down Expand Up @@ -183,6 +219,50 @@ public static FieldValue increment(double l) {
return new NumericIncrementFieldValue(l);
}

/**
* Returns a special value that can be used with {@code set()} or {@code update()} that tells the
* server to set the field to the minimum of its current value and the given value.
*
* @return The {@code FieldValue} sentinel for use in a call to {@code set()} or {@code update()}.
*/
@NonNull
public static FieldValue minimum(long l) {
return new NumericMinimumFieldValue(l);
}

/**
* Returns a special value that can be used with {@code set()} or {@code update()} that tells the
* server to set the field to the minimum of its current value and the given value.
*
* @return The {@code FieldValue} sentinel for use in a call to {@code set()} or {@code update()}.
*/
@NonNull
public static FieldValue minimum(double l) {
return new NumericMinimumFieldValue(l);
}

/**
* Returns a special value that can be used with {@code set()} or {@code update()} that tells the
* server to set the field to the maximum of its current value and the given value.
*
* @return The {@code FieldValue} sentinel for use in a call to {@code set()} or {@code update()}.
*/
@NonNull
public static FieldValue maximum(long l) {
return new NumericMaximumFieldValue(l);
}

/**
* Returns a special value that can be used with {@code set()} or {@code update()} that tells the
* server to set the field to the maximum of its current value and the given value.
*
* @return The {@code FieldValue} sentinel for use in a call to {@code set()} or {@code update()}.
*/
@NonNull
public static FieldValue maximum(double l) {
return new NumericMaximumFieldValue(l);
}

/**
* Creates a new {@link VectorValue} constructed with a copy of the given array of doubles.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
import com.google.firebase.firestore.FieldValue.ArrayRemoveFieldValue;
import com.google.firebase.firestore.FieldValue.ArrayUnionFieldValue;
import com.google.firebase.firestore.FieldValue.DeleteFieldValue;
import com.google.firebase.firestore.FieldValue.NumericIncrementFieldValue;
import com.google.firebase.firestore.FieldValue.NumericMaximumFieldValue;
import com.google.firebase.firestore.FieldValue.NumericMinimumFieldValue;
import com.google.firebase.firestore.FieldValue.ServerTimestampFieldValue;
import com.google.firebase.firestore.core.UserData;
import com.google.firebase.firestore.core.UserData.ParseAccumulator;
Expand All @@ -36,6 +39,8 @@
import com.google.firebase.firestore.model.mutation.ArrayTransformOperation;
import com.google.firebase.firestore.model.mutation.FieldMask;
import com.google.firebase.firestore.model.mutation.NumericIncrementTransformOperation;
import com.google.firebase.firestore.model.mutation.NumericMaximumTransformOperation;
import com.google.firebase.firestore.model.mutation.NumericMinimumTransformOperation;
import com.google.firebase.firestore.model.mutation.ServerTimestampOperation;
import com.google.firebase.firestore.pipeline.Expression;
import com.google.firebase.firestore.util.Assert;
Expand Down Expand Up @@ -369,16 +374,27 @@ private void parseSentinelFieldValue(
ArrayTransformOperation arrayRemove = new ArrayTransformOperation.Remove(parsedElements);
context.addToFieldTransforms(context.getPath(), arrayRemove);

} else if (value
instanceof com.google.firebase.firestore.FieldValue.NumericIncrementFieldValue) {
com.google.firebase.firestore.FieldValue.NumericIncrementFieldValue
numericIncrementFieldValue =
(com.google.firebase.firestore.FieldValue.NumericIncrementFieldValue) value;
} else if (value instanceof NumericIncrementFieldValue) {
NumericIncrementFieldValue numericIncrementFieldValue = (NumericIncrementFieldValue) value;
Value operand = parseQueryValue(numericIncrementFieldValue.getOperand());
NumericIncrementTransformOperation incrementOperation =
new NumericIncrementTransformOperation(operand);
context.addToFieldTransforms(context.getPath(), incrementOperation);

} else if (value instanceof NumericMinimumFieldValue) {
NumericMinimumFieldValue numericMinimumFieldValue = (NumericMinimumFieldValue) value;
Value operand = parseQueryValue(numericMinimumFieldValue.getOperand());
NumericMinimumTransformOperation minimumOperation =
new NumericMinimumTransformOperation(operand);
context.addToFieldTransforms(context.getPath(), minimumOperation);

} else if (value instanceof NumericMaximumFieldValue) {
NumericMaximumFieldValue numericMaximumFieldValue = (NumericMaximumFieldValue) value;
Value operand = parseQueryValue(numericMaximumFieldValue.getOperand());
NumericMaximumTransformOperation maximumOperation =
new NumericMaximumTransformOperation(operand);
context.addToFieldTransforms(context.getPath(), maximumOperation);

} else {
throw Assert.fail("Unknown FieldValue type: %s", Util.typeName(value));
}
Expand Down
Loading
Loading