简体   繁体   中英

mongoose - get a nested array of objects from Model.find() and manipulate the inner array

I am trying to get an array of objects with a certain ID from my database inside a route of my server, and then add change a property of an object inside the array (instead of returning an objectID to my client I want to return the Document as an object with the specific ID).

This is my code:

        let orders = await Order.find({restaurant: restaurantID, status: 'PROCESSING'})

        for(let order of orders){   //iterate through all orders
            for(let element of order.dishes){     //iterate through order.dishes array (called 'element' since the array contains objects)
                let dish = await Dish.findOne({_id: element._id})
                element['dish'] = dish  //create new property for the dish object
                delete element._id      //remove the ID property since it already exists inside the element.dish object
            }
        }

Every order object inside orders contains an array called dishes , which contains objects with the properties amount and an id . Since my frontend can't do much with the ID, I want to delete the id property and add a new property called dish, which contains the dish object the id points to.

The problem I am facing is that I don't know how to manipulate the orders arrays content. Whenever I parse orders to JSON and sent it inside my response, I recieve a JSON array of order objects just like I want to. But when I add the code I pasted above, it does not change anything inside my orders.

Whenever I log the element inside my for loops it looks like this: EmbeddedDocument {__parentArray: Proxy, __index: 0, $__parent: model, $__: InternalCache, $isNew: false, …} but when I parse it to JSON I recieve exactely what I want, namely this: {"amount":1,"_id":"6183b84fec1c3e109a2271be"}

Is orders even an array in this case? If not, what would be the easiest way to manipulate it/get the documents as an array?

This is what orders looks like when I watch it in my debug window:

price (get):ƒ () {\n        return this[getSymbol].call(this.$__[scopeSymbol] || this, path);\n      }
price (set):ƒ (v) {\n        this.$set.call(this.$__[scopeSymbol] || this, path, v);\n      }
restaurant (get):ƒ () {\n        return this[getSymbol].call(this.$__[scopeSymbol] || this, path);\n      }
restaurant (set):ƒ (v) {\n        this.$set.call(this.$__[scopeSymbol] || this, path, v);\n      }
status (get):ƒ () {\n        return this[getSymbol].call(this.$__[scopeSymbol] || this, path);\n      }
status (set):ƒ (v) {\n        this.$set.call(this.$__[scopeSymbol] || this, path, v);\n      }
timestamp (get):ƒ () {\n        return this[getSymbol].call(this.$__[scopeSymbol] || this, path);\n      }
timestamp (set):ƒ (v) {\n        this.$set.call(this.$__[scopeSymbol] || this, path, v);\n      }
user (get):ƒ () {\n        return this[getSymbol].call(this.$__[scopeSymbol] || this, path);\n      }
user (set):ƒ (v) {\n        this.$set.call(this.$__[scopeSymbol] || this, path, v);\n      }
__v (get):ƒ () {\n        return this[getSymbol].call(this.$__[scopeSymbol] || this, path);\n      }
__v (set):ƒ (v) {\n        this.$set.call(this.$__[scopeSymbol] || this, path, v);\n      }
__proto__:Model
length:1

And in postman, the res.body looks like this (almost exactely like it should but instead of the dishes object it contains only the id):

[
    {
        "_id": "6183b84fec1c3e109a2271bd",
        "user": "6166bc426181646198fc483c",
        "restaurant": "6176947ce8b10986b018930e",
        "dishes": [
            {
                "amount": 1,
                "_id": "6183b84fec1c3e109a2271be"
            },
            {
                "amount": 2,
                "_id": "6183b84fec1c3e109a2271bf"
            }
        ],
        "price": 30,
        "status": "PROCESSING",
        "timestamp": "2021-11-04T10:39:11.800Z",
        "__v": 0
    }
]

If this was already solved, please send me a link to the question/answer. I spent the last few hours trying to solve this and I already looked at a lot of questions but none of the answers were helpful for me

It is a common question. People are often disconcerted because they can't alter the data coming from MongoDB.

The thing is, Mongoose is kind of weird; it does not return simple JSON, but constructs and returns a collection of Mongoose objects. These objects have extra methods, like .save() , but above all their nested objects are immutable . Simply changing their properties have no effect.

In order to modify the data returned by Mongoose, you have two possibilities :

  1. Use .markModified()

It's a really weird concept, but the changes you make won't be applied until you mark them as modified. For instance :

element['dish'] = dish;
order.save();

This will have no effect. The name will remain unchanged in Mongo. You need .markModified() :

element['dish'] = dish;
order.markModified("dishes");
order.save(); // Works now :|
  1. Tell Mongoose to return simple JSON

You can prevent Mongoose from constructing its own objects by using .lean() . this will return simple JSON data that can be tampered with at will.

let orders = await Order
           .find({restaurant: restaurantID, status: 'PROCESSING'})
           .lean()
           .exec(); // Also add .exec(), it returns a true Promise and not a thenable 

Bonus : lean() is faster and lighter.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM