Home > Software engineering >  How to Update a specific Item in an array nested in a document
How to Update a specific Item in an array nested in a document

Time:05-16

Hi I am building an RESTful API in Node using mongoose to manage data on a practice food delivery site I am building.

I want to setup a patch route that will remove an order Item from my items array nested in my Orders document based on a request from the user identifying the specific item with a name or ID.

I have a patch route which pushes a new order item into the Items Array nested in the Orders document, I want this patch route to also be able to remove a specific Item from the array based on a prop such as name or ID

I have tried using the Update and UpdateOne methods and I think I'm just getting the syntax wrong or something as I keep getting errors.

Server.js:

require("dotenv").config()

const express = require("express");
const mongoose = require("mongoose");
const app = express();



mongoose.connect(process.env.DATABASE_URL)

const db = mongoose.connection

db.on("error", () => console.error(error))
db.once("open", () => console.log("connected to database"))

app.use(express.json())

const subscribersRouter = require("./routes/subscribers")
const suscribersLoginRouter = require ("./routes/login")
const restaurantsRouter = require("./routes/restaurants")
const ordersRouter = require("./routes/orders")


app.use("/subscribers", subscribersRouter)
app.use("/login", suscribersLoginRouter)
app.use("/restaurants", restaurantsRouter)
app.use("/orders", ordersRouter)


app.listen(3000, () => {
    console.log("Server has started on port 3000")
});

Order Model:

const mongoose = require("mongoose")

const orderSchema = new mongoose.Schema({
    userID: {
        type: String,
        required: true
    },
    total: {
        type: Number,
        required: true
    },
    items: {
        type: Array,
        required: true
    }
})

module.exports = mongoose.model("order", orderSchema)

Orders Route (you will see here that I have a patch route which pushes a new order item into the Items Array nested in the Orders document, I want this patch route to also be able to remove a specific Item from the array based on a prop such as name or ID, the issue I am have is 1. How to create an if statement that gets the update of the item to be triggered and the the code id use in that if statement to actually update that Item)

    const express = require("express")
const router = express.Router()
const Order = require("../models/order")

// Getting All

router.get("/", async (req, res) => {
    try {
        const orders = await Order.find()
        res.json(orders)
    } catch (err) {
        res.status(500).json({
            message: err.message
        })
    }
})

// Getting One
router.get("/:id", getOrder, (req, res) => {
    res.json(res.order)
})

// Creating One
router.post("/", async (req, res) => {
    const order = new Order({
        userID: req.body.userID,
        total: req.body.total,
        items: req.body.items
    })
    try {
        console.log(order)
        const newOrder = await order.save()
        res.status(201).json(newOrder)
    } catch (err) {
        res.status(400).json({
            message: err.message
        })
    }
})

// Updating One 
router.patch("/:id", getOrder, async (req, res) => {
    if (req.body.userID != null) {
        res.order.userID = req.body.userID
    }
    if (req.body.total != null) {
        res.order.total = req.body.total
    }
    if (req.body.items != null) {
        const currentItems = res.order.items
        const newItem = req.body.items
        currentItems.push(newItem)
    } 
    try {
        const updatedItems = await res.order.save()
        res.json(updatedItems)
    } catch (err) {
        res.status(400).json({
            message: err.message
        })
    }
})



// Deleting One
router.delete("/:id", getOrder, async (req, res) => {

 
    try {
        await res.order.remove()
        res.json({
            message: "Deleted Order"
        })
    } catch (err) {
        res.status(500).json({
            message: err.message
        })
    }
})


async function getOrder(req, res, next) {

    let order
    try {
        order = await Order.findById(req.params.id)
        if (order === null) {
            return res.status(404).json({
                message: "Cannot Find Order"
            })
        }
    } catch (err) {
        return res.status(500).json({
            message: err.message
        })
    }
    res.order = order
    next()
}





module.exports = router

TEST Requests:

# ORDERS
# Get All

GET http://localhost:3000/orders

###

#Get One
GET http://localhost:3000/orders/627fe8e575a8229d0ae81e73

###

#Create One
POST http://localhost:3000/orders
Content-Type: application/json


{
    "userID": "627f8b476fa64425928750c9",
    "total":50,
    "items": [
        {
        "name": "Burder",
        "price": "R20",
        "description": "A good Fuggen Waffel"
    },
    {
        "name": "Hotdog",
        "price": "R20",
        "description": "A good Fuggen Waffel"
    },
    {
        "name": "Bunny Chow",
        "price": "R20",
        "description": "A good Fuggen Waffel"
    },
    {
        "name": "Pizza",
        "price": "R20",
        "description": "A good Fuggen Waffel"
    }

    ]
    
}


###

#Delete One or all

DELETE http://localhost:3000/orders/628202c3b208aebc7f7f8f98


###
 # Update on (add Order Item)

PATCH http://localhost:3000/orders/628202c3b208aebc7f7f8f98
Content-Type: application/json

{
    
          "items": {
        "name": "gravy",
        "price": "R20",
        "description": "A good Fuggen Waffel"
    }
    
}

###

CodePudding user response:

I'm not sure I understood you correctly. I understood that you need the PATCH route to also delete an item from the items array by name. So here is my solution to it: Because you already fetched the order and you just want to delete a specific item from the items property, you can use filter to do so before saving the order document.

res.order.items = res.order.items.filter(({ name }) => name !== itemNameToRemove);

Like this:

// Updating One 
router.patch("/:id", getOrder, async(req, res) => {
  const {
    userID,
    total,
    items,
    itemNameToRemove
  } = req.body;

  if (userID != null) {
    res.order.userID = userID;
  }
  if (total != null) {
    res.order.total = total;
  }
  if (items != null) {
    const newItem = items;
    res.order.items.push(newItem);

    if (itemNameToRemove) {
      res.order.items = res.order.items.filter(({
        name
      }) => name !== itemNameToRemove);
    }
  }
  try {
    const updatedItems = await res.order.save()
    res.json(updatedItems)
  } catch (err) {
    res.status(400).json({
      message: err.message
    })
  }
})

CodePudding user response:

you can use $pull for this.

Order.update(
  { userID : "userID123" },
  {$pull : {"items" : {"name":"gravy"}}}
)

This will delete the object with name as gravy belong to the userID : userID123

Hope this helps!.

  • Related