Skip to content

Commit

Permalink
Merge pull request #3 from Entity-Access/feature/include
Browse files Browse the repository at this point in the history
Added Parameter Rewriter
  • Loading branch information
ackava authored Jul 11, 2023
2 parents 5943cd5 + 42982b8 commit 0e8e9ac
Show file tree
Hide file tree
Showing 18 changed files with 432 additions and 181 deletions.
4 changes: 2 additions & 2 deletions src/common/cache/TimedCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ export default class TimedCache<TKey = any, T = any> {
this.map.delete(key);
}

getOrCreate(key: TKey, factory: (k: TKey) => T, ttl: number = 15000) {
getOrCreate<TP>(key: TKey, p1: TP, factory: (k: TKey,p: TP) => T, ttl: number = 15000) {
let item = this.map.get(key);
if (!item) {
item = { value: factory(key), ttl, expire: Date.now() + ttl };
item = { value: factory(key, p1), ttl, expire: Date.now() + ttl };
this.map.set(key, item);
} else {
item.expire = Date.now() + ttl;
Expand Down
16 changes: 2 additions & 14 deletions src/drivers/sql-server/ExpressionToSqlServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,24 +48,12 @@ export default class ExpressionToSqlServer extends ExpressionToSql {
visitSelectStatement(e: SelectStatement): ITextQuery {

if (e.as && e.model) {
const scope = this.targets.get(e.as);
if (!scope) {
this.targets.set(e.as, {
parameter: e.as,
model:
e.model,
replace: e.as,
selectStatement: e
});
} else {
scope.selectStatement = e;
scope.model = e.model;
}
this.scope.create({ parameter: e.as, selectStatement: e });
}

const orderBy = e.orderBy?.length > 0 ? prepare `\n\t\tORDER BY ${this.visitArray(e.orderBy)}` : "";
const where = e.where ? prepare `\n\tWHERE ${this.visit(e.where)}` : "";
const joins = e.joins?.length > 0 ? prepare `\n\t\t${this.visitArray(e.joins)}` : [];
const joins = e.joins?.length > 0 ? prepare `\n\t\t${this.visitArray(e.joins, "\n")}` : [];

const fields = this.visitArray(e.fields, ",\n\t\t");

Expand Down
45 changes: 44 additions & 1 deletion src/entity-query/EntityType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { IClassOf } from "../decorators/IClassOf.js";
import { Query } from "../query/Query.js";
import NameParser from "../decorators/parser/NameParser.js";
import SchemaRegistry from "../decorators/SchemaRegistry.js";
import { QuotedLiteral, TableLiteral } from "../query/ast/Expressions.js";
import { Expression, ExpressionAs, QuotedLiteral, SelectStatement, TableLiteral } from "../query/ast/Expressions.js";
import InstanceCache from "../common/cache/InstanceCache.js";


Expand Down Expand Up @@ -40,6 +40,9 @@ export default class EntityType {
private columnMap: Map<string, IColumn> = new Map();
private relationMap: Map<string, IEntityRelation> = new Map();

private selectAll: SelectStatement;
private selectOne: SelectStatement;

public getProperty(name: string) {
const field = this.fieldMap.get(name);
const relation = this.relationMap.get(name);
Expand Down Expand Up @@ -113,4 +116,44 @@ export default class EntityType {

}

public selectAllFields() {
if (this.selectAll) {
return { ... this.selectAll };
}
const source = this.fullyQualifiedName;
const as = Expression.parameter(this.name[0] + "1");
const fields = this.columns.map((c) => c.name !== c.columnName
? ExpressionAs.create({
expression: Expression.member(as, c.columnName),
alias: QuotedLiteral.create({ literal: c.name })
})
: Expression.member(as, c.columnName));
this.selectAll = SelectStatement.create({
source,
model: this,
as,
fields,
names: JSON.stringify([as.name])
});
return { ... this.selectAll };
}

public selectOneNumber() {
if (this.selectOne) {
return { ... this.selectOne };
}
const source = this.fullyQualifiedName;
const as = Expression.parameter(this.name[0] + "1");
const fields = [
Expression.identifier("1")
];
this.selectOne = SelectStatement.create({
source,
model: this,
as,
fields,
names: JSON.stringify([as.name])
});
return { ... this.selectOne };
}
}
30 changes: 8 additions & 22 deletions src/model/EntitySource.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import type EntityContext from "./EntityContext.js";
import type EntityType from "../entity-query/EntityType.js";
import type { IEntityQuery, IFilterExpression } from "./IFilterWithParameter.js";
import { Expression, ExpressionAs, QuotedLiteral, SelectStatement } from "../query/ast/Expressions.js";
import { Expression } from "../query/ast/Expressions.js";
import EntityQuery from "./EntityQuery.js";
import TimedCache from "../common/cache/TimedCache.js";
import { contextSymbol, modelSymbol } from "../common/symbols/symbols.js";

const modelCache = new TimedCache<any, SelectStatement>();

export class EntitySource<T = any> {

get [modelSymbol]() {
Expand Down Expand Up @@ -61,30 +58,19 @@ export class EntitySource<T = any> {
return this.asQuery();
}

public where<P>(...[parameter, fx]: IFilterExpression<P, T>) {
return this.asQuery().where(parameter, fx);
public filtered(mode: "read" | "modify" = "read"): IEntityQuery<T> {
const query = this.asQuery();
const events = this.context.eventsFor(this.model.typeClass, true);
return mode === "modify" ? events.modify(query) : events.filter(query);
}

generateModel(): SelectStatement {
const source = this.model.fullyQualifiedName;
const as = Expression.parameter(this.model.name[0] + "1");
const fields = this.model.columns.map((c) => c.name !== c.columnName
? ExpressionAs.create({
expression: Expression.member(as, c.columnName),
alias: QuotedLiteral.create({ literal: c.name })
})
: Expression.member(as, c.columnName));
return SelectStatement.create({
source,
as,
fields,
names: JSON.stringify([as.name])
});
public where<P>(...[parameter, fx]: IFilterExpression<P, T>) {
return this.asQuery().where(parameter, fx);
}

public asQuery() {
const { model, context } = this;
const selectStatement = modelCache.getOrCreate(`select-model-${this.model.name}`, () => this.generateModel());
const selectStatement = this.model.selectAllFields();
selectStatement.model = model;
return new EntityQuery<T>({
context,
Expand Down
14 changes: 9 additions & 5 deletions src/model/events/EntityEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,35 @@ import ChangeEntry from "../changes/ChangeEntry.js";

const done = Promise.resolve() as Promise<void>;

export class ForeignKeyFilter<T = any> {
export class ForeignKeyFilter<T = any, TE = any> {

public type: EntityType;
public name: string;
public fkName: string;

private events: EntityEvents<any>;
private events: EntityEvents<TE>;
private context: EntityContext;

constructor(p: Partial<ForeignKeyFilter> & { context: EntityContext, events: EntityEvents<any> }) {
Object.setPrototypeOf(p, ForeignKeyFilter.prototype);
return p as any as ForeignKeyFilter;
}

public is<TR>(fx: (x: T) => TR): boolean {
public is<TR>(fx: (x: T) => TR): this is ForeignKeyFilter<T, TR> & boolean {
const name = NameParser.parseMember(fx);
return name === this.fkName || name === this.name;
}

public read() {
public read(): IEntityQuery<TE> {
const read = this.context.query(this.type.typeClass);
return this.events.filter(read);
}

public modify() {
public unfiltered(): IEntityQuery<TE> {
return this.context.query(this.type.typeClass);
}

public modify(): IEntityQuery<TE> {
const read = this.context.query(this.type.typeClass);
return this.events.modify(read);
}
Expand Down
55 changes: 51 additions & 4 deletions src/query/ast/DebugStringVisitor.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ArrowFunctionExpression, BigIntLiteral, BinaryExpression, BooleanLiteral, CallExpression, CoalesceExpression, ConditionalExpression, Constant, DeleteStatement, Expression, ExpressionAs, Identifier, MemberExpression, NewObjectExpression, NullExpression, NumberLiteral, ParameterExpression, QuotedLiteral, StringLiteral, TableLiteral, TemplateElement, TemplateLiteral } from "./Expressions.js";
import { ArrowFunctionExpression, BigIntLiteral, BinaryExpression, BooleanLiteral, CallExpression, CoalesceExpression, ConditionalExpression, Constant, DeleteStatement, ExistsExpression, Expression, ExpressionAs, Identifier, InsertStatement, JoinExpression, MemberExpression, NewObjectExpression, NullExpression, NumberLiteral, OrderByExpression, ParameterExpression, QuotedLiteral, ReturnUpdated, SelectStatement, StringLiteral, TableLiteral, TemplateElement, TemplateLiteral, UpdateStatement, ValuesStatement } from "./Expressions.js";
import Visitor from "./Visitor.js";

const isBinary = (type) => /^(BinaryExpression|CoalesceExpression)$/.test(type);
Expand Down Expand Up @@ -47,15 +47,15 @@ export default class DebugStringVisitor extends Visitor<string> {
}

visitConditionalExpression(e: ConditionalExpression): string {
return `${e.test} ? ${e.consequent} : ${e.alternate}`;
return `${this.visit(e.test)} ? ${this.visit(e.consequent)} : ${this.visit(e.alternate)}`;
}

visitConstant(e: Constant): string {
return `"Constant:${e.value}"`;
}

visitExpressionAs(e: ExpressionAs): string {
return `${e.expression} as ${e.alias}`;
return `${this.visit(e.expression)} as ${this.visit(e.alias)}`;
}

visitIdentifier(e: Identifier): string {
Expand Down Expand Up @@ -94,7 +94,7 @@ export default class DebugStringVisitor extends Visitor<string> {
}

visitStringLiteral(e: StringLiteral): string {
return `"${e.value}"`;
return `'${e.value}'`;
}

visitTemplateElement(e: TemplateElement): string {
Expand Down Expand Up @@ -122,6 +122,53 @@ export default class DebugStringVisitor extends Visitor<string> {
return "`" + items.join("") + "`";
}

visitDeleteStatement(e: DeleteStatement): string {
if (e.where) {
return `DELETE FROM ${this.visit(e.table)} WHERE ${this.visit(e.where)}`;
}
return `DELETE FROM ${this.visit(e.table)}`;
}

visitExistsExpression(e: ExistsExpression): string {
return `EXISTS (${this.visit(e.target)})`;
}

visitInsertStatement(e: InsertStatement): string {
return `INSERT INTO ${this.visit(e.table)} ${e.values} ${this.visit(e.returnValues)}`;
}

visitJoinExpression(e: JoinExpression): string {
return `\n${e.joinType} JOIN ${this.visit(e.source)}\n\t\tON ${this.visit(e.where)}\n`;
}

visitOrderByExpression(e: OrderByExpression): string {
return `${e.target} ${e.descending ? "DESC" : "ASC"}`;
}

visitReturnUpdated(e: ReturnUpdated): string {
return `\nRETURNING ${this.visitArray(e.fields)}`;
}

visitValuesStatement(e: ValuesStatement): string {
const rows = e.values.map((x) => `(${this.visit(x[0])})`).join(",\n\t");
return `(VALUES ${rows}) as ${this.visit(e.as)})`;
}

visitSelectStatement(e: SelectStatement): string {
const select = `SELECT\n\t${this.visitArray(e.fields, ",\n\t")}\n\tFROM ${this.visit(e.source)}`;
const as = e.as ? this.visit(e.as): "";
const joins = e.joins?.length > 0 ? this.visitArray(e.joins, "\n\t") : "";
const where = e.where ? `\n\tWHERE ${this.visit(e.where)}` : "";
const orderBy = e.orderBy ? `\n\tORDER BY ${this.visitArray(e.orderBy, "\n\t\tTHEN BY")}`: "";
const limit = e.limit > 0 ? `\n\tLIMIT ${e.limit}` : "";
const offset = e.offset > 0 ? `\n\OFFSET ${e.offset}` : "";
return `${select}${as}${joins}${where}${orderBy}${limit}${offset}`;
}

visitUpdateStatement(e: UpdateStatement): string {
return "UPDATE";
}

private visitArray(e: Expression[], separator = ", ") {
return e.map((x) => this.visit(x)).join(separator);
}
Expand Down
Loading

0 comments on commit 0e8e9ac

Please sign in to comment.