Skip to content

Commit c879f3a

Browse files
authored
test nested attribute expression (#237)
* test nested attribute expression * update json attribute resolver * simplify regex * update json attribute resolver * simple test nested attribute func * 2.22.0
1 parent f838646 commit c879f3a

9 files changed

Lines changed: 237 additions & 293 deletions

File tree

OnJsonAttribute.js

Lines changed: 175 additions & 183 deletions
Large diffs are not rendered by default.

data-attribute-resolver.js

Lines changed: 15 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
var {QueryField, QueryEntity, QueryUtils, MethodCallExpression, MemberExpression} = require('@themost/query');
1+
var {QueryField, QueryEntity, QueryUtils, MethodCallExpression, MemberExpression, ObjectNameValidator} = require('@themost/query');
22
var {sprintf} = require('sprintf-js');
33
var _ = require('lodash');
44
var {DataError} = require('@themost/common');
@@ -423,114 +423,33 @@ DataAttributeResolver.prototype.testAggregatedNestedAttribute = function(s) {
423423

424424
/**
425425
* @param {string} s
426-
* @returns {*}
426+
* @returns {{name: string, property?: string}|null}
427427
*/
428428
DataAttributeResolver.prototype.testNestedAttribute = function(s) {
429429
if (typeof s !== 'string')
430430
return null;
431-
/**
432-
* @private
433-
*/
434431
var matches;
435-
/**
436-
* nested attribute aggregate function with alias e.g. f(x/b) as a
437-
* @ignore
438-
*/
439-
matches = /^(\w+)\((\w+)\/(\w+)\)\sas\s([\u0020-\u007F\u0080-\uFFFF]+)$/i.exec(s);
440-
if (matches) {
441-
return { name: matches[1] + '(' + matches[2] + '/' + matches[3] + ')', property:matches[4] };
442-
}
443-
/**
444-
* nested attribute aggregate function with alias e.g. f(x/b/c) as a
445-
* @ignore
446-
*/
447-
matches = /^(\w+)\((\w+)\/(\w+)\/(\w+)\)\sas\s([\u0020-\u007F\u0080-\uFFFF]+)$/i.exec(s);
448-
if (matches) {
449-
return { name: matches[1] + '(' + matches[2] + '/' + matches[3] + '/' + matches[4] + ')', property:matches[5] };
450-
}
451-
/**
452-
* nested attribute aggregate function with alias e.g. f(x/b/c/d) as a
453-
* @ignore
454-
*/
455-
matches = /^(\w+)\((\w+)\/(\w+)\/(\w+)\/(\w+)\)\sas\s([\u0020-\u007F\u0080-\uFFFF]+)$/i.exec(s);
456-
if (matches) {
457-
return { name: matches[1] + '(' + matches[2] + '/' + matches[3] + '/' + matches[4] + '/' + matches[5] + ')', property:matches[6] };
458-
}
459-
/**
460-
* nested attribute with alias e.g. x/b as a
461-
* @ignore
462-
*/
463-
matches = /^(\w+)\/(\w+)\sas\s([\u0020-\u007F\u0080-\uFFFF]+)$/i.exec(s);
464-
if (matches) {
465-
return { name: matches[1] + '/' + matches[2], property:matches[3] };
466-
}
467-
/**
468-
* nested attribute with alias e.g. x/b/c as a
469-
* @ignore
470-
*/
471-
matches = /^(\w+)\/(\w+)\/(\w+)\sas\s([\u0020-\u007F\u0080-\uFFFF]+)$/i.exec(s);
472-
if (matches) {
473-
return { name: matches[1] + '/' + matches[2] + '/' + matches[3], property:matches[4] };
474-
}
475-
/**
476-
* nested attribute with alias e.g. x/b/c/d as a
477-
* @ignore
478-
*/
479-
matches = /^(\w+)\/(\w+)\/(\w+)\/(\w+)\sas\s([\u0020-\u007F\u0080-\uFFFF]+)$/i.exec(s);
480-
if (matches) {
481-
return { name: matches[1] + '/' + matches[2] + '/' + matches[3] + '/' + matches[4], property:matches[5] };
482-
}
483-
/**
484-
* nested attribute aggregate function with alias e.g. f(x/b)
485-
* @ignore
486-
*/
487-
matches = /^(\w+)\((\w+)\/(\w+)\)$/i.exec(s);
488-
if (matches) {
489-
return { name: matches[1] + '(' + matches[2] + '/' + matches[3] + ')' };
490-
}
491-
/**
492-
* nested attribute aggregate function with alias e.g. f(x/b/c)
493-
* @ignore
494-
*/
495-
matches = /^(\w+)\((\w+)\/(\w+)\/(\w+)\)$/i.exec(s);
496-
if (matches) {
497-
return { name: matches[1] + '(' + matches[2] + '/' + matches[3] + '/' + matches[4] + ')' };
498-
}
499-
/**
500-
* nested attribute aggregate function with alias e.g. f(x/b/c/d)
501-
* @ignore
502-
*/
503-
matches = /^(\w+)\((\w+)\/(\w+)\/(\w+)\/(\w+)\)$/i.exec(s);
504-
if (matches) {
505-
return { name: matches[1] + '(' + matches[2] + '/' + matches[3] + '/' + matches[4] + matches[5] + ')' };
506-
}
507-
/**
508-
* nested attribute with alias e.g. x/b
509-
* @ignore
510-
*/
511-
matches = /^(\w+)\/(\w+)$/.exec(s);
512-
if (matches) {
513-
return { name: s };
514-
}
515432

516-
/**
517-
* nested attribute with alias e.g. x/b/c
518-
* @ignore
519-
*/
520-
matches = /^(\w+)\/(\w+)\/(\w+)$/.exec(s);
433+
var pattern = (ObjectNameValidator.validator && ObjectNameValidator.validator.pattern) || new RegExp(ObjectNameValidator.Patterns.Default);
434+
var exprFuncWithAlias = new RegExp('^(\\w+)\\((\\w+(?:\\/\\w+)+)\\)(?:\\s+as\\s+' + pattern.source + ')?$');
435+
matches = exprFuncWithAlias.exec(s);
521436
if (matches) {
522-
return { name: s };
437+
// matches[1]: the function name
438+
// matches[2]: the nested attribute
439+
// matches[3]: the alias (optional)
440+
return { name: matches[2], property: matches[3] };
523441
}
524442

443+
var exprWithAlias = new RegExp('^(\\w+(?:\\/\\w+)+)(\\s+as\\s+' + pattern.source + ')?$')
525444
/**
526-
* nested attribute with alias e.g. x/b/c/d
527-
* @ignore
445+
* nested attribute with alias e.g. a/b/../c as a
528446
*/
529-
matches = /^(\w+)\/(\w+)\/(\w+)\/(\w+)$/.exec(s);
447+
matches = exprWithAlias.exec(s);
530448
if (matches) {
531-
return { name: s };
449+
// matches[2]: the nested attribute
450+
// matches[3]: the alias (optional)
451+
return { name: matches[1], property: matches[3] };
532452
}
533-
534453
};
535454

536455
/**

jest.config.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ module.exports = {
1717
// clearMocks: false,
1818

1919
// Indicates whether the coverage information should be collected while executing the test
20-
collectCoverage: true,
20+
collectCoverage: false,
2121

2222
// An array of glob patterns indicating a set of files for which coverage information should be collected
2323
// collectCoverageFrom: ["!spec/**/*.ts"],
@@ -128,12 +128,11 @@ module.exports = {
128128

129129
// The paths to modules that run some code to configure or set up the testing environment before each test
130130
setupFiles: [
131-
'<rootDir>/spec/helpers/env.js'
132131
],
133132

134133
// A list of paths to modules that run some code to configure or set up the testing framework before each test
135134
setupFilesAfterEnv: [
136-
'./jest.setup.js'
135+
'<rootDir>/jest.setup.js'
137136
],
138137

139138
// The number of seconds after which a test is considered as slow and reported as such in the results.

jest.setup.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
const {TraceUtils} = require('@themost/common');
2-
const JestLogger = require('./jest.logger');
2+
const {JsonLogger} = require('@themost/json-logger');
33
// noinspection JSCheckFunctionSignatures
4-
TraceUtils.useLogger(new JestLogger());
4+
TraceUtils.useLogger(new JsonLogger({
5+
level: 'debug',
6+
format: 'raw'
7+
}));
58
/* global jest */
69
jest.setTimeout(60000);

package-lock.json

Lines changed: 23 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@themost/data",
3-
"version": "2.21.4",
3+
"version": "2.22.0",
44
"description": "MOST Web Framework Codename Blueshift - Data module",
55
"main": "index.js",
66
"scripts": {
@@ -45,6 +45,7 @@
4545
"@babel/preset-env": "^7.16.11",
4646
"@babel/preset-typescript": "^7.16.7",
4747
"@themost/common": "^2.12.0",
48+
"@themost/json-logger": "^1.3.0",
4849
"@themost/peers": "^1.0.2",
4950
"@themost/query": "^2.14.7",
5051
"@themost/sqlite": "^2.10.1",

spec/JsonAttribute.spec.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,12 +177,16 @@ describe('JsonAttribute', () => {
177177
}
178178
await Products.save(item);
179179
expect(item.dateCreated).toBeTruthy();
180-
item = await Products.where('name').equal('Apple MacBook Air (13.3-inch, 2013 Version)')
180+
item = await Products
181181
.where((x: any) => {
182-
return x.extraAttributes.cpu.brand === 'Intel'
182+
return x.name === 'Apple MacBook Air (13.3-inch, 2013 Version)' && x.extraAttributes.cpu.brand === 'Intel'
183183
}).getItem();
184184
expect(item).toBeTruthy();
185185
expect(item.name).toBe('Apple MacBook Air (13.3-inch, 2013 Version)');
186+
// query again using expressions
187+
item = await Products.where('extraAttributes/cpu/brand').equal('Intel').getItem();
188+
expect(item).toBeTruthy();
189+
expect(item.name).toBe('Apple MacBook Air (13.3-inch, 2013 Version)');
186190
});
187191
});
188192

spec/test2/config/models/User.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"abstract": false,
88
"sealed": false,
99
"inherits": "Account",
10-
"version": "2.0",
10+
"version": "2.1.0",
1111
"fields": [
1212
{
1313
"@id": "https://themost.io/schemas/lockoutTime",
@@ -109,6 +109,13 @@
109109
"@id": "https://themost.io/schemas/userReviews",
110110
"name": "userReviews",
111111
"type": "UserReview"
112+
},
113+
{
114+
"@id": "https://themost.io/schemas/metadata",
115+
"many": false,
116+
"description": "A JSON object that contains additional metadata for the user.",
117+
"name": "metadata",
118+
"type": "Json"
112119
}
113120
],
114121
"privileges": [

types.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ export declare class DataEventArgs {
178178
query?: any;
179179
previous?: any;
180180
throwError?: boolean;
181+
result?: unknown
181182
}
182183

183184
export declare interface BeforeSaveEventListener {

0 commit comments

Comments
 (0)