简介
编辑:这个答案最初是在现代模块化 SDK 发布之前编写的,它已经更新以涵盖这个新的 SDK 以及当时可用的旧命名空间 SDK。对于新项目,请使用模块化 SDK。
FieldValue 对象
您的组合指令不起作用的原因(除了语法错误)是因为 FieldValue 对象的定义方式。
假设您定义了以下可在您的update() 调用中使用的对象:
// Firebase Namespaced SDK (v8 & older)
// import firebase as appropriate
const myArrayUnion = firebase.firestore.FieldValue.arrayUnion("greater_virginia")
const myArrayRemove = firebase.firestore.FieldValue.arrayRemove("east_coast")
// Firebase Modular SDK (v9+)
import { arrayUnion, arrayRemove } from "firebase/firestore";
const myArrayUnion = arrayUnion("greater_virginia")
const myArrayRemove = arrayRemove("east_coast")
返回的对象是 FieldValue 类的实现,相当于
const myArrayUnion = {
_method: "FieldValue.arrayUnion",
_elements: ["greater_virginia"]
}
const myArrayRemove = {
_method: "FieldValue.arrayRemove",
_elements: ["east_coast"]
}
然后,根据_method 的值,适当的字段转换指令为serialised using this code 并发送到Firestore API。因为操作是根据_method 的值切换的,所以在一条指令上只能执行arrayUnion 或arrayRemove 中的一个。
arrayUnion 和 arrayRemove
arrayUnion 和 arrayRemove 都可以接受多个参数,将每个参数添加到上面显示的内部 _elements 数组中。
因此要将"value1" 和"value2" 同时添加到指定字段,您可以使用:
// Firebase Namespaced SDK (v8 & older)
firebase.firestore.FieldValue.arrayUnion("value1", "value2");
// Firebase Modular SDK (v9+)
arrayUnion("value1", "value2");
要将一组项目同时添加到指定字段,您可以使用:
// Firebase Namespaced SDK (v8 & older)
const addedElements = ["greater_virginia", "east_coast", "central"];
firebase.firestore.FieldValue.arrayUnion.apply(null, addedElements);
// or
firebase.firestore.FieldValue.arrayUnion(...addedElements);
// Firebase Modular SDK (v9+)
const addedElements = ["greater_virginia", "east_coast", "central"];
arrayUnion.apply(null, addedElements);
// or
arrayUnion(...addedElements);
选项
因此,您有三个选项,按易用性和推荐排序:
选项 1:批量写入
使用batched write 将允许您将指令一起写入数据库,如果任一部分失败,则不会更改任何内容。
// Firebase Namespaced SDK (v8 & older)
// import firebase as appropriate
var db = firebase.firestore();
var washingtonRef = db.collection("cities").doc("DC");
var batch = db.batch();
batch.update(washingtonRef, {regions: firebase.firestore.FieldValue.arrayUnion("greater_virginia")});
batch.update(washingtonRef, {regions: firebase.firestore.FieldValue.arrayRemove("east_coast")});
batch.commit()
.then(() => console.log('Success!'))
.catch(err => console.error('Failed!', err));
// Firebase Modular SDK (v9+)
import { getFirestore, arrayRemove, arrayUnion, doc, writeBatch } from "firebase/firestore";
const db = getFirestore();
const washingtonRef = doc(db, "cities", "DC");
const batch = writeBatch(db);
batch.update(washingtonRef, {regions: arrayUnion("greater_virginia")});
batch.update(washingtonRef, {regions: arrayRemove("east_coast")});
batch.commit()
.then(() => console.log('Success!'))
.catch(err => console.error('Failed!', err));
注意:每个批量写入最多可包含 500 次写入。
在最初编写此答案时(此后已被删除),每个转换指令(arrayUnion、arrayRemove、increment 和 serverTimestamp)将计为 2 次操作到此限制,因为读取和写入都被计算在内,这意味着一个批次只能使用 250 个转换。
选项 2:反转数组
这种特殊的数据结构让人想起实时数据库,并且在引入 arrayUnion 和 arrayRemove 操作之前。
一般的前提是在将数组上传到数据库之前对其进行转换。
const originalArr = ["greater_virginia", "east_coast", "central"]
被反转并存储为
const keyedObjectOfArr = {
"greater_virginia": 1
"east_coast": 1,
"central": 1
}
上述结果可以使用:
const keyedObjectOfArr = originalArr.reduce((acc, v) => (acc[v] = 1, acc), {});
并恢复正常使用
const originalArr = Object.keys(keyedObjectOfArr);
然后,当您想应用联合/删除时,您将使用以下内容:
// Firebase Namespaced SDK (v8 & older)
// import firebase as appropriate
/**
* Creates (or adds to the given object) changes to be committed to the database.
*
* Note: Add operations will override remove operations if they exist in both arrays.
*
* @param fieldPath The path to the 'array' field to modify
* @param addedArray (optional) Elements to be added to the field
* @param removedArray (optional) Elements to be removed from the field
* @param changes (optional) A previous changes object for chaining
*/
function addArrayChanges(fieldPath, addedArray = [], removedArray = [], changes = {}) {
var fvDelete = firebase.firestore.FieldValue.delete();
removedElements.forEach(e => changes[fieldPath + '.' + e] = fvDelete);
addedElements.forEach(e => changes[fieldPath + '.' + e] = 1);
return changes;
}
var washingtonRef = db.collection("cities").doc("DC");
var addedElements = ["greater_virginia"];
var removedElements = ["east_coast"];
var changes = addArrayChanges("regions", addedElements, removedElements);
washingtonRef.update(changes)
.then(() => console.log('Success!'))
.catch(err => console.error('Failed!', err));
// Firebase Modular SDK (v9+)
import { getFirestore, arrayRemove, arrayUnion, deleteField, doc, updateDoc, writeBatch } from "firebase/firestore";
/**
* Creates (or adds to the given object) changes to be committed to the database.
*
* Note: Add operations will override remove operations if they exist in both arrays.
*
* @param fieldPath The path to the 'array' field to modify
* @param addedArray (optional) Elements to be added to the field
* @param removedArray (optional) Elements to be removed from the field
* @param changes (optional) A previous changes object for chaining
*/
function addArrayChanges(fieldPath, addedArray = [], removedArray = [], changes = {}) {
const fvDelete = deleteField();
removedElements.forEach(e => changes[fieldPath + '.' + e] = fvDelete);
addedElements.forEach(e => changes[fieldPath + '.' + e] = 1);
return changes;
}
const db = getFirestore();
const washingtonRef = doc(db, "cities", "DC");
const addedElements = ["greater_virginia"];
const removedElements = ["east_coast"];
const changes = addArrayChanges("regions", addedElements, removedElements);
updateDoc(washingtonRef, changes)
.then(() => console.log('Success!'))
.catch(err => console.error('Failed!', err));
选项 3:交易
虽然此方法是一种选择,但对于此用例来说是不切实际的,建议不要使用。 This blog post 涵盖 Firebase 实时数据库中的数组,但这些问题也适用于大规模单个文档的内容。
请求功能
Firestore API 中可能有空间同时支持添加和删除数组条目,因为它们在序列化层是分开的。
interface FieldTransform {
fieldPath?: string;
setToServerValue?: FieldTransformSetToServerValue;
appendMissingElements?: ArrayValue;
removeAllFromArray?: ArrayValue;
increment?: Value;
}
所以你也可以提交Feature Request 看看会发生什么。