Is there any methods in Objection.js for Leftjoin and RightJoin apart from knex querybuilder? - objection.js

I am writing get service in node js api . We are using Objectionjs as ORM module. We have two Table
'RoleDescription' and 'Features'.
Features is master table and RoleDescription is child table.
here how it is linked:
const { Model } = require('objection');
const BaseModel = require('./BaseModel')
const Features = require('./Features')
class RoleDescription extends BaseModel {
static get tableName() {
return 'RoleDescription';
}
static get relationMappings() {
return {
featureRelation: {
relation: Model.HasOneRelation,
modelClass: Features,
join: {
from: 'Features.Id',
to: 'RoleDescription.FeatureId'
}
},
}
}
}
module.exports = RoleDescription
Since, I want to get all data from features table in response . joinEager method does not solve the purpose beacuse i think its inner join.
so, i just want to know how to execute left join using objectionjs.
also wanted to know the difference between joinEager and Joins in objectionjs?

Related

Creating Knex Migration Referencing the Same Table Twice

Trying to create a table Transaction in a postgresql database that references Buyers and Sellers s.t. both are objects from the Users table.
I think I have the migration working to look something like the following:
exports.up = function(knex, Promise) {
return knex.schema.createTable('likes', t => {
t.increments('id').primary()
t.integer('buyers_id').references('users.id').onDelete('CASCADE')
t.integer('sellers_id').references('users.id').onDelete('CASCADE')
...
t.datetime("created_at");
t.datetime("updated_at");
})
};
Next, I need to manage the association in the model, s.t the Transaction belongs to a Buyer and a Seller, which are both members of the User class.
To clarify the question, I am able to create the model with these attributes, but the association does not seem to be working.
here is my Transaction model:
const BaseModel = require("./BaseModel");
// const Password = require('objection-password')();
class Transaction extends BaseModel {
static get tableName() {
return "transactions";
}
static get relationMappings () {
const User = require('./User');
const Item = require('./Item')
return {
buyer: {
relation: BaseModel.BelongsToOneRelation,
modelClass: User,
join: {
from: 'transactions.buyers_id',
to: 'users.id'
}
},
seller: {
relation: BaseModel.BelongsToOneRelation,
modelClass: User,
join: {
from: 'transactions.sellers_id',
to: 'users.id'
}
},
books: {
relation: BaseModel.BelongsToOneRelation,
modelClass: Item,
join: {
from: 'transactions.items_id',
to: 'items.id'
}
}
}
}
}
module.exports = Transaction;
Here is the relevant route where I try to eager load the buyer:
let router = express.Router();
router.get('/', async (req, res) => {
const transactions = await Transaction
.query()
.eager(['buyer', 'items')
res.json(transactions);
});
I have figured this out. The above code works, using the aliases buyers and sellers and associating those two types of Users with Transactions.
For anyone who is interested... Consolidating the working solution above using Node/ExpressJS for the server, Postgresql for the db, KnexJS/ObjectiveJS to manage Models and queries.
Here is the migration that sets up columns for buyers and sellers both of which reference the same Users table:
exports.up = function(knex, Promise) {
return knex.schema.createTable('likes', t => {
t.increments('id').primary()
t.integer('buyers_id').references('users.id').onDelete('CASCADE')
t.integer('sellers_id').references('users.id').onDelete('CASCADE')
...
t.datetime("created_at");
t.datetime("updated_at");
})
};
HEre is the Transactions Model including associations s.t. a Transaction belongs to Item Buyer(user) and Seller(user) and Item:
const BaseModel = require("./BaseModel");
// const Password = require('objection-password')();
class Transaction extends BaseModel {
static get tableName() {
return "transactions";
}
static get relationMappings () {
const User = require('./User');
const Item = require('./Item')
return {
buyer: {
relation: BaseModel.BelongsToOneRelation,
modelClass: User,
join: {
from: 'transactions.buyers_id',
to: 'users.id'
}
},
seller: {
relation: BaseModel.BelongsToOneRelation,
modelClass: User,
join: {
from: 'transactions.sellers_id',
to: 'users.id'
}
},
books: {
relation: BaseModel.BelongsToOneRelation,
modelClass: Item,
join: {
from: 'transactions.items_id',
to: 'items.id'
}
}
}
}
}
module.exports = Transaction;
Lastly, here is the express route which returns all transactions including eager loading the associated models:
let router = express.Router();
router.get('/', async (req, res) => {
const transactions = await Transaction
.query()
.eager(['buyer', 'items')
res.json(transactions);
});

Query by deep properties on relations

I am trying to query the memberRepository on two deep relations
const memberRepository = connection.getRepository(Member);
const where = {
"contact": {
"user":{
"uuid": "3322ebc7-7327-4567-95a9-619d36b8e0a6"
}
},
"organization": {
"uuid": "014f2226-729f-4b9f-bf59-5a6e8b8da781",
}
};
const relations = ['contact', 'contact.user', 'organization']
const x = await memberRepository.findOne({ where, relations })
This isn't working, how can I query a deep relation with typeorm?
You should think of relations as SQL joins, it is quite simple with TypeOrm to define them on entities as you build query with QueryBuilder. As long as you have a link enabled in your entity definition you can define a join, or you can use a subselect query otherwise for more complex cases.
const result = await memberRepository.createQueryBuilder("member")
.leftJoinAndSelect("member.contact", "contact")
.leftJoinAndSelect("contact.user", "user")
.leftJoinAndSelect("member.organization", "organization")
.where("user.uuid = :userUuid", {
userUuid: "3322ebc7-7327-4567-95a9-619d36b8e0a6"
})
.andWhere("organization.uuid = :organizationUuid", {
organizationUuid: "014f2226-729f-4b9f-bf59-5a6e8b8da781"
})
.getOne();

apollostack/graphql-server - how to get the fields requested in a query from resolver

I am trying to figure out a clean way to work with queries and mongdb projections so I don't have to retrieve excessive information from the database.
So assuming I have:
// the query
type Query {
getUserByEmail(email: String!): User
}
And I have a User with an email and a username, to keep things simple. If I send a query and I only want to retrieve the email, I can do the following:
query { getUserByEmail(email: "test#test.com") { email } }
But in the resolver, my DB query still retrieves both username and email, but only one of those is passed back by apollo server as the query result.
I only want the DB to retrieve what the query asks for:
// the resolver
getUserByEmail(root, args, context, info) {
// check what fields the query requested
// create a projection to only request those fields
return db.collection('users').findOne({ email: args.email }, { /* projection */ });
}
Of course the problem is, getting information on what the client is requesting isn't so straightforward.
Assuming I pass in request as context - I considered using context.payload (hapi.js), which has the query string, and searching it through various .split()s, but that feels kind of dirty. As far as I can tell, info.fieldASTs[0].selectionSet.selections has the list of fields, and I could check for it's existence in there. I'm not sure how reliable this is. Especially when I start using more complex queries.
Is there a simpler way?
In case you don't use mongDB, a projection is an additional argument you pass in telling it explicitly what to retrieve:
// telling mongoDB to not retrieve _id
db.collection('users').findOne({ email: 'test#test.com' }, { _id: 0 })
As always, thanks to the amazing community.
2020-Jan answer
The current answer to getting the fields requested in a GraphQL query, is to use the graphql-parse-resolve-info library for parsing the info parameter.
The library is "a pretty complete solution and is actually used under the hood by postgraphile", and is recommended going forward by the author of the other top library for parsing the info field, graphql-fields.
Use graphql-fields
Apollo server example
const rootSchema = [`
type Person {
id: String!
name: String!
email: String!
picture: String!
type: Int!
status: Int!
createdAt: Float
updatedAt: Float
}
schema {
query: Query
mutation: Mutation
}
`];
const rootResolvers = {
Query: {
users(root, args, context, info) {
const topLevelFields = Object.keys(graphqlFields(info));
return fetch(`/api/user?fields=${topLevelFields.join(',')}`);
}
}
};
const schema = [...rootSchema];
const resolvers = Object.assign({}, rootResolvers);
// Create schema
const executableSchema = makeExecutableSchema({
typeDefs: schema,
resolvers,
});
Sure you can. This is actually the same functionality that is implemented on join-monster package for SQL based db's. There's a talk by their creator: https://www.youtube.com/watch?v=Y7AdMIuXOgs
Take a look on their info analysing code to get you started - https://github.com/stems/join-monster/blob/master/src/queryASTToSqlAST.js#L6-L30
Would love to see a projection-monster package for us mongo users :)
UPDATE:
There is a package that creates a projection object from info on npm: https://www.npmjs.com/package/graphql-mongodb-projection
You can generate MongoDB projection from info argument. Here is the sample code that you can follow
/**
* #description - Gets MongoDB projection from graphql query
*
* #return { object }
* #param { object } info
* #param { model } model - MongoDB model for referencing
*/
function getDBProjection(info, model) {
const {
schema: { obj }
} = model;
const keys = Object.keys(obj);
const projection = {};
const { selections } = info.fieldNodes[0].selectionSet;
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
const isSelected = selections.some(
selection => selection.name.value === key
);
projection[key] = isSelected;
}
console.log(projection);
}
module.exports = getDBProjection;
With a few helper functions you can use it like this (typescript version):
import { parceGqlInfo, query } from "#backend";
import { GraphQLResolveInfo } from "graphql";
export const user = async (parent: unknown, args: unknown, ctx: unknown, info: GraphQLResolveInfo): Promise<User | null> => {
const { dbQueryStr } = parceGqlInfo(info, userFields, "id");
const [user] = await query(`SELECT ${dbQueryStr} FROM users WHERE id=$1;`, [1]);
return user;
};
Helper functions.
Few points:
gql_uid used as ID! string type from primary key to not change db types
required option is used for dataloaders (if field was not requested by user)
allowedFields used to filter additional fields from info like '__typename'
queryPrefix is used if you need to prefix selected fields like select u.id from users u
const userFields = [
"gql_uid",
"id",
"email"
]
// merge arrays and delete duplicates
export const mergeDedupe = <T>(arr: any[][]): T => {
// #ts-ignore
return ([...new Set([].concat(...arr))] as unknown) as T;
};
import { parse, simplify, ResolveTree } from "graphql-parse-resolve-info";
import { GraphQLResolveInfo } from "graphql";
export const getQueryFieldsFromInfo = <Required = string>(info: GraphQLResolveInfo, options: { required?: Required[] } = {}): string[] => {
const { fields } = simplify(parse(info) as ResolveTree, info.returnType) as { fields: { [key: string]: { name: string } } };
let astFields = Object.entries(fields).map(([, v]) => v.name);
if (options.required) {
astFields = mergeDedupe([astFields, options.required]);
}
return astFields;
};
export const onlyAllowedFields = <T extends string | number>(raw: T[] | readonly T[], allowed: T[] | readonly T[]): T[] => {
return allowed.filter((f) => raw.includes(f));
};
export const parceGqlInfo = (
info: GraphQLResolveInfo,
allowedFields: string[] | readonly string[],
gqlUidDbAlliasField: string,
options: { required?: string[]; queryPrefix?: string } = {}
): { pureDbFields: string[]; gqlUidRequested: boolean; dbQueryStr: string } => {
const fieldsWithGqlUid = onlyAllowedFields(getQueryFieldsFromInfo(info, options), allowedFields);
return {
pureDbFields: fieldsWithGqlUid.filter((i) => i !== "gql_uid"),
gqlUidRequested: fieldsWithGqlUid.includes("gql_uid"),
dbQueryStr: fieldsWithGqlUid
.map((f) => {
const dbQueryStrField = f === "gql_uid" ? `${gqlUidDbAlliasField}::Text AS gql_uid` : f;
return options.queryPrefix ? `${options.queryPrefix}.${dbQueryStrField}` : dbQueryStrField;
})
.join(),
};
};

