Home > Enterprise >  mongoose move ref document to main document as DTO
mongoose move ref document to main document as DTO

Time:09-18

I wanna to move sub document to main documents, and return single DTO without any nested document, below is my sample data.js

data.js

const mongoose = require('mongoose');

//city
const citySchema = new mongoose.Schema({
    cityName: { type: String, required: true, unique: true },
});

const City = mongoose.model('City', citySchema);

//country
const countrySchema = new mongoose.Schema({
    countryName: { type: String, required: true, unique: true },
});

const Country = mongoose.model('Country', countrySchema);

//user
const userSchema = new mongoose.Schema({
    username: { type: String, required: true },
    city: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'City',
        required: true,
    },
    country: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'Country',
        required: true,
    },
    createdAt: { type: Date, required: true },
    updatedAt: { type: Date, required: true },
});

const User = mongoose.model('User', userSchema);


function getUser(id) {
    return User.findById(id)
        .populate('city')
        .populate('country')
        .exec();
};

Current return JSON Response for User:

{
  "_id": "6321ac3d14a57c2716f7f4a0",
  "name": "David",
  "city": {
      "_id": "63218ce557336b03540c9ce9",
      "cityName": "New York",
      "__v": 0
  },
  "country": {
      "_id": "632185bbe499d5505cafdcbc",
      "countryName": "USA",
      "__v": 0
  },
  "createdAt": "2022-09-14T10:26:05.000Z",
  "__v": 0
}

How do I move the cityName and countryName to main model, and response JSON as below format?

{
  "_id": "6321ac3d14a57c2716f7f4a0",
  "username": "David",
  "cityName": "New York",
  "countryName": "USA",
  "createdAt": "2022-09-14T10:26:05.000Z",
}

CodePudding user response:

Using aggregation you can try something like this:

db.user.aggregate([
  {
    "$match": {
      _id: "6321ac3d14a57c2716f7f4a0"
    }
  },
  {
    "$lookup": {
      "from": "city",
      "localField": "city",
      "foreignField": "_id",
      "as": "city"
    }
  },
  {
    "$lookup": {
      "from": "country",
      "localField": "country",
      "foreignField": "_id",
      "as": "country"
    }
  },
  {
    "$addFields": {
      "country": {
        "$arrayElemAt": [
          "$country",
          0
        ]
      },
      "city": {
        "$arrayElemAt": [
          "$city",
          0
        ]
      }
    }
  },
  {
    "$addFields": {
      "countryName": "$country.countryName",
      "cityName": "$city.cityName"
    }
  },
  {
    "$unset": [
      "country",
      "city"
    ]
  }
])

Here's the playground link. The other probably simpler way of doing this would be, modify your function like this:

function getUser(id) {
    const user = User.findById(id)
        .populate('city')
        .populate('country')
        .exec();
    if(user.country) {
      user.countryName = user.country.countryName;
    }
    if(user.city) {
      user.cityName = user.city.cityName;
    }
    delete user.country;
    delete user.city;
    return user
};
  • Related