Mongoose custom population method that can be chained after find()

26 Views Asked by At

I have two models - Post and User, every post has a User. Post has user id. I want to create a custom static/instance method in Post model so I can populate only several properties of User - id, name, image.

I want to implement it something like example below. But I don't see this method after find()

PostSchema.method("populateAuthorProfile", function () {
  return this.populate("author", { id: 1, slug: 1, name: 1, image: 1 });
});

...
...

const posts = await PostModel.find().populateAuthorProfile();

Post model

const mongooseSlugUpdater = require("mongoose-slug-updater");
import mongoose, { Model, Schema, model } from "mongoose";

interface IPost {
  title: string;
  description: string;
  body: string;
  created: Date;
  updated: Date;
  image: string;
  slug: string;
  parent?: mongoose.Types.ObjectId;
  author: mongoose.Types.ObjectId;
  children: mongoose.Types.ObjectId;
  tags: mongoose.Types.ObjectId[];
  likes: mongoose.Types.ObjectId[];
}

// post instance methods
interface IPostMethods {
  populateAuthorProfile: () => any;
}

// post static methods
interface IPostModel extends Model<IPost, {}, IPostMethods> {
  findBySlug: (slug: string) => any;
}

const PostSchema = new Schema<IPost, IPostModel, IPostMethods>({
  title: { type: String, trim: true, required: true },
  description: { type: String, trim: true, required: true },
  body: { type: String, trim: true, required: true },
  created: { type: Date, default: Date.now() },
  updated: { type: Date },
  image: { type: String },
  slug: { type: String, slug: "title", slugPaddingSize: 4, unique: true },
  parent: {
    type: Schema.Types.ObjectId,
    ref: "Post",
  },
  author: {
    type: Schema.Types.ObjectId,
    ref: "User",
  },
  tags: [
    {
      type: Schema.Types.ObjectId,
      ref: "Tag",
    },
  ],
  likes: [
    {
      type: Schema.Types.ObjectId,
      ref: "User",
    },
  ],
  children: {
    type: Schema.Types.ObjectId,
    ref: "Post",
  },
});

// methods
PostSchema.method("populateAuthorProfile", function () {
  return this.populate("author", { id: 1, slug: 1, name: 1, image: 1 });
});
// statics

PostSchema.static("findBySlug", function (slug: string) {
  return this.findOne({ slug: slug });
});

PostSchema.plugin(mongooseSlugUpdater);

PostSchema.set("toJSON", {
  transform: (_document, returnedObject) => {
    returnedObject.id = returnedObject._id.toString();
    delete returnedObject._id;
    delete returnedObject.__v;
  },
});

const PostModel = model<IPost, IPostModel>("Post", PostSchema);

export default PostModel;

User model

import mongoose, { Model, Schema, model } from "mongoose";
const mongooseSlugUpdater = require("mongoose-slug-updater");

// user schema
interface IUser {
  name: string;
  bio?: string;
  email: string;
  image?: string;
  passwordHash?: string;
  emailVerified?: boolean;
  created: Date;
  updated?: Date;
  slug: string;
  roles: mongoose.Types.ObjectId[];
  posts: mongoose.Types.ObjectId[];
  likedPosts: mongoose.Types.ObjectId[];
  accounts: mongoose.Types.ObjectId[];
}

interface IUserAccount {
  id: string;
  name: string;
  email: string;
  image?: string;
  roles: mongoose.Types.ObjectId[];
}

// user instance methods
interface IUserMethods {
  toUserAccount: () => IUserAccount;
}

// user static methods
interface IUserModel extends Model<IUser, {}, IUserMethods> {
  findByEmail: (email: string) => any;
}

const UserSchema = new Schema<IUser, IUserModel, IUserMethods>({
  name: { type: String },
  bio: { type: String },
  email: { type: String, required: true, unique: true },
  image: { type: String },
  passwordHash: { type: String },
  emailVerified: { type: Boolean, default: false },
  created: { type: Date, default: Date.now() },
  updated: { type: Date },
  slug: { type: String, slug: "email", slugPaddingSize: 4, unique: true },
  roles: [
    {
      type: Schema.Types.ObjectId,
      ref: "Role",
    },
  ],
  posts: [
    {
      type: Schema.Types.ObjectId,
      ref: "Post",
    },
  ],
  likedPosts: [
    {
      type: Schema.Types.ObjectId,
      ref: "Post",
    },
  ],
  accounts: [
    {
      type: Schema.Types.ObjectId,
      ref: "Account",
    },
  ],
});

// methods

UserSchema.method("toUserAccount", function () {
  const userAccout: IUserAccount = {
    id: this._id.toString(),
    name: this.name,
    email: this.email,
    image: this.image,
    roles: this.roles,
  };

  return userAccout;
});

// statics
UserSchema.static("findByEmail", function (email: string) {
  return this.findOne({ email: email });
});

UserSchema.plugin(mongooseSlugUpdater);

UserSchema.set("toJSON", {
  transform: (_document, returnedObject) => {
    returnedObject.id = returnedObject._id.toString();
    delete returnedObject._id;
    delete returnedObject.passwordHash;
    delete returnedObject.__v;
  },
});

const UserModel = model<IUser, IUserModel>("User", UserSchema);

export default UserModel;

0

There are 0 best solutions below