How to exclude property of a collection in JSON rendering in Grails 2.3

I am trying to setup a rest webservice (JSON) this is what I am getting:
{"name":"test","routines":[{"class":"Routine","id":1},{"class":"Routine","id":2}]}
This is what I want to get:
{"name":"test","routines":[{"name": "routine-1"},{"name": "routine-2"}]}
I have these domains:
class Program {
String name;
static hasMany = [routines: Routine]
}
class Routine {
String name
}
I have this controller:
class ProgramController extends RestfulController {
static responseFormats = ['json']
def show(Program program) {
respond program
}
}
I added this in the resources.groovy
programRenderer(JsonRenderer, Program) {
excludes = ['class', 'id']
}
routineRenderer(JsonRenderer, Routine) {
excludes = ['class', 'id']
}
How do I include the name property of Routine in the json response using the show method/action of ProgramController?
The ObjectMarshaller approach is the technically correct way. However, the code is cumbersome to write and it's a maintenance headache syncing the fields of the domain with the marshaller.
In the spirit of being Groovy and keeping things really simple, we've been quite happy just adding a little out() method to each REST domain.
Program.groovy
class Program {
String name
static hasMany = [routines: Routine]
def out() {
return [
name: name,
count: routines?.size(),
routines: routines?.collect { [name: it.name] }
]
}
}
ProgramController.groovy
import grails.converters.JSON
class ProgramController {
def show() {
def resource = Program.read(params.id)
render resource.out() as JSON
}
}
JSON Response
{
name: "test",
count: 2,
routines: [{ name: "routine-1" }, { name: "routine-2" }]
}
The out() method approach makes it easy to customize the response JSON, such as adding count for the number of routines.

