elasticmagic
Advanced tools
Comparing version 0.0.4 to 0.0.5
@@ -1,10 +0,13 @@ | ||
import { ParamsExpression, ParamsType, Params, Expression } from "./expression"; | ||
import { Field, FieldType } from "./document"; | ||
import { Agg, Dictionary } from "./types"; | ||
import { DocClass, Field, FieldType } from './document'; | ||
import { Expression, Params, ParamsExpression, ParamsType } from './expression'; | ||
import { InstanceMapper } from './query'; | ||
import { Dictionary, RawAgg, RawAggBucket } from './types'; | ||
declare type BucketKey = string | number; | ||
declare class Bucket { | ||
private parent; | ||
key: any; | ||
key: BucketKey; | ||
docCount: number; | ||
aggregations: Dictionary<string, AggResult>; | ||
constructor(rawData: any, aggExpr: BucketAgg, parent: any, docClsMap: any, mapperRegistry: any); | ||
constructor(rawData: RawAggBucket, aggExpr: BucketAgg, parent: AggResult, // TODO used by self.parent._populate_instances() | ||
docClsMap: Dictionary<string, DocClass>, mapperRegistry: any); | ||
getAggregation(name: string): AggResult; | ||
@@ -20,45 +23,53 @@ toString(): string; | ||
export declare class AggExpression extends ParamsExpression { | ||
_visitName: string; | ||
_aggName: any; | ||
buildAggResult(rawData: any, docClsMap?: any, mapperRegistry?: any): void; | ||
visitName: string; | ||
aggName: any; | ||
buildAggResult(rawData: Dictionary<string, any>, docClsMap?: Dictionary<string, DocClass>, mapperRegistry?: any): AggResult; | ||
} | ||
export declare class BucketAgg extends AggExpression { | ||
private resultClass; | ||
_visitName: string; | ||
_aggName: any; | ||
_aggregations: Params; | ||
constructor(aggs: any, params: ParamsType, resultClass: any); | ||
buildAggResult(rawData: Agg, docClsMap?: any, mapperRegistry?: any): AggResult; | ||
visitName: string; | ||
aggName: string; | ||
aggregations: Params; | ||
constructor(aggs?: Dictionary<string, Filter>, params?: ParamsType); | ||
} | ||
declare class SingleBucketAggResult extends AggResult { | ||
docCount: number; | ||
aggregations: Dictionary<string, AggResult>; | ||
constructor(aggExpr: SingleBucketAgg, // TODO maybe later we will need to pass wider type | ||
rawData: RawAggBucket, docClsMap: Dictionary<string, DocClass>, mapperRegistry: any); | ||
getAggregation(name: string): AggResult; | ||
} | ||
export declare class MultiBucketAggResult extends AggResult { | ||
private instanceMapper; | ||
private instanceMapper?; | ||
private bucketClass; | ||
buckets: any; | ||
buckets: Bucket[]; | ||
private bucketsMap; | ||
private mapperRegistry; | ||
constructor(aggExpr: BucketAgg, rawData: any, docClsMap: any, mapperRegistry: any, instanceMapper: any); | ||
addBucket(bucket: any): void; | ||
getBucket(key: any): any; | ||
constructor(aggExpr: MultiBucketAgg, rawData: RawAgg, docClsMap: Dictionary<string, DocClass>, mapperRegistry: any, instanceMapper?: InstanceMapper<any, any> | undefined); | ||
addBucket(bucket: Bucket): void; | ||
getBucket(key: BucketKey): Bucket; | ||
} | ||
export declare class SingleBucketAgg extends BucketAgg { | ||
_aggName: any; | ||
constructor(aggs: any, params: any); | ||
aggName: string; | ||
constructor(aggs?: Dictionary<string, Filter>, params?: Dictionary<string, any>); | ||
buildAggResult(rawData: RawAggBucket, docClsMap?: Dictionary<string, DocClass>, mapperRegistry?: any): SingleBucketAggResult; | ||
} | ||
export declare class MultiBucketAgg extends BucketAgg { | ||
private type?; | ||
protected instanceMapper?: any; | ||
_aggName: any; | ||
constructor(aggs: any, params: TermsOptionsShrink, // TODO probably not appropriate type as MultiBucketAgg is parent class, replace with more generic | ||
type?: FieldType | undefined, instanceMapper?: any); | ||
buildAggResult(rawData: any, docClsMap?: any, mapperRegistry?: any): MultiBucketAggResult; | ||
protected instanceMapper?: InstanceMapper<any, any> | undefined; | ||
aggName: any; | ||
constructor(aggs?: Dictionary<string, Filter>, | ||
/** | ||
* TODO probably not appropriate type as MultiBucketAgg is parent class, replace with more generic | ||
*/ | ||
params?: TermsOptionsShrink, type?: FieldType | undefined, // TODO used by def clone | ||
instanceMapper?: InstanceMapper<any, any> | undefined); | ||
buildAggResult(rawData: RawAgg, docClsMap?: Dictionary<string, DocClass>, mapperRegistry?: any): MultiBucketAggResult; | ||
} | ||
declare type TermsOptions = { | ||
field?: Field; | ||
field: Field; | ||
script?: any; | ||
size?: number; | ||
type?: FieldType; | ||
aggs?: { | ||
[agg: string]: Filter; | ||
}; | ||
instanceMapper?: any; | ||
aggs?: Dictionary<string, Filter>; | ||
instanceMapper?: InstanceMapper<any, any>; | ||
}; | ||
@@ -71,3 +82,3 @@ declare type TermsOptionsShrink = { | ||
export declare class Terms extends MultiBucketAgg { | ||
_aggName: string; | ||
aggName: string; | ||
constructor({ field, type, aggs, instanceMapper, ...opts }: TermsOptions); | ||
@@ -77,9 +88,7 @@ } | ||
filter: Expression; | ||
aggs?: { | ||
[agg: string]: Filter; | ||
}; | ||
aggs?: Dictionary<string, Filter>; | ||
}; | ||
export declare class Filter extends SingleBucketAgg { | ||
_visitName: string; | ||
_aggName: string; | ||
visitName: string; | ||
aggName: string; | ||
filter: Expression; | ||
@@ -86,0 +95,0 @@ constructor({ filter, aggs, ...opts }: FilterOptions); |
@@ -17,3 +17,4 @@ "use strict"; | ||
class Bucket { | ||
constructor(rawData, aggExpr, parent, docClsMap, mapperRegistry) { | ||
constructor(rawData, aggExpr, parent, // TODO used by self.parent._populate_instances() | ||
docClsMap, mapperRegistry) { | ||
this.parent = parent; | ||
@@ -23,7 +24,6 @@ this.aggregations = {}; | ||
this.docCount = rawData.doc_count; | ||
aggExpr._aggregations.getParamsKvList().forEach((agg) => { | ||
aggExpr.aggregations.getParamsKvList().forEach((agg) => { | ||
const aggName = agg[0]; | ||
const aggExpr = agg[1]; | ||
this.aggregations[aggName] = aggExpr.buildAggResult(rawData[aggName], docClsMap, mapperRegistry); | ||
; | ||
const expr = agg[1]; | ||
this.aggregations[aggName] = expr.buildAggResult(rawData[aggName], docClsMap, mapperRegistry); | ||
}); | ||
@@ -49,5 +49,5 @@ } | ||
super(...arguments); | ||
this._visitName = 'agg'; | ||
this.visitName = 'agg'; | ||
} | ||
buildAggResult(rawData, docClsMap = null, mapperRegistry = null) { | ||
buildAggResult(rawData, docClsMap = {}, mapperRegistry = {}) { | ||
throw new Error('AggExpression: buildAggResult not implemented'); | ||
@@ -58,13 +58,7 @@ } | ||
class BucketAgg extends AggExpression { | ||
// TODO here must be interfact for resultClass, but in typescript it is hard to reason abount how to do this properly | ||
constructor(aggs, params, resultClass) { | ||
constructor(aggs, params) { | ||
super(params); | ||
this.resultClass = resultClass; | ||
this._visitName = 'bucketAgg'; | ||
// TODO kwargs.pop('aggregations', {}) | ||
this._aggregations = new expression_1.Params(aggs); | ||
this.visitName = 'bucketAgg'; | ||
this.aggregations = new expression_1.Params(aggs); | ||
} | ||
buildAggResult(rawData, docClsMap = null, mapperRegistry = null) { | ||
return new this.resultClass(this, rawData, docClsMap, mapperRegistry); | ||
} | ||
} | ||
@@ -80,17 +74,12 @@ exports.BucketAgg = BucketAgg; | ||
class SingleBucketAggResult extends AggResult { | ||
constructor(aggExpr, rawData, docClsMap, mapperRegistry, instanceMapper) { | ||
constructor(aggExpr, // TODO maybe later we will need to pass wider type | ||
rawData, docClsMap, mapperRegistry) { | ||
super(aggExpr); | ||
this.instanceMapper = instanceMapper; | ||
this.bucketClass = Bucket; | ||
this.bucketsMap = {}; | ||
this.mapperRegistry = {}; | ||
this.buckets = []; | ||
this.docCount = 0; | ||
this.aggregations = {}; | ||
this.docCount = rawData.doc_count; | ||
aggExpr._aggregations.getParamsKvList().forEach((agg) => { | ||
aggExpr.aggregations.getParamsKvList().forEach((agg) => { | ||
const aggName = agg[0]; | ||
const aggExpr = agg[1]; | ||
this.aggregations[aggName] = aggExpr.buildAggResult(rawData[aggName], docClsMap, mapperRegistry); | ||
; | ||
const expr = agg[1]; | ||
this.aggregations[aggName] = expr.buildAggResult(rawData[aggName], docClsMap, mapperRegistry); | ||
}); | ||
@@ -128,2 +117,3 @@ } | ||
if (this.instanceMapper) { | ||
// TOOD this piece of code is broken, as mapperRegistry must use sring as key, not instanceMapper itself | ||
if (!(this.instanceMapper in this.mapperRegistry)) { | ||
@@ -148,14 +138,23 @@ this.mapperRegistry[this.mapperRegistry] = []; | ||
constructor(aggs, params) { | ||
super(aggs, params, SingleBucketAggResult); | ||
super(aggs, params); | ||
} | ||
// TODO in python we just pass resultcls and parent call buildAggResult | ||
// but for any reason we do not do this right now, maybe we will do this later | ||
buildAggResult(rawData, docClsMap = {}, mapperRegistry = null) { | ||
return new SingleBucketAggResult(this, rawData, docClsMap, mapperRegistry); | ||
} | ||
} | ||
exports.SingleBucketAgg = SingleBucketAgg; | ||
class MultiBucketAgg extends BucketAgg { | ||
constructor(aggs, params, // TODO probably not appropriate type as MultiBucketAgg is parent class, replace with more generic | ||
type, instanceMapper) { | ||
super(aggs, params, MultiBucketAggResult); | ||
constructor(aggs, | ||
/** | ||
* TODO probably not appropriate type as MultiBucketAgg is parent class, replace with more generic | ||
*/ | ||
params, type, // TODO used by def clone | ||
instanceMapper) { | ||
super(aggs, params); | ||
this.type = type; | ||
this.instanceMapper = instanceMapper; | ||
} | ||
buildAggResult(rawData, docClsMap = null, mapperRegistry = null) { | ||
buildAggResult(rawData, docClsMap = {}, mapperRegistry = null) { | ||
return new MultiBucketAggResult(this, rawData, docClsMap, mapperRegistry, this.instanceMapper); | ||
@@ -165,11 +164,11 @@ } | ||
exports.MultiBucketAgg = MultiBucketAgg; | ||
function getType(type, field) { | ||
return type || (field ? field.getType() : null); | ||
function getType(field, type) { | ||
var _a, _b; | ||
return type || (_b = (_a = field) === null || _a === void 0 ? void 0 : _a.getType(), (_b !== null && _b !== void 0 ? _b : null)); | ||
} | ||
; | ||
class Terms extends MultiBucketAgg { | ||
constructor(_a) { | ||
var { field, type, aggs, instanceMapper } = _a, opts = __rest(_a, ["field", "type", "aggs", "instanceMapper"]); | ||
super(aggs, Object.assign({ field }, opts), getType(type, field), instanceMapper); | ||
this._aggName = 'terms'; | ||
super(aggs, Object.assign({ field }, opts), getType(field, type), instanceMapper); | ||
this.aggName = 'terms'; | ||
this.instanceMapper = instanceMapper; | ||
@@ -183,4 +182,4 @@ } | ||
super(aggs, opts); | ||
this._visitName = 'filterAgg'; | ||
this._aggName = 'filter'; | ||
this.visitName = 'filterAgg'; | ||
this.aggName = 'filter'; | ||
this.filter = filter; | ||
@@ -187,0 +186,0 @@ } |
@@ -1,5 +0,6 @@ | ||
import { Client } from "@elastic/elasticsearch"; | ||
import { SearchQuery, SearchQueryOptions } from "./query"; | ||
import { SearchResult } from "./result"; | ||
import { Doc } from "./document"; | ||
import { Client } from '@elastic/elasticsearch'; | ||
import { Doc } from './document'; | ||
import { SearchQuery, SearchQueryOptions } from './query'; | ||
import { SearchResult } from './result'; | ||
import { Nullable } from './types'; | ||
export declare class Index { | ||
@@ -21,7 +22,8 @@ private name; | ||
private client; | ||
private index; | ||
private index?; | ||
private esVersion?; | ||
constructor(client: Client, indexName: string); | ||
searchQuery(searchQueryOptions: SearchQueryOptions): SearchQuery; | ||
getIndex(): Index; | ||
constructor(client: Client, indexName?: string); | ||
searchQuery(searchQueryOptions?: SearchQueryOptions): SearchQuery; | ||
getIndex(): Nullable<Index>; | ||
addIndex(name: string): void; | ||
getEsVersion(): Promise<EsVersion>; | ||
@@ -31,4 +33,2 @@ private processEsVersionResult; | ||
* Make a request using underlying es client. | ||
* | ||
* NOTE: If you want to type response body, pass a generic type. | ||
* @param compiledQuery | ||
@@ -41,3 +41,2 @@ * @param params | ||
* | ||
* NOTE: If you want to type response body, pass a generic type. | ||
* @param rawResultBody \ | ||
@@ -51,4 +50,4 @@ * @param searchQueryContext | ||
*/ | ||
search<T extends Doc, TRaw>(searchQuery: SearchQuery): Promise<SearchResult<T>>; | ||
search<T extends Doc>(searchQuery: SearchQuery): Promise<SearchResult<T>>; | ||
} | ||
export {}; |
@@ -36,3 +36,2 @@ "use strict"; | ||
} | ||
; | ||
} | ||
@@ -42,5 +41,7 @@ class Cluster { | ||
this.client = client; | ||
this.index = new Index(indexName, this); | ||
if (indexName) { | ||
this.index = new Index(indexName, this); | ||
} | ||
} | ||
searchQuery(searchQueryOptions) { | ||
searchQuery(searchQueryOptions = {}) { | ||
return new query_1.SearchQuery(Object.assign({ cluster: this, index: this.index }, searchQueryOptions)); | ||
@@ -51,6 +52,10 @@ } | ||
} | ||
addIndex(name) { | ||
this.index = new Index(name, this); | ||
} | ||
getEsVersion() { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
if (this.esVersion) | ||
if (this.esVersion) { | ||
return this.esVersion; | ||
} | ||
const rawResult = yield this.client.info(); | ||
@@ -68,4 +73,2 @@ return this.processEsVersionResult(rawResult.body); | ||
* Make a request using underlying es client. | ||
* | ||
* NOTE: If you want to type response body, pass a generic type. | ||
* @param compiledQuery | ||
@@ -78,3 +81,6 @@ * @param params | ||
// TODO get client method to call, must be a accep-like function in searchQuery | ||
return this.client.search(Object.assign({ index: this.index.getName(), body: compiledQuery }, params)); | ||
if (!this.index) { | ||
throw new Error('index required'); | ||
} | ||
return this.client.search(Object.assign({ body: compiledQuery, index: this.index.getName() }, params)); | ||
}); | ||
@@ -85,3 +91,2 @@ } | ||
* | ||
* NOTE: If you want to type response body, pass a generic type. | ||
* @param rawResultBody \ | ||
@@ -91,5 +96,4 @@ * @param searchQueryContext | ||
processResult(rawResultBody, searchQueryContext) { | ||
return new result_1.SearchResult(rawResultBody, searchQueryContext.aggregations, searchQueryContext.docClass, searchQueryContext.instanceMapper); | ||
return new result_1.SearchResult(rawResultBody, searchQueryContext.aggregations, searchQueryContext.docClasses, searchQueryContext.instanceMapper); | ||
} | ||
; | ||
/** | ||
@@ -96,0 +100,0 @@ * run search query against elasticsearch cluster and return processed result. |
@@ -1,2 +0,4 @@ | ||
import { SearchQueryContext, Query } from "./query"; | ||
import { Field } from './document'; | ||
import { Query, SearchQueryContext } from './query'; | ||
export declare function isField(x: any): x is Field; | ||
export declare class CompilerVisitor { | ||
@@ -9,13 +11,7 @@ params: Query; | ||
/** | ||
* This is where we start and finish building our query (doc must be rewrited) | ||
* This is where we start building our query | ||
* @param queryContext | ||
*/ | ||
private visitSearchQueryContext; | ||
/** | ||
* @param expression | ||
*/ | ||
private visitQueryExpression; | ||
/** | ||
* @param expression | ||
*/ | ||
private visitFieldQuery; | ||
@@ -33,2 +29,4 @@ private visitTerm; | ||
private visitRange; | ||
private visitSort; | ||
private visitSource; | ||
} |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const document_1 = require("./document"); | ||
const expression_1 = require("./expression"); | ||
const util_1 = require("./util"); | ||
// TODO can not move to utils due to cirular inports | ||
function isField(x) { | ||
return x instanceof document_1.Field; | ||
} | ||
exports.isField = isField; | ||
class CompilerVisitor { | ||
@@ -15,6 +21,6 @@ constructor() { | ||
let visitName; | ||
if ((_a = expression) === null || _a === void 0 ? void 0 : _a._visitName) { | ||
visitName = expression._visitName; | ||
if ((_a = expression) === null || _a === void 0 ? void 0 : _a.visitName) { | ||
visitName = expression.visitName; | ||
} | ||
// visitName must be constant | ||
// TODO visitName must be constant | ||
switch (visitName) { | ||
@@ -27,5 +33,2 @@ case 'searchQueryContext': | ||
return this.visitParams(expression); | ||
case 'fieldExpression': | ||
console.log('in python it is not implemented'); | ||
return; | ||
case 'fieldQuery': | ||
@@ -49,2 +52,6 @@ return this.visitFieldQuery(expression); | ||
return this.visitRange(expression); | ||
case 'sort': | ||
return this.visitSort(expression); | ||
case 'source': | ||
return this.visitSource(expression); | ||
default: | ||
@@ -55,13 +62,9 @@ } | ||
} | ||
// if this is object | ||
if (util_1.isObject(expression)) { | ||
return this.visitObject(expression); | ||
} | ||
// return as is, mostly for simple types | ||
return expression; | ||
} | ||
getQuery(queryContext) { | ||
const q = queryContext.query; | ||
// TODO if wrap_function_score: | ||
return q; | ||
return queryContext.query; | ||
} | ||
@@ -74,7 +77,6 @@ getFilteredQuery(queryContext) { | ||
} | ||
// TODO(for es > 6) if not features.supports_mapping_types and doc_classes: | ||
if (filterClauses.length > 0) { | ||
// features.supports_bool_filter is always true for es > 2 | ||
if (filterClauses.length === 1) { | ||
return new expression_1.Bool({ must: q, filter: filterClauses[0] }); | ||
const clause = filterClauses[0]; | ||
return new expression_1.Bool({ must: q, filter: clause }); | ||
} | ||
@@ -86,3 +88,3 @@ return new expression_1.Bool({ must: q, filter: filterClauses }); | ||
/** | ||
* This is where we start and finish building our query (doc must be rewrited) | ||
* This is where we start building our query | ||
* @param queryContext | ||
@@ -96,6 +98,8 @@ */ | ||
} | ||
// TODO order_by (sort) | ||
if (queryContext.source != null && queryContext !== undefined) { | ||
params._source = queryContext.source; | ||
if (queryContext.sort.length > 0) { | ||
params.sort = this.visit(queryContext.sort); | ||
} | ||
if (!util_1.isNullOrUndef(queryContext.source)) { | ||
params._source = this.visit(queryContext.source); | ||
} | ||
if (queryContext.limit !== null) { | ||
@@ -109,37 +113,25 @@ params.size = queryContext.limit; | ||
} | ||
/** | ||
* @param expression | ||
*/ | ||
// TODO return type | ||
visitQueryExpression(expression) { | ||
return { | ||
[expression._queryName]: this.visit(expression.params) | ||
[expression.queryName]: this.visit(expression.params), | ||
}; | ||
} | ||
/** | ||
* @param expression | ||
*/ | ||
// TODO return type | ||
visitFieldQuery(expression) { | ||
// const exprParams = new Params(expression.params); | ||
const exprParams = expression.params; | ||
if (exprParams.length > 0) { // TODO maybe implement iterator for Params | ||
let params = { [expression._queryKey]: this.visit(expression.query) }; | ||
params = Object.assign(Object.assign({}, params), exprParams); // TODo here can be broken line | ||
if (exprParams.length > 0) { | ||
let params = { [expression.queryKey]: this.visit(expression.query) }; | ||
params = Object.assign(Object.assign({}, params), exprParams); | ||
return { | ||
[expression._queryName]: { | ||
[this.visit(expression.field)]: params | ||
} | ||
[expression.queryName]: { | ||
[this.visit(expression.field)]: params, | ||
}, | ||
}; | ||
} | ||
return { | ||
[expression._queryName]: { | ||
[this.visit(expression.field)]: this.visit(expression.query) | ||
} | ||
[expression.queryName]: { | ||
[this.visit(expression.field)]: this.visit(expression.query), | ||
}, | ||
}; | ||
} | ||
visitTerm(term) { | ||
// TODO in python there is a visit call to field type but we do not have it yet | ||
// const fieldName = term.field; | ||
// TODO if field_name == '_id': | ||
return this.visitFieldQuery(term); | ||
@@ -149,6 +141,5 @@ } | ||
const fieldName = this.visit(expression.field); | ||
let params = { [fieldName]: this.visit(expression.terms) }; | ||
params = Object.assign(Object.assign({}, params), this.visit(expression.params)); // { company_id: [123] } | ||
const params = { [fieldName]: this.visit(expression.terms) }; | ||
return { | ||
'terms': params | ||
terms: Object.assign(Object.assign({}, params), this.visit(expression.params)), | ||
}; | ||
@@ -180,3 +171,3 @@ } | ||
return { | ||
[agg._aggName]: this.visit(agg.params) | ||
[agg.aggName]: this.visit(agg.params), | ||
}; | ||
@@ -186,6 +177,6 @@ } | ||
const params = { | ||
[agg._aggName]: this.visit(agg.params), | ||
[agg.aggName]: this.visit(agg.params), | ||
}; | ||
if (agg._aggregations.length > 0) { | ||
params.aggregations = this.visit(agg._aggregations); | ||
if (agg.aggregations.length > 0) { | ||
params.aggregations = this.visit(agg.aggregations); | ||
} | ||
@@ -196,3 +187,3 @@ return params; | ||
const params = this.visitBucketAgg(agg); | ||
params[agg._aggName] = this.visit(agg.filter); | ||
params[agg.aggName] = this.visit(agg.filter); | ||
return params; | ||
@@ -202,9 +193,53 @@ } | ||
const fieldParams = { | ||
[this.visit(expr.field)]: this.visit(expr.params) | ||
[this.visit(expr.field)]: this.visit(expr.params), | ||
}; | ||
return { | ||
range: Object.assign(Object.assign({}, this.visit(expr.rangeParams)), fieldParams) | ||
range: Object.assign(Object.assign({}, this.visit(expr.rangeParams)), fieldParams), | ||
}; | ||
} | ||
visitSort(expr) { | ||
if (expr.params.length) { | ||
const params = Object.assign({ order: this.visit(expr.order) }, this.visit(expr.params)); | ||
return { | ||
[this.visit(expr.field)]: params, | ||
}; | ||
} | ||
else if (expr.order) { | ||
return { | ||
[this.visit(expr.field)]: this.visit(expr.order), | ||
}; | ||
} | ||
return this.visit(expr.field); | ||
} | ||
visitSource(expr) { | ||
var _a, _b; | ||
if (expr.include || expr.exclude) { | ||
const params = {}; | ||
if ((_a = expr.exclude) === null || _a === void 0 ? void 0 : _a.length) { | ||
const exclude = this.visit(expr.exclude); | ||
if (exclude.length) { | ||
params.exclude = exclude; | ||
} | ||
} | ||
if ((_b = expr.include) === null || _b === void 0 ? void 0 : _b.length) { | ||
const include = this.visit(expr.include); | ||
if (include.length) { | ||
params.include = include; | ||
} | ||
} | ||
return params; | ||
} | ||
if (util_1.isBoolean(expr.fields)) { | ||
return expr.fields; | ||
} | ||
if (util_1.isString(expr.fields)) { | ||
return expr.fields; | ||
} | ||
if (isField(expr.fields)) { | ||
return this.visit(expr.fields); | ||
} | ||
const fields = expr.fields; | ||
return fields.map((field) => this.visit(field)); | ||
} | ||
} | ||
exports.CompilerVisitor = CompilerVisitor; |
@@ -1,4 +0,4 @@ | ||
import { Terms, Term, Bool, Expression, TermValue, RangeExpr, RangeValue } from "./expression"; | ||
import { SearchResult } from "./result"; | ||
import { Hit } from "./types"; | ||
import { Bool, Expression, RangeExpr, RangeValue, Sort, SortOpts, Term, Terms, TermValue } from './expression'; | ||
import { SearchResult } from './result'; | ||
import { Hit } from './types'; | ||
export declare class FieldType { | ||
@@ -12,15 +12,23 @@ } | ||
} | ||
export declare type FieldOpts = { | ||
name?: string; | ||
parent?: DocClass; | ||
}; | ||
export declare class Field extends Expression { | ||
private type; | ||
name: string; | ||
readonly _visitName = "field"; | ||
constructor(type: FieldType, name: string); | ||
in_(terms: TermValue[]): Terms; | ||
not_(term: TermValue): Bool; | ||
eq_(other: TermValue): Term; | ||
lt_(other: RangeValue): RangeExpr; | ||
gt_(other: RangeValue): RangeExpr; | ||
lte_(other: RangeValue): RangeExpr; | ||
gte_(other: RangeValue): RangeExpr; | ||
readonly name: string; | ||
readonly parent: DocClass; | ||
readonly visitName = "field"; | ||
constructor(type: FieldType, name: string, parent: DocClass); | ||
in(terms: TermValue[]): Terms; | ||
not(term: TermValue): Bool; | ||
eq(other: TermValue): Term; | ||
lt(other: RangeValue): RangeExpr; | ||
gt(other: RangeValue): RangeExpr; | ||
lte(other: RangeValue): RangeExpr; | ||
gte(other: RangeValue): RangeExpr; | ||
asc(opts?: SortOpts): Sort; | ||
desc(opts?: SortOpts): Sort; | ||
getType(): FieldType; | ||
collectDocClasses(): Readonly<DocClass[]>; | ||
} | ||
@@ -31,18 +39,12 @@ declare type DocOpts = { | ||
}; | ||
export interface IDocument { | ||
_docType: string; | ||
new (opts: DocOpts): Doc; | ||
} | ||
export declare class Doc { | ||
static readonly _docType: string; | ||
private hit; | ||
private result; | ||
_id: string | number; | ||
static readonly docType: string; | ||
protected hit: Hit; | ||
protected result: SearchResult<any>; | ||
_id: string; | ||
constructor(opts: DocOpts); | ||
/** | ||
* If instanceMapper was defined for query then return instance mapped by instanceMapper | ||
* else return null; | ||
*/ | ||
get instance(): any | null; | ||
static getDocCls(): string; | ||
private populateFromSource; | ||
} | ||
export declare type DocClass = typeof Doc; | ||
export {}; |
@@ -17,37 +17,45 @@ "use strict"; | ||
class Field extends expression_1.Expression { | ||
constructor(type, name) { | ||
constructor(type, name, parent) { | ||
super(); | ||
this.type = type; | ||
this.name = name; | ||
this._visitName = 'field'; | ||
this.parent = parent; | ||
this.visitName = 'field'; | ||
} | ||
in_(terms) { | ||
in(terms) { | ||
return new expression_1.Terms(this, terms); | ||
} | ||
not_(term) { | ||
not(term) { | ||
return expression_1.Bool.mustNot(new expression_1.Term(this, term)); | ||
} | ||
eq_(other) { | ||
// TODO add if other is None: return self.missing() | ||
eq(other) { | ||
// TODO add if other is None: return self.missing() | ||
return new expression_1.Term(this, other); | ||
} | ||
lt_(other) { | ||
lt(other) { | ||
return new expression_1.RangeExpr(this, { lt: other }); | ||
} | ||
gt_(other) { | ||
gt(other) { | ||
return new expression_1.RangeExpr(this, { gt: other }); | ||
} | ||
lte_(other) { | ||
lte(other) { | ||
return new expression_1.RangeExpr(this, { lte: other }); | ||
} | ||
gte_(other) { | ||
gte(other) { | ||
return new expression_1.RangeExpr(this, { gte: other }); | ||
} | ||
asc(opts) { | ||
return new expression_1.Sort(this, 'asc', opts); | ||
} | ||
desc(opts) { | ||
return new expression_1.Sort(this, 'desc', opts); | ||
} | ||
getType() { | ||
return this.type; | ||
} | ||
collectDocClasses() { | ||
return this.parent ? [this.parent] : []; | ||
} | ||
} | ||
exports.Field = Field; | ||
// TODO maybe decorator can be used as an alternative to metaclass | ||
// https://github.com/Microsoft/TypeScript/issues/17454 | ||
class Doc { | ||
@@ -58,12 +66,18 @@ constructor(opts) { | ||
this._id = opts.hit._id; | ||
if (this.hit._source) { | ||
this.populateFromSource(); | ||
} | ||
} | ||
; | ||
/** | ||
* If instanceMapper was defined for query then return instance mapped by instanceMapper | ||
* else return null; | ||
*/ | ||
get instance() { | ||
return null; | ||
static getDocCls() { | ||
return this.docType; | ||
} | ||
populateFromSource() { | ||
Object.entries(this.hit._source).forEach((hitKV) => { | ||
const fieldName = hitKV[0]; | ||
const fieldValue = hitKV[1]; | ||
const _this = this; // TODO well you should't see this | ||
_this[fieldName] = fieldValue; | ||
}); | ||
} | ||
} | ||
exports.Doc = Doc; |
@@ -1,31 +0,21 @@ | ||
import { Field } from "./document"; | ||
import { DocClass, Field } from './document'; | ||
import { Dictionary, Nullable } from './types'; | ||
export declare type TermValue = number | string | boolean; | ||
export declare type TermField = { | ||
[field: string]: TermValue; | ||
}; | ||
export declare type TermField = Dictionary<string, TermValue>; | ||
export declare class Expression { | ||
readonly _visitName: string; | ||
readonly _queryName: string; | ||
readonly _queryKey: string; | ||
readonly visitName: string; | ||
readonly queryName: string; | ||
readonly queryKey: string; | ||
collectDocClasses(): Readonly<DocClass[]>; | ||
} | ||
declare type BoolOptions = { | ||
must?: any; | ||
filter?: any; | ||
must_not?: any; | ||
should?: any; | ||
mininum_should_match?: any; | ||
boost?: any; | ||
disable_coord?: any; | ||
}; | ||
export declare type ParamsType = { | ||
[key: string]: any; | ||
}; | ||
export declare type ParamsType = Dictionary<any, any>; | ||
export declare type ParamKV = [string, any]; | ||
export declare class Params extends Expression { | ||
_visitName: string; | ||
visitName: string; | ||
private params; | ||
private paramsKvList; | ||
constructor(params?: ParamsType); | ||
getParamsKvList(): Array<ParamKV>; | ||
constructor(params?: Nullable<ParamsType>); | ||
getParamsKvList(): ParamKV[]; | ||
getParams(): ParamsType; | ||
collectDocClasses(): Readonly<DocClass[]>; | ||
get length(): number; | ||
@@ -35,3 +25,3 @@ } | ||
obj: any; | ||
_visitName: string; | ||
visitName: string; | ||
constructor(obj: any); | ||
@@ -41,25 +31,36 @@ } | ||
params: Params; | ||
constructor(params: any); | ||
constructor(params?: Dictionary<any, any>); | ||
collectDocClasses(): Readonly<DocClass[]>; | ||
} | ||
export declare type SourceField = boolean | string | Field | null; | ||
export declare class Source extends Expression { | ||
fields: boolean | string | Field | Array<string | Field>; | ||
include?: (string | Field)[] | undefined; | ||
exclude?: (string | Field)[] | undefined; | ||
visitName: string; | ||
constructor(fields: boolean | string | Field | Array<string | Field>, include?: (string | Field)[] | undefined, exclude?: (string | Field)[] | undefined); | ||
collectDocClasses(): Readonly<DocClass[]>; | ||
} | ||
export declare class QueryExpression extends ParamsExpression { | ||
_visitName: string; | ||
visitName: string; | ||
} | ||
export declare class FieldExpression extends QueryExpression { | ||
_visitName: string; | ||
field: Expression; | ||
constructor(field: Field, params: any); | ||
private wrapLiteral; | ||
field: Field; | ||
visitName: string; | ||
constructor(field: Field, params?: Dictionary<any, any>); | ||
collectDocClasses(): Readonly<DocClass[]>; | ||
} | ||
export declare type FieldQueryValue = string | number | boolean | null; | ||
export declare class FieldQueryExpression extends FieldExpression { | ||
query: any; | ||
_visitName: string; | ||
_queryKey: string; | ||
constructor(field: Field, query: any); | ||
query: FieldQueryValue; | ||
visitName: string; | ||
queryKey: string; | ||
constructor(field: Field, query: FieldQueryValue); | ||
collectDocClasses(): Readonly<DocClass[]>; | ||
} | ||
export declare class Term extends FieldQueryExpression { | ||
_visitName: string; | ||
_queryName: string; | ||
_queryKey: string; | ||
constructor(field: Field, // TODO possibly a cyclic import | ||
term: TermValue); | ||
visitName: string; | ||
queryName: string; | ||
queryKey: string; | ||
constructor(field: Field, term: TermValue); | ||
} | ||
@@ -72,6 +73,7 @@ declare type TermsOptions = { | ||
terms: TermValue[]; | ||
_visitName: string; | ||
visitName: string; | ||
constructor(field: Field, terms: TermValue[], termsOptions?: TermsOptions); | ||
} | ||
export declare type RangeValue = number | string | Date; | ||
declare type ISOString = string; | ||
export declare type RangeValue = number | string | Date | ISOString; | ||
declare type RangeOptions = { | ||
@@ -84,4 +86,4 @@ gte?: RangeValue; | ||
to?: RangeValue; | ||
includeLower?: boolean; | ||
includeUpper?: boolean; | ||
include_lower?: boolean; | ||
include_upper?: boolean; | ||
}; | ||
@@ -95,8 +97,17 @@ declare type RangeSettings = { | ||
export declare class RangeExpr extends FieldExpression { | ||
_visitName: string; | ||
visitName: string; | ||
rangeParams: Params; | ||
constructor(field: Field, rangeOpts: RangeOptions, rangeSettings?: RangeSettings); | ||
} | ||
declare type BoolOptions = { | ||
must?: Nullable<Expression[]>; | ||
filter?: Nullable<Expression[]> | Expression; | ||
must_not?: Nullable<Expression[]>; | ||
should?: Nullable<Expression[]>; | ||
mininum_should_match?: any; | ||
boost?: any; | ||
disable_coord?: any; | ||
}; | ||
export declare class Bool extends QueryExpression { | ||
_queryName: string; | ||
queryName: string; | ||
constructor(options: BoolOptions); | ||
@@ -107,2 +118,16 @@ static must(...expressions: Expression[]): Expression; | ||
} | ||
export declare type SortOpts = { | ||
mode?: any; | ||
missing?: any; | ||
nested_path?: any; | ||
nested_filter?: any; | ||
ignore_unmapped?: any; | ||
}; | ||
export declare class Sort extends QueryExpression { | ||
field: Field; | ||
order: 'asc' | 'desc'; | ||
visitName: string; | ||
constructor(field: Field, order: 'asc' | 'desc', opts?: SortOpts); | ||
collectDocClasses(): Readonly<DocClass[]>; | ||
} | ||
export {}; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const util_1 = require("./util"); | ||
// TODo this must be interface ??? | ||
// TODO make Expression an interface | ||
class Expression { | ||
constructor() { | ||
this._visitName = 'notDefined_visitName'; | ||
this._queryName = 'notDefined_queryName'; | ||
this._queryKey = 'notDefined_queryKey'; | ||
collectDocClasses() { | ||
return []; | ||
} | ||
@@ -14,6 +12,6 @@ } | ||
class Params extends Expression { | ||
// private pointer = 0; | ||
constructor(params) { | ||
super(); | ||
this._visitName = 'params'; | ||
this.visitName = 'params'; | ||
this.params = {}; // TODO maybe change this to Map | ||
this.params = util_1.cleanParams(params); | ||
@@ -28,2 +26,5 @@ this.paramsKvList = this.params ? Object.entries(this.params) : []; | ||
} | ||
collectDocClasses() { | ||
return util_1.collectDocClasses(this.params); | ||
} | ||
get length() { | ||
@@ -38,3 +39,3 @@ return this.paramsKvList.length; | ||
this.obj = obj; | ||
this._visitName = 'literal'; | ||
this.visitName = 'literal'; | ||
} | ||
@@ -48,8 +49,26 @@ } | ||
} | ||
collectDocClasses() { | ||
return util_1.collectDocClasses(this.params); | ||
} | ||
} | ||
exports.ParamsExpression = ParamsExpression; | ||
class Source extends Expression { | ||
constructor(fields, include, exclude) { | ||
super(); | ||
this.fields = fields; | ||
this.include = include; | ||
this.exclude = exclude; | ||
this.visitName = 'source'; | ||
} | ||
collectDocClasses() { | ||
return util_1.uniqueArray(util_1.collectDocClasses(this.fields) | ||
.concat(util_1.collectDocClasses(this.include)) | ||
.concat(util_1.collectDocClasses(this.exclude))); | ||
} | ||
} | ||
exports.Source = Source; | ||
class QueryExpression extends ParamsExpression { | ||
constructor() { | ||
super(...arguments); | ||
this._visitName = 'queryExpression'; | ||
this.visitName = 'queryExpression'; | ||
} | ||
@@ -59,13 +78,9 @@ } | ||
class FieldExpression extends QueryExpression { | ||
// TODO field is an AttributeField, OrderedAttributes | ||
constructor(field, params) { | ||
super(params); // TODO pass null or field ??? | ||
this._visitName = 'fieldExpression'; | ||
this.field = this.wrapLiteral(field); | ||
super(params); | ||
this.field = field; | ||
this.visitName = 'fieldExpression'; | ||
} | ||
wrapLiteral(field) { | ||
if (field instanceof Expression) { | ||
return field; | ||
} | ||
return new Literal(field); | ||
collectDocClasses() { | ||
return util_1.uniqueArray(super.collectDocClasses().concat(util_1.collectDocClasses(this.field))); | ||
} | ||
@@ -76,17 +91,20 @@ } | ||
constructor(field, query) { | ||
super(field, null); | ||
super(field, {}); | ||
this.query = query; | ||
this._visitName = 'fieldQuery'; | ||
this._queryKey = 'query'; | ||
this.visitName = 'fieldQuery'; | ||
this.queryKey = 'query'; | ||
} | ||
collectDocClasses() { | ||
const parentClasses = super.collectDocClasses(); | ||
const ownClasses = util_1.collectDocClasses(this.query); | ||
return util_1.uniqueArray(parentClasses.concat(ownClasses)); | ||
} | ||
} | ||
exports.FieldQueryExpression = FieldQueryExpression; | ||
class Term extends FieldQueryExpression { | ||
constructor(field, // TODO possibly a cyclic import | ||
term // value | ||
) { | ||
constructor(field, term) { | ||
super(field, term); | ||
this._visitName = 'term'; | ||
this._queryName = 'term'; | ||
this._queryKey = 'value'; | ||
this.visitName = 'term'; | ||
this.queryName = 'term'; | ||
this.queryKey = 'value'; | ||
} | ||
@@ -99,3 +117,3 @@ } | ||
this.terms = terms; | ||
this._visitName = 'terms'; | ||
this.visitName = 'terms'; | ||
} | ||
@@ -107,3 +125,3 @@ } | ||
super(field, rangeOpts); | ||
this._visitName = 'range'; | ||
this.visitName = 'range'; | ||
this.rangeParams = new Params(); | ||
@@ -117,5 +135,4 @@ this.rangeParams = new Params(rangeSettings); | ||
super(options); | ||
this._queryName = 'bool'; | ||
this.queryName = 'bool'; | ||
} | ||
// TODO make it clear what Expression to return | ||
static must(...expressions) { | ||
@@ -138,2 +155,14 @@ if (expressions.length === 1) { | ||
exports.Bool = Bool; | ||
class Sort extends QueryExpression { | ||
constructor(field, order, opts) { | ||
super(opts); | ||
this.field = field; | ||
this.order = order; | ||
this.visitName = 'sort'; | ||
} | ||
collectDocClasses() { | ||
return util_1.uniqueArray(super.collectDocClasses().concat(util_1.collectDocClasses(this.field))); | ||
} | ||
} | ||
exports.Sort = Sort; | ||
// TODO add Exists, Missing, Limit, Sort, Not, Ids, Script, Match, HasChild, HasParent |
import { Cluster, Index } from './cluster'; | ||
import { Doc, Field, IntegerType, BooleanType, DateType } from './document'; | ||
import { BooleanType, DateType, Doc, Field, IntegerType } from './document'; | ||
import { Bool, RangeExpr, Term, Terms } from './expression'; | ||
import { SearchQuery } from './query'; | ||
import { Bool, RangeExpr, Term, Terms } from './expression'; | ||
export { Cluster, Index, Doc, Field, IntegerType, BooleanType, DateType, SearchQuery, Bool, RangeExpr, Term, Terms, }; | ||
export declare const fun: () => void; |
@@ -7,9 +7,7 @@ "use strict"; | ||
const document_1 = require("./document"); | ||
exports.BooleanType = document_1.BooleanType; | ||
exports.DateType = document_1.DateType; | ||
exports.Doc = document_1.Doc; | ||
exports.Field = document_1.Field; | ||
exports.IntegerType = document_1.IntegerType; | ||
exports.BooleanType = document_1.BooleanType; | ||
exports.DateType = document_1.DateType; | ||
const query_1 = require("./query"); | ||
exports.SearchQuery = query_1.SearchQuery; | ||
const expression_1 = require("./expression"); | ||
@@ -20,1 +18,4 @@ exports.Bool = expression_1.Bool; | ||
exports.Terms = expression_1.Terms; | ||
const query_1 = require("./query"); | ||
exports.SearchQuery = query_1.SearchQuery; | ||
exports.fun = () => console.log('this is fun!'); |
@@ -1,9 +0,10 @@ | ||
import { Expression, Params } from "./expression"; | ||
import { Cluster, Index } from "./cluster"; | ||
import { IDocument, Doc } from './document'; | ||
import { AggExpression } from "./agg"; | ||
import { SearchResult } from "./result"; | ||
import { AggExpression } from './agg'; | ||
import { Cluster, Index } from './cluster'; | ||
import { Doc, DocClass, Field } from './document'; | ||
import { Expression, Params, Sort, Source, SourceField } from './expression'; | ||
import { SearchResult } from './result'; | ||
import { Dictionary, Nullable, PlainObject } from './types'; | ||
export declare type SearchQueryOptions = { | ||
routing?: number; | ||
docClass?: IDocument; | ||
docClass?: DocClass; | ||
docType?: string; | ||
@@ -16,6 +17,5 @@ }; | ||
export declare type SearchParams = { | ||
routing?: number; | ||
doc_type?: string; | ||
routing?: string; | ||
type?: string; | ||
}; | ||
declare type SourceField = boolean | Array<string> | null; | ||
declare type MatchValue = string | number | boolean; | ||
@@ -25,25 +25,15 @@ declare type TermValue = string | number | boolean; | ||
declare type MatchFilter = { | ||
match: { | ||
[field: string]: MatchValue; | ||
}; | ||
match: Dictionary<string, MatchValue>; | ||
}; | ||
declare type MatchPhraseFilter = { | ||
match: { | ||
[field: string]: MatchValue; | ||
}; | ||
match: Dictionary<string, MatchValue>; | ||
}; | ||
declare type TermFilter = { | ||
term: { | ||
[field: string]: TermValue; | ||
}; | ||
term: Dictionary<string, TermValue>; | ||
}; | ||
declare type TermsFilter = { | ||
terms: { | ||
[field: string]: Array<TermValue>; | ||
}; | ||
terms: Dictionary<string, TermValue[]>; | ||
}; | ||
declare type ExistsFilter = { | ||
exists: { | ||
[field: string]: ExistsValue; | ||
}; | ||
exists: Dictionary<string, ExistsValue>; | ||
}; | ||
@@ -54,3 +44,3 @@ declare type BoolField = { | ||
}; | ||
declare type FilterRootField = BoolField | Array<ExistsFilter>; | ||
declare type FilterRootField = BoolField | ExistsFilter[]; | ||
export declare type BoolMustFilter = Array<MatchFilter | MatchPhraseFilter>; | ||
@@ -63,8 +53,4 @@ declare type BoolMustNotFilter = Array<MatchFilter | MatchPhraseFilter>; | ||
}; | ||
declare type AggregationsField = { | ||
[agg: string]: any; | ||
}; | ||
export declare type Aggregations = { | ||
[agg: string]: AggExpression; | ||
}; | ||
declare type AggregationsField = PlainObject; | ||
export declare type Aggregations = Dictionary<string, AggExpression>; | ||
declare type QueryRootField = { | ||
@@ -74,2 +60,9 @@ bool?: BoolRootField; | ||
}; | ||
declare type SortOrder = 'asc' | 'desc'; | ||
declare type SortMode = 'min' | 'max' | 'sum' | 'avg' | 'median'; | ||
declare type SortObject = Dictionary<string, SortOrder | { | ||
order: SortOrder; | ||
mode?: SortMode; | ||
}>; | ||
declare type SortRootField = SortObject[] | string | '_score'; | ||
export declare type Query = { | ||
@@ -80,4 +73,5 @@ query?: QueryRootField; | ||
aggregations?: AggregationsField; | ||
sort?: SortRootField; | ||
}; | ||
export declare type QueryOverride = object | null; | ||
export declare type QueryOverride = any | null; | ||
export declare type Limit = number | null; | ||
@@ -87,16 +81,24 @@ export declare type InstanceMapper<T1, T2> = (ids: T1[]) => T2; | ||
query: QueryOverride; | ||
source: SourceField; | ||
fields: any; | ||
filters: any; | ||
source: Source | null; | ||
fields: Field[]; | ||
filters: Expression[]; | ||
sort: Sort[]; | ||
limit: Limit; | ||
searchParams: Params; | ||
aggregations: Params; | ||
docClass?: IDocument | undefined; | ||
docClasses: Readonly<DocClass[]>; | ||
docType?: string | undefined; | ||
instanceMapper?: InstanceMapper<any, any> | undefined; | ||
_visitName: string; | ||
constructor(query: QueryOverride, source: SourceField, fields: any, filters: any, limit: Limit, searchParams: Params, aggregations: Params, docClass?: IDocument | undefined, // TODO maybe we should pass entire SearchQuery ? | ||
instanceMapper?: InstanceMapper<any, any> | undefined); | ||
visitName: string; | ||
docTypes: Readonly<string[]>; | ||
constructor(query: QueryOverride, source: Source | null, fields: Field[], filters: Expression[], sort: Sort[], limit: Limit, searchParams: Params, aggregations: Params, docClasses: Readonly<DocClass[]>, docType?: string | undefined, instanceMapper?: InstanceMapper<any, any> | undefined); | ||
private getUniqueDocTypes; | ||
} | ||
export declare class SearchQuery { | ||
private cluster?; | ||
/** | ||
* TODO this field needed when SearchQuery created on its own and hence not bound to cluster or query | ||
* * implement check _index_or_cluster | ||
* * add method for bound, like withIndex, withCluster | ||
*/ | ||
private index?; | ||
@@ -106,2 +108,3 @@ private _limit; | ||
private _filters; | ||
private _sort; | ||
private _aggregations; | ||
@@ -114,18 +117,30 @@ private _source; | ||
private _instanceMapper?; | ||
constructor(searchQueryOptions: ClusterSearchQueryOptions); | ||
constructor(searchQueryOptions?: ClusterSearchQueryOptions); | ||
getQueryContext(): SearchQueryContext; | ||
/** | ||
* Controls which fields of the document's ``_source`` field to retrieve. | ||
* @param include | ||
* | ||
* @param fields: list of fields which should be returned by elasticsearch. Can be one of the following types: | ||
* - field expression, for example: ``PostDocument.title`` | ||
* - ``str`` means field name or glob pattern. For example: | ||
* ``"title"``, ``"user.*"`` | ||
* - ``False`` disables retrieving source | ||
* - ``True`` enables retrieving all source document | ||
* - ``None`` cancels source filtering applied before | ||
* @param include: list of fields to include | ||
* @param exclude: list of fields to exclu | ||
* See `source filtering for more information. | ||
* <https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-source-filtering.html>`_ | ||
*/ | ||
source(fields: SourceField): SearchQuery; | ||
source(fields: boolean | null | undefined | string | Field | Array<string | Field>, opts?: { | ||
include?: Array<string | Field>; | ||
exclude?: Array<string | Field>; | ||
}): this; | ||
/** | ||
* TODO maybe we need clone Query instance on filter call? | ||
* | ||
* Multiple expressions may be specified, so they will be joined together using ``Bool.must`` expression. | ||
* @param filters | ||
*/ | ||
filter(...filters: Expression[]): SearchQuery; | ||
limit(limit: number): SearchQuery; | ||
query(query: QueryOverride): SearchQuery; | ||
filter(...filters: Expression[] | Nullable[]): this; | ||
limit(limit: number): this; | ||
query(query: QueryOverride): this; | ||
/** | ||
@@ -140,12 +155,19 @@ * Adds `aggregations <https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations.html>`_ | ||
*/ | ||
aggregations(aggs: Aggregations): SearchQuery; | ||
withInstanceMapper<T1, T2>(instanceMapper: InstanceMapper<T1, T2>): SearchQuery; | ||
aggregations(aggs: Nullable<Aggregations>): this; | ||
aggs(aggs: Nullable<Aggregations>): this; | ||
withInstanceMapper<T1, T2>(instanceMapper: InstanceMapper<T1, T2>): this; | ||
withDoc<T extends DocClass>(docClass: T): this; | ||
withDocType(docType: string): this; | ||
toJSON(): Query; | ||
get body(): Query; | ||
get params(): SearchParams; | ||
get prettyBody(): string; | ||
getResult<T extends Doc = any>(): Promise<SearchResult<T>>; | ||
clone(): this; | ||
sort(...orders: Sort[] | Field[] | Nullable[]): this; | ||
orderBy(...orders: Sort[] | Field[] | Nullable[]): this; | ||
private compile; | ||
private prepareSearchParams; | ||
get body(): Query; | ||
get params(): any; | ||
get prettyBody(): string; | ||
getResult<T extends Doc = any, TRaw = any>(): Promise<SearchResult<T>>; | ||
private collectDocClasses; | ||
} | ||
export {}; |
@@ -11,9 +11,13 @@ "use strict"; | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const lodash_clonedeep_1 = __importDefault(require("lodash.clonedeep")); | ||
const compiler_1 = require("./compiler"); | ||
const expression_1 = require("./expression"); | ||
const compiler_1 = require("./compiler"); | ||
const util_1 = require("./util"); | ||
// TODO make all fields readonly | ||
class SearchQueryContext { | ||
constructor(query, source, fields, filters, limit, searchParams, aggregations, docClass, // TODO maybe we should pass entire SearchQuery ? | ||
instanceMapper) { | ||
constructor(query, source, fields, filters, sort, limit, searchParams, aggregations, docClasses, docType, instanceMapper) { | ||
this.query = query; | ||
@@ -23,31 +27,49 @@ this.source = source; | ||
this.filters = filters; | ||
this.sort = sort; | ||
this.limit = limit; | ||
this.searchParams = searchParams; | ||
this.aggregations = aggregations; | ||
this.docClass = docClass; | ||
this.docClasses = docClasses; | ||
this.docType = docType; | ||
this.instanceMapper = instanceMapper; | ||
this._visitName = 'searchQueryContext'; | ||
if (!docClass) { | ||
// TODO collect_doc_classes | ||
this.visitName = 'searchQueryContext'; | ||
this.docTypes = []; | ||
const docTypes = []; | ||
// TODO debug this to make it right, maybe look at tests | ||
if (docType) { | ||
if (util_1.isString(docType)) { | ||
docTypes.push(...docType.split(',').map((type) => type.trim())); | ||
} | ||
else { | ||
docTypes.push(docType); | ||
} | ||
} | ||
this.docTypes = this.getUniqueDocTypes(docTypes, this.docClasses); | ||
} | ||
getUniqueDocTypes(docTypes, docClasses) { | ||
const docClassesTypes = docClasses.map((cls) => cls.getDocCls()); | ||
const uniqueDocTypes = new Set(docTypes.concat(docClassesTypes)); | ||
return Array.from(uniqueDocTypes); | ||
} | ||
} | ||
exports.SearchQueryContext = SearchQueryContext; | ||
// UTIL | ||
function getDocType(docType, docClass) { | ||
if (docType) | ||
if (docType) { | ||
return docType; | ||
if (docClass) | ||
return docClass._docType; | ||
} | ||
if (docClass) { | ||
return docClass.docType; | ||
} | ||
return null; | ||
} | ||
class SearchQuery { | ||
constructor(searchQueryOptions) { | ||
constructor(searchQueryOptions = {}) { | ||
this._limit = null; | ||
this._fields = null; // TODO not used right now | ||
this._fields = []; | ||
this._filters = []; | ||
this._sort = []; | ||
this._aggregations = new expression_1.Params(); | ||
this._source = null; | ||
this._query = null; | ||
this._searchParams = new expression_1.Params(); // TODO maybe add subtype like SearchParams | ||
this._searchParams = new expression_1.Params(); | ||
const { index, cluster, routing, docClass, docType, } = searchQueryOptions; | ||
@@ -63,22 +85,42 @@ this.cluster = cluster; | ||
this._searchParams = new expression_1.Params({ | ||
routing: routing, | ||
docType: getDocType(docType, docClass), | ||
routing, | ||
}); | ||
} | ||
getQueryContext() { | ||
return new SearchQueryContext(this._query, this._source, this._fields, this._filters, this._limit, this._searchParams, this._aggregations, this._docClass, this._instanceMapper); | ||
const docClasses = this._docClass ? [this._docClass] : this.collectDocClasses(); | ||
return new SearchQueryContext(this._query, this._source, this._fields, this._filters, this._sort, this._limit, this._searchParams, this._aggregations, docClasses, this._docType, this._instanceMapper); | ||
} | ||
/** | ||
* Controls which fields of the document's ``_source`` field to retrieve. | ||
* @param include | ||
* | ||
* @param fields: list of fields which should be returned by elasticsearch. Can be one of the following types: | ||
* - field expression, for example: ``PostDocument.title`` | ||
* - ``str`` means field name or glob pattern. For example: | ||
* ``"title"``, ``"user.*"`` | ||
* - ``False`` disables retrieving source | ||
* - ``True`` enables retrieving all source document | ||
* - ``None`` cancels source filtering applied before | ||
* @param include: list of fields to include | ||
* @param exclude: list of fields to exclu | ||
* See `source filtering for more information. | ||
* <https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-source-filtering.html>`_ | ||
*/ | ||
source(fields) { | ||
// TODO add exclude and include | ||
this._source = fields; | ||
source(fields, opts) { | ||
var _a, _b, _c, _d, _e, _f; | ||
if (util_1.isNullOrUndef(fields) || util_1.mustClean(fields)) { | ||
this._source = null; | ||
} | ||
else if (util_1.isBoolean(fields)) { | ||
this._source = new expression_1.Source(fields, (_a = opts) === null || _a === void 0 ? void 0 : _a.include, (_b = opts) === null || _b === void 0 ? void 0 : _b.exclude); | ||
} | ||
else if (util_1.isArray(fields)) { | ||
this._source = new expression_1.Source(fields, (_c = opts) === null || _c === void 0 ? void 0 : _c.include, (_d = opts) === null || _d === void 0 ? void 0 : _d.exclude); | ||
} | ||
else { | ||
this._source = new expression_1.Source(fields, (_e = opts) === null || _e === void 0 ? void 0 : _e.include, (_f = opts) === null || _f === void 0 ? void 0 : _f.exclude); | ||
} | ||
return this; | ||
} | ||
// TODO later there can be multiple filter func declarations, one with variadic args | ||
/** | ||
* TODO maybe we need clone Query instance on filter call? | ||
* | ||
* Multiple expressions may be specified, so they will be joined together using ``Bool.must`` expression. | ||
@@ -88,3 +130,8 @@ * @param filters | ||
filter(...filters) { | ||
this._filters.push(...filters); | ||
if (util_1.mustClean(filters)) { | ||
this._filters = []; | ||
} | ||
else { | ||
this._filters.push(...filters); | ||
} | ||
return this; | ||
@@ -96,2 +143,3 @@ } | ||
} | ||
// TODO QueryOverride type is incorrect, hack | ||
query(query) { | ||
@@ -112,5 +160,14 @@ this._query = query; | ||
// TODO implement null cleaning | ||
this._aggregations = new expression_1.Params(aggs); | ||
if (util_1.mustClean(aggs)) { | ||
this._aggregations = new expression_1.Params(); | ||
} | ||
else { | ||
this._aggregations = util_1.mergeParams(this._aggregations, new expression_1.Params(aggs)); | ||
} | ||
return this; | ||
} | ||
aggs(aggs) { | ||
this.aggregations(aggs); | ||
return this; | ||
} | ||
withInstanceMapper(instanceMapper) { | ||
@@ -120,17 +177,13 @@ this._instanceMapper = instanceMapper; | ||
} | ||
withDoc(docClass) { | ||
this._docClass = docClass; | ||
return this; | ||
} | ||
withDocType(docType) { | ||
this._docType = docType; | ||
return this; | ||
} | ||
toJSON() { | ||
return this.compile(); | ||
} | ||
compile() { | ||
const compiler = new compiler_1.CompilerVisitor(); | ||
return compiler.compile(this.getQueryContext()); | ||
} | ||
prepareSearchParams(params) { | ||
return { | ||
routing: `${params.routing}`, | ||
type: params.docType, | ||
}; | ||
} | ||
// TODO maybe reuse logic with Compiled. | ||
// Like hold class which can compile all on construction and then use that instance? | ||
get body() { | ||
@@ -154,3 +207,39 @@ return this.toJSON(); | ||
} | ||
clone() { | ||
return lodash_clonedeep_1.default(this); | ||
} | ||
sort(...orders) { | ||
if (util_1.mustClean(orders)) { | ||
this._sort = []; | ||
} | ||
else { | ||
this._sort.push(...orders); | ||
} | ||
return this; | ||
} | ||
orderBy(...orders) { | ||
this.sort(...orders); | ||
return this; | ||
} | ||
compile() { | ||
const compiler = new compiler_1.CompilerVisitor(); | ||
return compiler.compile(this.getQueryContext()); | ||
} | ||
prepareSearchParams(params) { | ||
return { | ||
routing: `${params.routing}`, | ||
type: params.docType, | ||
}; | ||
} | ||
collectDocClasses() { | ||
const expressions = [ | ||
this._query, | ||
this._source, | ||
this._fields, | ||
this._filters, | ||
Object.values(this._aggregations.getParams()), | ||
]; | ||
return util_1.uniqueArray(util_1.flatMap((expr) => util_1.collectDocClasses(expr), expressions)); | ||
} | ||
} | ||
exports.SearchQuery = SearchQuery; |
@@ -1,15 +0,16 @@ | ||
import { InstanceMapper } from "./query"; | ||
import { IDocument, Doc } from "./document"; | ||
import { Params } from "./expression"; | ||
import { Dictionary, RawResultBody } from "./types"; | ||
import { AggResult } from "./agg"; | ||
declare type InstanceMapperDict = Dictionary<string, InstanceMapper<IDocument, any>>; | ||
import { AggResult } from './agg'; | ||
import { Doc, DocClass } from './document'; | ||
import { Params } from './expression'; | ||
import { InstanceMapper } from './query'; | ||
import { Dictionary, RawResultBody } from './types'; | ||
declare type InstanceMapperDict = Dictionary<string, InstanceMapper<DocClass, any>>; | ||
declare class Result { | ||
raw: any; | ||
constructor(raw: any); | ||
raw: RawResultBody<any>; | ||
constructor(raw: RawResultBody<any>); | ||
get prettyRaw(): string; | ||
} | ||
export declare class SearchResult<T extends Doc, TRaw = any> extends Result { | ||
export declare class SearchResult<T extends Doc> extends Result { | ||
private docClasses; | ||
private queryAggs; | ||
private docClsMap; | ||
private docClasses; | ||
private instanceMappers; | ||
@@ -25,5 +26,6 @@ private mapperRegistry; | ||
scrollId: number | undefined; | ||
constructor(rawResult: RawResultBody<TRaw>, aggregations: Params, docClass?: IDocument, instanceMapper?: InstanceMapper<IDocument, any> | InstanceMapperDict); | ||
constructor(rawResult: RawResultBody<any>, aggregations: Params, docClasses: Readonly<DocClass[]>, instanceMapper?: InstanceMapper<DocClass, any> | InstanceMapperDict); | ||
getAggregation(name: string): AggResult; | ||
getIds(): number[]; | ||
} | ||
export {}; |
@@ -7,14 +7,4 @@ "use strict"; | ||
const DOC_TYPE_NAME_FIELD = `${DOC_TYPE_FIELD}.name`; | ||
function docClsMap(docCls) { | ||
let docClasses = []; | ||
if (!docCls) { | ||
docClasses = []; | ||
} | ||
else if (!Array.isArray(docCls)) { | ||
docClasses = [docCls]; | ||
} | ||
else { | ||
docClasses = docCls; | ||
} | ||
return util_1.arrayKVToDict(docClasses.map((cls) => [cls._docType, cls])); | ||
function docClsMap(docClasses) { | ||
return util_1.arrayKVToDict(docClasses.map((cls) => [cls.docType, cls])); | ||
} | ||
@@ -33,10 +23,12 @@ function getDocTypeForHit(hit) { | ||
} | ||
; | ||
get prettyRaw() { | ||
return JSON.stringify(this.raw, null, 2); | ||
} | ||
} | ||
class SearchResult extends Result { | ||
constructor(rawResult, aggregations, docClass, instanceMapper) { | ||
constructor(rawResult, aggregations, docClasses, instanceMapper) { | ||
super(rawResult); | ||
this.docClasses = docClasses; | ||
this.queryAggs = new expression_1.Params(); | ||
this.docClsMap = {}; | ||
this.docClasses = []; | ||
this.instanceMappers = {}; | ||
@@ -47,5 +39,6 @@ this.mapperRegistry = {}; | ||
this.queryAggs = aggregations || new expression_1.Params(); | ||
this.docClsMap = docClsMap(docClass); | ||
this.docClasses = Object.values(this.docClsMap); | ||
this.docClsMap = docClsMap(docClasses); | ||
if (instanceMapper) { | ||
// TODO although we can check if instanceMapper is a dict | ||
// it is not implemented to accept instanceMapper at a higher level yet | ||
if (isInstanceMapperDict(instanceMapper)) { | ||
@@ -55,3 +48,3 @@ this.instanceMappers = instanceMapper; | ||
else { | ||
this.instanceMappers = util_1.arrayKVToDict(this.docClasses.map((cls) => [cls._docType, instanceMapper])); | ||
this.instanceMappers = util_1.arrayKVToDict(this.docClasses.map((cls) => [cls.docType, instanceMapper])); | ||
} | ||
@@ -65,6 +58,6 @@ } | ||
this.maxScore = hits.max_score; | ||
// TODO add type for hit: any | ||
hits.hits.forEach((hit) => { | ||
const docType = getDocTypeForHit(hit); | ||
const docCls = this.docClsMap[docType]; // TODO DynamicDocument | ||
// TODO below use some sort of DynamicDocument, because fail if no docClass passed to SearchResult | ||
const docCls = this.docClsMap[docType]; | ||
// TODO below is a hack | ||
@@ -87,3 +80,6 @@ // the propblem is when having class type to create instance from it we need to know its type | ||
} | ||
getIds() { | ||
return this.hits.map((hit) => Number(hit._id)); | ||
} | ||
} | ||
exports.SearchResult = SearchResult; |
@@ -0,1 +1,2 @@ | ||
export declare type Nullable<T = any> = T | null | undefined; | ||
export declare type Dictionary<T1 extends string | number, T2 = any> = { | ||
@@ -15,28 +16,19 @@ [key in T1]: T2; | ||
_type?: string; | ||
_source: T; | ||
_source?: T; | ||
fields?: PlainObject; | ||
}; | ||
declare type AggOpts = { | ||
doc_count_error_upper_bound: number; | ||
sum_other_doc_count: number; | ||
declare type BucketFields = { | ||
doc_count: number; | ||
}; | ||
export declare type BucketAgg = { | ||
[key: string]: Agg; | ||
}; | ||
declare type AggBucketChild = { | ||
[key: string]: { | ||
doc_count: number; | ||
} & AggBucketChild; | ||
}; | ||
export declare type AggBucket = { | ||
export declare type RawAggBucketChild = Dictionary<string, BucketFields | Dictionary<string, BucketFields>>; | ||
export declare type RawAggBucket = { | ||
key: any; | ||
doc_count: number; | ||
} & AggBucketChild; | ||
export declare type Agg = { | ||
} & RawAggBucketChild; | ||
export declare type RawAgg = { | ||
doc_count_error_upper_bound: number; | ||
sum_other_doc_count: number; | ||
buckets: Array<AggBucket>; | ||
} & AggOpts; | ||
declare type Aggs = { | ||
[key: string]: Agg; | ||
buckets: RawAggBucket[]; | ||
}; | ||
declare type RawAggs = Dictionary<string, RawAgg>; | ||
declare type SearchResponseBody<T> = { | ||
@@ -58,8 +50,5 @@ error?: string; | ||
}; | ||
aggregations?: Aggs; | ||
aggregations?: RawAggs; | ||
}; | ||
declare type Source = { | ||
[key: string]: any; | ||
}; | ||
export declare type RawResultBody<T = Source> = SearchResponseBody<T>; | ||
export declare type RawResultBody<T = PlainObject> = SearchResponseBody<T>; | ||
export {}; |
@@ -1,2 +0,4 @@ | ||
import { ParamsType } from "./expression"; | ||
import { DocClass } from './document'; | ||
import { Expression, FieldQueryValue, Params, ParamsType } from './expression'; | ||
import { Nullable } from './types'; | ||
export declare function arrayKVToDict<T = any>(array: any[][]): T; | ||
@@ -7,3 +9,13 @@ /** | ||
*/ | ||
export declare function cleanParams(params?: ParamsType): ParamsType; | ||
export declare function isObject(value: any): boolean; | ||
export declare function cleanParams(params?: Nullable<ParamsType>): ParamsType; | ||
export declare function isArray<T>(x: any): x is T[]; | ||
export declare function isString(x: any): x is string; | ||
export declare function isBoolean(x: any): x is boolean; | ||
export declare function isObject(x: any): x is object; | ||
export declare function isExpression(x: any): x is Expression; | ||
export declare function isNullOrUndef(x: any): x is null | undefined; | ||
export declare function uniqueArray<T = any>(items: T[]): T[]; | ||
export declare function collectDocClasses(expr?: Expression | Expression[] | ParamsType | FieldQueryValue): Readonly<DocClass[]>; | ||
export declare function mergeParams(currentParams: Params, newParams: Params): Params; | ||
export declare function flatMap(f: (arg: any) => any, arr: any[]): any[]; | ||
export declare function mustClean(arg: any[] | Nullable): boolean; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const expression_1 = require("./expression"); | ||
function arrayKVToDict(array) { | ||
@@ -15,4 +16,5 @@ return array.reduce((acc, [key, val]) => { | ||
function cleanParams(params) { | ||
if (!params) | ||
if (!params) { | ||
return {}; | ||
} | ||
return Object.entries(params) | ||
@@ -27,24 +29,67 @@ .reduce((acc, [key, val]) => { | ||
exports.cleanParams = cleanParams; | ||
// TODO maybe rewrite all to Map. Could it be slow ?? | ||
function isObject(value) { | ||
const isObjectLike = typeof value === 'object' && value !== null; | ||
let tag = null; | ||
if (value == null) { | ||
tag = value === undefined ? '[object Undefined]' : '[object Null]'; | ||
function isArray(x) { | ||
return Array.isArray(x) && typeof x.length === 'number'; | ||
} | ||
exports.isArray = isArray; | ||
function isString(x) { | ||
return typeof x === 'string'; | ||
} | ||
exports.isString = isString; | ||
function isBoolean(x) { | ||
return typeof x === 'boolean'; | ||
} | ||
exports.isBoolean = isBoolean; | ||
function isObject(x) { | ||
return x && typeof x === 'object' && x.constructor === Object; | ||
} | ||
exports.isObject = isObject; | ||
function isExpression(x) { | ||
return x instanceof expression_1.Expression; | ||
} | ||
exports.isExpression = isExpression; | ||
function isNullOrUndef(x) { | ||
return x === null || x === undefined; | ||
} | ||
exports.isNullOrUndef = isNullOrUndef; | ||
function uniqueArray(items) { | ||
return Array.from(new Set(items)); | ||
} | ||
exports.uniqueArray = uniqueArray; | ||
function collectDocClasses(expr) { | ||
if (isExpression(expr)) { | ||
return expr.collectDocClasses(); | ||
} | ||
else { | ||
tag = toString.call(value); | ||
if (isArray(expr)) { | ||
return uniqueArray(flatMap((item) => collectDocClasses(item), expr)); | ||
} | ||
if (!isObjectLike || tag != '[object Object]') { | ||
if (isObject(expr)) { | ||
const kvListChain = Object.keys(expr).concat(Object.values(expr)); | ||
return uniqueArray(flatMap((item) => collectDocClasses(item), kvListChain)); | ||
} | ||
return []; | ||
} | ||
exports.collectDocClasses = collectDocClasses; | ||
function mergeParams(currentParams, newParams) { | ||
return new expression_1.Params(Object.assign(Object.assign({}, currentParams.getParams()), newParams.getParams())); | ||
} | ||
exports.mergeParams = mergeParams; | ||
function flatMap(f, arr) { | ||
return arr.reduce((x, y) => [...x, ...f(y)], []); | ||
} | ||
exports.flatMap = flatMap; | ||
function mustClean(arg) { | ||
if (isBoolean(arg)) { | ||
return false; | ||
} | ||
if (Object.getPrototypeOf(value) === null) { | ||
if (isObject(arg) && Object.keys(arg).length === 0) { | ||
return true; | ||
} | ||
let proto = value; | ||
while (Object.getPrototypeOf(proto) !== null) { | ||
proto = Object.getPrototypeOf(proto); | ||
if (isArray(arg) && arg.length === 1) { | ||
return isNullOrUndef(arg[0]); | ||
} | ||
return Object.getPrototypeOf(value) === proto; | ||
if (isArray(arg) && arg.length === 0) { | ||
return true; | ||
} | ||
return isNullOrUndef(arg) ? true : false; | ||
} | ||
exports.isObject = isObject; | ||
exports.mustClean = mustClean; |
{ | ||
"name": "elasticmagic", | ||
"version": "0.0.4", | ||
"version": "0.0.5", | ||
"description": "JS orm for elasticsearch.", | ||
@@ -16,4 +16,6 @@ "repository": { | ||
"build": "tsc", | ||
"test": "ES_HOST=es6 jest", | ||
"test:integ": "ES_HOST=es6 jest tests/integ", | ||
"test": "run-p test:unit test:integ", | ||
"test:integ": "ES_HOST=es6 jest tests/integ --runInBand", | ||
"test:unit": "ES_HOST=es6 jest tests/unit", | ||
"test:one": "ES_HOST=es6 jest", | ||
"test:ci": "ES_HOST=localhost jest", | ||
@@ -32,5 +34,6 @@ "lint": "tslint -c tslint.json 'src/**/*.ts'", | ||
"jest": "24.9.0", | ||
"npm-run-all": "^4.1.5", | ||
"ts-jest": "24.2.0", | ||
"typescript": "3.7.4", | ||
"tslint": "5.20.1" | ||
"tslint": "5.20.1", | ||
"typescript": "3.7.4" | ||
}, | ||
@@ -37,0 +40,0 @@ "author": "Kindritskiy Maksym <kindritskiy.m@gmail.com>", |
261
README.md
@@ -1,29 +0,44 @@ | ||
elasticmagic-js (alpha) | ||
elasticmagic-js - JS/Typescript DSL for Elasticsearch | ||
======================= | ||
> This is alpha. Api may/will change. | ||
> The project is still very much a work in progress and in an alpha state, api may/will change; input and contributions welcome! | ||
JS/Typescript DSL for Elasticsearch | ||
[![actions](https://github.com/kindritskyiMax/elasticmagic-js/workflows/tests/badge.svg?branch=master)](https://github.com/kindritskyiMax/elasticmagic-js/actions) | ||
This lib is a port of original library written in `python` by [@anti-social]( https://github.com/anti-social/elasticmagic ) | ||
Elasticmagic is an Elasticsearch query builder and ORM for JavaScript/Typescript. | ||
It helps you easily build queries which are typed and safe. | ||
You do not need to remember how to write `json` DSL for Elasticsearch, Elasticmagic will do it for you. | ||
> This lib is a port of original library written in `python` by [@anti-social]( https://github.com/anti-social/elasticmagic ) | ||
## Versions | ||
Supports Elasticsearch version 6.x | ||
# Getting Started | ||
## Docs | ||
Install elasticmagic-js using [`npm`](https://www.npmjs.com/): | ||
**Docs** - [https://elasticmagic.js.org/](https://elasticmagic.js.org/) | ||
**Changelog** - [https://elasticmagic.js.org/docs/changelog.html](elasticmagic.js.org/docs/changelog.html) | ||
## Installation | ||
To install Elasticmagic via NPM: | ||
```bash | ||
npm install elasticmagic | ||
npm install --save elasticmagic | ||
``` | ||
# Examples | ||
Also you need an Elasticseach official js client | ||
Let's get started by writing a simple query. | ||
```bash | ||
npm install --save @elastic/elasticsearch | ||
``` | ||
1. Declare class. We will use it both as `query builder` and container for data from elastic. | ||
## Getting Started | ||
As you can see we declare one static field and one intance field with almost same name but different types. | ||
Static field will be used to build queries. | ||
Instance field will be populated on search query so they must be the same as in elasticsearch document. | ||
#### Query building | ||
@@ -40,27 +55,33 @@ ```javascript | ||
enum OrderStatus { | ||
new = 1, | ||
paid = 2, | ||
handled = 3, | ||
canceled = 4, | ||
} | ||
enum OrderSource { | ||
desktop = 1, | ||
mobile = 2, | ||
} | ||
/** | ||
* Here we creating our document which maps structure of same document in Elasticsearch. | ||
* | ||
* We will use this class as our query builder. | ||
* Also when we will get result from elasticsearch, we instantiate this class | ||
* and populate it with data from Elasticsearch hits. | ||
* | ||
* First we declare docType - it must be the same as document in Elasticsearch mapping. | ||
* | ||
* Then we declare static fields that will be user to build our queries. | ||
* As we do not need an instance of this class to build queries, the fields are static. | ||
* | ||
* Next, conventionaly, we declare instance properties, as you can see, with almost same name. | ||
* Then lettercase is same as fields in Elasticseach mapping | ||
* | ||
* And thats is. | ||
*/ | ||
class OrderDoc extends Doc { | ||
public static docType: string = 'order'; | ||
public static userId = new Field(DateType, 'user_id', OrderDoc); | ||
public static userId = new Field(IntegerType, 'user_id', OrderDoc); | ||
public user_id?: number; | ||
public static status = new Field(DateType, 'status', OrderDoc); | ||
public static status = new Field(IntegerType, 'status', OrderDoc); | ||
public status?: number; | ||
public static source = new Field(DateType, 'source', OrderDoc); | ||
public static source = new Field(IntegerType, 'source', OrderDoc); | ||
public source?: number; | ||
public static price = new Field(DateType, 'price', OrderDoc); | ||
public static price = new Field(IntegerType, 'price', OrderDoc); | ||
public price?: number; | ||
@@ -71,63 +92,40 @@ | ||
} | ||
``` | ||
2. Create elasticsearch client and pass it to Cluester | ||
```javascript | ||
// Create a Elasticsearch client which will be passed to cluster. | ||
const client = new Client({ node: 'http://es6-test:9200' }); | ||
// Create cluster instance. Its an entrypiint for interacting with Elasticsearch. | ||
const cluster = new Cluster(client, 'test_order_index'); | ||
``` | ||
3. Now we ready to write our query | ||
```javascript | ||
// Lets start building our query. | ||
// Calling searchQuery method we start creating new query. | ||
// We using builder pattern, so you can chain any amount of methods | ||
const query = cluster.searchQuery({ routing: 1 }) | ||
.source(false) | ||
.source(true) | ||
.filter( | ||
Bool.must( | ||
OrderDoc.user_id.in([1]), | ||
OrderDoc.status.in([OrderStatus.new, OrderStatus.paid]), | ||
OrderDoc.source.not(OrderSource.mobile), | ||
OrderDoc.status.in([1, 2]), | ||
OrderDoc.source.not(1), | ||
OrderDoc.dateCreated.lte(new Date().toISOString()) | ||
) | ||
) | ||
.limit(0); | ||
); | ||
console.log(query.toJSON()) // or console.log(query.body) | ||
``` | ||
// To make a query to Elasticsearch we calling getResult. | ||
const result = await query.getResult<OrderDoc>(); | ||
console.log(result.getIds()); // prints ["1"] | ||
It will print: | ||
```bash | ||
{ | ||
query: { | ||
bool: { | ||
filter: { | ||
bool: { | ||
must: [ | ||
{terms: {user_id: [1]}}, | ||
{terms: {status: [1, 2]}}, | ||
{bool: { | ||
must_not: [ | ||
{term: {source: 2}} | ||
] | ||
}} | ||
] | ||
} | ||
} | ||
} | ||
}, | ||
_source: false, | ||
size: 0 | ||
} | ||
const hit = result.hits[0]; | ||
console.log(hit.user_id); // prints 1 | ||
``` | ||
4. To fetch results from elasticsearch: Lets suppouse we have one doc in index with id 1. | ||
We can check what query Elasticmagic will build for us. | ||
```javascript | ||
const result = await query.getResult<OrderDoc>(); | ||
console.log(result.getIds()); // prints [1] | ||
const hit = result.hits[0]; | ||
console.log(query.toJSON()) | ||
// or alias | ||
console.log(query.body) | ||
console.log(hit.user_id); // prints 1 | ||
// to see prettified query | ||
console.log(query.prettyQuery) | ||
``` | ||
@@ -199,7 +197,4 @@ | ||
# Development | ||
## Tests | ||
#### Tests | ||
Run all tests | ||
@@ -219,23 +214,99 @@ | ||
# TODO | ||
## TODO | ||
- [ ] documentation (https://typedoc.org, docusaurus, js.org) | ||
- [ ] add support for elasticsearch 5, 7 versions, compilers for different es versions | ||
#### Document API (CRUD) | ||
- [ ] Index API | ||
- [ ] Get API | ||
- [ ] Delete API | ||
- [ ] Delete By Query API | ||
- [ ] Update API | ||
- [ ] Update By Query API | ||
- [ ] Multi Get API | ||
- [ ] Bulk API | ||
- [ ] Reindex API | ||
#### Query DSL | ||
- [x] searchQuery | ||
- [x] aggregations | ||
- [ ] Match All Query | ||
- [ ] Match All Query | ||
- Full text queries | ||
- [ ] Match Query | ||
- [ ] Match Phrase Query | ||
- [ ] Match Phrase PrefixQuery | ||
- [ ] Multi Match Query | ||
- [ ] Common Terms Query | ||
- [ ] Query String Query | ||
- [ ] Simple Query String Query | ||
- Term level queries | ||
- [x] Term Query | ||
- [x] Terms Query | ||
- [ ] Terms Set Query | ||
- [x] Range Query | ||
- [ ] Exists Query | ||
- [ ] Prefix Query | ||
- [ ] Wildcard Query | ||
- [ ] Regexp Query | ||
- [ ] Fuzzy Query | ||
- [ ] Type Query | ||
- [ ] Ids Query | ||
- Compound queries | ||
- [ ] Constant Score Query | ||
- [ ] Bool Query | ||
- [ ] Dis Max Query | ||
- [ ] Function Score Query | ||
- [ ] Boosting Query | ||
- Joining queries | ||
- [ ] Nested Query | ||
- [ ] Has Child Query | ||
- [ ] Has Parent Query | ||
- [ ] Parent Id Query | ||
- Specialized queries | ||
- [ ] Distance Feature Query | ||
- [ ] More Like This Query | ||
- [ ] Script Query | ||
- [ ] Script Score Query | ||
- [ ] Percolate Query | ||
- Sorting | ||
- [ ] Sort by score | ||
- [x] Sort by field | ||
- [ ] Sort by geo distance | ||
- [ ] Sort by script | ||
- [ ] Sort by doc | ||
#### Search APIs ( | ||
- [ ] Multi Search | ||
- [ ] Search Shards API | ||
- [ ] Multi Search API | ||
- [ ] Count API | ||
- [ ] Validate API | ||
- [ ] Explain API | ||
- [ ] Profile API | ||
#### Elasticsearch versions interop | ||
- [ ] add support for elasticsearch 5, 7 versions, compilers for different es versions | ||
#### Development | ||
- [ ] precommit hooks | ||
- [ ] generate doc with jsDoc | ||
- [ ] generate doc like ttag has | ||
- [ ] elasticsearch must be devDep or peerDep, but not production dep (create Client interface) | ||
- [ ] scroll | ||
- [ ] pagination | ||
- [ ] queryFilters | ||
- [ ] function_score, inline functions | ||
- [ ] sub documents | ||
- [ ] more tests | ||
- [ ] indexing, delete, bulk (CRUD) | ||
- [ ] post_filters | ||
- [ ] rescores | ||
- [ ] highlight | ||
- [ ] add doc to methods | ||
- [ ] MultiMatch, Ids | ||
- [ ] api docs | ||
- [ ] fix integration tests by randomly creating indexes, so tests can be run in parallel | ||
- [ ] add documentation to methods | ||
- [ ] generate api docs (typedoc) | ||
#### cat APIs | ||
#### Other | ||
- [ ] Post filters | ||
- [ ] Rescores | ||
- [ ] Scroll | ||
- [ ] Pagination | ||
- [ ] QueryFilters | ||
- [ ] Sub document in field | ||
- [ ] Sub field in field | ||
- [ ] Highlight |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
98927
1819
309
9