web-dev-qa-db-ja.com

Firestoreで「オブジェクトの配列」を更新する方法は?

現在Firestoreを試していますが、「配列(サブドキュメント)の更新」という非常に単純なことにこだわっています。

私のDB構造はとてもシンプルです。例えば:

proprietary: "John Doe"
sharedWith:
  [
    {who: "[email protected]", when:timestamp}
    {who: "[email protected]", when:timestamp}
  ]

オブジェクトのshareWith配列に新しいレコードをプッシュしようとしています(成功しません)。

私はもう試した:

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

動作しません。これらのクエリは私の配列を上書きします。

答えは簡単かもしれませんが、見つけられませんでした...

ありがとう

58
nerotulip

2018年8月13日編集:Cloud Firestoreでのネイティブアレイ操作のサポートが追加されました。以下の Dougの答え を参照してください。


現在、Cloud Firestoreで単一の配列要素を更新する(または単一の要素を追加/削除する)方法はありません。

ここにこのコード:

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

これは、proprietary/docIDでドキュメントをsharedWith = [{ who: "[email protected]", when: new Date() }に設定するが、既存のドキュメントプロパティには影響を与えないことを意味します。これは、提供したupdate()呼び出しに非常に似ていますが、set()呼び出しが失敗する間にドキュメントが存在しない場合は、update()呼び出しで文書を作成します。

そのため、目的を達成するための2つのオプションがあります。

オプション1-アレイ全体を設定する

配列の内容全体でset()を呼び出します。最初にDBから現在のデータを読み取る必要があります。同時更新が心配な場合は、これらすべてをトランザクションで実行できます。

オプション2-サブコレクションを使用する

sharedWithをメインドキュメントのサブコレクションにすることができます。次に、単一のアイテムを追加すると次のようになります。

firebase.firestore()
  .collection('proprietary')
  .doc(docID)
  .collection('sharedWith')
  .add({ who: "[email protected]", when: new Date() })

もちろん、これには新しい制限があります。誰と共有されているかに基づいてドキュメントを照会することも、ドキュメントとすべてのsharedWithデータを単一の操作で取得することもできません。

46
Sam Stern

Firestoreには、全体を書き直さずに配列を更新できる2つの関数があります。

リンク: 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()は、指定された各要素のすべてのインスタンスを削除します。

79
Doug Galante

トランザクション( 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);
    });
13

パーティーに遅れて申し訳ありませんが、Firestoreは2018年8月にそれを解決しましたので、それをここでまだ探している場合は、アレイに関するすべての問題が解決されています。

https://firebase.googleblog.com/2018/08/better-arrays-in-cloud-firestore.html公式ブログ投稿

array-contains、arrayRemove、arrayUnionは、配列をチェック、削除、更新します。それが役に立てば幸い。

3
Guru

Sam Stern's answer に基づいて構築するために、3番目のオプションがあります。基本的に辞書であるマップを呼び出します。

辞書は、あなたが説明しているユースケースにとってはるかに優れていると思います。私は通常、あまり更新されていないものには配列を使用しているため、多少静的です。しかし、データベース内の他の何かにリンクされているフィールドの更新が特に必要な値については、大量に記述されるものについては、辞書の保守と操作がはるかに簡単であることがわかります。

したがって、特定のケースでは、DB構造は次のようになります。

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

これにより、次のことが可能になります。

var whoEmail = '[email protected]';

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

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

オブジェクトを変数として定義する理由は、少なくともNode.jsクラウド関数で使用する場合、'sharedWith.' + whoEmail + '.when'をsetメソッドで直接使用するとエラーが発生するためです。

2
Horea

これは私がそれを動作させる方法です。私はそれがより良い解決策だと思っているので、それがあなたのすべてに役立つことを願っています。ここでは、オブジェクト内の他のすべてを同じに保ちながら、タスクステータスを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);
    }

そしてここに実際にそれを更新するサービスがあります

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

return leadRef.update(updateData);
}
1
Leandrit Ferizi

上記の回答以外。これでできます。 Angular 5およびAngularFire2を使用またはthis.afsの代わりにfirebase.firestore()を使用

  // 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
    });
 }  
1
Jassi

John Doeをコレクションではなくドキュメントと考える

物と物のコレクションを与えるSharedWithOthers

その後、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: "[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 }
)

配列フィールドにアイテムを追加するために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
A_01