Home > Blockchain >  Replace field value with first element of an array in the same document
Replace field value with first element of an array in the same document

Time:06-16

I have two models

// Product model
const ProductSchema = {
  vendors: [{ type: Schema.Types.ObjectId, ref: 'Vendor' }],
  mainVendor: { type: Schema.Types.ObjectId, ref: 'Vendor' },
}
// Vendor model
const VendorSchema = { ... }

When I delete a Vendor, I want all Products with mainVendor field of deleted Vendor's _id to be replaced with the first vendor of vendors array. If vendors array is empty, I want mainVendor to be set to null.

Say I have Product

const product = {
  mainVendor: 'mainVendorObjectId'
  vendors: ['secondVendorObjectid', 'thirdVendorObjectId']
}

When I delete Vendor with mainVendorObjectId _id, I want my product to be

const product = {
  mainVendor: 'secondVendorObjectId',
  vendors: ['thirdVendorObjectId']
}

If I have a product with empty vendors array

const product = {
  mainVendor: 'mainVendorObjectId',
  vendors: []
}

After deleting Vendor with mainVendorObjectId _id I want my product to be like

const product = {
  mainVendor: null,
  vendors: []
}

I want to run it in post hook.
What I have now

VendorSchema.post('findOneAndDelete', async function (res: TVendorModel) {
    try {
        const products = await ProductModel.updateMany(
            { mainVendor: res._id },
            { $expr: { $set: { mainVendor: '$vendors.0' } } }
        );
    } catch (error) {
        console.error(error);
    }
});

but it doesn't work and it won't set mainVendor to null if vendors array is empty.

CodePudding user response:

A bit long query, but you can try with Update with aggregation pipeline.

With $cond operator to check whether vendors is an empty array, if yes set null, else take the first value from the vendors array to mainVendor field.

For the vendors field, it does the same concept as mainVendor field, the outcome will be different in removing the first item from vendors array.

db.collection.update({
  mainVendor: "mainVendorObjectId"
},
[
  {
    $set: {
      mainVendor: {
        $cond: {
          if: {
            $eq: [
              "$vendors",
              []
            ]
          },
          then: null,
          else: {
            $first: "$vendors"
          }
        }
      },
      vendors: {
        $cond: {
          if: {
            $eq: [
              "$vendors",
              []
            ]
          },
          then: [],
          else: {
            $slice: [
              "$vendors",
              1,
              {
                $size: "$vendors"
              }
            ]
          }
        }
      }
    }
  }
],
{
  multi: true
})

Sample Mongo Playground

CodePudding user response:

Too late but this is my solution

$first extracts the first object of an array

$ifNull checks if $first operation is null, if true, adds null

$slice extracts a portion of the array

$sum does a sum operation. In this case is just a trick to avoid errors when the length of the array is 0 (because $slice needs a value greater than 0)

db.collection.update({
  "mainVendor": "mainVendorObjectId"
},
[
  {
    "$set": {
      "mainVendor": {
        $ifNull: [
          {
            "$first": "$vendors"
          },
          null
        ]
      },
      "vendors": {
        "$slice": [
          "$vendors",
          1,
          {
            $sum: [
              {
                $size: "$vendors"
              },
              1
            ]
          }
        ]
      },
      
    }
  }
],
{
  multi: true
})
  • Related