I have a Post Model:
const PostSchema = new Schema<IPost>(
{
// ...
likes: [{ type: Schema.Types.ObjectId, ref: "User" }],
// ...
}
)
export default model<IPost>("Post", PostSchema)
export interface IPost {
// ...
likes: ObjectId[]
// ...
}
export interface IPostDocument extends Document, IPost {}
And I'm trying to toggle a user like:
export const toggleLike: TController = async (req, res, next) => {
const user = req.user as IUserDocument;
const userId = user._id;
const postId = req.params.postId;
try {
const disliked = await PostModel.findOneAndUpdate(
{ _id: postId, likes: userId },
{ $pull: { likes: userId } }
); // works with no problem
if (disliked)
res.json({ message: `User ${userId} disliked post ${postId}` });
else {
const liked = await PostModel.findOneAndUpdate(
{ _id: postId },
{ $push: { likes: userId } }
); // the $push throws an error "Type instantiation is excessively deep and possibly infinite."
if (liked) res.json({ message: `User ${userId} liked post ${postId}` });
else return next(createError(404, "Post not found"));
}
} catch (error) {
next(createError(500, error as Error));
}
};
The mongo $push operator is throwing an error "Type instantiation is excessively deep and possibly infinite."
I doubt it helps but the description of the error is:
(property) likes?: _AllowStringsForIds<(((((((((((... | any[])[] | any[])[] | any[])[] | any[])[] | any[])[] | any[])[] | any[])[] | any[])[] | any[])[] | any[])[] | any[])[] | any[]> | ArrayOperator<(_AllowStringsForIds<(((((((((((... | any[])[] | any[])[] | any[])[] | any[])[] | any[])[] | any[])[] | any[])[] | any[])[] | any[])[] | any[])[] | any[])[] | any[]> | undefined)[]> | undefined
Any idea what's happening?
In mongoose, the ObjectId type is used not to create a new uuid, rather it is mostly used to reference other documents. Here is an example: var mongoose = require('mongoose'); var Schema = mongoose. Schema, ObjectId = Schema.
_id field is auto generated by Mongoose and gets attached to the Model, and at the time of saving/inserting the document into MongoDB, MongoDB will use that unique _id field which was generated by Mongoose.
yes, the unique: true index guarantees it "in this one collection" - the algorithm "almost" guarantees it universally.
I guess your question is very relevant to this one mine: Nestjs: Correct schema for array of subdocuments in mongoose (without default _id or redefine ObjectId). Except I don't want to have ObjectID
but you are.
The problem is that MongooseDocument<Array>
and TS Array
are pretty the same, but have different methods.
I am working with Nestjs, so I will provide an example for it, but I guess you found my example very useful.
For example, for array of embed documents you need to have the following schema interface:
In child schema:
@Schema()
class Field extends Document {
@Prop({ type: MongooseSchema.Types.ObjectId })
_id: MongooseSchema.Types.ObjectId
@Prop({ type: String })
name: string;
}
export const FieldSchema = SchemaFactory.createForClass(Field);
In parent schema:
@Prop({ _id: true, type: [FieldSchema] })
field: MongooseSchema.Types.Array<Field>;
And the most important part are imports:
import { Document, Schema as MongooseSchema, Types } from "mongoose";
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
Of course in the default TS project, without Nest.js you don't have nest/mongoose
but as you may already understand, everything is relevant with interfaces and its types we are taking from mongoose
itself.
So class
is a Schema interface, which is extended from mongoose Document
interface and all the arrays are not just plain TS arrays, but Schema.Types.Array (from mongoose). You may also use not just ObjectId
, but other special Mongo only types, like Decimal128
numbers and Mixed
and so on, according to the docs.
So in your case with Arrays of ObjectIds, you just need to use:
MongooseSchema.Types.Array<MongooseSchema.Types.ObjectId>
or a bit more primitive way:
In Nest in @Prop decorator you provide type for mongoDB field type, so it's the same as default TS:
new Schema({ languages: { type: ObjectId} })
So I hope it will help you in your project.
Importing ObjectId from mongoose on my interface declaration solved the issue. Before that, I was using it but without importing it from anywhere (I didn't even know what that was).
I am not sure but it might be interesting with: The type script side might be expecting other items to come there, because it's focused as an array.
Could you try to use each { $push: { : { $each: [ , ... ] } } }
value1 is enough on your position
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With