Struggling with establishing connection logic between two database tables using Prisma and JavaScript

I'm facing a perplexing logic problem that is eluding my understanding. Within the context of using next-connect, I have a function designed to update an entry in the database:

  .put(async (req, res) => {
    const data = req.body;
    const { dob, roles, cases } = data ?? {};
    const convertedDob = dob ? new Date(dob) : null;

    const roleIds = roles?.map((role: Role) => {
      return { id: role.id };
    });

    const caseIds = cases?.map((_case: Case) => {
      return { id: _case.id };
    });

    data.dob = convertedDob;
    delete data.roles;
    delete data.formData;

    const user = await getUserDataFromSession(req, res);
    throwIfNoCurrentTeam(user?.currentTeam);

    try {
      const person: Person = await prisma.person.update({
        where: { id: data.id },
        data: {
          ...data,
          roles: { connect: roleIds },
          cases: { connect: caseIds },
        },
      });

      if (person && person.linkedIds) {
        // get linkedId arrays of all people linked to this person
        const associatedPeopleIdArrays = await prisma.$transaction(
          person.linkedIds.map((id) =>
            prisma.person.findUnique({
              where: { id },
              select: { id: true, linkedIds: true },
            }),
          ),
        );

        const backLinkArray = associatedPeopleIdArrays.map((linkedPerson) => {
          const linkedPersonIds = linkedPerson?.linkedIds;
          const linkedPersonOwnId = linkedPerson?.id;

          // if the array of linkedIds already includes the person we are back-linking, enter next check
          if (linkedPersonIds?.includes(person.id)) {
            // if they both include each other, do nothing
            if (person.linkedIds.includes(linkedPersonOwnId as string)) {
              return { id: linkedPersonOwnId, linkedIds: linkedPersonIds };
            }
            // if linkedPersonIds includes person.id but person.linkedIds does not include linkedPersonOwnId, remove the relationship
            return { id: linkedPersonOwnId, linkedIds: linkedPersonIds.filter((id) => id !== person.id) };
            // else add the current person's id to each person's array that we are back-linking
          } else {
            return {
              id: linkedPersonOwnId,
              linkedIds: linkedPersonIds ? [...linkedPersonIds, person.id] : [person.id],
            };
          }
        });

        // write the new linkedId array to each associated person
        await prisma.$transaction(
          backLinkArray.map((linkedPerson) =>
            prisma.person.update({
              where: { id: linkedPerson?.id },
              data: {
                linkedIds: linkedPerson?.linkedIds,
              },
            }),
          ),
        );
      }

      return res.status(200).json({ updated: person });
    } catch (err: any) {
      console.log({ err });
      handleApiError('Failed to update person', 500, err);
    }
  });

In attempting to unravel this puzzle, I have included comments throughout the code to aid comprehension. While logs have been strategically placed to shed light on the process, the challenge lies within the intricate if/else logic.

The function effectively establishes relationships between individuals. When adding an id to one person's linkedIds array, the same id should be added to the associated person's array as well.

However, there exists a need for additional criteria to sever a relationship when an id is removed from a person's linkedIds. This aspect seems to be faltering. The issue arises after entering the statement

if (linkedPersonIds?.includes(person.id))
, followed by invariably executing the subsequent condition
if (person.linkedIds.includes(linkedPersonOwnId as string))
.

It was anticipated that deleting an id from the linkedIds array would yield a false outcome, yet it persistently returns true, impeding the deletion of the relationship. What am I overlooking? Where has my understanding lapsed?

Pardon the chaotic presentation, and I trust you can navigate through this labyrinthine scenario!

Answer №1

It became apparent to me what the main issue was... when constructing the associatedPeopleArrays, it failed to include the recently deleted id in the backLinkArray. This meant that there would never be a validation for that particular id!

Therefore, I realized the necessity to retrieve the previous version of the linkedIds array before any updates were made. This required adjustments on the frontend to ensure this information was transmitted alongside the updated data.

