Search code examples
javascriptdatabaseinsert-updateprismaprisma2

Map over collection to upsert into the database. How to batch upsert?


Say, I have a data structure coming in from the frontend as follows:

const userData = [
  {
    id: 11223,
    bb: [
      {
        id: 12,
      },
      {
        id: 34,
        bbb: "bbb",
      },
    ],
  },
  {
    id:4234,
    ...
  },
];

Because, none/ some/ all of the data may already be in the database, here is what I have come up with:

const collection = [];
for (let i = 0; i < userData.length; i++) {
  const cur = userData[i];
  const subCur = cur.bb;
  const updatedCur = await db.cur.upsert({
      where: {
        id : cur.id
      },
      update: {
        ...
      },
      create: {
        ...
      },
    })
  );
  collection.push(updatedCur);
  for (let j = 0; j < subCur.length; j++) {
    const latest = subCur[j];
    await db.subcur.upsert({
      where: {
        id : latest.id
      },
      update: {
        ...
      },
      create: {
        ...
      },
    });
  }
}

To summarise, I am mapping over the userData & upsert each object one by one. Within the loop, I map over the child collections & upsert them in the db.

My concern is that I am making a lot of entries into the Db this way. Is this the best way to do this?

Aside: I previously, tried to do multiple inserts within the upsert, however, I got stuck with the update section as to my knowledge, we cannot upsert multiple records within the update nested within upsert. Is this correct?

UPDATE:

As requested by Ryan, here is what the Schema looks like:

model Cur {
  id      Int,
  subCur  SubCur[]
  ...
}

model SubCur {
  id      Int,
  cur     Cur  @relation(fields: [curId], references : [id])
  curId   Int
  ...
}

To summarise, there are many models like 'SubCur' with 1-n relation with 'Cur' model. As the 'UserData' payload, may have some data that is new, some that is update for existing data already in Db, I was curious, whats the best approach to upsert the data into the db. To be specific, do I have to insert each one, one at a time?


Solution

  • I assumed your schema to be this:

    model Cur {
      id Int @id
    }
    
    model Subcur {
      id  Int     @id
      bbb String?
    }
    
    

    And here's a better version:

    const collection = await prisma.$transaction(
        userData.map(cur =>
          prisma.cur.upsert({
            where: { id: cur.id },
            update: {},
            create: { id: cur.id },
          })
        )
      )
    
      await prisma.$transaction(
        userData
          .flatMap(cur => cur.bb)
          .map(latest =>
            prisma.subcur.upsert({
              where: {
                id: latest.id,
              },
              update: {
                bbb: latest.bbb,
              },
              create: {
                id: latest.id,
                bbb: latest.bbb,
              },
            })
          )
      )