ga-wdi-boston / team-project

Other
2 stars 39 forks source link

Iterating Through an Embedded Array; Mongoose #349

Closed slammyde7113 closed 7 years ago

slammyde7113 commented 7 years ago

So I'm having an issue iterating through my embedded document in mongoose. This is what my schema looks like:

const cartSchema = new mongoose.Schema({
  product: [{
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Product',
    required: false
  }],
  owner: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'User',
    required: true
  },
  stripe: {
    type: String,
    required: false
  }
}, {
  timestamps: true,
  toJSON: {
    virtuals: true,
    transform: function (doc, ret, options) {
      const userId = (options.user && options.user._id) || false
      ret.editable = userId && userId.equals(doc._owner)
      return ret
    }
  }
})

and when I make a show request on my back end I want to iterate through the product array. Here's what a show request looks like right now:

const show = (req, res) => {
  Product.find({_id: ObjectId(req.cart.product[0])},function(err,products){
        console.log(req.cart.product)
         if (err) {
            res.sendStatus(500)
        } else {
            res.json({
              cart: req.cart.toJSON({ virtuals: true, user: req.user }),
              product: products
            })
          }
    })
}

Example:

{
    "cart": {
        "_id": "5953b153d2108941d15a7fe9",
        "updatedAt": "2017-06-28T13:38:27.406Z",
        "createdAt": "2017-06-28T13:38:27.406Z",
        "owner": "595153ad6f18427ef38c416b",
        "__v": 0,
        "product": [
            "5952b57ea52d092b8d34c6b0",
            "5952b57ea52d092b8d34c6b0"
        ],
        "id": "5953b153d2108941d15a7fe9",
        "editable": false
    },
    "product": [
        {
            "_id": "5952b57ea52d092b8d34c6b0",
            "name": "test00000",
            "price": 0,
            "description": "test",
            "__v": 0,
            "id": "5952b57ea52d092b8d34c6b0"
        }
    ]
}

How do I correctly retrieve the information from each product? If I throw in a for loop and replace the req.cart.product[0] with req.cart.product[i], I get a "cannot read 'product' of undefined" error.

slammyde7113 commented 7 years ago

Tried building an object to return:

const show = (req, res) => {
  const product = {}
  product.array = []
  for (let i = 0; i < req.cart.product.length; i++) {
  Product.find({_id: ObjectId(req.cart.product[i])},function(err,products){
        console.log(req.cart.product)
         if (err) {
            res.sendStatus(500)
        } else {
              console.log(products)
              product.array.push(products)
            }
          })
        }
          res.json({
            cart: req.cart.toJSON({ virtuals: true, user: req.user }),
            product: product
          })
    }

but got this response:

{
    "cart": {
        "_id": "5953b153d2108941d15a7fe9",
        "updatedAt": "2017-06-28T13:38:27.406Z",
        "createdAt": "2017-06-28T13:38:27.406Z",
        "owner": "595153ad6f18427ef38c416b",
        "__v": 0,
        "product": [
            "5952b57ea52d092b8d34c6b0",
            "5952b57ea52d092b8d34c6b0"
        ],
        "id": "5953b153d2108941d15a7fe9",
        "editable": false
    },
    "product": {
        "array": []
    }
}

Seems as though the array isn't properly being appended. Any thoughts?

slammyde7113 commented 7 years ago

I'm getting the product in the console.log but I don't think I'm iterating through the for loop.

const show = (req, res) => {
  const product = {}
  product.array = new Array()
  for (let i = 0; i < req.cart.product.length; i++) {
  Product.find({_id: ObjectId(req.cart.product[i])},function(err,products){
        console.log(req.cart.product)
         if (err) {
            res.sendStatus(500)
        } else {
              console.log(products[i])
              product.array[i] = products[i]
            }
          })
        }
          res.json({
            cart: req.cart.toJSON({ virtuals: true, user: req.user }),
            product: product.array
          })
    }

Still, when I send a curl script I'm getting an empty array for the products

