I am trying to populate some collection in another collection. However without specifying model in populate method it is not replacing with actual document. Here are my code
cabinet.schema.ts
import {Prop, Schema, SchemaFactory} from '@nestjs/mongoose';
import { Date, HydratedDocument, Model, Schema as MongooseSchema } from 'mongoose';
export type CabinetDocument = HydratedDocument<Cabinet>;
export const CabinetProjection = {
_id: false,
__v: false,
}
@Schema()
export class Cabinet {
@Prop({
required: true,
unique: true
})
id: string;
@Prop()
items: [{
item: { type: MongooseSchema.Types.ObjectId, ref: "Item" },
status: string;
amount: number;
}];
}
export const CabinetSchema = SchemaFactory.createForClass(Cabinet)
item.schema.ts
import {Prop, Schema, SchemaFactory} from '@nestjs/mongoose';
import mongoose, { HydratedDocument } from 'mongoose';
export type ItemDocument = HydratedDocument<Item>;
export const ItemProjection = {
_id: false,
__v: false,
}
@Schema()
export class Item {
@Prop({
required: true,
unique: true
})
qId: string;
@Prop()
name: string;
@Prop()
description: string;
}
export const ItemSchema = SchemaFactory.createForClass(Item)
cabinet.service.ts
import { Injectable } from '@nestjs/common';
import { CreateCabinetDto } from './dto/create-cabinet.dto';
import { UpdateCabinetDto } from './dto/update-cabinet.dto';
import { Cabinet, CabinetProjection, CabinetSchema } from './schemas/cabinet.schema';
import { Model } from 'mongoose';
import { InjectModel } from '@nestjs/mongoose';
@Injectable()
export class CabinetService {
constructor(@InjectModel(Cabinet.name) private cabinetModel: Model<Cabinet>) {}
async findOne(id: string): Promise<Cabinet> {
const result = this.cabinetModel.findOne({
id: id,
}, CabinetProjection).populate({path: "items.item", model: "Item"}).exec();
return result;
}
}
With this setup it works perfectly, but if i remove model: "Item", instead of Item document populated, i see object reference. Isn't ref: "Item" parameter sufficent?
Also my collection names are cabinets and items.
*Edit:
Appearently the problem was in the Cabinet class structure. If i change to below code, it started to work without model parameter. Any explanation would be appreciated.
@Schema()
export class Cabinet {
@Prop({
required: true,
unique: true
})
id: string;
@Prop({ type: [CabinetItem] })
items: CabinetItem[];
}
@Schema()
export class CabinetItem{
@Prop({ type: MongooseSchema.Types.ObjectId, ref: Item.name })
item: Item,
@Prop()
status: string;
@Prop()
amount: number;
}
This is one of the biggest sources of confusion when I'm working with developers using mongoose. Hopefully this will explain.
When I set up a
refon a schema property I need to specify the model that I want mongoose to use to do thepopulateand replace all of myObjectIdwith the actual documents from the referenced collection.Here is a simple example of users that listen to songs:
populateall theUser.songsmongoose will first look at the options object of thepopulatemethod for themodelparameter. If it's not there therefvalue from the schema will be used to look for which model to use.mongoose.connection.modelsto find thatSongmodel and it can now use it to do thepopulate. Under the hood it uses functions calledmodelNamesFromRefPathandgetModelsMapForPopulate.populateto work bothUserandSongmodels would need to be registered on the same connection. In my case I used the defaultmongoose.connectconnection so both my models are registered onmongoose.connection.models. However, some libraries usemongoose.createConnectionand register one model on one connection and another model on a different connection. In that scenario you would have to pass the actual model and not just the name of the model as a parameter topopulatelike so:So to answer your question "Isn't ref: "Item" parameter sufficient?", yes it is sufficient, normally. It may be that the
Itemmodel is not in scope within that class when you runpopulateor it's theNest.jslibrary that needs the model name as part of it's own model/connection management at that stage in your design. You should try to import theitem.schemainto thecabinet.schemato see if you still need to explicity stateItemin thepopulate.