如何使用Firestore更新“对象数组”?


104

我目前正在尝试使用Firestore,但遇到了非常简单的问题:“更新数组(又称子文档)”。

我的数据库结构非常简单。例如:

proprietary: "John Doe",
sharedWith:
  [
    {who: "first@test.com", when:timestamp},
    {who: "another@test.com", when:timestamp},
  ],

我正在尝试(没有成功)将新记录推入shareWith对象数组。

我试过了:

// With SET
firebase.firestore()
.collection('proprietary')
.doc(docID)
.set(
  { sharedWith: [{ who: "third@test.com", when: new Date() }] },
  { merge: true }
)

// With UPDATE
firebase.firestore()
.collection('proprietary')
.doc(docID)
.update({ sharedWith: [{ who: "third@test.com", when: new Date() }] })

没有办法。这些查询将覆盖我的数组。

答案可能很简单,但我找不到它...

Answers:


71

编辑2018年8月13日:Cloud Firestore现在支持本机阵列操作。请参阅下面的道格答案


当前无法在Cloud Firestore中更新单个数组元素(或添加/删除单个元素)。

这段代码在这里:

firebase.firestore()
.collection('proprietary')
.doc(docID)
.set(
  { sharedWith: [{ who: "third@test.com", when: new Date() }] },
  { merge: true }
)

