Can I add non-persistent fields to a model? - objection.js

Is there a recommended practice for adding non-persistent attributes to an Objection model object such that it that won't overwrite predefined attributes?

Objection models have the virtualAttributes field. From the documentation:
The virtual values are not written to database. Only the “external” JSON format will contain them.
It is important to note these are functions, not just model properties.
Example from the docs:
class Person extends Model {
static get virtualAttributes() {
return ['fullName', 'isFemale'];
}
fullName() {
return `${this.firstName} ${this.lastName}`;
}
get isFemale() {
return this.gender === 'female';
}
}
const person = Person.fromJson({
firstName: 'Jennifer',
lastName: 'Aniston',
gender: 'female'
});
console.log(person.toJSON());
// --> {"firstName": "Jennifer", "lastName": "Aniston", "isFemale": true, "fullName": "Jennifer Aniston"}

Related

Why does Amplify Datastore return null in objects having #hasmany relationships?

I'm new here and I hope to find a solution to this problem. I am new to Flutter and AWS Amplify as well. Here it goes.
I created a very simple schema using Admin UI. The schema has two models, Order and Item. Order contains many Item(s).
I was surprised that call to get OrderItems() returns null. I read a post elsewhere that said #hasmany relationships do not work with with AWS Amplify and a second query was needed to retrieve #hasmany fields.
The CLI generated models seem to support #hasmany relationships.
Here is the simple schema generated by Amplify:
type Items #model #auth(rules: [{allow: public}]) {
id: ID!
productName: String
quantity: String
orderID: ID #index(name: "byOrder")
}
type Order #model #auth(rules: [{allow: public}]) {
id: ID!
Customer: String
OrderItems: [Items] #hasMany(indexName: "byOrder", fields: ["id"])
}
Amplify generated the corresponding models in my project. For brevity, I will include selected parts of the CLI generated code for the Order object. I don't believe the generated models can be modified. Moreover, If the schema is changed, the model dart project files are overwritten.
#override
String getId() {
return id;
}
String? get Customer {
return _Customer;
}
// Returns null
List<Items>? get OrderItems {
return _OrderItems;
}
It appears that ['OrderItems'] should be populate in Order.fromJson.
Order.fromJson(Map<String, dynamic> json)
: id = json['id'],
_Customer = json['Customer'],
_OrderItems = json['OrderItems'] is List
? (json['OrderItems'] as List)
.where((e) => e?['serializedData'] != null)
.map((e) => Items.fromJson(new Map<String, dynamic>.from(e['serializedData'])))
.toList()
: null;
What am I missing? What is the purpose of this code?
The remainder of the CLI generated code seems to support CRUD operations on both Order and Item.
Map<String, dynamic> toJson() => {
'id': id, 'Customer': _Customer, 'OrderItems': _OrderItems?.map((Items? e) => e?.toJson()).toList()
};
static final QueryField ID = QueryField(fieldName: "order.id");
static final QueryField CUSTOMER = QueryField(fieldName: "Customer");
static final QueryField ORDERITEMS = QueryField(
fieldName: "OrderItems",
fieldType: ModelFieldType(ModelFieldTypeEnum.model, ofModelName: (Items).toString()));
static var schema = Model.defineSchema(define: (ModelSchemaDefinition modelSchemaDefinition) {
modelSchemaDefinition.name = "Order";
modelSchemaDefinition.pluralName = "Orders";
modelSchemaDefinition.authRules = [
AuthRule(
authStrategy: AuthStrategy.PUBLIC,
operations: [
ModelOperation.CREATE,
ModelOperation.UPDATE,
ModelOperation.DELETE,
ModelOperation.READ
])
];
modelSchemaDefinition.addField(ModelFieldDefinition.id());
modelSchemaDefinition.addField(ModelFieldDefinition.field(
key: Order.CUSTOMER,
isRequired: false,
ofType: ModelFieldType(ModelFieldTypeEnum.string)
));
modelSchemaDefinition.addField(ModelFieldDefinition.hasMany(
key: Order.ORDERITEMS,
isRequired: false,
ofModelName: (Items).toString(),
associatedKey: Items.ORDERID
));
});
}
class _OrderModelType extends ModelType<Order> {
const _OrderModelType();
#override
Order fromJson(Map<String, dynamic> jsonData) {
return Order.fromJson(jsonData);
}
}
It appears that all the plumbing is in place but the water is turned off. Like I stated, I'm new at this so...
Is this a bug?
One last thing...
ModelProvider.dart throw this compile time error.
Missing concrete implementations of 'getter ModelProviderInterface.customTypeSchemas' and 'setter ModelProviderInterface.customTypeSchemas'.
Try implementing the missing methods, or make the class abstract
Whenever ModelProvider.dart is regenerated I have to add the following line of code.
#override
List<ModelSchema> customTypeSchemas =[];
Thanks in advance
Apparently, #hasOne and #hasMany directives do not support referencing a model which then references the initial model via #hasOne or #hasMany if DataStore is enabled.
Details: Has-One-Relationship

