จะอัปเดต "อาร์เรย์ของวัตถุ" ด้วย Firestore ได้อย่างไร


121

ตอนนี้ฉันกำลังลองใช้ 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() }] })

ไม่มีผล ข้อความค้นหาเหล่านี้เขียนทับอาร์เรย์ของฉัน

คำตอบอาจจะง่าย แต่หาไม่เจอ ...


เฮ้คุณคิดออกไหม ฉันยังไม่สามารถหาคำตอบได้
Yash Jain

คำตอบ:


80

แก้ไข 08/13/2018 : ขณะนี้มีการรองรับการทำงานของอาร์เรย์เนทีฟใน Cloud Firestore ดูคำตอบของ Dougด้านล่าง


ขณะนี้ไม่มีวิธีอัปเดตองค์ประกอบอาร์เรย์เดียว (หรือเพิ่ม / ลบองค์ประกอบเดียว) ใน 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() })

แน่นอนว่าสิ่งนี้มาพร้อมกับข้อ จำกัด ใหม่ ๆ คุณจะไม่สามารถสืบค้นเอกสารโดยอิงจากผู้ที่แชร์ด้วยและคุณจะไม่สามารถรับเอกสารและข้อมูลทั้งหมดได้sharedWithในการดำเนินการเดียว


9
มันน่าหงุดหงิดมาก ... แต่ขอบคุณที่ทำให้ฉันรู้ว่าฉันจะไม่บ้า
ItJustWerks

53
นี่เป็นข้อเสียเปรียบอย่างมาก Google ต้องแก้ไขโดยเร็ว
สัจจะมัน ธ รัตน์

3
คำตอบของ @ DougGalante ระบุว่าสิ่งนี้ได้รับการแก้ไขแล้ว ใช้arrayUnionวิธีการ
quicklikerabbit

168

ตอนนี้ 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 () ลบอินสแตนซ์ทั้งหมดของแต่ละองค์ประกอบที่กำหนด


66
มีวิธีใดบ้างในการอัปเดตดัชนีเฉพาะจากอาร์เรย์
Artur Carvalho

1
จะใช้คุณลักษณะการอัปเดตอาร์เรย์นี้กับ "react-native-firebase" ได้อย่างไร (ฉันไม่พบสิ่งนี้ในเอกสารอย่างเป็นทางการของ react-native-
firebase

6
@ArturCarvalho ไม่ครับเหตุผลที่อธิบายในวิดีโอนี้youtube.com/…
อดัม

3
สำหรับผู้ที่ต้องการทำในไคลเอนต์ให้ใช้ "import * as firebase จาก" firebase / app ";" แล้ว "firebase.firestore.FieldValue.arrayUnion (NEW_ELEMENT)"
michelepatrassi

2
จะดีมากหากมีวิธีอัปเดตรายการในอาร์เรย์ด้วยรหัสเฉพาะ เหมือน arrayUnion แต่มีการผสาน: จริง ในขณะนี้ต้องใช้การดำเนินการ 2 รายการเพื่อลบรายการอาร์เรย์แล้วเพิ่มอีกครั้งด้วยข้อมูลใหม่
MadMac

17

คุณสามารถใช้ธุรกรรม ( 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);
    });

1
หากคำสั่งคุณควรเปลี่ยนเป็นสิ่งนี้เพราะคุณไม่มีส่วนdocumentReferenceเสริมuserRefดังที่แสดง: transaction.set(userRef, { bookings: [booking] });
Ilir Hushi

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

1
@nifCody สิ่งนี้จะเพิ่มองค์ประกอบสตริงใหม่ "greater_virginia" ให้กับ "พื้นที่" อาร์เรย์ที่มีอยู่ ฉันทดสอบสำเร็จแล้วและไม่ได้เพิ่ม "วัตถุ" อย่างแน่นอน มันตรงกับคำถามที่ระบุไว้: "push new records"
Veeresh Devireddy

7

ขออภัยสายไปปาร์ตี้ แต่ Firestore แก้ไขได้ในเดือนสิงหาคม 2018 ดังนั้นหากคุณยังคงมองหาที่นี่ปัญหาทั้งหมดได้รับการแก้ไขเกี่ยวกับอาร์เรย์