This allowed me to compare the old linkedIds with the new ones and identify any discrepancies. While I admit my code may seem lengthy and intricately detailed, this solution proved effective!

      if (person?.linkedIds || initialLinkedIds) {

        // ADD RELATIONSHIPS
        // obtain arrays of linkedIds for all newly associated people with this person
        const newlyAssociatedPeopleIdArrays = await prisma.$transaction(
          person.linkedIds.map((id: string) =>
            prisma.person.findUnique({
              where: { id },
              select: { id: true, linkedIds: true },
            }),
          ),
        );

        // populate the associated peoples' arrays with the person that just added them
        const addRelationshipArray = newlyAssociatedPeopleIdArrays?.map((linkedPerson) => {
          const linkedPersonIds = linkedPerson?.linkedIds;
          const linkedPersonOwnId = linkedPerson?.id;

          if (!linkedPersonIds?.includes(person.id)) {
            return { id: linkedPersonOwnId, linkedIds: [...linkedPersonIds, person.id] };
          }

          return linkedPerson;
        });

        // REMOVE RELATIONSHIPS
        // find the difference between the old linkedIds and the current linkedIds
        const removedIds = initialLinkedIds.filter((id: string) => !person.linkedIds.includes(id));

        // obtain linkedId arrays for each id that has been removed
        const newlyRemovedPeopleIdArrays = await prisma.$transaction(
          removedIds.map((id: string) =>
            prisma.person.findUnique({
              where: { id },
              select: { id: true, linkedIds: true },
            }),
          ),
        );

        // iterate over these linkedId arrays and remove the current person
        const removeRelationshipArray = newlyRemovedPeopleIdArrays.map((unlinkedPerson) => ({
          ...unlinkedPerson,
          linkedIds: unlinkedPerson.linkedIds.filter((id: string) => id !== person.id),
        }));

        // merge the two relationship arrays
        const combinedUpdateArray = [...removeRelationshipArray, ...addRelationshipArray];

        // update the database with these new arrays
        await prisma.$transaction(
          combinedUpdateArray.map((linkedPerson) =>
            prisma.person.update({
              where: { id: linkedPerson?.id },
              data: {
                linkedIds: linkedPerson?.linkedIds,
              },
            }),
          ),
        );
      }

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

There is no overload match for the HttpClient.get call at this time

I'm trying to set up a file download feature using a blob, but I need to extract the filename from the server's "content-disposition" header. Here's the code I have: const header = {Authorization: 'Bearer ' + token}; const config ...

Tips on resolving the Hydration error in localStorage while using Next.js

