Tuesday, 7 June 2022

How to efficiently replace values for id fields in an object that was cloned?

Suppose I have two records in a table:

[
    {
        id: 1,
        name: 'Michael',
        associations: [
            {
                from: 1,
                to: 2
            }
        ]
    },
    {
        id: 2,
        name: 'John',
        associations: [
            {
                from: 2,
                to: 1
            }
        ]
    },
]

If I clone these two objects, I need to end up with the following:

[
    {
        id: 1,
        name: 'Michael',
        associations: [
            {
                from: 1,
                to: 2
            }
        ]
    },
    {
        id: 2,
        name: 'John',
        associations: [
            {
                from: 2,
                to: 1
            }
        ]
    },
    {
        id: 3,
        name: 'Michael',
        associations: [
            {
                from: 3,
                to: 4
            }
        ]
    },
    {
        id: 4,
        name: 'John',
        associations: [
            {
                from: 4,
                to: 3
            }
        ]
    },
]

How can I effectively achieve this?

What I am currently doing is cycling through all records and running an INSERT for each. For each element, I track its oldId and newId and afterwards, I query for all records that have one of these newIds. Then, I replace the values. Obviously, this is fine for two records, but if I have thousands of records that need to be cloned, each containing thousands of associations, this can hit performance very hard.

Here is the code. The function cloneElements is the initiator.

const updateElementAssociationsById = async ({ id, associations }) => {
    return dbQuery(id, associations); // UPDATE ... SET associations WHERE id
}

const handleUpdateAssociations = async ({ data }) => {
    const promises = [];
    const records = await dbQuery(data.map(({ newId }) => newId)) ; // SELECT * FROM ... WHERE id in ANY 

    records.forEach(({ id, ...record }) => {
        const associations = [];

        record.associations.forEach((association) => {
            const sourceElement = data.find(({ oldId }) => oldId === association.from);
            const targetElement = data.find(({ oldId }) => oldId === association.to)

            association.from = sourceElement.newId;
            association.to = targetElement.newId;

            associations.push(association);
        });

        promises.push(updateElementAssociationsById({ id, associations }))
    });

    await Promise.all(promises);
}

const createElement = async ({ element, data }) => {
    const newElement = await dbQuery(element); // INSERT INTO ... RETURNING *;

    data.push({
        oldId: element.id,
        newId: newElement.id,
    });   
}

const cloneElements = async (records) => {
    const promises = [];
    const data = [];

    records.forEach((element) => {
        promises.push(createElement({ element, records, data }));
    });

    await Promise.all(promises);

    await handleUpdateAssociations({ data });
}


from How to efficiently replace values for id fields in an object that was cloned?

No comments:

Post a Comment