Mongoose: add a transform property to populate objects

Created on 8 Oct 2018  路  3Comments  路  Source: Automattic/mongoose

Linus on slack posed an interesting question,

Is there any way I can choose how populate should be returned? If I for example want to populate('user', 'name contact.address'), the return object will be { name: 'foo', contact.address: 'bar' }
can I in any way get { name: 'foo', address 'bar' } as returnobject from populate instead?

I think it would be cool to have a transform property on the populate object that would be applied to each doc returned from the population before it gets assigned back to the document's path.

linus.js

#!/usr/bin/env node
'use strict';

const assert = require('assert');
const mongoose = require('mongoose');
const { Schema, connection} = mongoose;
const DB = 'linus';
const URI = `mongodb://localhost:27017/${DB}`;
const OPTS = { family: 4, useNewUrlParser: true };

const userSchema = new Schema({
  name: String,
  contact: {
    address: String,
    phone: String
  },
  friends: [{
    type: Schema.Types.ObjectId,
    ref: 'user'
  }]
});

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

const ted = new User({
  name: 'Ted',
  contact: {
    address: '123 Wild Stallions blvd, Long Beach, CA 90808',
    phone: '555-123-4567'
  }
});

const bill = new User({
  name: 'Bill',
  contact: {
    address: '456 Woah blvd, Long Beach, CA 90808',
    phone: '555-123-4568'
  }
});

bill.friends.push(ted._id);
ted.friends.push(bill._id);

async function run() {
  await mongoose.connect(URI, OPTS);
  await connection.dropDatabase();
  const docs = await User.create([bill, ted]);
  const popObj = {
    path: 'friends',
    select: 'name contact.address',
    transform: function(friend) {
      friend.address = friend.contact.address;
      delete friend.contact;
    }
  };
  let popped = await User.findOne({ _id: ted._id }).populate(popObj);
  assert.strictEqual(popped.friends[0].contact, undefined, 'transform not yet implemented.');
  assert.strictEqual(popped.friends[0].address, '456 Woah blvd, Long Beach, CA 90808');
  console.log('All Assertions Pass.');
  await connection.close();
}

run().catch(error);

function error(e) {
  console.error(e);
  return connection.close();
}

Current Output:

slack: ./linus.js
{ AssertionError [ERR_ASSERTION]: transform not yet implemented.
    at run (/Users/lineus/dev/node/mongoose/slack/linus.js:57:10)
    at process._tickCallback (internal/process/next_tick.js:68:7)
  generatedMessage: false,
  name: 'AssertionError [ERR_ASSERTION]',
  code: 'ERR_ASSERTION',
  actual: { address: '456 Woah blvd, Long Beach, CA 90808' },
  expected: undefined,
  operator: 'strictEqual' }
slack:

Desired Output:

slack: ./linus.js
All Assertions Pass.
slack:
discussion enhancement

Most helpful comment

@vkarpov15 yeah, a virtual getter solves this already ( I can't believe this never even crossed my mind )

This works ( full gist here ):

adding a virtual with getter:

userSchema.virtual('$friends').get(function() {
  return this.friends.map(friend => {
    return {
      address: friend.contact.address,
      name: friend.name
    };
  });
});

and adding the $ to the assertions:

  assert.strictEqual(popped.$friends[0].contact, undefined);
  assert.strictEqual(popped.$friends[0].address, '456 Woah blvd, Long Beach, CA 90808');

All 3 comments

That's an interesting idea. But can't you already do this with virtual populate and a getter?

@vkarpov15 yeah, a virtual getter solves this already ( I can't believe this never even crossed my mind )

This works ( full gist here ):

adding a virtual with getter:

userSchema.virtual('$friends').get(function() {
  return this.friends.map(friend => {
    return {
      address: friend.contact.address,
      name: friend.name
    };
  });
});

and adding the $ to the assertions:

  assert.strictEqual(popped.$friends[0].contact, undefined);
  assert.strictEqual(popped.$friends[0].address, '456 Woah blvd, Long Beach, CA 90808');

I'll close this for now. Re-open if this is still an issue.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

efkan picture efkan  路  3Comments

lukasz-zak picture lukasz-zak  路  3Comments

Soviut picture Soviut  路  3Comments

p3x-robot picture p3x-robot  路  3Comments

adamreisnz picture adamreisnz  路  3Comments