Skip to content

Commit

Permalink
Merge pull request #41 in LFOR/fhirpath.js from bugfix/LF-2253/extens…
Browse files Browse the repository at this point in the history
…ion-for-primitive-types to master

* commit 'a32eaf924c6c8f6e3c0c8048e034d882034c9752':
  Added a suite of performance tests for engine.MemberInvocation
  Fixed issues found during review
  Fixed "extension()" for primitive types not working properly without a value
  Added version number to fhirpath.js demo page
  Add language to make fixture valid
  Example of failing test with extensions
  • Loading branch information
yuriy-sedinkin committed Jun 15, 2022
2 parents b461e88 + a32eaf9 commit a3a0445
Show file tree
Hide file tree
Showing 12 changed files with 239 additions and 94 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
This log documents significant changes for each release. This project follows
[Semantic Versioning](http://semver.org/).

## [2.14.5] - 2022-06-07
### Added
- Version number to fhirpath.js demo page.
### Fixed
- "extension()" for primitive types did not work properly without a value.

## [2.14.4] - 2022-05-23
### Added
- Suite of performance tests.
Expand Down
2 changes: 2 additions & 0 deletions demo/public/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ require("codemirror/lib/codemirror.css");

const example = require("json-loader!yaml-loader!./patient-example.yaml");

document.getElementById('version').innerText = '(version ' + fhirpath.version + ')';

const debounce = (func, delay) => {
let inDebounce;
return () => {
Expand Down
2 changes: 2 additions & 0 deletions demo/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- <link rel="stylesheet" href="app.css"> -->
<title>fhirpath.js Demo</title>
<!-- anti-clickjacking script from:
Expand All @@ -21,6 +22,7 @@
<div id="header">
A demo of the
<a href="https://github.com/HL7/fhirpath.js">fhirpath.js</a>
<span id="version"></span>
JavaScript Engine for HL7 FHIRPath
</div>
<input id="path"
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "fhirpath",
"version": "2.14.4",
"version": "2.14.5",
"description": "A FHIRPath engine",
"main": "src/fhirpath.js",
"dependencies": {
Expand Down
17 changes: 7 additions & 10 deletions src/fhirpath.js
Original file line number Diff line number Diff line change
Expand Up @@ -336,29 +336,26 @@ engine.MemberInvocation = function(ctx, parentData, node ) {
// Use actualTypes to find the field's value
for (let t of actualTypes) {
let field = key + t;
toAdd = res.data[field];
if (toAdd !== undefined) {
toAdd = res.data?.[field];
_toAdd = res.data?.['_' + field];
if (toAdd !== undefined || _toAdd !== undefined) {
childPath = t;
_toAdd = res.data['_' + key];
break;
} else {
toAdd = res._data[key];
}
}
}
else {
toAdd = res.data[key];
if (toAdd !== undefined) {
_toAdd = res.data['_' + key];
} else {
toAdd = res.data?.[key];
_toAdd = res.data?.['_' + key];
if (toAdd === undefined && _toAdd === undefined) {
toAdd = res._data[key];
}
if (key === 'extension') {
childPath = 'Extension';
}
}

if (util.isSome(toAdd)) {
if (util.isSome(toAdd) || util.isSome(_toAdd)) {
if(Array.isArray(toAdd)) {
acc = acc.concat(toAdd.map((x, i)=>
makeResNode(x, childPath, _toAdd && _toAdd[i])));
Expand Down
4 changes: 2 additions & 2 deletions src/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -984,7 +984,7 @@ class ResourceNode {
constructor(data, path, _data) {
// If data is a resource (maybe a contained resource) reset the path
// information to the resource type.
if (data.resourceType)
if (data?.resourceType)
path = data.resourceType;
this.path = path;
this.data = getResourceNodeData(data, path);
Expand Down Expand Up @@ -1021,7 +1021,7 @@ class ResourceNode {
* @return {FP_Quantity|Object|...}
*/
function getResourceNodeData(data, path) {
if (path === 'Quantity' && data.system === ucumSystemUrl) {
if (path === 'Quantity' && data?.system === ucumSystemUrl) {
if (typeof data.value === 'number' && typeof data.code === 'string') {
data = new FP_Quantity(data.value, FP_Quantity.mapUCUMCodeToTimeUnits[data.code] || '\'' + data.code + '\'');
}
Expand Down
28 changes: 27 additions & 1 deletion test/benchmark.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,39 @@ const { spawn } = require('child_process');
const myArgs = process.argv.slice(2);
const prevVersion = myArgs.length === 1 ? '@' + myArgs[0] : '@latest';

const benny = require('benny');
const open = require('open');
const current_fhirpath = require('../src/fhirpath');
const current_r4_model = require('../fhir-context/r4');
const minimumDataset = require('./resources/Minimum-Data-Set---version-3.0.R4.json');
const currentVersion = require('../package.json').version;


const child = spawn('npm', ['i','--prefix', './test/benchmark/prev-fhirpath', 'fhirpath' + prevVersion], {
stdio: 'inherit'
});

child.on('exit', code => {
if (code === 0) {
const previous_fhirpath = require('./benchmark/prev-fhirpath/node_modules/fhirpath');
const previous_r4_model = require('./benchmark/prev-fhirpath/node_modules/fhirpath/fhir-context/r4');
const previousVersion = require('./benchmark/prev-fhirpath/node_modules/fhirpath/package.json').version;
// Insert performance test suites here:
require('./benchmark/intersect');
[
'intersect',
'member-invocation'
].forEach(filename => {
require('./benchmark/'+ filename)({
benny,
open,
previous_fhirpath,
previous_r4_model,
current_fhirpath,
current_r4_model,
minimumDataset,
currentVersion,
previousVersion
})
})
}
});
157 changes: 79 additions & 78 deletions test/benchmark/intersect.js
Original file line number Diff line number Diff line change
@@ -1,88 +1,89 @@
const b = require('benny');
const open = require('open');

const previous_fhirpath = require('./prev-fhirpath/node_modules/fhirpath');
const previous_r4_model = require('./prev-fhirpath/node_modules/fhirpath/fhir-context/r4');
const current_fhirpath = require('../../src/fhirpath');
const current_r4_model = require('../../fhir-context/r4');
const _ = require('lodash');

const minimumDataset = require('../resources/Minimum-Data-Set---version-3.0.R4.json');

const bigItems = current_fhirpath.evaluate(minimumDataset,'repeat(item)', {}, current_r4_model);
const bigItemsCopy = _.cloneDeep(bigItems);
const numberOfBigItems = current_fhirpath.evaluate({}, '%items.count()', { items: bigItems }, current_r4_model)[0];
module.exports = ({
benny,
open,
previous_fhirpath,
previous_r4_model,
current_fhirpath,
current_r4_model,
minimumDataset,
currentVersion,
previousVersion
}) => {

const smallItems = current_fhirpath.evaluate(minimumDataset,'repeat(item).repeat(code)', {}, current_r4_model);
const smallItemsCopy = _.cloneDeep(smallItems);
const numberOfSmallItems = current_fhirpath.evaluate({}, '%items.count()', { items: smallItems }, current_r4_model)[0];
const bigItems = current_fhirpath.evaluate(minimumDataset,'repeat(item)', {}, current_r4_model);
const bigItemsCopy = _.cloneDeep(bigItems);
const numberOfBigItems = current_fhirpath.evaluate({}, '%items.count()', { items: bigItems }, current_r4_model)[0];

const previousVersion = require('./prev-fhirpath/node_modules/fhirpath/package.json').version;
const currentVersion = require('../../package.json').version;
const smallItems = current_fhirpath.evaluate(minimumDataset,'repeat(item).repeat(code)', {}, current_r4_model);
const smallItemsCopy = _.cloneDeep(smallItems);
const numberOfSmallItems = current_fhirpath.evaluate({}, '%items.count()', { items: smallItems }, current_r4_model)[0];

const expression = '%items.intersect(%itemsCopy)';
const expression = '%items.intersect(%itemsCopy)';

const cases = [
{
name: `${numberOfBigItems} big items using evaluate()`,
testFunction: (fhirpath, model) => {
fhirpath.evaluate({}, expression, { items: bigItems, itemsCopy: bigItemsCopy }, model);
}
},
{
name: `${numberOfBigItems} big items using compile()`,
testFunction: (fhirpath, model, compiledFn) => {
compiledFn({}, { items: bigItems, itemsCopy: bigItemsCopy });
}
},
{
name: `${numberOfSmallItems} small items using evaluate()`,
testFunction: (fhirpath, model) => {
fhirpath.evaluate({}, expression, { items: smallItems, itemsCopy: smallItemsCopy }, model);
}
},
{
name: `${numberOfSmallItems} small items using compile()`,
testFunction: (fhirpath, model, compiledFn) => {
compiledFn({}, { items: smallItems, itemsCopy: smallItemsCopy });
const cases = [
{
name: `${numberOfBigItems} big items using evaluate()`,
testFunction: (fhirpath, model) => {
fhirpath.evaluate({}, expression, { items: bigItems, itemsCopy: bigItemsCopy }, model);
}
},
{
name: `${numberOfBigItems} big items using compile()`,
testFunction: (fhirpath, model, compiledFn) => {
compiledFn({}, { items: bigItems, itemsCopy: bigItemsCopy });
}
},
{
name: `${numberOfSmallItems} small items using evaluate()`,
testFunction: (fhirpath, model) => {
fhirpath.evaluate({}, expression, { items: smallItems, itemsCopy: smallItemsCopy }, model);
}
},
{
name: `${numberOfSmallItems} small items using compile()`,
testFunction: (fhirpath, model, compiledFn) => {
compiledFn({}, { items: smallItems, itemsCopy: smallItemsCopy });
}
}
}
].reduce((arr, item) => {
arr.push(
b.add(
`${item.name} [${previousVersion}]`,
item.testFunction.bind(
this,
previous_fhirpath,
previous_r4_model,
previous_fhirpath.compile(expression, previous_r4_model)
].reduce((arr, item) => {
arr.push(
benny.add(
`${item.name} [${previousVersion}]`,
item.testFunction.bind(
this,
previous_fhirpath,
previous_r4_model,
previous_fhirpath.compile(expression, previous_r4_model)
)
)
)
);
arr.push(
b.add(
`${item.name} [${currentVersion}]`,
item.testFunction.bind(
this,
current_fhirpath,
current_r4_model,
current_fhirpath.compile(expression, current_r4_model)
);
arr.push(
benny.add(
`${item.name} [${currentVersion}]`,
item.testFunction.bind(
this,
current_fhirpath,
current_r4_model,
current_fhirpath.compile(expression, current_r4_model)
)
)
)
);
return arr;
}, []);
);
return arr;
}, []);

b.suite(
'intersect() of two collections with',
...cases,
b.cycle(),
b.complete(),
b.configure({
minDisplayPrecision: 2
}),
b.save({ file: 'intersect', folder: __dirname + '/results', version: currentVersion }),
b.save({ file: 'intersect', folder: __dirname + '/results', format: 'chart.html' }),
).then(() => {
open(__dirname + '/results/intersect.chart.html');
});
benny.suite(
'intersect() of two collections with',
...cases,
benny.cycle(),
benny.complete(),
benny.configure({
minDisplayPrecision: 2
}),
benny.save({ file: 'intersect', folder: __dirname + '/results', version: currentVersion }),
benny.save({ file: 'intersect', folder: __dirname + '/results', format: 'chart.html' }),
).then(() => {
open(__dirname + '/results/intersect.chart.html');
});
}
70 changes: 70 additions & 0 deletions test/benchmark/member-invocation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
const _ = require('lodash');

module.exports = ({
benny,
open,
previous_fhirpath,
previous_r4_model,
current_fhirpath,
current_r4_model,
minimumDataset,
currentVersion,
previousVersion
}) => {

const numberOfItems = current_fhirpath.evaluate(minimumDataset,'Questionnaire.item.count()', {}, current_r4_model);
const expression = 'Questionnaire.item';

const cases = [
{
name: `using evaluate()`,
testFunction: (fhirpath, model) => {
fhirpath.evaluate(minimumDataset, expression, {}, model);
}
},
{
name: `using compile()`,
testFunction: (fhirpath, model, compiledFn) => {
compiledFn(minimumDataset, {});
}
}
].reduce((arr, item) => {
arr.push(
benny.add(
`${item.name} [${previousVersion}]`,
item.testFunction.bind(
this,
previous_fhirpath,
previous_r4_model,
previous_fhirpath.compile(expression, previous_r4_model)
)
)
);
arr.push(
benny.add(
`${item.name} [${currentVersion}]`,
item.testFunction.bind(
this,
current_fhirpath,
current_r4_model,
current_fhirpath.compile(expression, current_r4_model)
)
)
);
return arr;
}, []);

benny.suite(
`Getting ${numberOfItems} items with engine.MemberInvocation`,
...cases,
benny.cycle(),
benny.complete(),
benny.configure({
minDisplayPrecision: 2
}),
benny.save({ file: 'member-invocation', folder: __dirname + '/results', version: currentVersion }),
benny.save({ file: 'member-invocation', folder: __dirname + '/results', format: 'chart.html' }),
).then(() => {
open(__dirname + '/results/member-invocation.chart.html');
});
}
Loading

0 comments on commit a3a0445

Please sign in to comment.