📖
Datastoreで入れ子のオブジェクトをインデックスから除外する
Datastoreはデフォルトで全てのプロパティに対してインデックスを 張るが、様々な制限事項があるため、除外する必要があることが多い。
この時、オブジェクトが入れ子になっている場合、かなりクセがある仕様になっているためメモしておく。
Nodeの場合
以下はダメな例。
let text = "";
for (let i = 0; i < 1600; i++) text += "a";
await datastore.update({
key,
data: { obj: { text } },
excludeFromIndexes: ["obj"],
})
このように書くと obj
にはインデックスが張られないが、 obj.text
にインデックスを張ろうとしてしまうため、1500byte制限に引っかかってエラーになる。
obj.text
にインデックスを張りたくない場合は以下のように書く必要がある。
excludeFromIndexes: ["obj.text"],
ただ、 obj
にはインデックスが貼られてしまうし、多くの場合他のプロパティにもインデックスを張りたくないため、以下のようにするのがおすすめ。
excludeFromIndexes: ["obj", "obj.*"],
このようにしておくと、さらにオブジェクトが入れ子になっていても全ての子孫オブジェクトのインデックスが除外される模様。
配列に関して
配列に関してはさらにややこしくなる。
以下はダメな例。
let text = "";
for (let i = 0; i < 1600; i++) text += "a";
await datastore.update({
key,
data: { arr: [text] },
excludeFromIndexes: ["arr"],
});
これだと、なんと arr
にはインデックスが張られてしまう。
正しくは以下のように書く。
excludeFromIndexes: ["arr[]"],
ただ、これだと配列がオブジェクトを持つ場合そちらのプロパティにはインデックスが張られてしまうため、以下のようにするのがおすすめ。
excludeFromIndexes: ["arr[]", "arr[].*"],
弊社の場合
この仕様はとてもわかりづらいので、弊社ではDatastoreクライアントのラッパーで吸収している。
const createExcludeFromIndexes = (output: z.output<Schema>): string[] =>
kind.excludeFromIndexes.flatMap((excludeFromIndex) => {
const prop = output[excludeFromIndex];
if (typeof prop !== "object") return [excludeFromIndex];
if (prop == undefined) return [excludeFromIndex];
if (Array.isArray(prop)) return [`${excludeFromIndex}[]`, `${excludeFromIndex}[].*`];
return [excludeFromIndex, `${excludeFromIndex}.*`];
});
Goの場合
Goでは、Nodeとは打って変わって noindex
が子オブジェクトにも派生するよう実装されている。
type Entity struct {
Obj Inner `datastore:"obj,noindex"`
ArrObj []Inner `datastore:"arr_obj,noindex"`
Arr []string `datastore:"arr,noindex"`
}
type Inner struct {
S string `datastore:"s"`
}
これで問題なく全てのインデックスが除外される。
逆に言うと、親オブジェクトではインデックスを張らずに、子オブジェクトでのみインデックスを張るという書き方はできなさそう。まぁ需要ないし・・・。
子オブジェクトだけインデックスを貼らないことはできる。
type Entity struct {
Obj Inner `datastore:"obj"`
ArrObj []Inner `datastore:"arr_obj"`
}
type Inner struct {
S string `datastore:"s,noindex"`
}
Discussion