Làm cách nào để cập nhật “mảng đối tượng” với Firestore?


104

Tôi hiện đang dùng thử Firestore và tôi bị mắc kẹt ở một thứ rất đơn giản: "cập nhật một mảng (hay còn gọi là tài liệu phụ)".

Cấu trúc DB của tôi rất đơn giản. Ví dụ:

proprietary: "John Doe",
sharedWith:
  [
    {who: "first@test.com", when:timestamp},
    {who: "another@test.com", when:timestamp},
  ],

Tôi đang cố gắng (nhưng không thành công) để đẩy các bản ghi mới vào shareWithmảng đối tượng.

Tôi đã thử:

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

Không có tác dụng. Các truy vấn này sẽ ghi đè lên mảng của tôi.

Câu trả lời có thể đơn giản, nhưng tôi không thể tìm thấy nó ...

Câu trả lời:


71

Chỉnh sửa 13/08/2018 : Hiện đã có hỗ trợ cho các hoạt động mảng gốc trong Cloud Firestore. Xem câu trả lời của Doug bên dưới.


Hiện tại không có cách nào để cập nhật một phần tử mảng (hoặc thêm / xóa một phần tử) trong Cloud Firestore.

Mã này đây:

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

Điều này cho biết để đặt tài liệu ở mức proprietary/docIDđó sharedWith = [{ who: "third@test.com", when: new Date() }nhưng không ảnh hưởng đến bất kỳ thuộc tính tài liệu hiện có nào. Nó rất giống với update()cuộc gọi bạn đã cung cấp, tuy nhiên set()cuộc gọi với tạo tài liệu nếu nó không tồn tại trong khi update()cuộc gọi sẽ thất bại.

Vì vậy, bạn có hai lựa chọn để đạt được những gì bạn muốn.

Tùy chọn 1 - Đặt toàn bộ mảng

Gọi set()với toàn bộ nội dung của mảng, sẽ yêu cầu đọc dữ liệu hiện tại từ DB trước. Nếu bạn lo lắng về các cập nhật đồng thời, bạn có thể thực hiện tất cả những điều này trong một giao dịch.

Tùy chọn 2 - Sử dụng một bộ sưu tập con

Bạn có thể tạo sharedWithmột bộ sưu tập phụ của tài liệu chính. Sau đó, thêm một mục duy nhất sẽ giống như sau:

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

Tất nhiên điều này đi kèm với những hạn chế mới. Bạn sẽ không thể truy vấn tài liệu dựa trên người mà chúng được chia sẻ, cũng như không thể lấy tài liệu và tất cả sharedWithdữ liệu chỉ trong một thao tác.


9
Thật là bực bội ... nhưng cảm ơn vì đã cho tôi biết tôi sẽ không phát điên.
ItJustWerks

50
đây là nhược điểm lớn, Google phải khắc phục càng sớm càng tốt.
Sajith Mantharath

3
Câu trả lời của @ DougGalante cho biết điều này đã được khắc phục. Sử dụng arrayUnionphương pháp.
quicklikerabbit

151

Firestore hiện có hai chức năng cho phép bạn cập nhật một mảng mà không cần viết lại toàn bộ.

Liên kết: https://firebase.google.com/docs/firestore/manage-data/add-data , cụ thể là https://firebase.google.com/docs/firestore/manage-data/add-data#update_elements_in_an_array

Cập nhật các phần tử trong một mảng

Nếu tài liệu của bạn chứa trường mảng, bạn có thể sử dụng arrayUnion () và arrayRemove () để thêm và xóa các phần tử. arrayUnion () thêm các phần tử vào một mảng nhưng chỉ các phần tử chưa có mặt. arrayRemove () loại bỏ tất cả các phiên bản của mỗi phần tử đã cho.


56
Có cách nào để cập nhật một chỉ mục cụ thể từ mảng không?
Artur Carvalho,

1
Làm cách nào để sử dụng tính năng cập nhật mảng này với "react-native-firebase"? (I cant tìm thấy điều này trên tài liệu chính thức của phản ứng bản địa-căn cứ hỏa lực)
kernelman

4
@ArturCarvalho Không, lý do tại sao được giải thích trong video này youtube.com/…
Adam

@ArturCarvalho bạn có tìm thấy giải pháp nào để cập nhật một chỉ mục cụ thể từ mảng không?
Yogendra Patel

3
đối với những ai cần thực hiện việc đó trên máy khách, hãy sử dụng "import * làm firebase từ 'firebase / app';" sau đó "firebase.firestore.FieldValue.arrayUnion (NEW_ELEMENT)"
michelepatrassi

15

Bạn có thể sử dụng một giao dịch ( https://firebase.google.com/docs/firestore/manage-data/transactions ) để lấy mảng, đẩy lên nó và sau đó cập nhật tài liệu:

    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
Tại câu lệnh if bạn nên thay đổi nó thành này, bởi vì bạn đang thiếu các documentReferenceadd userRefnhư: transaction.set(userRef, { bookings: [booking] });
Ilir Hushi

8

Đây là ví dụ mới nhất từ ​​tài liệu 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")
});