{
    "cart": {
        "_id": "5953b153d2108941d15a7fe9",
        "updatedAt": "2017-06-28T13:38:27.406Z",
        "createdAt": "2017-06-28T13:38:27.406Z",
        "owner": "595153ad6f18427ef38c416b",
        "__v": 0,
        "product": [
            "5952b57ea52d092b8d34c6b0",
            "5952b57ea52d092b8d34c6b0"
        ],
        "id": "5953b153d2108941d15a7fe9",
        "editable": false
    },
    "product": []
}
jordanallain commented 7 years ago

looks like you have two product arrays

slammyde7113 commented 7 years ago

The first one is just displaying the information from the cart. One of those attributes are the product ids from the products users have added to them. Once I get the bottom array to populate with product information then I'll add it to the cart object. I'm just leaving that there for reference

slammyde7113 commented 7 years ago

I still can't query the product information from the product id. Something is wrong when I'm trying to push to the array. I changed the name of the last array to avoid potential confusion:

const show = (req, res) => {
  const product = {}
  product.array = new Array()
  console.log(req.cart.product[1])
  for (let i = 0; i < req.cart.product.length-1; i++) {
  Product.find({_id: ObjectId(req.cart.product[i])},function(err,products){

         if (err) {
            res.sendStatus(500)
        } else {
              console.log(products)
              console.log(product)
              product.array = products
              console.log(product.array)
            }
          })
        }
          res.json({
            cart: req.cart.toJSON({ virtuals: true, user: req.user }),
            items: product.array
          })
    }

Output

{
    "cart": {
        "_id": "5953b153d2108941d15a7fe9",
        "updatedAt": "2017-06-28T13:38:27.406Z",
        "createdAt": "2017-06-28T13:38:27.406Z",
        "owner": "595153ad6f18427ef38c416b",
        "__v": 0,
        "product": [
            "5952b57ea52d092b8d34c6b0",
            "5952b57ea52d092b8d34c6b0"
        ],
        "id": "5953b153d2108941d15a7fe9",
        "editable": false
    },
    "items": []
}
slammyde7113 commented 7 years ago

also, here are the results from the console.logs

[ { _id: 5952b57ea52d092b8d34c6b0,
    name: 'test00000',
    price: 0,
    description: 'test',
    __v: 0 } ]
{ array: [] }
[ { _id: 5952b57ea52d092b8d34c6b0,
    name: 'test00000',
    price: 0,
    description: 'test',
    __v: 0 } ]
slammyde7113 commented 7 years ago

I'm definitely getting something put into the array but it wont get returned in the response

slammyde7113 commented 7 years ago

any thoughts? I feel like i'm missing something super basic

slammyde7113 commented 7 years ago

i'm an inch away from the finish line, i can feel it

slammyde7113 commented 7 years ago

Tried to convert the product to JSON but that didn't change the outcome

res.json({
            cart: req.cart.toJSON({ virtuals: true, user: req.user }),
            items: JSON.stringify(product.array)
          })
slammyde7113 commented 7 years ago

After taking Jordan's suggestion, I did this

const show = (req, res) => {
  const product = {}
  product.array = new Array()
  console.log(req.cart.product[1])
  for (let i = 0; i < req.cart.product.length-1; i++) {
  Product.find({_id: ObjectId(req.cart.product[i])},function(err,products){
         if (err) {
            res.sendStatus(500)
        } else {
          debugger
              console.log(products)
              console.log(product)
              product.array = products
              console.log(product.array)
            }
          })
        }
        req.cart.product = product.array
          res.json({
            cart: req.cart.toJSON({ virtuals: true, user: req.user })
          })
    }

Output:

{
    "cart": {
        "_id": "5953b153d2108941d15a7fe9",
        "updatedAt": "2017-06-28T13:38:27.406Z",
        "createdAt": "2017-06-28T13:38:27.406Z",
        "owner": "595153ad6f18427ef38c416b",
        "__v": 0,
        "product": [],
        "id": "5953b153d2108941d15a7fe9",
        "editable": false
    }
}