https://firebase.googleblog.com/2018/08/better-arrays-in-cloud-firestore.html บล็อกโพสต์อย่างเป็นทางการ

array-contains, arrayRemove, arrayUnion สำหรับตรวจสอบลบและอัปเดตอาร์เรย์ หวังว่าจะช่วยได้


4

เพื่อสร้างคำตอบของ Sam Sternนอกจากนี้ยังมีตัวเลือกที่ 3ซึ่งทำให้สิ่งต่างๆง่ายขึ้นสำหรับฉันและนั่นคือการใช้สิ่งที่ Google เรียกว่าแผนที่ซึ่งโดยพื้นฐานแล้วคือพจนานุกรม

ฉันคิดว่าพจนานุกรมดีกว่ามากสำหรับกรณีการใช้งานที่คุณอธิบาย ฉันมักจะใช้อาร์เรย์สำหรับสิ่งที่ไม่ได้อัปเดตมากเกินไปดังนั้นจึงมีความคงที่มากหรือน้อย แต่สำหรับสิ่งที่มีการเขียนจำนวนมากโดยเฉพาะค่าที่ต้องได้รับการอัปเดตสำหรับฟิลด์ที่เชื่อมโยงกับอย่างอื่นในฐานข้อมูลพจนานุกรมพิสูจน์ได้ว่าง่ายต่อการดูแลรักษาและใช้งานได้ง่ายกว่ามาก

ดังนั้นสำหรับกรณีเฉพาะของคุณโครงสร้าง 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);

เหตุผลในการกำหนดวัตถุเป็นตัวแปรคือการใช้'sharedWith.' + whoEmail + '.when'โดยตรงในเมธอด set จะส่งผลให้เกิดข้อผิดพลาดอย่างน้อยเมื่อใช้ในฟังก์ชันคลาวด์ Node.js


2

นอกเหนือจากคำตอบที่กล่าวข้างต้น นี้จะทำมัน ใช้ 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
    });
 }  

1

พิจารณาเอกสารของ John Doe มากกว่าคอลเล็กชัน

ให้คอลเลกชันของสิ่งต่างๆและสิ่งที่แชร์กับคนอื่น ๆ

จากนั้นคุณสามารถแมปและค้นหาสิ่งที่แชร์ของ John Doe ในคอลเล็กชัน ThingsSharedWithOthers แบบคู่ขนาน

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



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


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

0

# แก้ไข (เพิ่มคำอธิบาย :)) บอกว่าคุณมีอาร์เรย์ที่คุณต้องการอัปเดตฟิลด์เอกสาร firestore ที่มีอยู่ คุณสามารถใช้การset(yourData, {merge: true} )ส่ง setOptions (พารามิเตอร์ที่สองในฟังก์ชัน set) ด้วย{merge: true}is must เพื่อผสานการเปลี่ยนแปลงแทนการเขียนทับ นี่คือสิ่งที่เอกสารอย่างเป็นทางการกล่าวเกี่ยวกับเรื่องนี้

อ็อบเจ็กต์อ็อพชันที่กำหนดค่าลักษณะการทำงานของการเรียก set () ใน DocumentReference, WriteBatch และ Transaction การเรียกเหล่านี้สามารถกำหนดค่าให้ทำการผสานแบบละเอียดแทนที่จะเขียนทับเอกสารเป้าหมายทั้งหมดโดยการจัดเตรียม 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 },
);

หวังว่านี่จะช่วยได้ :)


1
แม้ว่าสิ่งนี้อาจตอบคำถามได้ แต่ถ้าเป็นไปได้คุณควรแก้ไขคำตอบของคุณให้มีคำอธิบายสั้น ๆว่าบล็อกโค้ดนี้ตอบคำถามได้อย่างไร สิ่งนี้ช่วยให้บริบทและทำให้คำตอบของคุณมีประโยชน์มากขึ้นสำหรับผู้อ่านในอนาคต
Hoppeduppeanut

@Hoppeduppeanut ใช่คุณพูดถูก ฉันยอมรับคำวิจารณ์ที่ไม่เพิ่มคำอธิบายสำหรับคำตอบของฉัน ฉันได้แก้ไขคำตอบของฉันแล้ว หวังว่าตอนนี้จะช่วยให้ดีขึ้นมาก
Benyam
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.