@steedos/odata-v4-mongodb
Version:
Service OData requests from a MongoDB data store
313 lines (259 loc) • 9.14 kB
text/typescript
import { Literal } from "odata-v4-literal";
export class Visitor{
query: any
sort: any
skip: number
limit: number
projection: any
collection: string
navigationProperty: string
includes:Visitor[]
inlinecount: boolean
ast:any
constructor(){
this.query = {};
this.sort = {};
this.projection = {};
this.includes = [];
let _ast;
Object.defineProperty(this, "ast", {
get: () => { return _ast; },
set: (v) => { _ast = v; },
enumerable: false
});
}
Visit(node:any, context?:any){
this.ast = this.ast || node;
context = context || {};
if (node){
var visitor = this[`Visit${node.type}`];
if (visitor) visitor.call(this, node, context);
}
return this;
}
protected VisitODataUri(node:any, context:any){
this.Visit(node.value.resource, context);
this.Visit(node.value.query, context);
}
protected VisitEntitySetName(node:any, context:any){
this.collection = node.value.name;
}
protected VisitExpand(node: any, context: any) {
var innerContexts:any = {};
node.value.items.forEach((item) => {
var expandPath = item.value.path.raw;
var innerVisitor = this.includes.filter(v => v.navigationProperty === expandPath)[0];
if (!innerVisitor){
innerVisitor = new Visitor();
innerContexts[expandPath] = {
query: {},
sort: {},
projection: {},
options: {}
};
this.includes.push(innerVisitor);
}
let innerContext:any = innerContexts[expandPath] || {};
innerVisitor.Visit(item, innerContext);
innerVisitor.query = innerContext.query || innerVisitor.query || {};
innerVisitor.sort = innerContext.sort || innerVisitor.sort;
innerVisitor.projection = innerContext.projection || innerVisitor.projection;
});
}
protected VisitExpandItem(node: any, context: any) {
this.Visit(node.value.path, context);
node.value.options && node.value.options.forEach((item) => this.Visit(item, context));
}
protected VisitExpandPath(node: any, context: any) {
this.navigationProperty = node.raw;
}
protected VisitQueryOptions(node:any, context:any){
context.options = {};
node.value.options.forEach((option) => this.Visit(option, context));
this.query = context.query || {};
delete context.query;
this.sort = context.sort;
delete context.sort;
}
protected VisitInlineCount(node:any, context:any){
this.inlinecount = Literal.convert(node.value.value, node.value.raw);
}
protected VisitFilter(node:any, context:any){
context.query = {};
this.Visit(node.value, context);
delete context.identifier;
delete context.literal;
}
protected VisitOrderBy(node:any, context:any){
context.sort = {};
node.value.items.forEach((item) => this.Visit(item, context));
}
protected VisitSkip(node:any, context:any){
this.skip = +node.value.raw;
}
protected VisitTop(node:any, context:any){
this.limit = +node.value.raw;
}
protected VisitOrderByItem(node:any, context:any){
this.Visit(node.value.expr, context);
if (context.identifier) context.sort[context.identifier] = node.value.direction;
delete context.identifier;
delete context.literal;
}
protected VisitSelect(node:any, context:any){
context.projection = {};
node.value.items.forEach((item) => this.Visit(item, context));
this.projection = context.projection;
delete context.projection;
}
protected VisitSelectItem(node:any, context:any){
context.projection[node.raw.replace(/\//g, '.')] = 1;
}
protected VisitAndExpression(node:any, context:any){
var query = context.query;
var leftQuery = {};
context.query = leftQuery;
this.Visit(node.value.left, context);
var rightQuery = {};
context.query = rightQuery;
this.Visit(node.value.right, context);
if (Object.keys(leftQuery).length > 0 && Object.keys(rightQuery).length > 0){
query.$and = [leftQuery, rightQuery];
}
context.query = query;
}
protected VisitOrExpression(node:any, context:any){
var query = context.query;
var leftQuery = {};
context.query = leftQuery;
this.Visit(node.value.left, context);
var rightQuery = {};
context.query = rightQuery;
this.Visit(node.value.right, context);
if (Object.keys(leftQuery).length > 0 && Object.keys(rightQuery).length > 0){
query.$or = [leftQuery, rightQuery];
}
context.query = query;
}
protected VisitBoolParenExpression(node:any, context:any){
this.Visit(node.value, context);
}
protected VisitCommonExpression(node:any, context:any){
this.Visit(node.value, context);
}
protected VisitFirstMemberExpression(node:any, context:any){
this.Visit(node.value, context);
}
protected VisitMemberExpression(node:any, context:any){
this.Visit(node.value, context);
}
protected VisitPropertyPathExpression(node:any, context:any){
if (node.value.current && node.value.next){
this.Visit(node.value.current, context);
if (context.identifier) context.identifier += ".";
this.Visit(node.value.next, context);
}else this.Visit(node.value, context);
}
protected VisitSingleNavigationExpression(node:any, context:any){
if (node.value.current && node.value.next){
this.Visit(node.value.current, context);
this.Visit(node.value.next, context);
}else this.Visit(node.value, context);
}
protected VisitODataIdentifier(node:any, context:any){
context.identifier = (context.identifier || "") + node.value.name;
}
protected VisitNotExpression(node:any, context:any){
this.Visit(node.value, context);
if (context.query){
for (var prop in context.query){
context.query[prop] = { $not: context.query[prop] };
}
}
}
protected VisitEqualsExpression(node:any, context:any){
this.Visit(node.value.left, context);
this.Visit(node.value.right, context);
if (context.identifier) context.query[context.identifier] = context.literal;
delete context.identifier;
delete context.literal;
}
protected VisitNotEqualsExpression(node:any, context:any){
this.Visit(node.value.left, context);
this.Visit(node.value.right, context);
if (context.identifier) context.query[context.identifier] = { $ne: context.literal };
delete context.identifier;
delete context.literal;
}
protected VisitInExpression(node:any, context:any){
this.Visit(node.value.left, context);
this.Visit(node.value.right, context);
if (context.identifier)
context.query[context.identifier] = {
$in: JSON.parse(`[${node.value.right.raw.replace(/\'/g, "\"").slice(1).slice(0, -1)}]`)
};
delete context.identifier;
delete context.literal;
};
protected VisitNotInExpression(node:any, context:any){
this.Visit(node.value.left, context);
this.Visit(node.value.right, context);
if (context.identifier)
context.query[context.identifier] = {
$nin: JSON.parse(`[${node.value.right.raw.replace(/\'/g, "\"").slice(1).slice(0, -1)}]`)
};
delete context.identifier;
delete context.literal;
};
protected VisitLesserThanExpression(node:any, context:any){
this.Visit(node.value.left, context);
this.Visit(node.value.right, context);
if (context.identifier) context.query[context.identifier] = { $lt: context.literal };
delete context.identifier;
delete context.literal;
}
protected VisitLesserOrEqualsExpression(node:any, context:any){
this.Visit(node.value.left, context);
this.Visit(node.value.right, context);
if (context.identifier) context.query[context.identifier] = { $lte: context.literal };
delete context.identifier;
delete context.literal;
}
protected VisitGreaterThanExpression(node:any, context:any){
this.Visit(node.value.left, context);
this.Visit(node.value.right, context);
if (context.identifier) context.query[context.identifier] = { $gt: context.literal };
delete context.identifier;
delete context.literal;
}
protected VisitGreaterOrEqualsExpression(node:any, context:any){
this.Visit(node.value.left, context);
this.Visit(node.value.right, context);
if (context.identifier) context.query[context.identifier] = { $gte: context.literal };
delete context.identifier;
delete context.literal;
}
protected VisitLiteral(node:any, context:any){
context.literal = Literal.convert(node.value, node.raw);
}
protected VisitMethodCallExpression(node: any, context: any) {
var method = node.value.method;
(node.value.parameters || []).forEach(p => this.Visit(p, context));
if (context.identifier) {
switch (method) {
case "contains":
context.query[context.identifier] = new RegExp(context.literal, "gi");
break;
case "endswith":
context.query[context.identifier] = new RegExp(context.literal + "$", "gi");
break;
case "startswith":
context.query[context.identifier] = new RegExp("^" + context.literal, "gi");
break;
default:
throw new Error("Method call not implemented.")
}
delete context.identifier;
}
}
}