如何使用 Firebase 的 Firestore 在单个查询中执行 arrayUnion 和 arrayRemove?

rap*_*dko 14 javascript firebase google-cloud-firestore

我正在使用 firestore 更新我的对象上的数组。我在文档中发现我可以执行数组联合和删除,这很棒,这是文档中给出的示例:

var washingtonRef = db.collection("cities").doc("DC");

// Atomically add a new region to the "regions" array field.
washingtonRef.update({
    regions: firebase.firestore.FieldValue.arrayUnion("greater_virginia")
});

// Atomically remove a region from the "regions" array field.
washingtonRef.update({
    regions: firebase.firestore.FieldValue.arrayRemove("east_coast")
});
Run Code Online (Sandbox Code Playgroud)

如您所见,对数据库有 2 个单独的查询,这使我的应用程序在数据库调用方面的成本增加了一倍,因此我想将它们组合在一个查询中,如下所示:

var washingtonRef = db.collection("cities").doc("DC");

washingtonRef.update({
    regions: firebase.firestore.FieldValue.arrayUnion("greater_virginia"),
    regions: firebase.firestore.FieldValue.arrayRemove("east_coast")
});
Run Code Online (Sandbox Code Playgroud)

不幸的是,这不起作用,只有最后一个命令被执行。

有没有办法让它发挥作用?

sam*_*man 22

介绍

FieldValue 对象

您的组合指令不起作用的原因(除了语法错误)是因为 FieldValue 对象的定义方式。

假设您使用了以下对象:

const arrayUnion = firebase.firestore.FieldValue.arrayUnion("greater_virginia")
const arrayRemove = firebase.firestore.FieldValue.arrayRemove("east_coast")
Run Code Online (Sandbox Code Playgroud)

返回的对象是FieldValue类的实现,相当于

const arrayUnion = {
  _method: "FieldValue.arrayUnion",
  _elements: ["greater_virginia"]
}

const arrayRemove = {
  _method: "FieldValue.arrayRemove",
  _elements: ["east_coast"]
}
Run Code Online (Sandbox Code Playgroud)

然后,根据 的值_method使用此代码序列化适当的字段转换指令并将其发送到 Firestore API。由于操作是根据 的值切换的,因此在单个指令上只能发生或_method之一。arrayUnionarrayRemove

arrayUnionarrayRemove

二者arrayUnionarrayRemove可以采取多个参数,将每个所述内部_elements上面显示阵列。

因此,要同时将"value1"和添加"value2"到指定字段,您将使用:

firebase.firestore.FieldValue.arrayUnion("value1", "value2");
Run Code Online (Sandbox Code Playgroud)

要将一组项目同时添加到指定字段,您可以使用:

var addedElements = ["greater_virginia", "east_coast", "central"];

firebase.firestore.FieldValue.arrayUnion.apply(null, addedElements);
// or
firebase.firestore.FieldValue.arrayUnion(...addedElements);
Run Code Online (Sandbox Code Playgroud)

选项

因此,您只剩下三个选项,按易用性和推荐排序:

选项 1:批量写入

使用批量写入将允许您将指令一起写入数据库,如果任一部分失败,则不会有任何更改。

firebase.firestore.FieldValue.arrayUnion("value1", "value2");
Run Code Online (Sandbox Code Playgroud)

注意:每个批量写入最多可包含 500 个操作。但是,每个转换指令(arrayUnionarrayRemoveincrementserverTimestamp)在此限制中计为 2 个操作。这意味着您最多只能执行 250 个单独的转换作为批处理的一部分。

选项 2:反转数组

这种特殊的数据结构让人联想到实时数据库以及引入arrayUnionarrayRemove操作之前。

一般前提是在将数组上传到数据库之前对其进行转换。

var addedElements = ["greater_virginia", "east_coast", "central"];

firebase.firestore.FieldValue.arrayUnion.apply(null, addedElements);
// or
firebase.firestore.FieldValue.arrayUnion(...addedElements);
Run Code Online (Sandbox Code Playgroud)

被反转并存储为

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));
Run Code Online (Sandbox Code Playgroud)

可以使用以下方法实现上述结果:

const originalArr = ["greater_virginia", "east_coast", "central"]
Run Code Online (Sandbox Code Playgroud)

并恢复正常使用

const keyedObjectOfArr = {
  "greater_virginia": 1
  "east_coast": 1,
  "central": 1
}
Run Code Online (Sandbox Code Playgroud)

然后,当您想应用联合/删除时,您将使用以下内容:

const keyedObjectOfArr = originalArr.reduce((acc, v) => (acc[v] = 1, acc), {});
Run Code Online (Sandbox Code Playgroud)

选项 3:交易

虽然此方法是一种选择,但对于此用例来说是不切实际的,因此建议不要使用。这篇博文涵盖了 Firebase 实时数据库中的数组,但这些问题在涉及大规模单个文档的内容时也适用。

请求功能

Firestore API 中可能有空间支持同时添加和删除数组条目,因为它们在序列化层分开。

interface FieldTransform {
  fieldPath?: string;
  setToServerValue?: FieldTransformSetToServerValue;
  appendMissingElements?: ArrayValue;
  removeAllFromArray?: ArrayValue;
  increment?: Value;
}
Run Code Online (Sandbox Code Playgroud)

所以你也可以提交一个功能请求,看看会发生什么。