How to get schema attributes names from model in dynamoose v3? - javascript

In dynamoose v2:
T extends Document;
model: ModelType<T>;
const attributes: string[] = model.schemas[0].attributes();
In this way I get the attributes names of the schema.
How I can get attributes names from model in dynamoose v3?
In dynamoose v3:
T extends Item;
model: ModelType<T>;
const attributes: string[] = model.schemas[0].attributes();
I have the following error: Property 'schemas' does not exist on type 'ModelType<T>'.

I encountered a similar issue while working with Dynamoose 3. Here's a working example how to get model attributes:
import * as dynamoose from "dynamoose";
import {Item} from "dynamoose/dist/Item";
// Strongly typed model
class Person extends Item {
id: number;
name: string;
}
const PersonModel = dynamoose.model<Person>("Person", {"id": Number, "name": String});
If you attempt to create a new record with an undeclared field e.g surname, Typescript will yell at you.
CatModel.create({"id": 1, "surname": "string"});
After creating or fetching the model, your IDE will correctly provide you the attributes.
Example 1:
const Person = await PersonModel.get(1);
//console.log(Person.id)
//console.log(Person.name)
Example 2:
const Person = await PersonModel.create({id: "1", name: "John" })
//console.log(Person.id)
//console.log(Person.name)

Related

Accessing the name of a class as a string in JavaScript when class was created using another constructor [duplicate]