@nifCody, điều này thực sự sẽ thêm một phần tử chuỗi mới "great_virginia" vào một "vùng" mảng hiện có. Tôi đã thử nghiệm nó thành công, và chắc chắn không thêm "đối tượng". Nó đồng bộ với câu hỏi như đã nêu: "đẩy kỷ lục mới".
Veeresh Devireddy

7

Xin lỗi vì đã đến muộn nhưng Firestore đã giải quyết nó theo cách trở lại vào tháng 8 năm 2018 vì vậy Nếu bạn vẫn đang tìm kiếm điều đó ở đây, tất cả các vấn đề đã được giải quyết liên quan đến mảng.

https://firebase.googleblog.com/2018/08/better-arrays-in-cloud-firestore.html Bài đăng trên blog chính thức

array-chứa, arrayRemove, arrayUnion để kiểm tra, xóa và cập nhật mảng. Hy vọng nó giúp.


3

Để xây dựng câu trả lời của Sam Stern , cũng có một tùy chọn thứ 3 giúp tôi làm mọi thứ dễ dàng hơn và đó là sử dụng cái mà Google gọi là Bản đồ, về cơ bản là một từ điển.

Tôi nghĩ rằng từ điển tốt hơn nhiều cho trường hợp sử dụng mà bạn đang mô tả. Tôi thường sử dụng mảng cho những thứ không thực sự được cập nhật quá nhiều, vì vậy chúng ít nhiều tĩnh. Nhưng đối với những thứ được viết nhiều, cụ thể là các giá trị cần được cập nhật cho các trường được liên kết với một thứ khác trong cơ sở dữ liệu, thì từ điển chứng minh là dễ bảo trì và làm việc hơn nhiều.

Vì vậy, đối với trường hợp cụ thể của bạn, cấu trúc DB sẽ giống như sau:

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

Điều này sẽ cho phép bạn làm những việc sau:

var whoEmail = 'first@test.com';

var sharedObject = {};
sharedObject['sharedWith.' + whoEmail + '.when'] = new Date();
sharedObject['merge'] = true;

firebase.firestore()
.collection('proprietary')
.doc(docID)
.update(sharedObject);

Lý do xác định đối tượng là một biến là việc sử dụng 'sharedWith.' + whoEmail + '.when'trực tiếp trong phương thức set sẽ dẫn đến lỗi, ít nhất là khi sử dụng nó trong một hàm đám mây Node.js.


2

Khác với các câu trả lời đã đề cập ở trên. Điều này sẽ làm được. Sử dụng Angular 5 và AngularFire2. hoặc sử dụng firebase.firestore () thay vì 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

Hãy coi John Doe là một tài liệu hơn là một bộ sưu tập

Cung cấp cho nó một bộ sưu tập mọi thứ và mọi thứ

Sau đó, bạn có thể lập bản đồ và truy vấn những thứ được chia sẻ của John Doe trong bộ sưu tập Những thứ song song đóSharedWithOthers.

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

Nếu ai đó đang tìm kiếm giải pháp sdk cửa hàng lửa Java để thêm các mục trong trường mảng:

List<String> list = java.util.Arrays.asList("A", "B");
Object[] fieldsToUpdate = list.toArray();
DocumentReference docRef = getCollection().document("docId");
docRef.update(fieldName, FieldValue.arrayUnion(fieldsToUpdate));

Để xóa các mục khỏi người dùng mảng: FieldValue.arrayRemove()


-1

Đây là cách tôi làm cho nó hoạt động. Hy vọng nó sẽ giúp tất cả các bạn vì tôi thấy nó là một giải pháp tốt hơn. Ở đây tôi muốn thay đổi trạng thái tác vụ từ mở (đóng = sai) thành đóng (đóng = đúng), trong khi giữ nguyên mọi thứ khác trong đối tượng.

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

và đây là dịch vụ thực sự cập nhật nó

 public updateLeadData(updateData, leadId) {
    const leadRef: AngularFirestoreDocument<LeadModel> = this.afs.doc(
      `leads/${leadId}`);

return leadRef.update(updateData);
}

Chào. Xin lưu ý rằng cách tiếp cận này có nhiều vấn đề và có nguy cơ mất / hỏng / không linh hoạt dữ liệu do việc sử dụng không may các mảng và "cập nhật" không gia tăng. Bạn không nên lưu trữ các nhiệm vụ trong một mảng. Thay vào đó, hãy có chúng trong một bộ sưu tập.
KarolDepka

Xin lỗi, tôi đã phải từ chối vì nó có những hoạt động không tốt trong đó.
KarolDepka
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.