📖

Datastoreで入れ子のオブジェクトをインデックスから除外する

2022/12/17に公開

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