简体   繁体   中英

Node, Mongoose, Express - REST api GET subdocument with id

Thought I'd check out node a couple days back and decided to create a simple API as an introduction to the platform. I am using node with express and mongoose. My issues at the moment revolve around trying to get a single subdocument from an array of subdocuments in my primary document.

My app has a simple structure. I have three models: place, address, and people. Each place embeds many addresses, and each address embeds many people (I picked this to get practice on subdocuments, and sub-subdocuments). The models themselves are separated into their own files as such:

./models/place.js

var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var AddressSchema = require('./address')

var PlaceSchema = new Schema({
    name: String,
    addresses: [AddressSchema.Schema]
});

module.exports = mongoose.model('Place', PlaceSchema);

.models/address.js

var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var PersonSchema = require('./person');

var AddressSchema = new Schema({
    street: String,
    city: String,
    state: String,
    zip: Number,
    people: [PersonSchema.Schema]
});

module.exports = mongoose.model('Address', AddressSchema);

.models/person.js

var mongoose = require('mongoose');
var Schema = mongoose.Schema;

var PersonSchema = new Schema({
    name: String,
});

module.exports = mongoose.model('Person', PersonSchema);

As we can see, a place can contain an array of addresses, and each address can contain an array of people.

Now as far as the my actual routes are concerned, I have no issue adding places, and adding addresses to places (I haven't drilled down to the person level as of yet). The issue I am having is trying to get a single address. I am thinking once I receive a single address, I can next assign a bunch of people to that address.

Here is the route that is causing trouble:

router.route('/places/:place_id/addresses/:address_id')

    .get(function(req, res) {
        Place.findById(req.params.place_id, function(err, place) {
            if (err)
                res.send(err);
            res.json(place.addresses.id(req.params.address_id));
        });
    });

From the mongoose docs, it looks like the .id() method is the goto in this situation, but I get a "TypeError" when doing this, with logs saying that the object has no method 'id'.

On the other hand, I am having zero issue getting all the addresses of a single place:

router.route('/places/:place_id/addresses') 

    .get(function(req, res) {
        Place.findById(req.params.place_id, function(err, place) {
            if (err)
                res.send(err);
            res.json(place.addresses);
        });
    })

I also have zero issue creating and saving places, creating and saving addresses to a place, etc.

I've tried to dig and find others with a similar issue. I've found others who have experienced the same type error, but typically it has had to do with the order of initialization of their models. Most seem to be initializing their models in one big document, in which order of initialization is important. I do not understand how that would be an issue here?

Hopefully this is just me doing something stupid as I'm pretty stumped. Any help is very much appreciated.

EDIT----------------------------------------------------------------------------------------------

I'm thinking my problems are revolving around the id value of my :address_id parameter in the GET request above.

Taking some inspiration from this post:

MongoDB, Mongoose: How to find subdocument in found document?

I updated my get function to the following (though I do not know why what I had initially isn't working):

router.route('/places/:place_id/addresses/:address_id')

    .get(function(req, res) {
        Place.findById(req.params.place_id, function(err, place) {
            if (err)
                res.send(err);

            var address = place.addresses.filter(function (address) {
                    return address._id === req.params.address_id;
             }).pop();

            res.json(address);
        });
    });

If instead of ._id I use .city for example to filter my addresses, then I return the correct addresses in the total array. Obviously this is not what I'm looking for. I'd like the return an address based on its ID.

Any help is appreciated.

In Mongoose there is a difference between the MongooseArray type and the MongooseDocumentArray type, which has the id() method you are looking for.

I was able to get your code working by moving the AddressSchema into the same file as PlaceSchema .

var mongoose = require('mongoose');
var Schema = mongoose.Schema;

var AddressSchema = Schema({
    street: String,
    city: String,
    state: String,
    zip: Number
});

var PlaceSchema = Schema({
    name: String,
    addresses: [AddressSchema]
});

module.exports = mongoose.model('Place', PlaceSchema);

Now the callback from Place.findById(...) shows place.addresses as a DocumentArray and the individual subdocuments are listed as an EmbeddedDocument .

I also moved the schemas back to their separate files. This time I made sure to export the schema instead of the model. This also works.

Inspecting the code further I have found the problem!

You are referencing Address.Schema . This should be Address.schema (note the lower case). Since Address.Schema is undefined , mongoose was accepting anything.

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