質問: data["value"] as! String print(str) //この出力時には順序が保持されない } } } ``` よろしくお願いいたします。
回答: 順序がバラバラになっ (たように見え) てしまうのは、 **`getDocument()` の呼び出しが非同期に処理される** ためかと存じます。解決するには、クライアント側で順序を保持しておき、全ての document が取得された後に `print()` するようなコードを書く必要があるのではないでしょうか。 原因 == Firebase SDK は Firestore への読み書きを行う際、その処理が非同期に行われるような設計になって居ります。態々取得結果への処理をクロージャで書いて `getDocument()` 等のメソッドに渡しているのもこのためですね。換言すれば、 `getDocument()` 等を **呼び出した段階では document の取得を "要求" するだけ** であって、その時点で結果は得られないということです。そして実際の取得結果はその後 **取得できたものから次々と (クロージャに) 渡される** ため、必ずしも "要求" の順序と一致するとは限りません。 結果、御呈示のコードでは次のような順序で処理が行われ、 `CollectionA` の取得結果が順不同となってしまいます。 1. `CollectionB` の document を `date` の降順で要求する 2. 1 の要求に対する結果が返り、 `date` 順に並んだ document の集合が得られる 3. 集合の各 `CollectionB` document に対して、 `CollectionA` への reference を用いて紐づく document を一つ一つ "要求" する **(この時点では希望の順番通りになっている)** 4. 3 で行った複数の要求が並列処理され、データ取得に成功したものから次々に渡される **(この時点で希望の順序は失われている)** 解決策 === `getDocument()` の結果が非同期に順不同に渡されるのは避けられませんので、その **順序を維持するには自身で何らかの工夫をする** 必要があります。例えば、次のような方法があるのではないでしょうか。 1. `CollectionB` の各 document が持つ reference だけを要素とした配列 `references` と、同じ長さの配列 `resolvedDocuments` を用意しておく 2. `references` の全ての要素に対して `getDocument()` をするが、そのクロージャでは単に `references` 内で `documentID` が一致する reference と同じ index で `resolvedDocuments` に結果を保持しておくだけとする 3. 但し、最後の document が得られたら (== 全ての document の取得が終わったら) 、改めて `resolvedDocuments` を引数にして `print()` なりなんなりの処理を行う別の関数を呼び出す処理としておく データ構造の見直しについて ============= 非正規化などでデータ構造を見直すべきか否かは、 **各 collection の document がどのような性質のデータを持ち、どのように用いられるか次第** かと存じますので、この処理による順序性を維持する目的だけでは「こうすべき」「ああすべき」とは言えないように思います。 例えば `CollectionA` が本当に `value` フィールドしか持たないのであれば、それ自体を `CollectionB` の document に持たせてもいいかもしれません。あるいは他にも複数のフィールドがあったとしても、今回の御質問のような read がとにかく多く発生するのであれば、 reference ではなく object にして `CollectionA` の document そのものを含めてしまう非正規化を行うのも手でしょう (最初の `getDocuments()` の時点で必要なデータが全て揃うので、順序性を気にする必要がなくなります) 。但しこの場合、 `CollectionA` への更新をするときは各 `CollectionB` document に含めたすべてのコピーを更新する必要がありますから、更新が頻繁に生じる場合は不適なやり方と言えます。 何れにせよ、 Firestore 自体が **「必要なデータを都度取得する」** / **「クエリの機能を単純化し、複雑な処理が必要ならデータを取得してからクライアント側で行う」** 思想で作られていることもあり、クライアント側で多少のデータ加工や工夫が必要な事自体は不自然なことでは (そして常に避けられることでは) 御座いませんから、それ自体だけがデータ構造見直しの理由にはならないという事です。 とはいえ、「データ量が少なく、性能やら何やらを気にするほどではないから、多少 write のコストが高くてもクライアント実装を簡単にするために非正規化する」という判断も十分あり得ますから、その辺りは御事情に合わせて御検討下さい。 余談 == 因みに、この「複数の `getDocument()` 呼び出しの順序を維持し、全ての結果が得られてから関数を呼ぶ」という処理は多くの人に需要があるようで、 Node.js 用の SDK では (恐らくは `Promise` という強力な仕組みが存在するおかげで) `Firestore.getAll()` という関数 として実装されていたりします。 ただ残念ながら、 iOS SDK では Feature Request は行われている ものの、優先度が低いとして実装されるには至って居りません。当面は上記のように自分で何とかするしかなさそうです。