Change Typegoose collection name after initialize - mongodb

I am using typegoose to create models. During creation of model, I found that it is possible to provide collection name. But once it is assigned, I am not able to find way to modify it.
export const MyModel: ModelType<MyModel> = new MyModel().setModelForClass(MyModel, {
existingMongoose: mongoose,
schemaOptions: {collection: 'my_collection_name'}
});
So in above MyModel, I want to change collection name where I am importing.
How can I change a collection name in model? Or am I only left with the option of creating this model where I want to use it?

Never mind. I just had to make function of exported object. So I changed it to below, so that I can pass collectionName where I am consuming this model.
const DocumentFieldBooleanValueModel = (collectionName: string) : ReturnModelType<typeof DocumentFieldBooleanValue, BeAnObject> => {
return getModelForClass(DocumentFieldBooleanValue, {
schemaOptions: { collection: collectionName },
});
};
export { DocumentFieldBooleanValueModel };
So now above exported model function I can use as below.
DocumentFieldBooleanValueModel('MyCustomCollectionName')
And it will give same typegoose model.

Related

Mongodb Ref dynamic populate with grapqhl?

I have to decide whether to populate or not according to the query request, but I don't know how to do it.
So Example
If my model User is looks like this
below syntax is from typegoose and typegraphql
class User {
#Field()
#prop()
name: string;
#Field()
#prop(ref:"House")
house: Ref<House>
}
And here is two diffent query
Query1
user {
name
}
Query2
user {
name
house {
location
}
}
And in the resolver
User: () => {
const user = UserModel.find(blahblah)**.populate("house")**
return user
}
Query1 dose not need populate
but Query2 need
in same resolver!
I want to decide whether to populate or not depending on the requirements of the query.
I can't decide whether to populate or not without knowing what is the actual query was in resolver.
I found very similar question in stackoverflow
But there is not proper answer...
Solving relationships in Mongoose and GraphQL
i dont know much about graphql, but i think there is some method to get if that path is requested, so the following should work:
let query = Model.find(basicQuery);
if (req.path) { // replace this with graphql's method to get if the path is requested
query = query.populate(yourPath);
}
const doc = await query.exec();
PS: but as an comment already noted, i think there is some better method to do this in graphql (another resolver?)

Missing Subdocument Methods in Mongoose with Typescript

I'm working on a project and need to retrieve specific subdocuments from a model by their subdocument _id's. I then plan on making updates to those subdocuments and saving the main document. The mongoose subdocument documentation lists a number of methods you can call on the parent.children array, but methods that don't already exist in Javascript for arrays give an error saying they do not exist and it doesn't compile. I'm referencing this documentation: https://mongoosejs.com/docs/subdocs.html
I understand that should be able to use .findOneAndUpdate to make my updates, and using the runValidators option everything should still be validated, but I would also like to just retrieve the subdocument itself as well.
I looked at this post: MongoDB, Mongoose: How to find subdocument in found document? , and I will comment that the answer is incorrect that if a subdocument schema is registered it automatically creates a collection for that schema, the collection is only made if that schema is saved separately. You cannot use ChildModel.findOne() and retrieve a subdocument, as the collection does not exist, there is nothing in it.
Having IChildModel extend mongoose.Types.Subdocument and having the IParent interface reference that instead of IChild and not registering the ChildModel does not change anything other than no longer allowing calls to .push() to not accept simple objects (missing 30 or so properties). Also trying mongoose.Types.Array<IChild> in the IParent interface with this method does not change anything.
Changing the IParent interface to use mongoose.Types.Array<IChild> for the children property allows addToSet() to work, but not id() or create()
I'm using Mongoose version 5.5.10, MongoDb version 4.2.0 and Typescript version 3.4.5
import mongoose, { Document, Schema } from "mongoose";
// Connect to mongoDB with mongoose
mongoose.connect(process.env.MONGO_HOST + "/" + process.env.DB_NAME, {useNewUrlParser: true, useFindAndModify: false});
// Interfaces to be used throughout application
interface IParent {
name: string;
children: IChild[];
}
interface IChild {
name: string;
age: number;
}
// Model interfaces which extend Document
interface IParentModel extends IParent, Document { }
interface IChildModel extends IChild, Document { }
// Define Schema
const Child: Schema = new Schema({
name: {
type: String,
required: true
},
age: {
type: Number,
required: true
}
});
const ChildSchema: Schema = Child;
const Parent: Schema = new Schema({
name: {
type: String,
required: true
},
children: [ChildSchema]
});
const ParentSchema: Schema = Parent;
// Create the mongoose models
const ParentModel = mongoose.model<IParentModel>("Parent", Parent);
const ChildModel = mongoose.model<IChildModel>("Child", Child);
// Instantiate instances of both models
const child = new ChildModel({name: "Lisa", age: 7});
const parent = new ParentModel({name: "Steve", children: [child]});
const childId = child._id;
// Try to use mongoose subdocument methods
const idRetrievedChild = parent.children.id(childId); // Property 'id' does not exist on type 'IChild[]'.ts(2339)
parent.children.addToSet({ name: "Liesl", age: 10 }); // Property 'addToSet' does not exist on type 'IChild[]'.ts(2339)
parent.children.create({ name: "Steve Jr", age: 2 }); // Property 'create' does not exist on type 'IChild[]'.ts(2339)
// If I always know the exact position in the array of what I'm looking for
const arrayRetrievedChild = parent.children[0]; // no editor errors
parent.children.unshift(); // no editor errors
parent.children.push({ name: "Emily", age: 18 }); // no editor errors
Kind of a late response, but I looked through the typings and found the DocumentArray
import { Document, Embedded, Types } from 'mongoose';
interface IChild extends Embedded {
name: string;
}
interface IParent extends Document {
name: string;
children: Types.DocumentArray<IChild>;
}
Just wanted to put this here incase anyone else needs it.
Gross:
For now, I'm going with a very quick and dirty polyfill solution that doesn't actually answer my question:
declare module "mongoose" {
namespace Types {
class Collection<T> extends mongoose.Types.Array<T> {
public id: (_id: string) => (T | null);
}
}
}
then we declare IParent as such:
interface IParent {
name: string;
children: mongoose.Types.Collection<IChild>;
}
Because the function id() already exists and typescript just doesn't know about it, the code works and the polyfill lets it compile.
Even Grosser: Otherwise for an even quicker and dirtier solution, when you create the parent model instance simply typecast it to any and throw out all typescript checks:
const child = new ChildModel({name: "Lisa", age: 7});
const parent: any = new ParentModel({name: "Steve", children: [child]});
const idRetrievedChild = parent.children.id(childId); // works because declared any

