2017-10-15 87 views
6

我目前正在尝试Firestore,而且我被困在一件非常简单的事情上:“更新数组(又名一个子文档)”。如何用Firestore更新“对象数组”?

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

proprietary: "John Doe" 
sharedWith: 
    [ 
    {who: "[email protected]", when:timestamp} 
    {who: "[email protected]", when:timestamp} 
    ] 

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

我已经试过:

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

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

无工作。 这些查询覆盖我的数组。

答案可能很简单,但我could'nt找到它...

感谢

回答

8

目前没有办法更新单个数组元素(或添加/删除单个元素)云Firestore。

这这里代码:

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

这是说给文件设置为proprietary/docID这样sharedWith = [{ who: "[email protected]", when: new Date() }但不会影响现有的文档属性。这与您提供的update()呼叫非常相似,但是set()呼叫会在呼叫失败时创建文档(如果不存在)。

所以你有两个选择来实现你想要的。

选项1 - 设置整个阵列

呼叫set()与阵列中的全部内容,这将需要首先读取来自DB的当前数据。如果你关心并发更新,你可以在交易中完成所有这些。

选项2 - 使用子集合

你可以做sharedWith主文档的一个子集合。然后 添加单个项目应该是这样的:

firebase.firestore() 
    .collection('proprietary') 
    .doc(docID) 
    .collection('sharedWith') 
    .add({ who: "[email protected]", when: new Date() }) 

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

+0

这太令人沮丧了...但是,谢谢让我知道我不会发疯。 – ItJustWerks

+8

这是一个很大的缺点,谷歌必须尽快解决它。 –

1

除了上面提到的答案。这将做到这一点。 使用Angular 5和AngularFire2。或者使用firebase.firestore()代替这个。AFS

// say you have have the following object and 
    // database structure as you mentioned in your post 
    data = { who: "[email protected]", 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 
    }); 
} 
2

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

要建立在Sam Stern's answer,也有第三选项这让我更容易,那就是使用谷歌称之为Map的地图,这本质上是一本字典。

我认为字典对于您描述的用例来说要好得多。我通常使用数组来处理并非真正更新太多的东西,所以它们或多或少都是静态的。但对于写得很多的东西,特别是需要更新与数据库中其他内容相关的字段的值,字典证明更易于维护和使用。

因此,对于您的特定情况下,数据库的结构是这样的:

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

这将允许你做到以下几点:

var whoEmail = '[email protected]'; 

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云功能中使用时会导致错误。