Having issues persisting context using localStorage in a Next.js project, resulting in hydration error upon page refresh. Any ideas on how to resolve this issue? type AppState = { name: string; salary: number; info: { email: string; departme ...

An error has occurred: Type 'x' is not compatible with type 'x' (during Vercel deployment)

I encountered an issue during Vercel deployment which displays the following error message: - Type error: Type ' ({ params }: DashboardPageProps) = Promise' is not compatible with type 'FC<.DashboardPageProps>' Type 'Promise ...

Step-by-step guide on invoking an asynchronous method in canActivate for Ionic and Angular

My objective is to acquire a token for authenticating users. I am utilizing the import { Storage } from '@ionic/storage-angular'; to store the data, but I am encountering an issue where the Storage methods only function in asynchronous mode. Her ...

What is the best way to add an external .js file to my Angular 2 application?

I'm currently working on a project using Angular 2's TypeScript API along with webpack to develop a web application. However, I've encountered an issue where one of my components needs to utilize functions from an external .js file that is p ...

Utilizing Protractor, TypeScript, and Jasmine for Automated Testing

Just landed a new job at a company that uses protractor, jasmine, and Type Script for automation testing. While I have experience with selenium and Java, I'm unfamiliar with protractor. Can someone guide me on how to start protractor testing? Is there ...

Instructions for deploying a next.js + mongo app to AWS (or any other similar service such as G Cloud)

My background in JS development is solid, but I lack experience in devops. There is an overwhelming amount of documentation available, leaving me unsure of where to start. I have successfully built a next.js app (both frontend and backend) connected to mo ...

Exploring the capabilities of argon2-browser in a cutting-edge setup with vite

After spending several hours attempting to implement the argon2-browser library in a Vue app with Vite, I have been encountering a persistent error. Despite following the documentation closely, I keep receiving the following message: This require call is ...

Utilize the data storage API within Next.js or directly in the user's

Struggling to store this ini file on either the server or client, any help would be greatly appreciated. Additionally, I would like to display the ini info in the browser so that clients can easily copy and paste the information. However, I seem to be fac ...

Implementing an 'or' operator in Supabase is a straightforward task that can enhance your query capabilities. By

Hey there! I'm struggling with incorporating the or operator in supabase. I'm trying to set up a 'for you' tab where posts are filtered based on the user's followed tags and people. Initially, using the in and contains operators ...

What is the best way to link together Angular observables?

In order for my component to make API requests, it needs to check if certain app preferences are set. Currently, I have implemented a method where the component's data is refreshed every 2 minutes using a timer: ngOnInit(): void { this.subscriptio ...

Having trouble reading a file in Android 11 due to a Capacitor Filesystem error

I attempted to access a file within my emulator using the readFile function of Capacitor filesystem. The file I am trying to manipulate is located in the Download folder of the emulator, and upon execution, the function returned the following error: Erro ...

Encountering a top-level-await issue while utilizing the NextJS API

Currently, I am in the process of creating an API using NextJS and MongoDB. To start off, I have set up some basic code at the beginning of the API file: const { db } = await connectToDatabase(); const scheduled = db.collection('scheduled'); Fol ...

Unable to utilize the fetch method in Node.js for sending the API request

I am currently using fetch to send a POST request and retrieve data from an API on the server side (using nextjs 13 + node 18). Interestingly, I can successfully retrieve the data using postman and axios, but not with fetch. Below is the snippet of code ...

Encountering issues with displaying images in React Next.js when utilizing dangerouslySetInnerHtml

While working on creating a simple WYSIWYG editor in nextjs, I encountered an issue with displaying uploaded images on the screen. When generating a blob URL for the image and using it as the src attribute of the image tag, it worked fine unless dangerousl ...

Next.js: Executing functions during server initialization

Looking for a way to implement configuration methods upon the first boot of my Next.js server. Specifically, I want to set up variables and establish a database connection. Currently, I am repeatedly checking if the variables have been initialized in eac ...

Is localStorage.getItem() method in NextJS components behaving differently?

I'm working on my nextjs application and I wanted to utilize the power of localstorage for storing important data throughout my app. Within the pages directory, specifically in the [slug].tsx file, I implemented the following logic: export default fu ...

Is the naming convention for parameterized types (T, U, V, W) in Generics adhered to by Typescript?

Is TypeScript following the same naming convention for parameterized types as other languages like C++ and Java, using T,U,V,W, or is there a mixed usage of conventions? In the TS 2.8 release notes, we see examples like: type ReturnType<T> = T exten ...

A guide on launching a Next.js app and Express server simultaneously with the npm concurrently package

Running a Next.js App and Express Server Simultaneously with npm and the concurrently Package I have a project that combines Node Express as the backend and Next.js as the frontend. I am looking to utilize the concurrently npm package to launch both the E ...

The path specified in "[...nextauth]/route.ts" does not meet the necessary criteria of a Next.js Route. The configuration "GET" is not valid

After upgrading from V4 to V5 of next-auth, I encountered a type error in my route.ts file which is now causing my app to fail compiling and running. The problematic file location is: src/app/api/auth/[...nextauth]/route.ts next: ^14.2.3 => 14.2.3 next ...