Non-Singleton Services in AngularJS

AngularJS clearly states in its documentation that Services are Singletons:
AngularJS services are singletons
Counterintuitively, module.factory also returns a Singleton instance.
Given that there are plenty of use-cases for non-singleton services, what is the best way to implement the factory method to return instances of a Service, so that each time an ExampleService dependency is declared, it is satisfied by a different instance of ExampleService?
I'm not entirely sure what use case you are trying to satisfy. But it is possible to have a factory return instances of an object. You should be able to modify this to suit your needs.
var ExampleApplication = angular.module('ExampleApplication', []);
ExampleApplication.factory('InstancedService', function(){
function Instance(name, type){
this.name = name;
this.type = type;
}
return {
Instance: Instance
}
});
ExampleApplication.controller('InstanceController', function($scope, InstancedService){
var instanceA = new InstancedService.Instance('A','string'),
instanceB = new InstancedService.Instance('B','object');
console.log(angular.equals(instanceA, instanceB));
});
JsFiddle
Updated
Consider the following request for non-singleton services. In which Brian Ford notes:
The idea that all services are singletons does not stop you from
writing singleton factories that can instantiate new objects.
and his example of returning instances from factories:
myApp.factory('myService', function () {
var MyThing = function () {};
MyThing.prototype.foo = function () {};
return {
getInstance: function () {
return new MyThing();
}
};
});
I would also argue his example is superior due to the fact that you do not have to use the new keyword in your controller. It is encapsulated within the getInstance method of the service.
I don't think we should ever have a factory return a newable function as this begins to break down dependency injection and the library will behave awkwardly, especially for third parties. In short, I am not sure there are any legitimate use cases for non-singleton sevices.
A better way to accomplish the same thing is to use the factory as an API to return a collection of objects with getter and setter methods attached to them. Here is some pseudo-code showing how using that kind of service might work:
.controller( 'MainCtrl', function ( $scope, widgetService ) {
$scope.onSearchFormSubmission = function () {
widgetService.findById( $scope.searchById ).then(function ( widget ) {
// this is a returned object, complete with all the getter/setters
$scope.widget = widget;
});
};
$scope.onWidgetSave = function () {
// this method persists the widget object
$scope.widget.$save();
};
});
This is just pseudo-code for looking up a widget by ID and then being able to save changes made to the record.
Here's some pseudo-code for the service:
.factory( 'widgetService', function ( $http ) {
function Widget( json ) {
angular.extend( this, json );
}
Widget.prototype = {
$save: function () {
// TODO: strip irrelevant fields
var scrubbedObject = //...
return $http.put( '/widgets/'+this.id, scrubbedObject );
}
};
function getWidgetById ( id ) {
return $http( '/widgets/'+id ).then(function ( json ) {
return new Widget( json );
});
}
// the public widget API
return {
// ...
findById: getWidgetById
// ...
};
});
Though not included in this example, these kinds of flexible services could also easily manage state.
I don't have time right now, but if it will be helpful I can put together a simple Plunker later to demonstrate.
Another way is to copy service object with angular.extend().
app.factory('Person', function(){
return {
greet: function() { return "Hello, I'm " + this.name; },
copy: function(name) { return angular.extend({name: name}, this); }
};
});
and then, for example, in your controller
app.controller('MainCtrl', function ($scope, Person) {
michael = Person.copy('Michael');
peter = Person.copy('Peter');
michael.greet(); // Hello I'm Michael
peter.greet(); // Hello I'm Peter
});
Here is a plunk.
I know this post has already been answered but I still think there would be some legitimate scenarios that you need to have non-singleton service. Let's say there are some reusable business logic which can be shared between several controllers. In this scenario the best place to put the logic would be a service, but what if we need to keep some state in our reusable logic? Then we need non-singleton service so can be shared across different controllers in app. This is how I would implement these services:
angular.module('app', [])
.factory('nonSingletonService', function(){
var instance = function (name, type){
this.name = name;
this.type = type;
return this;
}
return instance;
})
.controller('myController', ['$scope', 'nonSingletonService', function($scope, nonSingletonService){
var instanceA = new nonSingletonService('A','string');
var instanceB = new nonSingletonService('B','object');
console.log(angular.equals(instanceA, instanceB));
}]);
Here's my example of a non singleton service, It's from a ORM im working on. In the example I show a Base Model (ModelFactory) which I want services('users','documents') to inherit and potential extend.
In my ORM ModelFactory injects other services to provide extra functionality(query,persistence,schema mapping) which is sandboxed using the module system.
In the example both user and document service have the same functionality but have their own independent scopes.
/*
A class which which we want to have multiple instances of,
it has two attrs schema, and classname
*/
var ModelFactory;
ModelFactory = function($injector) {
this.schema = {};
this.className = "";
};
Model.prototype.klass = function() {
return {
className: this.className,
schema: this.schema
};
};
Model.prototype.register = function(className, schema) {
this.className = className;
this.schema = schema;
};
angular.module('model', []).factory('ModelFactory', [
'$injector', function($injector) {
return function() {
return $injector.instantiate(ModelFactory);
};
}
]);
/*
Creating multiple instances of ModelFactory
*/
angular.module('models', []).service('userService', [
'ModelFactory', function(modelFactory) {
var instance;
instance = new modelFactory();
instance.register("User", {
name: 'String',
username: 'String',
password: 'String',
email: 'String'
});
return instance;
}
]).service('documentService', [
'ModelFactory', function(modelFactory) {
var instance;
instance = new modelFactory();
instance.register("Document", {
name: 'String',
format: 'String',
fileSize: 'String'
});
return instance;
}
]);
/*
Example Usage
*/
angular.module('controllers', []).controller('exampleController', [
'$scope', 'userService', 'documentService', function($scope, userService, documentService) {
userService.klass();
/*
returns
{
className: "User"
schema: {
name : 'String'
username : 'String'
password: 'String'
email: 'String'
}
}
*/
return documentService.klass();
/*
returns
{
className: "User"
schema: {
name : 'String'
format : 'String'
formatileSize: 'String'
}
}
*/
}
]);
angular only gives a singleton service/factory option.
one way around it is to have a factory service that will build a new instance for you inside your controller or other consumer instances.
the only thing that is injected is the class that creates new instances.
this is a good place to inject other dependencies or to initialize your new object to the specification of the user (adding services or config)
namespace admin.factories {
'use strict';
export interface IModelFactory {
build($log: ng.ILogService, connection: string, collection: string, service: admin.services.ICollectionService): IModel;
}
class ModelFactory implements IModelFactory {
// any injection of services can happen here on the factory constructor...
// I didnt implement a constructor but you can have it contain a $log for example and save the injection from the build funtion.
build($log: ng.ILogService, connection: string, collection: string, service: admin.services.ICollectionService): IModel {
return new Model($log, connection, collection, service);
}
}
export interface IModel {
// query(connection: string, collection: string): ng.IPromise<any>;
}
class Model implements IModel {
constructor(
private $log: ng.ILogService,
private connection: string,
private collection: string,
service: admin.services.ICollectionService) {
};
}
angular.module('admin')
.service('admin.services.ModelFactory', ModelFactory);
}
then in your consumer instance you need the factory service and call the build method on the factory to get a new instance when you need it
class CollectionController {
public model: admin.factories.IModel;
static $inject = ['$log', '$routeParams', 'admin.services.Collection', 'admin.services.ModelFactory'];
constructor(
private $log: ng.ILogService,
$routeParams: ICollectionParams,
private service: admin.services.ICollectionService,
factory: admin.factories.IModelFactory) {
this.connection = $routeParams.connection;
this.collection = $routeParams.collection;
this.model = factory.build(this.$log, this.connection, this.collection, this.service);
}
}
you can see it provides opperatiunity to inject some specific services that are not available in the factory step.
you can always have injection happen on the factory instance to be used by all Model instances.
Note I had to strip off some code so I might made some context errors...
if you need a code sample that works let me know.
I believe that NG2 will have the option to inject a new instance of your service in the right place in your DOM so you dont need to build your own factory implementation. will have to wait and see :)
I believe there is good reason to create a new instance of an object within a service. We should keep an open mind as well rather than just say we ought never do such a thing, but the singleton was made that way for a reason. Controllers are created and destroyed often within the lifecycle of the app, but the services must be persistent.
I can think of a use case where you have a work flow of some kind, like accepting a payment and you have multiple properties set, but must now change their payment type because the customer's credit card failed and they need to provide a different form of payment. Of course, this does have a lot to do with the way you create your app. You could reset all properties for the payment object, or you could create a new instance of an object within the service. But, you would not want a new instance of the service, nor would you want to refresh the page.
I believe a solution is providing an object within the service that you can create a new instance of and set. But, just to be clear, the single instance of the service is important because a controller may be created and destroyed many times, but the services need persistence. What you are looking for may not be a direct method within Angular, but an object pattern that you can manage inside your service.
As an example, I have a made a reset button. (This is not tested, its really just a quick idea of a use case for creating a new object within a service.
app.controller("PaymentController", ['$scope','PaymentService',function($scope, PaymentService) {
$scope.utility = {
reset: PaymentService.payment.reset()
};
}]);
app.factory("PaymentService", ['$http', function ($http) {
var paymentURL = "https://www.paymentserviceprovider.com/servicename/token/"
function PaymentObject(){
// this.user = new User();
/** Credit Card*/
// this.paymentMethod = "";
//...
}
var payment = {
options: ["Cash", "Check", "Existing Credit Card", "New Credit Card"],
paymentMethod: new PaymentObject(),
getService: function(success, fail){
var request = $http({
method: "get",
url: paymentURL
}
);
return ( request.then(success, fail) );
}
//...
}
return {
payment: {
reset: function(){
payment.paymentMethod = new PaymentObject();
},
request: function(success, fail){
return payment.getService(success, fail)
}
}
}
}]);
Here's another approach to the problem that I was quite satisfied with, specifically when used in combination with Closure Compiler with advanced optimizations enabled:
var MyFactory = function(arg1, arg2) {
this.arg1 = arg1;
this.arg2 = arg2;
};
MyFactory.prototype.foo = function() {
console.log(this.arg1, this.arg2);
// You have static access to other injected services/factories.
console.log(MyFactory.OtherService1.foo());
console.log(MyFactory.OtherService2.foo());
};
MyFactory.factory = function(OtherService1, OtherService2) {
MyFactory.OtherService1_ = OtherService1;
MyFactory.OtherService2_ = OtherService2;
return MyFactory;
};
MyFactory.create = function(arg1, arg2) {
return new MyFactory(arg1, arg2);
};
// Using MyFactory.
MyCtrl = function(MyFactory) {
var instance = MyFactory.create('bar1', 'bar2');
instance.foo();
// Outputs "bar1", "bar2" to console, plus whatever static services do.
};
angular.module('app', [])
.factory('MyFactory', MyFactory)
.controller('MyCtrl', MyCtrl);