Last active
October 20, 2024 09:06
-
-
Save sagar88826/e137a291be930c9a149ba2d5ce4433a6 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import mongoose, { InferSchemaType, Require_id } from "mongoose"; | |
// ########################################################################################## | |
// ######################################BLUEPRINT START##################################### | |
// ########################################################################################## | |
// Speciality of this blueprint | |
// 1. It will convert the document object array type to lean object array type | |
// 2. It will handle both optional and required fields of type array and object as well as nested object and array | |
// 3. No manual document interface creation is required | |
// Rules | |
// 1. Always use `as const` to define schema definition | |
// 2. Always use `InferSchemaType<typeof schema>` to get the raw object type | |
// 3. Always use `LeanDocumentArray<T>` to get the lean object type | |
// 4. Always use `mongoose.HydratedDocument` to define the hydrated object type | |
// 5. Always use `mongoose.Model` to define the model type | |
// Definitions | |
// 1. SchemaTypeOrder: This is the raw object type of the schema | |
// 2. LeanedTypeOrder: This is the lean object type of the schema | |
// 3. OrderHydratedDocument: This is the hydrated object type of the schema | |
/** | |
* Now it will convert the document object array type to lean object array type | |
* It will handle both optional and required fields of type array and object as | |
* well as nested object and array and this utility is only for ARRAYS | |
*/ | |
export type LeanDocumentArray<T> = { | |
[K in keyof T]: T[K] extends mongoose.Types.DocumentArray<infer U> | |
? Array<Require_id<U>> | |
: T[K] extends mongoose.Types.DocumentArray<infer U> | null | undefined | |
? Array<Require_id<U>> | null | undefined | |
: T[K] extends Date | |
? Date | |
: T[K] extends Date | null | undefined | |
? Date | null | undefined | |
: T[K] extends Record<string, any> | |
? LeanDocumentArray<T[K]> | |
: T[K] extends Record<string, any> | null | undefined | |
? LeanDocumentArray<T[K]> | null | undefined | |
: T[K]; | |
}; | |
// ######################################HANDLING ARRAYS##################################### | |
const orderSchemaDefinition = { | |
rname: { type: String, required: true }, | |
oname: String, | |
remail: { type: String, required: true }, | |
oemail: String, | |
ravatar: { type: String, required: true }, | |
oavatar: String, | |
rtags: { | |
type: [{ name: { type: String, required: true } }], | |
required: true, | |
}, | |
otags: [{ name: { type: String, required: true } }], | |
rObj: { | |
type: { | |
rname: { type: String, required: true }, | |
rtag: { | |
type: [ | |
{ | |
name: { type: String, required: true }, | |
}, | |
], | |
required: true, | |
}, | |
}, | |
required: true, | |
}, | |
oObj: { | |
oname: String, | |
otag: { | |
type: [ | |
{ | |
name: { type: String, required: false }, | |
}, | |
], | |
required: false, | |
}, | |
}, | |
} as const; | |
const orderSchema = new mongoose.Schema(orderSchemaDefinition); | |
// ######################################Types##################################### | |
// Schema object type | |
type SchemaTypeOrder = InferSchemaType<typeof orderSchema>; // This can be used for picking Types.DocumentArray type only | |
// Lean object type | |
type LeanedTypeOrder = LeanDocumentArray<SchemaTypeOrder>; // These are the type which is return by toObject() method and lean() method | |
// Hydrated Schema object Type | |
type HydratedDocumentTypeOrder = mongoose.HydratedDocument< | |
LeanedTypeOrder, // lean type and it is used by toObject() method directly so therefore anything which you want to change then do it 2nd argument of HydratedDocument | |
SchemaTypeOrder | |
>; | |
// Model type | |
type ModelTypeOrder = mongoose.Model< | |
LeanedTypeOrder, | |
{}, | |
{}, | |
{}, | |
HydratedDocumentTypeOrder | |
>; | |
// ################################################################################ | |
// Model | |
const OrderModel = mongoose.model<LeanedTypeOrder, ModelTypeOrder>( | |
"Order", | |
orderSchema | |
); | |
const doc = new OrderModel({ tags: [{ name: "test" }] }); | |
doc._id; // mongoose.Types.ObjectId // this should be string | |
doc.rname; // string | |
doc.oname; // string | null | undefined | |
doc.ravatar; // string | |
doc.oavatar; // string | null | undefined | |
doc.rtags; // mongoose.Types.DocumentArray<{ name: string }> // this should be Array<{ name: string, _id:string }> and this will come from | |
doc.otags; // mongoose.Types.DocumentArray<{ name: string }> | null | undefined // this should be Array<{ name: string, _id:string }> and this will come from | |
doc.toObject().rtags; // ({ name: string } & { _id: mongoose.Types.ObjectId })[] | |
// ##################################HANDLING POPULATION################################### | |
const parentSchemaDefintion = { | |
name: String, | |
children: { type: mongoose.Schema.Types.ObjectId, ref: "Child" }, | |
} as const; | |
const parentSchema = new mongoose.Schema(parentSchemaDefintion); | |
// ######################################Types##################################### | |
// Schema object type | |
type SchemaTypeParent = InferSchemaType<typeof parentSchema>; // This can be used for model creation of hydrate object type | |
// Lean object type | |
type LeanedTypeParent = LeanDocumentArray<SchemaTypeParent>; // This can be used for lean object type | |
// Hydrated Schema object Type | |
type ParentHydratedDocument = mongoose.HydratedDocument< | |
LeanedTypeParent, | |
SchemaTypeParent | |
>; | |
// Model type | |
type ParentModelType = mongoose.Model< | |
LeanedTypeParent, | |
{}, | |
{}, | |
{}, | |
ParentHydratedDocument | |
>; | |
// ################################################################################ | |
// Model | |
export const ParentModel = mongoose.model<LeanedTypeParent, ParentModelType>( | |
"Parent", | |
parentSchema | |
); | |
const childSchemaDefinition = { | |
name: String, | |
rtag: { type: [{ name: { type: String, required: true } }], required: true }, | |
} as const; | |
const childSchema = new mongoose.Schema(childSchemaDefinition); | |
// ######################################Types##################################### | |
// Schema object type | |
type SchemaTypeChild = InferSchemaType<typeof childSchema>; // This can be used for model creation of hydrate object type | |
// Lean object type | |
type LeanedTypeChild = LeanDocumentArray<SchemaTypeChild>; // This can be used for lean object type | |
// Hydrated object Type | |
type HydratedDocumentTypeChild = mongoose.HydratedDocument< | |
LeanedTypeChild, | |
SchemaTypeChild | |
>; | |
// Model type | |
type ChildModelType = mongoose.Model< | |
LeanedTypeChild, | |
{}, | |
{}, | |
{}, | |
HydratedDocumentTypeChild | |
>; | |
// ################################################################################ | |
// Model | |
export const ChildModel = mongoose.model<LeanedTypeChild, ChildModelType>( | |
"Child", | |
childSchema | |
); | |
// Now testing will begin | |
export async function test() { | |
const parent = await ParentModel.create({ name: "parent" }); | |
const child = await ChildModel.create({ name: "child" }); | |
parent.children = child._id; | |
await parent.save(); | |
const populatedParentDoc = await ParentModel.findById(parent._id) | |
.populate<{ children: HydratedDocumentTypeChild | null }>("children") | |
.orFail(); | |
const plainPopulatedParentObj = { | |
...populatedParentDoc.toObject(), // because its an instance of Mongoose document | |
children: populatedParentDoc.children | |
? populatedParentDoc.children.toObject() | |
: null, | |
}; | |
if (plainPopulatedParentObj.children) { | |
plainPopulatedParentObj.children._id; // mongoose.Types.ObjectId (required field) | |
plainPopulatedParentObj.children.name; // string | null | undefined (optional field) | |
plainPopulatedParentObj.children.rtag; // ({ name: string } & { _id: mongoose.Types.ObjectId })[] (required field) | |
} | |
} | |
// ######################################################################################## | |
// ######################################BLUEPRINT END##################################### | |
// ######################################################################################## |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment