I have an app and when the user signs in, I want it to retrieve the FirmDoc
based on the user id passed through my currentUser
middleware (req.currentUser.id
). That user id is a mongoose.Types.ObjectID
(corresponds to _id
in mongoose). The FirmDoc has a users
property that is an array of ObjectID strings. I am new to mongoose so there may be some errors below but I ultimately want to know how to pull the FirmDoc per a given user id from the users property.
firm.ts:
import mongoose from 'mongoose';
import { FirmStatus } from "@mavata/common";
export { FirmStatus };
interface FirmAttrs {
domain: string;
name: string;
type?: string;
}
interface FirmModel extends mongoose.Model<FirmDoc> {
build(attrs: FirmAttrs): FirmDoc;
deleteById(id: string): FirmDoc;
}
interface FirmDoc extends mongoose.Document {
domain: string;
status: FirmStatus;
name: string;
type: string;
users: string[];
companies: string[];
}
const firmSchema = new mongoose.Schema({
domain: {
type: String,
required: true
},
name: {
type: String,
required: true
},
status: {
type: String,
enum: Object.values(FirmStatus),
default: FirmStatus.Created,
required: true,
},
type: {
type: String,
default: '',
required: false
},
users: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
default: [],
required: true
}],
companies: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Company',
default: [],
required: true
}]
},
{
toJSON: {
transform(doc, ret) {
ret.id = ret._id; // rename _id property to id
delete ret._id; // delete the _id property off the object
delete ret.__v; // delete useless property
}
}
});
firmSchema.pre('save', async function(done) {
console.log('firm saved of type ', this.type);
done();
});
firmSchema.statics.build = (attrs: FirmAttrs) => {
return new Firm(attrs);
};
firmSchema.statics.deleteById = async (id: string) => {
return await Firm.deleteOne({ _id: new mongoose.Types.ObjectId(id)});
};
const Firm = mongoose.model<FirmDoc, FirmModel>('Firm', firmSchema);
export { Firm };
index.ts (router):
import express, { Request, Response } from "express";
import { requireAuth } from "@mavata/common";
import { Firm } from "../models/firm";
const router = express.Router();
router.get("/api/firm", requireAuth, async (req: Request, res: Response) => {
const firms = await Firm.find({users: req.currentUser!.id}, (err: any, docs: any) => {
if (err) {
console.log(err);
} else {
console.log(docs);
}
})
res.send(firms);
});
export { router as indexFirmRouter };
CodePudding user response:
There are multiple ways to handle this with Mongoose, but as far as I can tell from this snippet, your current logic should work. You are querying all 'Firm' documents where the users field contains 'req.currentUser.id'.
Try hard-coding a user ID instead of passing the req just for debugging purposes. If that works, then obviously the dynamic variable is not passing correctly. Perhaps you are missing a 'req.params', 'req.headers', 'req.body' when pulling the user id? Depending on your authorization config.
Alternative...
See the mongoose docs regarding populate() and virtuals. Mongoose allows for creating a 'virtual' reference from one model to another. In your case, create a virtual on User pointing to Firm.
// user.ts
userSchema(
{...},
{toJSON: {virtuals: true}, toObject: {virtuals: true}}
);
userSchema.virtual('firm', {
ref: 'Firm',
localField: '_id',
foreignField: 'users',
justOne: false,
});
Then in your user route, you can pass the user id and reverse populate the Firm doc.
router.get("/api/user", requireAuth, async (req: Request, res: Response) => {
const userId = req.currentUser.id
const user = await User.findById(userId).populate('firm').exec();
if (!user) {
console.log('User not found');
} else {
console.log(user);
}
}
res.status(200).json(user);
});