如何更新"对象数组"与Firestore?

时间:2017-10-15 17:00:36

标签: javascript arrays object firebase google-cloud-firestore

我目前正在尝试使用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() }] })

无效。 这些查询会覆盖我的数组。

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

由于

15 个答案:

答案 0 :(得分:64)

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()   删除每个给定元素的所有实例。

答案 1 :(得分:40)

编辑08/13/2018 :现在支持Cloud Firestore中的本机阵列操作。请参阅下面的Doug's answer

目前无法在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(),这将需要首先从数据库中读取当前数据。如果您担心并发更新,则可以在事务中完成所有这些操作。

选项2 - 使用子收藏

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

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

当然这带来了新的限制。你将无法查询 文件基于他们与谁分享,你也不能 在一次操作中获取doc和所有sharedWith数据。

答案 2 :(得分:16)

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

答案 3 :(得分:10)

您可以使用事务(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);
    });

答案 4 :(得分:3)

抱歉,聚会晚了,但是Firestore早在2018年8月就解决了它,因此,如果您仍然在这里寻找,那么所有有关阵列的问题都可以解决。

https://firebase.googleblog.com/2018/08/better-arrays-in-cloud-firestore.html Official blog post

array-contains,arrayRemove,arrayUnion用于检查,删除和更新数组。希望对您有所帮助。

答案 5 :(得分:2)

要在Sam Stern's answer上构建,还有一个第三个选项,这让我更容易,而且使用谷歌称之为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);

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

答案 6 :(得分:1)

除了上面提到的答案。这样做。 使用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
    });
 }  

答案 7 :(得分:1)

这就是我开始工作的方式。希望它能帮助你们所有人,因为我认为这是一种更好的解决方案。 在这里,我想将任务状态从open(close = false)更改为close(close = true),同时保持对象中的其他内容相同。

closeTask(arrayIndex) {
        let tasks = this.lead.activityTasks;

        const updatedTask = {
          name: tasks[arrayIndex].name,
          start: tasks[arrayIndex].start,
          dueDate:tasks[arrayIndex].dueDate,
          close: true, // This is what I am changing.
        };
        tasks[arrayIndex] = updatedTask;

        const data = {
          activityTasks: tasks
        };

        this.leadService.updateLeadData(data, this.lead.id);
    }

这是实际更新它的服务<​​/ p>

 public updateLeadData(updateData, leadId) {
    const leadRef: AngularFirestoreDocument<LeadModel> = this.afs.doc(
      `leads/${leadId}`);

return leadRef.update(updateData);
}

答案 8 :(得分:1)

考虑John Doe是文档而不是收藏

给它一个东西的集合,SharedWithOthers

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

test group

答案 9 :(得分:1)

addToCart(docId: string, prodId: string): Promise<void> {
    return this.baseAngularFirestore.collection('carts').doc(docId).update({
        products:
        firestore.FieldValue.arrayUnion({
            productId: prodId,
            qty: 1
        }),
    });
}

答案 10 :(得分:1)

我们可以使用arrayUnion({})方法来实现这一目标。

尝试一下:

collectionRef.doc(ID).update({
    sharedWith: admin.firestore.FieldValue.arrayUnion({
       who: "first@test.com",
       when: new Date()
    })
});

文档可在此处找到:https://firebase.google.com/docs/firestore/manage-data/add-data#update_elements_in_an_array

答案 11 :(得分: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()

答案 12 :(得分:0)

如果要更新Firebase文档中的数组。 你可以做到的。

    var documentRef = db.collection("Your collection name").doc("Your doc name")

    documentRef.update({
yourArrayName: firebase.firestore.FieldValue.arrayUnion("The Value you want to enter")});

答案 13 :(得分:0)

#Edit(添加说明:)) 说您有一个数组,您想使用它更新现有的Firestore文档字段。您可以将npm run build 与必须将set(yourData, {merge: true} )一起使用的setOptions(集合函数中的第二个参数)与{merge: true}一起使用,以便合并更改而不是覆盖更改。这是官方文件所说的

一个选项对象,用于配置DocumentReference,WriteBatch和Transaction中set()调用的行为。通过为SetOptions提供merge:true,可以将这些调用配置为执行粒度合并,而不是完全覆盖目标文档。

您可以使用此

const yourNewArray = [{who: "first@test.com", when:timestamp}
{who: "another@test.com", when:timestamp}]    


collectionRef.doc(docId).set(
  {
    proprietary: "jhon",
    sharedWith: firebase.firestore.FieldValue.arrayUnion(...yourNewArray),
  },
  { merge: true },
);

希望这会有所帮助:)

答案 14 :(得分:0)

虽然firebase.firestore.FieldValue.arrayUnion()提供了firestore中数组更新的解决方案,但同时需要使用{merge:true}。如果您不使用 {merge:true},它将在使用新值更新时删除文档中的所有其他字段。以下是使用 .set() 方法更新数组而不丢失参考文档中的数据的工作代码:


const docRef = firebase.firestore().collection("your_collection_name").doc("your_doc_id");

docRef.set({yourArrayField: firebase.firestore.FieldValue.arrayUnion("value_to_add")}, {merge:true});