What happens behind the scene when you update a nested array element in a MongoDB Document

When I do a nested object update inside an array in a Document. Does Mongo DB Engine needs to fetch and parse the whole document update the field and reinsert the document ?
db.ControllerPointCollection.updateOne({
"_id": "Ashutosh Das_MigrationTest_0_1_0"
}, {
$set: {
"Tables.$[t].Blocks.$[b].Points.$[p].Description": "Hey You"
}
}, {
arrayFilters: [{
"t.ID": 32
}, {
"b.ID": 268
}, {
"p.PointDefinitionID": 280
}]
})
Behind the scene, mongodb has a class called Model and inside Model class compose other behaviours with initializing other classes and one of them, I call it Sync which is implemented like this. this is not exact code, but you get the idea:
interface HasId {
id?: number; //optional
}
export class ApiSync<T extends HasId> {
constructor(public rootUrl: string) {}
// if user has Id, that means it is already stored in db, so we make a put request, if it does not then we make post
// so in mongoose, saving means Http request to db
save(data: T): AxiosPromise {
const { id } = data;
if (id) {
return axios.put(this.rootUrl + id, data);
} else {
return axios.post(this.rootUrl, data);
}
}
fetch(id: number): AxiosPromise {
return axios.get(this.rootUrl + id);
}
}

MongoDB and TypeScript: Decouple a domain entity's id type from MongoDB's ObjectID

Inside my MongoDB repositories, entities have an _id: ObjectID type to be handled properly. However, I would like my domain entities to have a simple id: string attribute to avoid any dependencies on any database or framework. The solution I came up with so far looks as follows:
export interface Book {
id: string;
title: string;
}
// A MongodbEntity<Book> would now have an _id instead of its string id
export type MongodbEntity<T extends { id: string; }> = Omit<T, 'id'> & { _id: ObjectID; };
In my repository this would work:
async findOneById(id: string): Promise<Book | null> {
const res = await this.collection.findOneById({_id: new ObjectId(id)});
return res ? toBook(res) : null;
}
function toBook(dbBook: MongodbEntity<Book>): Book {
const {_id, ...rest} = dbBook;
return {...rest, id: _id.toHexString() };
}
What doesn't work is to make this behavior generic. A converter function like this:
function toDomainEntity<T extends {id: string}>(dbEntity: MongoDbEntity<T>): T {
const {_id, ...rest} = dbEntity;
return {...rest, id: _id.toHexString() };
}
leads to an error described here.
What I am looking for is either a working solution for the generic toDomainEntity function or a different (generic) approach that would let me decouple my domain entity types from MongoDB's _id: ObjectID type.

angular 2 how to add a model within model

I am getting a response json from API like below.
student: {
name : abc,
address : {
city : ca,
state:abc
},
age : 10
}
In order to bind this to a model, i need a model similar to this
class student {
name:string;
age:number;
address:{
city:string;
state:string
}
}
But when I bind the data to the above model. Address data is not binding to model.
Please suggest the right way to write a model to bind the above data.
export class A {
name:string;
age:number;
address:Adress;
}
export class Adress
city:string;
state:string
}
You need to instantiate an instance of the Student class, i.e.
class Student {
// Define Student properties here
name:string;
age:number;
// ...
constructor(options) {
// Set Student properties here
this.name = options.name;
this.age = options.age;
this.address = options.address;
// ...
}
}
// Then:
const student = new Student(jsonData);

Can I hook up a model to an existing database?

I have mongodb sitting behind an existing API and want to migrate the API to use sailsjs.
The data structure isn't anything crazy - just standard stuff using default mongodb ObjectIds as primary keys.
Will I be able to use this existing db with sails by just wiring up sails models? Do I need to specify the _id field? And, if so, what datatype should I use?
E.g. Existing mongodb with user collection with the following schema:
_id
name
fname
lname
age
Can I just wire up using something like the following for it to work?:
// User.js
var User = {
attributes: {
name: {
fname: 'STRING',
lname: 'STRING'
},
age: 'INTEGER'
}
};
module.exports = Person;
First: you dont have to define _id (waterline do this for you)
Waterline wants to help you using the same Functions and Models for all types of databases. A "Sub-Field" is not supported in mysql for example. So this don't work.
You can do this:
// User.js
var User = {
attributes: {
name: 'json',
age: 'integer'
}
};
module.exports = User;
If you want to validate "name" you can add your own validation:
// User.js
var User = {
types: {
myname: function(json){
if(typeof json.fname == "string" && typeof json.lname == "string"){
return true;
}else{
return false;
}
}
},
attributes: {
name: {
type: "json",
myname: true
},
age: 'integer'
}
};
module.exports = User;