I´m using mongoose and I need to find a model name from a model instance.
In one part of the code I have:
const schema = new mongoose.Schema({
name: {
type: String,
required: true
},
phone: {
type: String,
required: true
}
}
const schema = new mongoose.Schema('MyData', schema);
let instance = new this({
name: 'Pete',
phone: '123'
});
Ths instance variable is passed around in my code. Later I need to find out instance name, but I´m no sure if there is a way to do it, something like:
let instanceName = getInstanceName(instance); <== Expects 'MyData' to be returned
Is that possible using mongoose ?
I realized I had a model not an instance of a model so I needed to use something else.
If you have a model, you can get the name as below:
const model = mongoose.model("TestModel", schema);
const collectionName = model.collection.collectionName;
If you have a specific item/instance of the model:
const instance = new model({...});
const collectionName = instance.constructor.modelName
as Hannah posted.
The name of the model can be accessed using this instance.constructor.modelName.
In my case I was looking for how to get a discriminator model name from a mongoose model, and the suggested solutions didn't work:
const PerformanceResult = DropDown.discriminator('PerformanceResult', new db.Schema({
name: { type: String, required: true }
}))
export default PerformanceResult
console.log(PerformanceResult.constructor.modelName) // undefined
console.log(PerformanceResult.collection.collectionName) // DropDown (parent name)
you can use this:
console.log(PerformanceResult.modelName) // PerformanceResult
mongoose version: "^5.11.8"

How can I convert a string property to a custom type within a TypeScript array?

I have an interface that is extending a MongoDB document, and some sample data that is extending that interface.
The interface is like this:
export default interface IModel extends Document {
_id: ObjectId;
name: string;
data:string;
}
The sample data matches this interface. The object ID type looks like a string of numbers and letter. However, when I define a value for the sample data in the _id fields, it throws an error because TypeScript types it as a string and the type should be ObjectId. So how can I cast the value of the id to be of type ObjectId?
I am trying to do something like this:
export const ModelSampleData: IModel = {
"_id": toObjectId(240nfkfn38943),
"name": "model",
"data": "modelstuffetc"
}
Appreciate any help!
according to this link, you can cast your string type to objectId like so:
export const ModelSampleData: IModel = {
"_id": ObjectId("240nfkfn38943"),
"name": "model",
"data": "modelstuffetc"
}
All you need to do is remove "", you tried to copy response and assign it to ModelSampleData which is json object, not javascript object, if you want to use then use JSON.parse
below both samples should work
import { ObjectId } from 'mongodb';
export default interface IModel extends Document{
_id: ObjectId ;
name: string;
data:string;
}
export const ModelSampleData1: IModel = JSON.parse(`{
"_id": ObjectId(240nfkfn38943),
"name": "model",
"data": "modelstuffetc"
}`);
or
export const ModelSampleData: IModel = <IModel> {
_id: new ObjectId("240nfkfn38943"),
name: "model",
data: "modelstuffetc"
}

Typeorm doesn't return generated data id

Im using Typeorm (v8.0.2) and Nestjs(v8) with Nodejs(v16).
My problem is when I create a book Typeorm doesn't return generated book id
Here is Book.entity
#Entity()
export class Book {
#PrimaryGeneratedColumn('increment')
id: number;
#Column()
title: string;
#Column()
author: string;
}
And this is book.service
async createBook(createBookDto: CreateBookDto): Promise<Book> {
const book = await this.bookRepository.create(createBookDto)
await this.bookRepository.save(createBookDto)
return book
}
and when I use postman and create a Book it just returns
{
title: "example"
author: "foo"
}
id of generated book is missing
TL;DR: return result of this.bookRepository.save(createBookDto), not this.bookRepository.create(createBookDto)
From the docs:
create - Creates a new instance of User. Optionally accepts an object literal with user properties which will be written into newly created user object.
const user = repository.create(); // same as const user = new User();
const user = repository.create({
id: 1,
firstName: "Timber",
lastName: "Saw"
}); // same as const user = new User(); user.firstName = "Timber"; user.lastName = "Saw";
In your example you are using #PrimaryGeneratedColumn() decorator that uses database level auto-increment function. The value of this column will be generated after the save() method, not after create().

How to make class instance constructable in TypeScript?

I'm trying to convert this package to TypeScript without any breaking changes. I have the following code in TypeScript.
// DocumentCarrier.ts
/* export */ class DocumentCarrier {
internalObject: {};
model: Model;
save: (this: DocumentCarrier) => void;
constructor(model: Model, object: {}) {
this.internalObject = object;
this.model = model;
}
}
DocumentCarrier.prototype.save = function(this: DocumentCarrier): void {
console.log(`Saved document ${JSON.stringify(this.model)} to ${this.model.myName}`);
};
// Model.ts
// import {DocumentCarrier} from "./DocumentCarrier.ts";
/* export */class Model {
myName: string;
Document: typeof DocumentCarrier;
get: (id: number) => void;
constructor(name: string) {
this.myName = name;
const self: Model = this;
class Document extends DocumentCarrier {
static Model: Model;
constructor(object: {}) {
super(self, object);
}
}
Document.Model = self;
Object.keys(Object.getPrototypeOf(this)).forEach((key) => {
Document[key] = this[key].bind(this);
});
this.Document = Document;
return this.Document as any;
}
}
Model.prototype.get = function(id: number): void {
console.log(`Retrieving item with id = ${id}`);
}
// Usage
// index.ts
// import {Model} from "./Model.ts";
const User = new Model("User");
const user = new User({"id": 5, "name": "Bob"});
user.save(); // "Saved document {"id": 5, "name": "Bob"} to User"
console.log(User.Model.myName); // "User"
// console.log(User.myName); // "User" // This option would be even better, but isn't supported in the existing code
User.get(5); // "Retrieving item with id = 5"
In the Usage section (very bottom of the code example above) I'm getting multiple errors in TypeScript. But running that code in a JavaScript file, works perfectly. So I know it's working and the code is accurate.
I think the biggest problem of what I'm trying to do is return this.Document as any. TypeScript is interpreting that as casting this.Document to a Model instance, when in reality it's not.
My question is this. In TypeScript how can I set it up where you can run new MyClassInstance() and have it return an instance of a different class? That has a bidirectional reference from MyClassInstance and the different class. In short, how do I get the following code working?
It's important that any solution works with the Usage section, and no modifications are made to that section. Except for the User.Model.myName vs User.myName section, which would be preferred as User.myName, but in the existing version functions as User.Model.myName.
For easy use, I also created a TypeScript Playground.
I'm going to interpret this question strictly as "how can I give typings to the existing code so that the compiler understands the code in the Usage section?" That is, the answer should not touch the emitted JavaScript, but instead should only alter type definitions, annotations, and assertions.
Aside: the more general question "how should I implement a class whose instances are themselves class constructors" is one I won't attempt to address, since from my research the best answer here is "don't try to do that" since it plays poorly with the prototypical inheritance model in JS. I'd instead lean strongly toward having a non-constructible class instance hold a property which is the constructor of the new class. Something like this Playground code. You'd be a lot happier in the long run, I expect.
Back to the typings: the main problem here is that TypeScript has no way to specify that a class constructor returns a type other than the class being defined. This is either intentional (see microsoft/TypeScript#11588 or a missing feature (see microsoft/TypeScript#27594) but in any case it's not part of the language.
What we can do here is to use declaration merging. When you write class Model {} you introduce both a class constructor object named Model and an interface type named Model. That interface can be merged into, adding methods and properties that the compiler doesn't already know about. In your case you could do this:
interface Model {
new(object: {}): DocumentCarrier;
Model: Model;
}
This lets the compiler know that Model instances, in addition to having the properties/methods declared in the class, also has a Model property whose type is Model, and, importantly, a constructor signature. That's enough to get the following code to compile without error:
const User = new Model("User");
const user = new User({ "id": 5, "name": "Bob" });
user.save(); // "Saved document {"id": 5, "name": "Bob"} to User"
console.log(User.Model.myName); // "User"
User.get(5); // "Retrieving item with id = 5"
The compiler does think that User.myName exists, which it doesn't at runtime, but that's already a problem with the existing code so I'm not touching that here. It's possible to change the typings further so that the compiler knows that User.Model.myName exists and that User.myName does not exist, but that becomes quite complicated as it requires you to split Model's interface into multiple types that you carefully assign to the right values. So for now I'm ignoring it.
The only other change I'd make here would be to give different typings to the implementation of Model, like this:
class Model {
myName: string;
Document: Model;
get!: (id: number) => void;
constructor(name: string) {
this.myName = name;
const self: Model = this;
class Document extends DocumentCarrier {
static Model: Model;
constructor(object: {}) {
super(self, object);
}
}
Document.Model = self;
(Object.keys(Object.getPrototypeOf(this)) as
Array<keyof typeof DocumentCarrier>).forEach((key) => {
Document[key] = this[key].bind(this);
});
this.Document = Document as Model;
return this.Document;
}
}
The only thing the compiler won't be able to verify in the above is that the Document class is a valid Model, so we use the assertion Document as Model. Other than that I just put a few assertions (get is definitely assigned, and Object.keys() will return an array of keys of the DocumentCarrier constructor) so that you don't need to turn off the --strict compiler flag.
Okay, hope that helps. Good luck!
Playground link to code
After roaming a bit, I got something.
Typescript complains about your solution because, even if you are returning a class Document internally in Model constructor, the compiler expects a Model instance, which is not constructable.
So, we need to make Model constructable. In fact, the same as making a function which returns instances of something.
First, let's declare your preovious DocumentCarrier class. Now, DocumentCarrier will have two properties, model and name (this was your previously keyed myName from Model class).
class DocumentCarrier {
name: string = ``;
constructor(public model: {}) { }
save = () => console.log(`Saved document ${JSON.stringify(this.model)} to ${this.name}`)
}
After that, we need that function declaration that returns an instance model of type DocumentCarrier.
const Model = (name: string) => {
return class extends DocumentCarrier {
name: string = name;
constructor(model: any) {
super(model);
}
}
}
The function takes a string parameter and returns a constructor of type DocumentCarrier which takes an any object on its constructor and passes to the DocumentCarrier constructor.
We can call Model like this.
const User = Model('User');
const user = new User({id: 5, name: 'Bob'});
user.save(); // Saved document {"id":5,"name":"Bob"} to User
The call to Model is the only change made. Now Model call does not need a new keyword.
On the other hand, name in DocumentCarrier can be accessed from the last instance constructed.
In addition, this solution could be a bit more generic by defining generic types.
type Constructor<T> = new (...args: any[]) => T;
The type Constructor constraints a constructor function of any type T.
Now, we need to rewrite the Model function:
const Model = <T extends Constructor<{}>>(Type: T, name: string) => {
return class extends Type {
name: string = name;
constructor(...args: any[]) {
super(...args);
}
}
}
Model now expects the same string as before but an additional parameter which is the type we want to instantiate, so
const User = Model(DocumentCarrier, 'User');
const user = new User({id: 5, name: 'Bob'});
user.save(); // Saved document {"id":5,"name":"Bob"} to User
Even more, since we are fulfilling a property name that belongs to the instance we are creating inside the Model function, we should constraint the input type to expect that name property.
type Constructor<T> = new (...args: any[]) => T;
interface DynamicallyConstructed {
name: string;
}
class DocumentCarrier implements DynamicallyConstructed {
name: string = ``;
constructor(public model: {}) { }
save = () => console.log(`Saved document ${JSON.stringify(this.model)} to ${this.name}`)
}
const Model = <T extends Constructor<DynamicallyConstructed>>(Type: T, name: string) => {
return class extends Type {
name: string = name;
constructor(...args: any[]) {
super(...args);
}
}
}
const User = Model(DocumentCarrier, 'User');
const user = new User({id: 5, name: 'Bob'});
user.save();
Hope this helps a bit.
Please comment any issue.

Mongoose and Typescript - Link a model to a specific mongoose connection

I defined a Model in a ts file.
I would like to use a specific mongoose connection (not the default one) with that model.
How can I associate my model to the connection ?
Excerpt form my TS file :
export interface iSuppliers extends mongoose.Document {
suppliers: string[];
fields: number[];
}
export const supplierSchema = new mongoose.Schema({
suppliers: {type:[String], required: true},
fields: [Number]})
.index({suppliers: 1}); // Additional index
export const supplier = mongoose.model<iSuppliers>('supplier', supplierSchema);
In my server.ts file :
import {supplier} from '....';
....
let database_2 = mongoose.createConnection(....);
Nothing happens when I use my supplier model to find data.
Obviously, I need to bind it to my database_2 connection ...
I am not sure about the way to that....
I found out my way...
I export a function that returns the model and use my connection as a parameter...
export function importedGameDatabase(mongooseConnection: mongoose.connection) { return mongooseConnection.model<iImportedGames>('importedGame', importedGamesSchema); }

Categories

Resources