Updating SOME fields in nested object without overwriting others with spread operator

How to update some field in nested object without overwriting other fields in that nested object with spread operator? My function is as follows
exports.handler = ((data, context) => {
const profile = data.profile
const uid = context.auth.uid
const newRef = db.collection("user").doc(uid)
return newRef.update({
profile: {...profile}
}).then(() => {
return "Data updated seccusfully"
})
})
which is suppose to update nested profile object. However the function removes also all not specified fields in that object. Is there any way to achieve update object with spread operator without removing other fields or do we have to specify each fields as the documentation says?
db.collection("users").doc("frank").update({
"age": 13,
"favorites.color": "Red"
})
My profile object contains different fields for each type of user and I'm updating the profile fileds in multiple places therefore I was hoping to simplify it like this
It looks like we can update some object properties without overwriting others with set method like this. Strange it doesn't work with update
return newRef.set({
profile: { ...profile }
}, { merge: true }).then(() => {
return "Data updated seccusfully"
})
Really appreciate all your help

Mongoose Querying Views

I'm currently using mongoose v. 5.25, against mongoDB v.3.6.
My application is supposed to query data from many different views, for instance, a view I currently have at my DB: db.joboffers_view.find()
will return many records that have been aggregated from different collections.
For a normal collection model, I query it like so:
const model = db.model(attribute);
/*where attribute, can be any registered schema */
model.find().
then((result) => {
resolve(result);
}).
catch((err) => {
reject(err);
});
Then way I register my models is something like this (simplified code):
//...
//abstracting boring connection methods
const db = mongoose.connection
//...
//simple model schema
const users_schema = {
_id: ObjectId,
another_field: String
};
//here I'm registering a schema for a VIEW, instead of normal collection
const view_schema = {
_id: ObjectId,
another_field: String
};
//...
//then
db.model('users', users_schema);
db.model('view', view_schema);
When I run a query from any of my registered models, I get the results just fine. However, when I run it against a model that represents a view on my mongo database, it returns an empty array.
No errors, no nothing, just an empty array.
I have looked through mongoose documentation, and I didn't find any specific method or pattern for querying a view, instead of a collection data.
It seems to be the same way I would do for any other collection I have in my system.
Am I missing something?
I also faced the same issue and figured out the problem is that mongoose, by default, reads collection names by pluralizing the model/view name.
So when you create any view and want to use it in mongoose, either make sure your view name is plural (add s to end of view name) or pass a collection name when initializing a schema.
Example
const users_schema = {
_id: ObjectId,
another_field: String
};
mongoose.model('vw_user_info', users_schema, 'vw_user_info');
I have same problem, but i solved it, please check the name of the view in mongodb, it must be same with db.model('view_name', view_schema);
You can open Mongoose debug by config like this mongoose.set('debug', true);
Add 3rd argument
db.model('view', view_schema, 'view_name_in_db')

Cannot use Collection as Model PhalconPHP

I'm trying to setup MongoDB collection for my PhalconPHP application.
I have the mongo and collectionManager set up.
$di->set('collectionManager', function(){
return new Phalcon\Mvc\Collection\Manager();
}, true);
//MongoDB Database
$di->set('mongo', function() {
$mongo = new MongoClient("localhost:27017");
return $mongo->selectDb("test");
}, true);
I have a model
class User extends \Phalcon\Mvc\Collection {
....
}
Then I got this error
Unexpected value type: expected object implementing Phalcon\Mvc\ModelInterface, object of type MyApp\Models\User given
Disclaimer: This was extracted from the question.
I was using Model validation instead Validation in the model body.