这是说,在设置文件proprietary/docID,使得sharedWith = [{ who: "third@test.com", when: new Date() }但是不会影响现有的文档属性。这与update()您提供的set()呼叫非常相似,但是如果在update()呼叫将会失败。

因此,您有两种选择来实现所需的目标。

选项1-设置整个数组

调用set()数组的全部内容,这将需要首先从DB中读取当前数据。如果您担心并发更新,则可以在事务中完成所有这些操作。

选项2-使用子集合

您可以sharedWith对主文档进行子集合。然后添加单个项目将如下所示:

firebase.firestore()
  .collection('proprietary')
  .doc(docID)
  .collection('sharedWith')
  .add({ who: "third@test.com", when: new Date() })

当然,这带来了新的限制。您将无法基于共享对象来查询文档,也无法sharedWith通过单个操作获取文档和所有数据。


9
这真令人沮丧...但是感谢您让我知道我不会发疯。
ItJustWerks

52
这是一个很大的缺点,Google必须尽快修复它。
萨吉斯·曼萨拉斯

3
@DougGalante的回答表明此问题已得到解决。使用arrayUnion方法。
quicklikerabbit

152

Firestore现在具有两个功能,可让您在不重写整个内容的情况下更新阵列。

链接:https : //firebase.google.com/docs/firestore/manage-data/add-data,特别是https://firebase.google.com/docs/firestore/manage-data/add-data#update_elements_in_an_array

更新数组中的元素

如果您的文档包含一个数组字段,则可以使用arrayUnion()和arrayRemove()添加和删除元素。arrayUnion()将元素添加到数组,但仅不存在的元素。arrayRemove()删除每个给定元素的所有实例。


57
有什么方法可以更新数组中的特定索引吗?
阿图尔·卡瓦略

1
如何与“ react-native-firebase”一起使用此数组更新功能?(我在react-native-
firebase的

4
@ArturCarvalho不,其原因在此视频youtube.com/…中
亚当(Adam)

3
对于需要在客户端上执行此操作的用户,请使用“ import *作为'firebase / app'的firebase;” 然后是“ firebase.firestore.FieldValue.arrayUnion(NEW_ELEMENT)”
michelepatrassi

1
如果有一种方法可以更新具有特定ID的数组中的项目,那就太好了。像arrayUnion一样,但带有合并:true。目前,它需要2次操作才能删除数组项,然后再次将其与新数据一起添加。
MadMac

15

您可以使用事务(https://firebase.google.com/docs/firestore/manage-data/transactions)获取数组,将其压入数组,然后更新文档:

    const booking = { some: "data" };
    const userRef = this.db.collection("users").doc(userId);

    this.db.runTransaction(transaction => {
        // This code may get re-run multiple times if there are conflicts.
        return transaction.get(userRef).then(doc => {
            if (!doc.data().bookings) {
                transaction.set({
                    bookings: [booking]
                });
            } else {
                const bookings = doc.data().bookings;
                bookings.push(booking);
                transaction.update(userRef, { bookings: bookings });
            }
        });
    }).then(function () {
        console.log("Transaction successfully committed!");
    }).catch(function (error) {
        console.log("Transaction failed: ", error);
    });

1
在if语句中,应将其更改为该语句,因为您缺少显示的documentReference添加userRef,如下所示: transaction.set(userRef, { bookings: [booking] });
Ilir Hushi 19'Aug

8

这是Firestore文档中的最新示例:

firebase.firestore.FieldValue。ArrayUnion

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")
});

@nifCody,这确实将一个新的字符串元素“ greater_virginia”添加到现有数组“ regions”中。我已经成功测试了它,并且绝对不会添加“ object”。它与所述问题保持同步:“推送新记录”。
Veeresh Devireddy


3

要以山姆·斯特恩的答案为基础,还有第三个选择,它使我更轻松,并且使用了Google所谓的Map,本质上是字典。

我认为字典对于您要描述的用例要好得多。我通常将数组用于未真正更新太多的内容,因此它们或多或少是静态的。但是对于很多东西来说,尤其是那些需要链接到数据库中其他字段的字段需要更新的值,字典被证明更易于维护和使用。

因此,对于您的特定情况,数据库结构如下所示:

proprietary: "John Doe"
sharedWith:{
  whoEmail1: {when: timestamp},
  whoEmail2: {when: timestamp}
}

这将允许您执行以下操作:

var whoEmail = 'first@test.com';

var sharedObject = {};
sharedObject['sharedWith.' + whoEmail + '.when'] = new Date();
sharedObject['merge'] = true;

firebase.firestore()
.collection('proprietary')
.doc(docID)
.update(sharedObject);

将对象定义为变量的原因是,'sharedWith.' + whoEmail + '.when'至少在Node.js云函数中直接使用set方法时,将导致错误。


2

除了上面提到的答案。这样就可以了。 使用Angular 5和AngularFire2。或使用firebase.firestore()代替this.afs

  // say you have have the following object and 
  // database structure as you mentioned in your post
  data = { who: "third@test.com", when: new Date() };

  ...othercode


  addSharedWith(data) {

    const postDocRef = this.afs.collection('posts').doc('docID');

    postDocRef.subscribe( post => {

      // Grab the existing sharedWith Array
      // If post.sharedWith doesn`t exsit initiated with empty array
      const foo = { 'sharedWith' : post.sharedWith || []};

      // Grab the existing sharedWith Array
      foo['sharedWith'].push(data);

      // pass updated to fireStore
      postsDocRef.update(foo);
      // using .set() will overwrite everything
      // .update will only update existing values, 
      // so we initiated sharedWith with empty array
    });
 }  

1

认为John Doe是文档而不是收藏

给它一个事物和事物的集合

然后,您可以在并行的ThingsSharedWithOthers集合中映射和查询John Doe的共享项目。

proprietary: "John Doe"(a document)

things(collection of John's things documents)

thingsSharedWithOthers(collection of John's things being shared with others):
[thingId]:
    {who: "first@test.com", when:timestamp}
    {who: "another@test.com", when:timestamp}

then set thingsSharedWithOthers

firebase.firestore()
.collection('thingsSharedWithOthers')
.set(
{ [thingId]:{ who: "third@test.com", when: new Date() } },
{ merge: true }
)

0

如果有人在寻找Java firestore sdk解决方案以在数组字段中添加项目:

List<String> list = java.util.Arrays.asList("A", "B");
Object[] fieldsToUpdate = list.toArray();
DocumentReference docRef = getCollection().document("docId");
docRef.update(fieldName, FieldValue.arrayUnion(fieldsToUpdate));

要从阵列用户中删除项目: FieldValue.arrayRemove()


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.