Looks much clearer but still the same problem where the product array isn't populated with product information. I'm going to try and implement what I found on this stackoverflow article: https://stackoverflow.com/questions/40057893/res-json-is-not-a-function-mongoose

BenGitsCode commented 7 years ago

Try taking off the -1 in your for condition.

console.log(products)
              console.log(product)
              product.array = products
              console.log(product.array)

And then instead of redefining product.array, try pushing products into product.array

What does that ouput?

slammyde7113 commented 7 years ago

Code:

const show = (req, res) => {
  const product = {}
  product.array = new Array()
  console.log(req.cart.product[1])
  for (let i = 0; i < req.cart.product.length; i++) {
  Product.find({_id: ObjectId(req.cart.product[i])},function(err,products){
         if (err) {
            res.sendStatus(500)
        } else {
              product.array.push(products)
              console.log(product.array)
            }
          })
        }
        req.cart.product = product.array
          res.json({
            cart: req.cart.toJSON({ virtuals: true, user: req.user })
          })
    }

Console.logs:

[ [ { _id: 5952b57ea52d092b8d34c6b0,
      name: 'test00000',
      price: 0,
      description: 'test',
      __v: 0 } ] ]
[ [ { _id: 5952b57ea52d092b8d34c6b0,
      name: 'test00000',
      price: 0,
      description: 'test',
      __v: 0 } ],
  [ { _id: 5952b57ea52d092b8d34c6b0,
      name: 'test00000',
      price: 0,
      description: 'test',
      __v: 0 } ] ]

URL Response:

{
    "cart": {
        "_id": "5953b153d2108941d15a7fe9",
        "updatedAt": "2017-06-28T13:38:27.406Z",
        "createdAt": "2017-06-28T13:38:27.406Z",
        "owner": "595153ad6f18427ef38c416b",
        "__v": 0,
        "product": [],
        "id": "5953b153d2108941d15a7fe9",
        "editable": false
    }
}
slammyde7113 commented 7 years ago

I kept getting an error when I was using .push before, I have no clue why its working now. The array looks solid but it has any extra item because req.car.product.length returns the length starting from 1 and the for loop starts at 0. Is there an extra step we have to take to prepare the cart.product information to be sent?

slammyde7113 commented 7 years ago

So I think the issue is that the req.cart.items is not being cast to a JSON object. I used JSON.stringify and it still yielded an empty array in the response. Is there a specific way to cast an object into JSON? Something other than JSON.stringify?

tvcrawley commented 7 years ago

Explain: req.cart.product = product.array

slammyde7113 commented 7 years ago

Replaces the product id in the cart object with the product object associated with it. So instead of just having an id, it has the name, price, and description of the product.

jordanallain commented 7 years ago

where we at with this @slammyde7113 ? same point?

slammyde7113 commented 7 years ago

solved it. Here are the edits I made: Schema

const cartSchema = new mongoose.Schema({
  product: {
    type: Array,
    required: false
  },
  owner: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'User',
    required: true
  },
  items: [{
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Product',
    required: false
  }]
}, {
  timestamps: true,
  toJSON: {
    virtuals: true,
    transform: function (doc, ret, options) {
      const userId = (options.user && options.user._id) || false
      ret.editable = userId && userId.equals(doc._owner)
      return ret
    }
  }
})

Controller

const show = (req, res) => {
  const product = {}
  product.array = new Array()
//  console.log(req.cart.product)
  const promises = []
  req.cart.product.forEach(function(id, i){
    promises.push(Product.find({_id: ObjectId(req.cart.product[i])}))
  })
  Promise.all(req.cart.product.map(function(id) {
    return Product.find({_id: ObjectId(id)})
  })).then(function(products){
    console.log(products)
    req.cart.product = products
    return res.json({
      cart: req.cart.toJSON({virtuals: true, user: req.user})
    })
  }).catch(function(err){
    return res.sendStatus(500)
  })
  }

The problem was that I was using synchronous and asynchronus functions (for loop and database query), so the controller was returning an empty response.