如何使用 Firestore 更新 "array of objects"?
How to update an "array of objects" with Firestore?
我目前正在尝试使用 Firestore,但遇到了一些非常简单的问题:"updating an array (aka a subdocument)"。
我的数据库结构超级简单。例如:
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() }] })
None 有效。这些查询覆盖了我的数组。
答案可能很简单,但我找不到...
编辑 08/13/2018:Cloud Firestore 现在支持本机数组操作。请参阅下面的 。
目前无法更新 Cloud Firestore 中的单个数组元素(或 add/remove 单个元素)。
这里的代码:
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() })
当然这有新的限制。你将无法查询
文档基于与谁共享,您也无法
在一次操作中获取文档和所有 sharedWith
数据。
除了上面提到的答案。这样就可以了。
使用 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
});
}
您可以使用事务 (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);
});
在 的基础上,还有一个 第三个选项 这让我的事情变得更容易,它使用 Google 所谓的地图,这本质上是一本字典。
我认为字典对于您描述的用例来说要好得多。我通常将数组用于不会真正更新太多的东西,因此它们或多或少是静态的。但是对于经常写入的内容,特别是需要为链接到数据库中其他内容的字段更新的值,字典被证明更容易维护和使用。
因此对于您的具体情况,数据库结构如下所示:
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 视为文档而不是集合
给它一个 things 和 thingsSharedWithOthers 的集合
然后您可以在该并行 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 }
)
Firestore 现在有两个函数可以让您更新数组而无需重写整个数组。
Link: https://firebase.google.com/docs/firestore/manage-data/add-data, specifically https://firebase.google.com/docs/firestore/manage-data/add-data#update_elements_in_an_array
Update elements in an array
If your document contains an array field, you can use arrayUnion() and
arrayRemove() to add and remove elements. arrayUnion() adds elements
to an array but only elements not already present. arrayRemove()
removes all instances of each given element.
抱歉聚会迟到了,但 Firestore 早在 2018 年 8 月就解决了这个问题,所以如果您仍在此处寻找它,那么与数组相关的所有问题都已解决。
https://firebase.googleblog.com/2018/08/better-arrays-in-cloud-firestore.htmlOfficial blog post
array-contains、arrayRemove、arrayUnion 用于检查、删除和更新数组。希望对你有帮助。
如果有人正在寻找 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()
这是 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")
});
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
如果您想更新 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")});
#编辑(添加说明:))
假设您有一个数组要用来更新现有的 firestore 文档字段。您可以使用 set(yourData, {merge: true} )
传递 setOptions(set 函数中的第二个参数)和 {merge: true}
是必须的,以便合并更改而不是覆盖。这是官方文档对它的描述
An options object that configures the behavior of set() calls in DocumentReference, WriteBatch, and Transaction. These calls can be configured to perform granular merges instead of overwriting the target documents in their entirety by providing a SetOptions with 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()
提供了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});
如果文档包含数组形式的嵌套对象,.dot 表示法可用于引用和更新嵌套字段。
Node.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'
);
firestore 中有一个简单的 hack:
使用带“.”的路径作为 属性 姓名:
propertyname.arraysubname.${id}
:
我知道这真的很老了,但是为了帮助新手解决这个问题
firebase V9 提供了使用 arrayUnion 和 arrayRemove 的解决方案
await updateDoc(documentRef, {
proprietary: arrayUnion( { sharedWith: [{ who: "third@test.com", when: new Date() }] }
});
我目前正在尝试使用 Firestore,但遇到了一些非常简单的问题:"updating an array (aka a subdocument)"。
我的数据库结构超级简单。例如:
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() }] })
None 有效。这些查询覆盖了我的数组。
答案可能很简单,但我找不到...
编辑 08/13/2018:Cloud Firestore 现在支持本机数组操作。请参阅下面的
目前无法更新 Cloud Firestore 中的单个数组元素(或 add/remove 单个元素)。
这里的代码:
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() })
当然这有新的限制。你将无法查询
文档基于与谁共享,您也无法
在一次操作中获取文档和所有 sharedWith
数据。
除了上面提到的答案。这样就可以了。 使用 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
});
}
您可以使用事务 (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);
});
在
我认为字典对于您描述的用例来说要好得多。我通常将数组用于不会真正更新太多的东西,因此它们或多或少是静态的。但是对于经常写入的内容,特别是需要为链接到数据库中其他内容的字段更新的值,字典被证明更容易维护和使用。
因此对于您的具体情况,数据库结构如下所示:
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 视为文档而不是集合
给它一个 things 和 thingsSharedWithOthers 的集合
然后您可以在该并行 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 }
)
Firestore 现在有两个函数可以让您更新数组而无需重写整个数组。
Link: https://firebase.google.com/docs/firestore/manage-data/add-data, specifically https://firebase.google.com/docs/firestore/manage-data/add-data#update_elements_in_an_array
Update elements in an array
If your document contains an array field, you can use arrayUnion() and arrayRemove() to add and remove elements. arrayUnion() adds elements to an array but only elements not already present. arrayRemove() removes all instances of each given element.
抱歉聚会迟到了,但 Firestore 早在 2018 年 8 月就解决了这个问题,所以如果您仍在此处寻找它,那么与数组相关的所有问题都已解决。
https://firebase.googleblog.com/2018/08/better-arrays-in-cloud-firestore.htmlOfficial blog post
array-contains、arrayRemove、arrayUnion 用于检查、删除和更新数组。希望对你有帮助。
如果有人正在寻找 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()
这是 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")
});
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
如果您想更新 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")});
#编辑(添加说明:))
假设您有一个数组要用来更新现有的 firestore 文档字段。您可以使用 set(yourData, {merge: true} )
传递 setOptions(set 函数中的第二个参数)和 {merge: true}
是必须的,以便合并更改而不是覆盖。这是官方文档对它的描述
An options object that configures the behavior of set() calls in DocumentReference, WriteBatch, and Transaction. These calls can be configured to perform granular merges instead of overwriting the target documents in their entirety by providing a SetOptions with 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()
提供了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});
如果文档包含数组形式的嵌套对象,.dot 表示法可用于引用和更新嵌套字段。 Node.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'
);
firestore 中有一个简单的 hack:
使用带“.”的路径作为 属性 姓名:
propertyname.arraysubname.${id}
:
我知道这真的很老了,但是为了帮助新手解决这个问题
firebase V9 提供了使用 arrayUnion 和 arrayRemove 的解决方案
await updateDoc(documentRef, {
proprietary: arrayUnion( { sharedWith: [{ who: "third@test.com", when: new Date() }] }
});