Skip to content

Commit c399917

Browse files
Firestore VectorValue type (#13404)
Co-authored-by: Nick Cooke <[email protected]>
1 parent e76d746 commit c399917

28 files changed

+1241
-148
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2024 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#import <FirebaseFirestoreInternal/FIRVectorValue.h>

Firestore/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# 11.1.0
2+
- [feature] Add `VectorValue` type support.
3+
14
# 11.0.0
25
- [removed] **Breaking change**: The deprecated `FirebaseFirestoreSwift` module
36
has been removed. See

Firestore/Example/Firestore.xcodeproj/project.pbxproj

Lines changed: 25 additions & 3 deletions
Large diffs are not rendered by default.

Firestore/Example/Firestore.xcodeproj/xcshareddata/xcschemes/Firestore_Tests_iOS.xcscheme

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,17 @@
2626
buildConfiguration = "Debug"
2727
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
2828
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
29-
enableASanStackUseAfterReturn = "YES"
30-
shouldUseLaunchSchemeArgsEnv = "YES">
29+
shouldUseLaunchSchemeArgsEnv = "YES"
30+
enableASanStackUseAfterReturn = "YES">
31+
<MacroExpansion>
32+
<BuildableReference
33+
BuildableIdentifier = "primary"
34+
BlueprintIdentifier = "6003F5AD195388D20070C39A"
35+
BuildableName = "Firestore_Tests_iOS.xctest"
36+
BlueprintName = "Firestore_Tests_iOS"
37+
ReferencedContainer = "container:Firestore.xcodeproj">
38+
</BuildableReference>
39+
</MacroExpansion>
3140
<Testables>
3241
<TestableReference
3342
skipped = "NO">
@@ -40,17 +49,6 @@
4049
</BuildableReference>
4150
</TestableReference>
4251
</Testables>
43-
<MacroExpansion>
44-
<BuildableReference
45-
BuildableIdentifier = "primary"
46-
BlueprintIdentifier = "6003F5AD195388D20070C39A"
47-
BuildableName = "Firestore_Tests_iOS.xctest"
48-
BlueprintName = "Firestore_Tests_iOS"
49-
ReferencedContainer = "container:Firestore.xcodeproj">
50-
</BuildableReference>
51-
</MacroExpansion>
52-
<AdditionalOptions>
53-
</AdditionalOptions>
5452
</TestAction>
5553
<LaunchAction
5654
buildConfiguration = "Debug"
@@ -71,8 +69,6 @@
7169
ReferencedContainer = "container:Firestore.xcodeproj">
7270
</BuildableReference>
7371
</MacroExpansion>
74-
<AdditionalOptions>
75-
</AdditionalOptions>
7672
</LaunchAction>
7773
<ProfileAction
7874
buildConfiguration = "Release"

Firestore/Example/Tests/API/FIRFieldValueTests.mm

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616

1717
#import <FirebaseFirestore/FIRFieldValue.h>
18+
#import <FirebaseFirestore/FIRVectorValue.h>
1819

1920
#import <XCTest/XCTest.h>
2021

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright 2024 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#import <FirebaseFirestore/FIRFieldValue.h>
18+
#import <FirebaseFirestore/FIRVectorValue.h>
19+
20+
#import <XCTest/XCTest.h>
21+
22+
NS_ASSUME_NONNULL_BEGIN
23+
24+
@interface FIRVectorValueTests : XCTestCase
25+
@end
26+
27+
@implementation FIRVectorValueTests
28+
29+
- (void)testCreateAndReadVectorValue {
30+
FIRVectorValue *vector = [FIRFieldValue vectorWithArray:@[
31+
@DBL_MIN, @0.0, [NSNumber numberWithLong:((long)pow(2, 53)) + 1], @DBL_MAX, @DBL_EPSILON,
32+
@INT64_MAX
33+
]];
34+
NSArray<NSNumber *> *outArray = vector.array;
35+
36+
XCTAssertEqualObjects([outArray objectAtIndex:0], @DBL_MIN);
37+
XCTAssertEqualObjects([outArray objectAtIndex:1], @0.0);
38+
// Assert that if the vector is created with large long values,
39+
// then the data will be truncated as a double.
40+
XCTAssertEqual([outArray objectAtIndex:2].longValue, pow(2, 53));
41+
XCTAssertEqualObjects([outArray objectAtIndex:3], @DBL_MAX);
42+
XCTAssertEqualObjects([outArray objectAtIndex:4], @DBL_EPSILON);
43+
XCTAssertEqualObjects([outArray objectAtIndex:5], @INT64_MAX);
44+
}
45+
46+
@end
47+
48+
NS_ASSUME_NONNULL_END

Firestore/Source/API/FIRFieldValue.mm

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616

1717
#import "Firestore/Source/API/FIRFieldValue+Internal.h"
18+
#import "Firestore/Source/Public/FirebaseFirestore/FIRVectorValue.h"
1819

1920
NS_ASSUME_NONNULL_BEGIN
2021

@@ -176,6 +177,10 @@ + (instancetype)fieldValueForIntegerIncrement:(int64_t)l {
176177
return [[FSTNumericIncrementFieldValue alloc] initWithOperand:@(l)];
177178
}
178179

180+
+ (nonnull FIRVectorValue *)vectorWithArray:(nonnull NSArray<NSNumber *> *)array {
181+
return [[FIRVectorValue alloc] initWithArray:array];
182+
}
183+
179184
@end
180185

181186
NS_ASSUME_NONNULL_END

Firestore/Source/API/FIRVectorValue.m

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright 2024 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#import <Foundation/Foundation.h>
18+
19+
#include "Firestore/Source/Public/FirebaseFirestore/FIRVectorValue.h"
20+
21+
NS_ASSUME_NONNULL_BEGIN
22+
23+
@implementation FIRVectorValue
24+
25+
- (instancetype)initWithArray:(NSArray<NSNumber *> *)array {
26+
if (self = [super init]) {
27+
_array = [array valueForKey:@"doubleValue"];
28+
}
29+
return self;
30+
}
31+
32+
- (BOOL)isEqual:(nullable id)object {
33+
if (self == object) {
34+
return YES;
35+
}
36+
37+
if (![object isKindOfClass:[FIRVectorValue class]]) {
38+
return NO;
39+
}
40+
41+
FIRVectorValue *otherVector = ((FIRVectorValue *)object);
42+
43+
return [self.array isEqualToArray:otherVector.array];
44+
}
45+
46+
@end
47+
48+
NS_ASSUME_NONNULL_END

Firestore/Source/API/FSTUserDataReader.mm

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#import "Firestore/Source/API/FSTUserDataReader.h"
2626

2727
#import "FIRGeoPoint.h"
28+
#import "FIRVectorValue.h"
2829

2930
#import "Firestore/Source/API/FIRDocumentReference+Internal.h"
3031
#import "Firestore/Source/API/FIRFieldPath+Internal.h"
@@ -341,6 +342,42 @@ - (ParsedUpdateData)parsedUpdateData:(id)input {
341342
return std::move(result);
342343
}
343344

345+
- (Message<google_firestore_v1_Value>)parseVectorValue:(FIRVectorValue *)vectorValue
346+
context:(ParseContext &&)context {
347+
__block Message<google_firestore_v1_Value> result;
348+
result->which_value_type = google_firestore_v1_Value_map_value_tag;
349+
result->map_value = {};
350+
351+
result->map_value.fields_count = 2;
352+
result->map_value.fields = nanopb::MakeArray<google_firestore_v1_MapValue_FieldsEntry>(2);
353+
354+
result->map_value.fields[0].key = nanopb::CopyBytesArray(model::kTypeValueFieldKey);
355+
result->map_value.fields[0].value = *[self encodeStringValue:MakeString(@"__vector__")].release();
356+
357+
NSArray<NSNumber *> *vectorArray = vectorValue.array;
358+
359+
__block Message<google_firestore_v1_Value> arrayMessage;
360+
arrayMessage->which_value_type = google_firestore_v1_Value_array_value_tag;
361+
arrayMessage->array_value.values_count = CheckedSize([vectorArray count]);
362+
arrayMessage->array_value.values =
363+
nanopb::MakeArray<google_firestore_v1_Value>(arrayMessage->array_value.values_count);
364+
365+
[vectorArray enumerateObjectsUsingBlock:^(id entry, NSUInteger idx, BOOL *) {
366+
if (![entry isKindOfClass:[NSNumber class]]) {
367+
ThrowInvalidArgument("VectorValues must only contain numeric values.",
368+
context.FieldDescription());
369+
}
370+
371+
// Vector values must always use Double encoding
372+
arrayMessage->array_value.values[idx] = *[self encodeDouble:[entry doubleValue]].release();
373+
}];
374+
375+
result->map_value.fields[1].key = nanopb::CopyBytesArray(model::kVectorValueFieldKey);
376+
result->map_value.fields[1].value = *arrayMessage.release();
377+
378+
return std::move(result);
379+
}
380+
344381
- (Message<google_firestore_v1_Value>)parseArray:(NSArray<id> *)array
345382
context:(ParseContext &&)context {
346383
__block Message<google_firestore_v1_Value> result;
@@ -529,7 +566,9 @@ - (void)parseSentinelFieldValue:(FIRFieldValue *)fieldValue context:(ParseContex
529566
_databaseID.database_id(), context.FieldDescription());
530567
}
531568
return [self encodeReference:_databaseID key:reference.key];
532-
569+
} else if ([input isKindOfClass:[FIRVectorValue class]]) {
570+
FIRVectorValue *vector = input;
571+
return [self parseVectorValue:vector context:std::move(context)];
533572
} else {
534573
ThrowInvalidArgument("Unsupported type: %s%s", NSStringFromClass([input class]),
535574
context.FieldDescription());

Firestore/Source/API/FSTUserDataWriter.mm

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
#include "Firestore/Protos/nanopb/google/firestore/v1/document.nanopb.h"
2323
#include "Firestore/Source/API/FIRDocumentReference+Internal.h"
24+
#include "Firestore/Source/API/FIRFieldValue+Internal.h"
2425
#include "Firestore/Source/API/converters.h"
2526
#include "Firestore/core/include/firebase/firestore/geo_point.h"
2627
#include "Firestore/core/include/firebase/firestore/timestamp.h"
@@ -105,6 +106,8 @@ - (id)convertedValue:(const google_firestore_v1_Value &)value {
105106
case TypeOrder::kGeoPoint:
106107
return MakeFIRGeoPoint(
107108
GeoPoint(value.geo_point_value.latitude, value.geo_point_value.longitude));
109+
case TypeOrder::kVector:
110+
return [self convertedVector:value.map_value];
108111
case TypeOrder::kMaxValue:
109112
// It is not possible for users to construct a kMaxValue manually.
110113
break;
@@ -123,6 +126,18 @@ - (id)convertedValue:(const google_firestore_v1_Value &)value {
123126
return result;
124127
}
125128

129+
- (FIRVectorValue *)convertedVector:(const google_firestore_v1_MapValue &)mapValue {
130+
for (pb_size_t i = 0; i < mapValue.fields_count; ++i) {
131+
absl::string_view key = MakeStringView(mapValue.fields[i].key);
132+
const google_firestore_v1_Value &value = mapValue.fields[i].value;
133+
if ((0 == key.compare(absl::string_view("value"))) &&
134+
value.which_value_type == google_firestore_v1_Value_array_value_tag) {
135+
return [FIRFieldValue vectorWithArray:[self convertedArray:value.array_value]];
136+
}
137+
}
138+
return [FIRFieldValue vectorWithArray:@[]];
139+
}
140+
126141
- (NSArray<id> *)convertedArray:(const google_firestore_v1_ArrayValue &)arrayValue {
127142
NSMutableArray *result = [NSMutableArray arrayWithCapacity:arrayValue.values_count];
128143
for (pb_size_t i = 0; i < arrayValue.values_count; ++i) {

0 commit comments

Comments
 (0)