Javascript 如何使用 Firestore 更新“对象数组”?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/46757614/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to update an "array of objects" with Firestore?
提问by nerotulip
I'm currently trying Firestore, and I'm stuck at something very simple: "updating an array (aka a subdocument)".
我目前正在尝试 Firestore,但我遇到了一些非常简单的问题:“更新数组(又名子文档)”。
My DB structure is super simple. For example:
我的数据库结构超级简单。例如:
proprietary: "John Doe",
sharedWith:
[
{who: "[email protected]", when:timestamp},
{who: "[email protected]", when:timestamp},
],
I'm trying (without success) to push new records into shareWitharray of objects.
我正在尝试(但没有成功)将新记录推送到shareWith对象数组中。
I've tried:
我试过了:
// 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() }] })
None works. These queries overwrite my array.
没有工作。这些查询会覆盖我的数组。
The answer might be simple, but I could'nt find it...
答案可能很简单,但我找不到...
采纳答案by Sam Stern
Edit 08/13/2018: There is now support for native array operations in Cloud Firestore. See Doug's answerbelow.
2018 年 8 月 13 日编辑:现在支持 Cloud Firestore 中的原生数组操作。请参阅下面道格的回答。
There is currently no way to update a single array element (or add/remove a single element) in Cloud Firestore.
目前无法在 Cloud Firestore 中更新单个数组元素(或添加/删除单个元素)。
This code here:
这段代码在这里:
firebase.firestore()
.collection('proprietary')
.doc(docID)
.set(
{ sharedWith: [{ who: "[email protected]", when: new Date() }] },
{ merge: true }
)
This says to set the document at proprietary/docIDsuch that sharedWith = [{ who: "[email protected]", when: new Date() }but to not affect any existing document properties. It's very similar to the update()call you provided however the set()call with create the document if it does not exist while the update()call will fail.
这是说,在设置文件proprietary/docID,使得sharedWith = [{ who: "[email protected]", when: new Date() }但是不会影响现有的文档属性。它与update()您提供的set()调用非常相似,但是如果文档不存在,则update()调用会创建文档,而调用将失败。
So you have two options to achieve what you want.
所以你有两种选择来实现你想要的。
Option 1 - Set the whole array
选项 1 - 设置整个数组
Call set()with the entire contents of the array, which will require reading the current data from the DB first. If you're concerned about concurrent updates you can do all of this in a transaction.
调用set()数组的全部内容,这将需要首先从数据库读取当前数据。如果您担心并发更新,您可以在一个事务中完成所有这些。
Option 2 - Use a subcollection
选项 2 - 使用子集合
You could make sharedWitha subcollection of the main document. Then
adding a single item would look like this:
您可以创建sharedWith主文档的子集合。然后添加单个项目如下所示:
firebase.firestore()
.collection('proprietary')
.doc(docID)
.collection('sharedWith')
.add({ who: "[email protected]", when: new Date() })
Of course this comes with new limitations. You would not be able to query
documents based on who they are shared with, nor would you be able to
get the doc and all of the sharedWithdata in a single operation.
当然,这带来了新的限制。您将无法根据共享对象来查询文档,也无法sharedWith通过单个操作获取文档和所有数据。
回答by Doug Galante
Firestore now has two functions that allow you to update an array without re-writing the entire thing.
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
链接: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
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.
更新数组中的元素
如果您的文档包含数组字段,您可以使用 arrayUnion() 和 arrayRemove() 添加和删除元素。arrayUnion() 向数组添加元素,但只添加不存在的元素。arrayRemove() 删除每个给定元素的所有实例。
回答by Gabriel McCallin
You can use a transaction (https://firebase.google.com/docs/firestore/manage-data/transactions) to get the array, push onto it and then update the document:
您可以使用事务(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);
});
回答by Guru
Sorry Late to party but Firestore solved it way back in aug 2018 so If you still looking for that here it is all issues solved with regards to arrays.
抱歉,聚会迟到了,但 Firestore 早在 2018 年 8 月就解决了,所以如果您仍在寻找它,那么所有与数组有关的问题都已解决。
https://firebase.googleblog.com/2018/08/better-arrays-in-cloud-firestore.htmlOfficial blog post
https://firebase.googleblog.com/2018/08/better-arrays-in-cloud-firestore.html官方博文
array-contains, arrayRemove, arrayUnion for checking, removing and updating arrays. Hope it helps.
array-contains、arrayRemove、arrayUnion 用于检查、删除和更新数组。希望能帮助到你。
回答by Veeresh Devireddy
Here is the latest example from the Firestore documentation:
这是 Firestore 文档中的最新示例:
firebase.firestore.FieldValue.ArrayUnion
firebase.firestore.FieldValue。阵列联合
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")
});
回答by Horea
To build on Sam Stern's answer, there is also a 3rd optionwhich made things easier for me and that is using what Google call a Map, which is essentially a dictionary.
以Sam Stern 的回答为基础,还有第三个选项让我更轻松,那就是使用 Google 所谓的地图,它本质上是一本字典。
I think a dictionary is far better for the use case you're describing. I usually use arrays for stuff that isn't really updated too much, so they are more or less static. But for stuff that gets written a lot, specifically values that need to be updated for fields that are linked to something else in the database, dictionaries prove to be much easier to maintain and work with.
我认为字典对于您描述的用例要好得多。我通常将数组用于没有真正更新太多的东西,所以它们或多或少是静态的。但是对于需要大量编写的内容,特别是需要为链接到数据库中其他内容的字段更新的值,事实证明字典更易于维护和使用。
So for your specific case, the DB structure would look like this:
因此,对于您的特定情况,数据库结构如下所示:
proprietary: "John Doe"
sharedWith:{
whoEmail1: {when: timestamp},
whoEmail2: {when: timestamp}
}
This will allow you to do the following:
这将允许您执行以下操作:
var whoEmail = '[email protected]';
var sharedObject = {};
sharedObject['sharedWith.' + whoEmail + '.when'] = new Date();
sharedObject['merge'] = true;
firebase.firestore()
.collection('proprietary')
.doc(docID)
.update(sharedObject);
The reason for defining the object as a variable is that using 'sharedWith.' + whoEmail + '.when'directly in the set method will result in an error, at least when using it in a Node.js cloud function.
将对象定义为变量的原因是,'sharedWith.' + whoEmail + '.when'直接在 set 方法中使用会导致错误,至少在 Node.js 云函数中使用时如此。
回答by Jassi
Other than the answers mentioned above. This will do it. Using Angular 5 and AngularFire2.or use firebase.firestore() instead of this.afs
除了上面提到的答案。这将做到。 使用 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: "[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
});
}
回答by Richard Taylor-Kenny
Consider John Doe a document rather than a collection
将 John Doe 视为文档而不是集合
Give it a collection of things and thingsSharedWithOthers
给它一个东西和东西的集合SharedWithOthers
Then you can map and query John Doe's shared things in that parallel thingsSharedWithOthers collection.
然后,您可以在并行的 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: "[email protected]", when:timestamp}
{who: "[email protected]", when:timestamp}
then set thingsSharedWithOthers
firebase.firestore()
.collection('thingsSharedWithOthers')
.set(
{ [thingId]:{ who: "[email protected]", when: new Date() } },
{ merge: true }
)
回答by A_01
If anybody is looking for Java firestore sdk solution to add items in array field:
如果有人正在寻找 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));
To delete items from array user: FieldValue.arrayRemove()
从数组用户中删除项目: FieldValue.arrayRemove()
回答by Leandrit Ferizi
This is how I got it to work. Hope it helps you all since I see it as a way better solution. Here I want to change the task status from open(close=false) to close (close=true), while keeping everything else the same in the object.
这就是我让它工作的方式。希望它对大家有所帮助,因为我认为它是一种更好的解决方案。在这里,我想将任务状态从 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);
}
and here is the service which actually updates it
这是实际更新它的服务
public updateLeadData(updateData, leadId) {
const leadRef: AngularFirestoreDocument<LeadModel> = this.afs.doc(
`leads/${leadId}`);
return leadRef.update(updateData);
}

