Closed2

Prismaでランダムにレコード取得したい人生だった

takky94takky94

skipは使えないのか?

要は1つ飛ばしみたいなことがまだできないのでskipでは要件に満たない
例えば a, b, c, d, ..., z に対して skip: 3, take: 3とかでクエリ発行すると d, e, fが返ってくる
欲しいのは d, m, z みたいな感じ

https://www.prisma.io/docs/concepts/components/prisma-client/pagination

方法

  • 素のSQL書く
  • 乱数でsort対象のキーを指定する
  • 複数回GETする

『乱数でsort対象のキーを指定する』について

async pickRandom(count: number): Promise<CognitiveTest[]>{
    const itemCount = await this.prisma.cognitiveTest.count();
    const skip = Math.max(0, Math.floor(Math.random() * itemCount) - count);
    const orderBy = randomPick(['id', 'field1', 'field2']);
    const orderDir = randomPick([SortOrder.asc, SortOrder.desc]);

    return this.prisma.cognitiveTest.findMany({
      take: count,
      skip: skip,
      orderBy: { [orderBy]: orderDir },
    });
  }

https://github.com/prisma/prisma/discussions/5886#discussioncomment-1518446

これで都度異なるキーによってソートされるため、一応ランダムな感じにはなる
ただ、キーごとのソートに差異がない場合はランダムなキーでのソートはあまり意味ない

複数回GETする

フロントで何回もfetchするんでもバックエンドでAPI叩きまくるんでもいいけど、仮に9個ランダムなレコードを取得する場合、

// frontで行う
for (let i = 0; i < 9; i++) {
  await fetch(URL)
}

// backで行う
const totalCount = await prisma.sample.count()
const args = {/* ... */}
const random = []
for (let i = 0; i < 9; i++) {
  const sample = await prisma.sample.findFirst(args)
  if (sample) {
    random.push(sample)
    args.skip = Math.floor(Math.random() * totalCount)
  }
}

SSRとかだと取得回数によってはだいぶ難ありそうだけどrevalidateが大きいSGとかなら複数回DB叩いても悪影響なさそうではある

参考

https://github.com/prisma/prisma/discussions/5886
https://github.com/prisma/prisma/issues/5894

このスクラップは2023/04/10にクローズされました