mirror of
https://github.com/zhigang1992/react-native-firebase.git
synced 2026-04-11 11:29:13 +08:00
[Android][IOS][Firestore] add arrayRemove and arrayUnion to FieldValue (#1624)
### Summary Add methods arrayRemove and arrayUnion to work with arrays in firestore. Fixes #1389 ### Checklist * [x] Supports `Android` * [x] Supports `iOS` * [x] `e2e` tests added or updated in [/tests/e2e/*](/tests/e2e) * [x] Updated the documentation in the [docs repo](https://github.com/invertase/react-native-firebase-docs) https://github.com/invertase/react-native-firebase-docs/pull/134 * [x] Flow types updated * [x] Typescript types updated
This commit is contained in:
committed by
Michael Diarmid
parent
991ed4f628
commit
44c7c92acf
@@ -68,6 +68,10 @@ class FirestoreSerialize {
|
||||
private static final String TYPE_FIELDVALUE = "fieldvalue";
|
||||
private static final String TYPE_FIELDVALUE_DELETE = "delete";
|
||||
private static final String TYPE_FIELDVALUE_TIMESTAMP = "timestamp";
|
||||
private static final String TYPE_FIELDVALUE_UNION = "union";
|
||||
private static final String TYPE_FIELDVALUE_REMOVE = "remove";
|
||||
private static final String TYPE_FIELDVALUE_TYPE = "type";
|
||||
private static final String TYPE_FIELDVALUE_ELEMENTS = "elements";
|
||||
|
||||
// Document Change Types
|
||||
private static final String CHANGE_ADDED = "added";
|
||||
@@ -446,7 +450,9 @@ class FirestoreSerialize {
|
||||
}
|
||||
|
||||
if (TYPE_FIELDVALUE.equals(type)) {
|
||||
String fieldValueType = typeMap.getString(VALUE);
|
||||
ReadableMap fieldValueMap = typeMap.getMap(VALUE);
|
||||
String fieldValueType = fieldValueMap.getString(TYPE_FIELDVALUE_TYPE);
|
||||
|
||||
|
||||
if (TYPE_FIELDVALUE_TIMESTAMP.equals(fieldValueType)) {
|
||||
return FieldValue.serverTimestamp();
|
||||
@@ -456,6 +462,16 @@ class FirestoreSerialize {
|
||||
return FieldValue.delete();
|
||||
}
|
||||
|
||||
if (TYPE_FIELDVALUE_UNION.equals(fieldValueType)) {
|
||||
ReadableArray elements = fieldValueMap.getArray(TYPE_FIELDVALUE_ELEMENTS);
|
||||
return FieldValue.arrayUnion(elements.toArrayList().toArray());
|
||||
}
|
||||
|
||||
if (TYPE_FIELDVALUE_REMOVE.equals(fieldValueType)) {
|
||||
ReadableArray elements = fieldValueMap.getArray(TYPE_FIELDVALUE_ELEMENTS);
|
||||
return FieldValue.arrayRemove(elements.toArrayList().toArray());
|
||||
}
|
||||
|
||||
Log.w(TAG, "Unknown FieldValue type: " + fieldValueType);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -37,6 +37,10 @@ static NSString *const typeTimestamp = @"timestamp";
|
||||
static NSString *const typeReference = @"reference";
|
||||
static NSString *const typeDocumentId = @"documentid";
|
||||
static NSString *const typeFieldValue = @"fieldvalue";
|
||||
static NSString *const typeFieldValueUnion = @"union";
|
||||
static NSString *const typeFieldValueRemove = @"remove";
|
||||
static NSString *const typeFieldValueType = @"type";
|
||||
static NSString *const typeFieldValueElements = @"elements";
|
||||
|
||||
- (id)initWithPath:(RCTEventEmitter *)emitter
|
||||
appDisplayName:(NSString *)appDisplayName
|
||||
@@ -408,8 +412,9 @@ static NSString *const typeFieldValue = @"fieldvalue";
|
||||
}
|
||||
|
||||
if ([type isEqualToString:typeFieldValue]) {
|
||||
NSString *string = (NSString *) value;
|
||||
|
||||
NSDictionary *fieldValueMap = (NSDictionary *) value;
|
||||
NSString *string = (NSString *) fieldValueMap[typeFieldValueType];
|
||||
|
||||
if ([string isEqualToString:typeDelete]) {
|
||||
return [FIRFieldValue fieldValueForDelete];
|
||||
}
|
||||
@@ -417,6 +422,16 @@ static NSString *const typeFieldValue = @"fieldvalue";
|
||||
if ([string isEqualToString:typeTimestamp]) {
|
||||
return [FIRFieldValue fieldValueForServerTimestamp];
|
||||
}
|
||||
|
||||
if ([string isEqualToString:typeFieldValueUnion]) {
|
||||
NSDictionary *elements = (NSDictionary *) value[typeFieldValueElements];
|
||||
return [FIRFieldValue fieldValueForArrayUnion:elements];
|
||||
}
|
||||
|
||||
if ([string isEqualToString:typeFieldValueRemove]) {
|
||||
NSDictionary *elements = (NSDictionary *) value[typeFieldValueElements];
|
||||
return [FIRFieldValue fieldValueForArrayRemove:elements];
|
||||
}
|
||||
|
||||
DLog(@"RNFirebaseFirestore: Unsupported field-value sent to parseJSTypeMap - value is %@",
|
||||
NSStringFromClass([value class]));
|
||||
|
||||
6
src/index.d.ts
vendored
6
src/index.d.ts
vendored
@@ -2404,10 +2404,16 @@ declare module 'react-native-firebase' {
|
||||
constructor(...segments: string[]);
|
||||
}
|
||||
|
||||
type AnyJs = null | undefined | boolean | number | string | object;
|
||||
|
||||
class FieldValue {
|
||||
static delete(): FieldValue;
|
||||
|
||||
static serverTimestamp(): FieldValue;
|
||||
|
||||
static arrayUnion(...elements: AnyJs[]): FieldValue;
|
||||
|
||||
static arrayRemove(...elements: AnyJs[]): FieldValue;
|
||||
}
|
||||
|
||||
class GeoPoint {
|
||||
|
||||
@@ -2,16 +2,45 @@
|
||||
* @flow
|
||||
* FieldValue representation wrapper
|
||||
*/
|
||||
import AnyJs from './utils/any';
|
||||
|
||||
// TODO: Salakar: Refactor in v6
|
||||
export default class FieldValue {
|
||||
_type: string;
|
||||
|
||||
_elements: AnyJs[] | any;
|
||||
|
||||
constructor(type: string, elements?: AnyJs[]) {
|
||||
this._type = type;
|
||||
this._elements = elements;
|
||||
}
|
||||
|
||||
get type(): string {
|
||||
return this._type;
|
||||
}
|
||||
|
||||
get elements(): AnyJs[] {
|
||||
return this._elements;
|
||||
}
|
||||
|
||||
static delete(): FieldValue {
|
||||
return DELETE_FIELD_VALUE;
|
||||
return new FieldValue(TypeFieldValueDelete);
|
||||
}
|
||||
|
||||
static serverTimestamp(): FieldValue {
|
||||
return SERVER_TIMESTAMP_FIELD_VALUE;
|
||||
return new FieldValue(TypeFieldValueTimestamp);
|
||||
}
|
||||
|
||||
static arrayUnion(...elements: AnyJs[]) {
|
||||
return new FieldValue(TypeFieldValueUnion, elements);
|
||||
}
|
||||
|
||||
static arrayRemove(...elements: AnyJs[]) {
|
||||
return new FieldValue(TypeFieldValueRemove, elements);
|
||||
}
|
||||
}
|
||||
|
||||
export const DELETE_FIELD_VALUE = new FieldValue();
|
||||
export const SERVER_TIMESTAMP_FIELD_VALUE = new FieldValue();
|
||||
export const TypeFieldValueDelete = 'delete';
|
||||
export const TypeFieldValueRemove = 'remove';
|
||||
export const TypeFieldValueUnion = 'union';
|
||||
export const TypeFieldValueTimestamp = 'timestamp';
|
||||
|
||||
4
src/modules/firestore/utils/any.js
Normal file
4
src/modules/firestore/utils/any.js
Normal file
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* @url https://github.com/firebase/firebase-js-sdk/blob/master/packages/firestore/src/util/misc.ts#L26
|
||||
*/
|
||||
export type AnyJs = null | undefined | boolean | number | string | object;
|
||||
@@ -5,10 +5,7 @@
|
||||
import DocumentReference from '../DocumentReference';
|
||||
import Blob from '../Blob';
|
||||
import { DOCUMENT_ID } from '../FieldPath';
|
||||
import {
|
||||
DELETE_FIELD_VALUE,
|
||||
SERVER_TIMESTAMP_FIELD_VALUE,
|
||||
} from '../FieldValue';
|
||||
import FieldValue from '../FieldValue';
|
||||
import GeoPoint from '../GeoPoint';
|
||||
import Path from '../Path';
|
||||
import { typeOf } from '../../../utils';
|
||||
@@ -72,20 +69,6 @@ export const buildTypeMap = (value: any): NativeTypeMap | null => {
|
||||
};
|
||||
}
|
||||
|
||||
if (value === DELETE_FIELD_VALUE) {
|
||||
return {
|
||||
type: 'fieldvalue',
|
||||
value: 'delete',
|
||||
};
|
||||
}
|
||||
|
||||
if (value === SERVER_TIMESTAMP_FIELD_VALUE) {
|
||||
return {
|
||||
type: 'fieldvalue',
|
||||
value: 'timestamp',
|
||||
};
|
||||
}
|
||||
|
||||
if (value === DOCUMENT_ID) {
|
||||
return {
|
||||
type: 'documentid',
|
||||
@@ -139,6 +122,17 @@ export const buildTypeMap = (value: any): NativeTypeMap | null => {
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: Salakar: Refactor in v6 - add internal `type` flag
|
||||
if (value instanceof FieldValue) {
|
||||
return {
|
||||
type: 'fieldvalue',
|
||||
value: {
|
||||
elements: value.elements,
|
||||
type: value.type,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'object',
|
||||
value: buildNativeMap(value),
|
||||
|
||||
@@ -7,7 +7,7 @@ const {
|
||||
|
||||
describe('firestore()', () => {
|
||||
describe('FieldValue', () => {
|
||||
before(async () => {
|
||||
beforeEach(async () => {
|
||||
await resetTestCollectionDoc(DOC_2_PATH, DOC_2);
|
||||
});
|
||||
|
||||
@@ -46,5 +46,42 @@ describe('firestore()', () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('arrayUnion()', () => {
|
||||
it('should add new values to array field', async () => {
|
||||
const { data } = await testCollectionDoc(DOC_2_PATH).get();
|
||||
should.equal(data().elements, undefined);
|
||||
|
||||
await testCollectionDoc(DOC_2_PATH).update({
|
||||
elements: firebase.firestore.FieldValue.arrayUnion('element 1'),
|
||||
elements2: firebase.firestore.FieldValue.arrayUnion('element 2'),
|
||||
});
|
||||
|
||||
const { data: dataAfterUpdate } = await testCollectionDoc(
|
||||
DOC_2_PATH
|
||||
).get();
|
||||
|
||||
dataAfterUpdate().elements.should.containDeep(['element 1']);
|
||||
dataAfterUpdate().elements2.should.containDeep(['element 2']);
|
||||
});
|
||||
});
|
||||
describe('arrayRemove()', () => {
|
||||
it('should remove value from array', async () => {
|
||||
await testCollectionDoc(DOC_2_PATH).set({
|
||||
elements: ['element 1', 'element 2'],
|
||||
});
|
||||
const { data } = await testCollectionDoc(DOC_2_PATH).get();
|
||||
data().elements.should.containDeep(['element 1', 'element 2']);
|
||||
|
||||
await testCollectionDoc(DOC_2_PATH).update({
|
||||
elements: firebase.firestore.FieldValue.arrayRemove('element 2'),
|
||||
});
|
||||
|
||||
const { data: dataAfterUpdate } = await testCollectionDoc(
|
||||
DOC_2_PATH
|
||||
).get();
|
||||
|
||||
dataAfterUpdate().elements.should.not.containDeep(['element 2']);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user