如何更新一个“对象数组”与火还原?

我目前正在尝试 Firestore,而且我陷入了一个非常简单的问题: “更新一个数组(也就是子文档)”。

我的 DB 结构非常简单,例如:

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() }] })

没用,这些查询覆盖了我的数组。

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

192249 次浏览

编辑08/13/2018 : 现在云火还原中支持本地数组操作。


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

这里的代码是:

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()调用,但是如果文档不存在,而 update()调用将失败,则使用创建文档的 set()调用。

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

选项1-设置整个数组

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

选项2-使用子集合

您可以使 sharedWith成为主文档的一个子集 添加一个单独的项目看起来像这样:

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

当然,这会带来新的限制。您将无法查询 文件的基础上,他们与谁共享,你也不能够 在一次操作中获得文档和所有 sharedWith数据。

除了上面提到的答案,这个就可以了。 使用 Angular 5和 AngularFire2. 或者使用 firebase.firest ()代替 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
});
}

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

为了构建在 Sam Stern 的回答基础上,还有一个 第三个选择,它让我的工作变得更容易,这就是使用谷歌所说的 Map,它本质上是一个字典。

我认为字典更适合您描述的用例。我通常使用数组来处理没有经过太多更新的内容,所以它们或多或少是静态的。但是对于需要大量编写的内容,特别是需要为数据库中链接到其他内容的字段更新的值,事实证明字典更容易维护和使用。

因此,对于您的特定情况,DB 结构应该是这样的:

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 云函数中使用它时会导致错误。

将 John Doe 视为一个文档而不是一个集合

给它一个东西和东西的集合与他人分享

然后,您可以映射并查询 John Doe 在 SharedWithOther 集合中的共享内容。

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

火恢复现在有两个函数,允许您在不重写整个数组的情况下更新数组。

链接: 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 ()和 用于添加和删除元素。 arrayUnion ()添加元素 指向一个数组,但只包含尚未存在的元素 删除每个给定元素的所有实例。

对不起,派对迟到了,但火恢复在2018年8月解决了这个问题,所以如果你仍然在这里寻找,它是所有的问题解决了有关数组。

Https://firebase.googleblog.com/2018/08/better-arrays-in-cloud-firestore.html 官方博客文章

用于检查、删除和更新数组。希望对您有所帮助。

如果有人正在寻找 Java 的 firestorsdk 解决方案来添加数组字段中的项:

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()

下面是来自 Firest 文档的最新示例:

Firebase.fireste.FieldValue. < a href = “ https://firebase.google.com/docs/reference/js/firebase.firestore.FieldValue # static-ArrayUnion”rel = “ norefrer”> 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")
});
addToCart(docId: string, prodId: string): Promise<void> {
return this.baseAngularFirestore.collection('carts').doc(docId).update({
products:
firestore.FieldValue.arrayUnion({
productId: prodId,
qty: 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

如果您要更新基地文件中的数组。 你能做到的。

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


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

# 编辑(添加解释:) 假设你有一个数组,你想用它来更新你现有的文档字段。您可以使用 set(yourData, {merge: true} )传递 setOptions (set 函数中的第二个参数)和 {merge: true},以便合并更改而不是覆盖。官方文件是这么说的

配置 DocumentReference、 WriteBatch 和 Transaction 中 set ()调用行为的 options 对象。这些调用可以配置为执行粒度合并,而不是通过提供一个 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 },
);

希望这能有所帮助:)

虽然 firebase.firestore.FieldValue.arrayUnion()提供了数组更新的火灾恢复解决方案,但同时也需要使用 {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});


如果文档包含数组形式的嵌套对象,则。点符号可用于引用和更新嵌套字段。 Js 的例子:

const users = {
name: 'Tom',
surname: 'Smith',
favorites: {
sport: 'tennis',
color: 'red',
subject: 'math'
}
};


const update = await db.collection('users').doc('Tom').update({
'favorites.sport': 'snowboard'
});

或 Android sdk 的例子:

db.collection("users").document("Tom")
.update(
'favorites.sport': 'snowboard'
);

有一个简单的方法可以解决这个问题:

使用“ .”作为属性名称的路径:

返回文章页面

我知道这很老套,但是为了帮助新人解决这个问题

Firebase V9提供了一个使用 arrayUnion 和 arrayRemove 的解决方案

await updateDoc(documentRef, {
proprietary: arrayUnion( { sharedWith: [{ who: "third@test.com", when: new Date() }] }
});

看看这个更多的解释

db.collection("collection")
.doc("docId")
.update({arrayOfObj: fieldValue.arrayUnion({...item})})