@opra/elastic 1.0.0-alpha.32 → 1.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,14 +1,31 @@
1
1
  /* eslint-disable camelcase */
2
2
  import '@opra/core';
3
3
  import { OpraFilter } from '@opra/common';
4
- const isNil = (v) => v == null;
5
- export default function prepareFilter(ast, negative) {
6
- if (!ast)
4
+ export default function prepareFilter(filters) {
5
+ const filtersArray = Array.isArray(filters) ? filters : [filters];
6
+ if (!filtersArray.length)
7
7
  return;
8
- if (ast instanceof OpraFilter.QualifiedIdentifier) {
9
- return ast.value;
8
+ const out = [];
9
+ for (const filter of filtersArray) {
10
+ if (!filter)
11
+ continue;
12
+ let x = filter;
13
+ if (typeof filter === 'string')
14
+ x = prepareFilterAst(OpraFilter.parse(filter));
15
+ else if (filter instanceof OpraFilter.Expression)
16
+ x = prepareFilterAst(filter);
17
+ out.push(x);
10
18
  }
11
- if (ast instanceof OpraFilter.NumberLiteral ||
19
+ if (out.length > 1) {
20
+ return { bool: { must: [...out] } };
21
+ }
22
+ return out[0] ? out[0] : undefined;
23
+ }
24
+ function prepareFilterAst(ast, negative) {
25
+ if (!ast)
26
+ return;
27
+ if (ast instanceof OpraFilter.QualifiedIdentifier ||
28
+ ast instanceof OpraFilter.NumberLiteral ||
12
29
  ast instanceof OpraFilter.StringLiteral ||
13
30
  ast instanceof OpraFilter.BooleanLiteral ||
14
31
  ast instanceof OpraFilter.NullLiteral ||
@@ -17,86 +34,77 @@ export default function prepareFilter(ast, negative) {
17
34
  return ast.value;
18
35
  }
19
36
  if (ast instanceof OpraFilter.ArrayExpression) {
20
- return ast.items.map(x => prepareFilter(x, negative)).filter(x => !isNil(x));
37
+ return ast.items.map(x => prepareFilterAst(x, negative));
21
38
  }
22
39
  if (ast instanceof OpraFilter.NegativeExpression) {
23
- return prepareFilter(ast.expression, !negative);
40
+ return prepareFilterAst(ast.expression, !negative);
24
41
  }
25
42
  if (ast instanceof OpraFilter.LogicalExpression) {
26
- const v = ast.items.map(x => prepareFilter(x)).filter(x => !isNil(x));
27
- if (ast.op === 'and') {
28
- return {
29
- bool: {
30
- [negative ? 'must_not' : 'must']: v,
31
- },
32
- };
33
- }
34
- return wrapNot({
35
- bool: { should: v },
36
- }, negative);
43
+ const items = ast.items
44
+ .map(x => prepareFilterAst(x))
45
+ /** Filter nullish items */
46
+ .filter(x => x != null);
47
+ const k = (ast.op === 'or' ? 'should' : 'must') + (negative ? '_not' : '');
48
+ return { bool: { [k]: items } };
37
49
  }
38
50
  if (ast instanceof OpraFilter.ParenthesizedExpression) {
39
- return prepareFilter(ast.expression, negative);
40
- }
41
- if (ast instanceof OpraFilter.ComparisonExpression)
42
- return _transformComparisonExpression(ast, !!negative);
43
- throw new Error(`${ast.kind} is not implemented yet`);
44
- }
45
- function _transformComparisonExpression(ast, negative) {
46
- const left = prepareFilter(ast.left, negative);
47
- if (ast.right instanceof OpraFilter.QualifiedIdentifier) {
48
- throw new TypeError('not implemented yet');
51
+ return prepareFilterAst(ast.expression, negative);
49
52
  }
50
- const right = prepareFilter(ast.right);
51
- if (right == null) {
52
- const op = ast.op === '=' ? (negative ? '!=' : '=') : negative ? '=' : '!=';
53
- if (op === '=')
54
- return { bool: { must_not: { exists: { field: left } } } };
55
- if (op === '!=')
56
- return { bool: { exists: { field: left } } };
57
- }
58
- switch (ast.op) {
59
- case '=':
60
- return wrapNot({ term: { [left]: right } }, negative);
61
- case '!=':
62
- return wrapNot({ term: { [left]: right } }, !negative);
63
- case '>':
64
- return wrapNot({ range: { [left]: { gt: right } } }, negative);
65
- case '>=':
66
- return wrapNot({ range: { [left]: { gte: right } } }, negative);
67
- case '<':
68
- return wrapNot({ range: { [left]: { lt: right } } }, negative);
69
- case '<=':
70
- return wrapNot({ range: { [left]: { lte: right } } }, negative);
71
- case 'in':
72
- return wrapNot({ terms: { [left]: Array.isArray(right) ? right : [right] } }, negative);
73
- case '!in':
74
- return wrapNot({ terms: { [left]: Array.isArray(right) ? right : [right] } }, !negative);
75
- case 'like':
76
- return wrapNot({ wildcard: { [left]: String(right) } }, negative);
77
- case '!like':
78
- return wrapNot({ wildcard: { [left]: String(right) } }, !negative);
79
- case 'ilike':
80
- return wrapNot({
81
- wildcard: {
82
- [left]: {
83
- value: String(right),
84
- case_insensitive: true,
85
- },
86
- },
87
- }, negative);
88
- case '!ilike':
89
- return wrapNot({
90
- wildcard: {
91
- [left]: {
92
- value: String(right),
93
- case_insensitive: true,
94
- },
95
- },
96
- }, !negative);
97
- default:
98
- break;
53
+ if (ast instanceof OpraFilter.ComparisonExpression) {
54
+ if (!(ast.left instanceof OpraFilter.QualifiedIdentifier)) {
55
+ throw new Error('Left side of ComparisonExpression must be a QualifiedIdentifier');
56
+ }
57
+ const left = prepareFilterAst(ast.left);
58
+ const right = prepareFilterAst(ast.right);
59
+ let out;
60
+ if (right == null) {
61
+ negative = !negative;
62
+ out = { exists: { field: left } };
63
+ }
64
+ else {
65
+ switch (ast.op) {
66
+ case '!=':
67
+ case '=':
68
+ case 'in':
69
+ case '!in': {
70
+ out = { match: { [left]: right } };
71
+ break;
72
+ }
73
+ case '>': {
74
+ out = { range: { [left]: { gt: right } } };
75
+ break;
76
+ }
77
+ case '>=': {
78
+ out = { range: { [left]: { gte: right } } };
79
+ break;
80
+ }
81
+ case '<': {
82
+ out = { range: { [left]: { lt: right } } };
83
+ break;
84
+ }
85
+ case '<=': {
86
+ out = { range: { [left]: { lte: right } } };
87
+ break;
88
+ }
89
+ case '!like':
90
+ case 'like': {
91
+ out = { wildcard: { [left]: { value: String(right).replace(/%/g, '*') } } };
92
+ break;
93
+ }
94
+ case '!ilike':
95
+ case 'ilike': {
96
+ out = { wildcard: { [left]: { value: String(right).replace(/%/g, '*'), case_insensitive: true } } };
97
+ break;
98
+ }
99
+ default:
100
+ /* istanbul ignore next */
101
+ throw new TypeError(`Unknown ComparisonExpression operation (${ast.op})`);
102
+ }
103
+ }
104
+ if ((ast.op.startsWith('!') && !negative) || (!ast.op.startsWith('!') && negative)) {
105
+ return { bool: { must_not: { ...out } } };
106
+ }
107
+ return out;
99
108
  }
100
- throw new Error(`ComparisonExpression operator (${ast.op}) not implemented yet`);
109
+ throw new Error(`${ast.kind} is not implemented yet`);
101
110
  }
102
- const wrapNot = (o, negative) => (negative ? { bool: { must_not: o } } : o);
@@ -0,0 +1,32 @@
1
+ export default function preparePatch(doc) {
2
+ const script = [];
3
+ const params = {};
4
+ _preparePatch(doc, script, params, '');
5
+ return {
6
+ source: script.join('\n'),
7
+ params,
8
+ lang: 'painless',
9
+ };
10
+ }
11
+ function _preparePatch(src, script, params, path) {
12
+ let f;
13
+ let field;
14
+ for (const [k, v] of Object.entries(src)) {
15
+ f = k.startsWith('*') ? k.substring(1) : k;
16
+ field = path ? path + '.' + f : f;
17
+ if (v == null) {
18
+ script.push(`ctx._source.remove('${field}');`);
19
+ continue;
20
+ }
21
+ if (v &&
22
+ typeof v === 'object' &&
23
+ !Array.isArray(v) &&
24
+ /** If field name starts with "*", do "replace" operation except "merge" */
25
+ !k.startsWith('*')) {
26
+ _preparePatch(v, script, params, field);
27
+ continue;
28
+ }
29
+ script.push(`ctx._source['${field}'] = params['${field}'];`);
30
+ params[field] = v;
31
+ }
32
+ }
@@ -1,109 +1,62 @@
1
- "use strict";
2
- // import { ApiField, ComplexType, FieldsProjection, omitNullish, parseFieldsProjection } from '@opra/common';
3
- // import mongodb, { Document } from 'mongodb';
4
- //
5
- // export default function prepareProjection(
6
- // dataType: ComplexType,
7
- // projection?: string | string[] | Document,
8
- // ): mongodb.Document | undefined {
9
- // if (projection && typeof projection === 'object' && !Array.isArray(projection)) return projection;
10
- // const out: Record<string, boolean> = {};
11
- // const projection_ =
12
- // typeof projection === 'string' || Array.isArray(projection) ? parseFieldsProjection(projection) : projection;
13
- // // const exclusionProjection = !pick && !!omit;
14
- // prepare(dataType, out, projection_);
15
- // return Object.keys(out).length ? out : undefined;
16
- // }
17
- //
18
- // export function prepare(dataType: ComplexType, target: mongodb.Document, projection?: FieldsProjection) {
19
- // const defaultFields = !projection || !Object.values(projection).find(p => !p.sign);
20
- // const projectionKeys = projection && Object.keys(projection).map(x => x.toLowerCase());
21
- // const projectionKeysSet = new Set(projectionKeys);
22
- // let fieldName: string;
23
- // let field: ApiField;
24
- // let k: string;
25
- // /** Add fields from data type */
26
- // for (field of dataType.fields.values()) {
27
- // fieldName = field.name;
28
- // k = fieldName.toLowerCase();
29
- // projectionKeysSet.delete(k);
30
- // const p = projection?.[k];
31
- // if (
32
- // /** Ignore if field is omitted */
33
- // p?.sign === '-' ||
34
- // /** Ignore if default fields and field is not in projection */
35
- // (!defaultFields && !p) ||
36
- // /** Ignore if default fields enabled and fields is exclusive */
37
- // (defaultFields && field.exclusive && !p)
38
- // ) {
39
- // continue;
40
- // }
41
- //
42
- // if (field.type instanceof ComplexType && typeof p?.projection === 'object') {
43
- // target[fieldName] = {};
44
- // prepare(field.type, target[fieldName], p.projection);
45
- // continue;
46
- // }
47
- // target[fieldName] = 1;
48
- // }
49
- // /** Add additional fields */
50
- // if (dataType.additionalFields) {
51
- // for (k of projectionKeysSet.values()) {
52
- // const n = projectionKeysSet[k];
53
- // if (n?.sign !== '-') target[k] = 1;
54
- // }
55
- // }
56
- // }
57
- //
58
- // // function __prepareProjection(
59
- // // dataType: ComplexType,
60
- // // args: {
61
- // // pick?: string[];
62
- // // omit?: string[];
63
- // // include?: string[];
64
- // // },
65
- // // ): any {
66
- // // let includes: string[] | undefined;
67
- // // let excludes: string[] | undefined;
68
- // //
69
- // // if (args.include && !args.pick) {
70
- // // includes = includes || [];
71
- // // for (const [k, f] of dataType.fields) {
72
- // // if (f.exclusive) continue;
73
- // // if (f.type instanceof ComplexType) includes.push(k + '.*');
74
- // // else includes.push(k);
75
- // // }
76
- // // }
77
- // //
78
- // // if (args.pick) {
79
- // // includes = includes || [];
80
- // // for (const k of args.pick) {
81
- // // const f = dataType.getField(k);
82
- // // if (f.type instanceof ComplexType) includes.push(k + '.*');
83
- // // else includes.push(k);
84
- // // }
85
- // // }
86
- // //
87
- // // if (args.include) {
88
- // // includes = includes || [];
89
- // // for (const k of args.include) {
90
- // // const f = dataType.getField(k);
91
- // // if (f.type instanceof ComplexType) includes.push(k + '.*');
92
- // // else includes.push(k);
93
- // // }
94
- // // }
95
- // //
96
- // // if (args.omit) {
97
- // // excludes = excludes || [];
98
- // // for (const k of args.omit) {
99
- // // const f = dataType.getField(k);
100
- // // if (f.type instanceof ComplexType) excludes.push(k + '.*');
101
- // // else excludes.push(k);
102
- // // }
103
- // // }
104
- // //
105
- // // return omitNullish({
106
- // // includes,
107
- // // excludes,
108
- // // });
109
- // // }
1
+ import { ComplexType, parseFieldsProjection } from '@opra/common';
2
+ export default function prepareProjection(dataType, projection) {
3
+ const out = {};
4
+ const includes = [];
5
+ const excludes = [];
6
+ const projection_ = typeof projection === 'string' || Array.isArray(projection) ? parseFieldsProjection(projection) : projection;
7
+ prepare(dataType, includes, excludes, '', projection_);
8
+ if (includes.length)
9
+ out.includes = includes;
10
+ if (excludes.length)
11
+ out.excludes = excludes;
12
+ return includes.length || excludes.length ? out : undefined;
13
+ }
14
+ function getNeedIncludes(projection) {
15
+ return !!(projection && Object.values(projection).find(p => !p.sign));
16
+ }
17
+ export function prepare(dataType, includes, excludes, curPath, projection) {
18
+ const needIncludes = getNeedIncludes(projection);
19
+ const projectionKeys = projection && Object.keys(projection);
20
+ const projectionKeysSet = new Set(projectionKeys);
21
+ let fieldName;
22
+ let fieldPath;
23
+ let field;
24
+ let k;
25
+ /** Add fields from data type */
26
+ for (field of dataType.fields.values()) {
27
+ fieldName = field.name;
28
+ fieldPath = curPath + (curPath ? '.' : '') + fieldName;
29
+ k = fieldName.toLowerCase();
30
+ projectionKeysSet.delete(k);
31
+ const p = projection?.[k];
32
+ if (
33
+ /** if field is omitted */
34
+ p?.sign === '-' ||
35
+ /** if no projection defined for this field and includeDefaultFields is true and the field is exclusive */
36
+ (!p && field.exclusive)) {
37
+ if (!needIncludes)
38
+ excludes.push(fieldPath);
39
+ continue;
40
+ }
41
+ if (needIncludes && p && !includes.includes(fieldPath)) {
42
+ if (!getNeedIncludes(p?.projection)) {
43
+ includes.push(fieldPath);
44
+ }
45
+ }
46
+ if (field.type instanceof ComplexType && typeof p?.projection === 'object') {
47
+ prepare(field.type, includes, excludes, fieldPath, p.projection);
48
+ }
49
+ }
50
+ if (dataType.additionalFields) {
51
+ for (k of projectionKeysSet.values()) {
52
+ const n = projection?.[k];
53
+ fieldPath = curPath + (curPath ? '.' : '') + k;
54
+ if (n?.sign === '-') {
55
+ if (!needIncludes)
56
+ excludes.push(fieldPath);
57
+ }
58
+ else
59
+ includes.push(fieldPath);
60
+ }
61
+ }
62
+ }
@@ -1,14 +1,13 @@
1
+ const SIGN_PATTERN = /^([+-])?(.+)$/;
1
2
  export default function prepareSort(sort) {
2
3
  if (!(sort && sort.length))
3
4
  return;
4
5
  const out = [];
5
6
  sort.forEach(k => {
6
- if (k.startsWith('-'))
7
- out.push({ [k.substring(1)]: 'desc' });
8
- else if (k.startsWith('+'))
9
- out.push(k.substring(1));
10
- else
11
- out.push(k);
7
+ const m = SIGN_PATTERN.exec(k);
8
+ if (m) {
9
+ out.push({ [m[2]]: { order: m[1] === '-' ? 'desc' : 'asc' } });
10
+ }
12
11
  });
13
12
  return out;
14
13
  }
@@ -1,15 +1,12 @@
1
- // import { SearchRequest } from '@elastic/elasticsearch/lib/api/types';
2
- // import { TransportRequestOptions } from '@elastic/transport';
3
- // import { omitNullish } from '@opra/common';
4
1
  import _prepareFilter from './adapter-utils/prepare-filter.js';
5
- import _prepareKeyValues from './adapter-utils/prepare-key-values.js';
6
- // import _prepareProjection from './adapter-utils/prepare-projection.js';
2
+ import _preparePatch from './adapter-utils/prepare-patch.js';
3
+ import _prepareProjection from './adapter-utils/prepare-projection.js';
7
4
  import _prepareSort from './adapter-utils/prepare-sort.js';
8
5
  export var ElasticAdapter;
9
6
  (function (ElasticAdapter) {
10
7
  ElasticAdapter.prepareFilter = _prepareFilter;
11
- ElasticAdapter.prepareKeyValues = _prepareKeyValues;
12
- // export const prepareProjection = _prepareProjection;
8
+ ElasticAdapter.preparePatch = _preparePatch;
9
+ ElasticAdapter.prepareProjection = _prepareProjection;
13
10
  ElasticAdapter.prepareSort = _prepareSort;
14
11
  async function parseRequest(context) {
15
12
  const { operation } = context;