Skip to content

Commit

Permalink
Merge pull request #472 from MauroDataMapper/feature/gh-470_query_bui…
Browse files Browse the repository at this point in the history
…lder_duplicate_fields

gh-470: Allow for duplicate field names
  • Loading branch information
joe-crawford authored Oct 15, 2024
2 parents 8e7a146 + 59b2602 commit 6915230
Show file tree
Hide file tree
Showing 7 changed files with 57 additions and 108 deletions.
4 changes: 2 additions & 2 deletions src/app/data-explorer/pipes/meql.pipe.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ describe('MeqlPipe', () => {
condition: 'and',
rules: [
{
field: 'field name',
field: 'some entity.field name',
entity: 'some entity',
operator: '=',
value: 'String 1',
Expand All @@ -52,7 +52,7 @@ describe('MeqlPipe', () => {
};

const line1 = `(${newline}`;
const line2 = `${tab}"${query.rules[0].entity}.${query.rules[0].field}" ${query.rules[0].operator} "${query.rules[0].value}"${newline}`;
const line2 = `${tab}"${query.rules[0].field}" ${query.rules[0].operator} "${query.rules[0].value}"${newline}`;
const line3 = ')';

const actual = pipe.transform(query);
Expand Down
8 changes: 2 additions & 6 deletions src/app/data-explorer/pipes/meql.pipe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,7 @@ export class MeqlPipe implements PipeTransform {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
const date = moment.isMoment(value) ? value : moment(value);

return `${quotes}${this._date
.transform(date.toDate(), 'dd/MM/yyyy')
?.toString()}${quotes}`;
return `${quotes}${this._date.transform(date.toDate(), 'dd/MM/yyyy')?.toString()}${quotes}`;
}

private parseQuery(
Expand Down Expand Up @@ -115,9 +113,7 @@ export class MeqlPipe implements PipeTransform {
}
break;
case 'field': {
const entity = value.entity as string;
const fullName = entity ? `${entity}.${value[key]}` : value[key];
meql += this.formattedValue(fullName, true) + ' ';
meql += this.formattedValue(value[key], true) + ' ';
break;
}
case 'value':
Expand Down
29 changes: 15 additions & 14 deletions src/app/data-explorer/query-builder-wrapper.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import {
import { QueryBuilderConfig } from './query-builder/query-builder.interfaces';
import { createProfileServiceStub } from '../testing/stubs/profile.stub';
import { QueryBuilderTestingHelper } from '../testing/querybuilder.testing.helpers';
import { Console } from 'console';
describe('QueryBuilderService', () => {
let service: QueryBuilderWrapperService;
const profilesStub = createProfileServiceStub();
Expand Down Expand Up @@ -255,7 +256,7 @@ describe('QueryBuilderService', () => {
condition: 'and',
rules: [
{
field: `field (${expectedMappedType})`,
field: `schema.class.field (${expectedMappedType})`,
operator: '=',
value: 'value-1',
},
Expand All @@ -268,15 +269,15 @@ describe('QueryBuilderService', () => {
fields:
expectedMappedType !== undefined
? {
field: {
[`${entityName}.field`]: {
defaultValue:
expectedMappedType === 'number'
? 0
: expectedMappedType === 'string'
? ''
: expectedMappedType === 'boolean'
? false
: null,
? ''
: expectedMappedType === 'boolean'
? false
: null,
name: `field (${expectedMappedType})`,
entity: entityName,
options:
Expand All @@ -286,14 +287,14 @@ describe('QueryBuilderService', () => {
{ name: 'Option 2', value: 'Option 2' },
]
: expectedMappedType === 'terminology'
? [
{
name: 'modelResourceDomainType',
value: dataType.modelResourceDomainType,
},
{ name: 'modelResourceId', value: dataType.modelResourceId },
]
: [],
? [
{
name: 'modelResourceDomainType',
value: dataType.modelResourceDomainType,
},
{ name: 'modelResourceId', value: dataType.modelResourceId },
]
: [],
type: expectedMappedType,
},
}
Expand Down
35 changes: 17 additions & 18 deletions src/app/data-explorer/query-builder-wrapper.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,7 @@ export const mapModelDataTypeToOptionsArray = (dataType: DataType): Option[] =>
];
};

export const mapOptionsArrayToModelDataType = (
options: Option[]
): Required<CatalogueItem> => {
export const mapOptionsArrayToModelDataType = (options: Option[]): Required<CatalogueItem> => {
const domainType = options.find((o) => o.name === 'modelResourceDomainType')
?.value as CatalogueItemDomainType;
const id = options.find((o) => o.name === 'modelResourceId')?.value;
Expand Down Expand Up @@ -90,10 +88,7 @@ export class QueryBuilderWrapperService {
map(([items, coreTableProfile]) => {
let errorMessage = '';

errorMessage = this.appendErrorMessages(
errorMessage,
this.getDataTypeProfileErrors(items)
);
errorMessage = this.appendErrorMessages(errorMessage, this.getDataTypeProfileErrors(items));

errorMessage = this.appendErrorMessages(
errorMessage,
Expand Down Expand Up @@ -128,6 +123,12 @@ export class QueryBuilderWrapperService {
.join('.');
}

private getEntityAndLabel(dataElement: DataElementSearchResult) {
const entity = this.getEntity(dataElement);
const label = dataElement.label;
return entity ? `${entity}.${label}` : label;
}

private getQueryBuilderDatatypeProfile(dataType?: DataType): Observable<Profile> {
if (dataType?.domainType === CatalogueItemDomainType.PrimitiveType) {
const requestOptions = {
Expand All @@ -148,8 +149,7 @@ export class QueryBuilderWrapperService {
private getDataTypeString(data: Profile, dataElement: DataElementSearchResult) {
if (
dataElement.dataType?.domainType === CatalogueItemDomainType.ModelDataType &&
(dataElement.dataType?.modelResourceDomainType ===
CatalogueItemDomainType.Terminology ||
(dataElement.dataType?.modelResourceDomainType === CatalogueItemDomainType.Terminology ||
dataElement.dataType?.modelResourceDomainType === CatalogueItemDomainType.CodeSet)
) {
return 'terminology';
Expand Down Expand Up @@ -190,10 +190,10 @@ export class QueryBuilderWrapperService {
return dataTypeString.toLowerCase() === 'number'
? 0
: dataTypeString.toLowerCase() === 'string'
? ''
: dataTypeString.toLowerCase() === 'boolean'
? false
: null;
? ''
: dataTypeString.toLowerCase() === 'boolean'
? false
: null;
}

private getQueryCondition(query?: DataSpecificationQueryPayload): QueryCondition {
Expand All @@ -211,7 +211,7 @@ export class QueryBuilderWrapperService {
dataElement: DataElementSearchResult,
config: QueryBuilderConfig
) {
config.fields[dataElement.label] = {
config.fields[this.getEntityAndLabel(dataElement)] = {
name: dataElement.label + ' (' + dataTypeString + ')',
type: dataTypeString,
entity: this.getEntity(dataElement),
Expand All @@ -225,12 +225,11 @@ export class QueryBuilderWrapperService {
config: QueryBuilderConfig,
queryCondition: QueryCondition
) {
const dataElementPath = this.getEntityAndLabel(dataElement);
if (
queryCondition?.rules?.find((x) =>
(x as QueryExpression)?.field?.startsWith(dataElement.label)
)
queryCondition?.rules?.find((x) => (x as QueryExpression)?.field?.startsWith(dataElementPath))
) {
config.fields[dataElement.label] = {
config.fields[dataElementPath] = {
name: dataElement.label + ' (string)',
type: 'string',
entity: this.getEntity(dataElement),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@
<mat-form-field appearance="outline" class="query-builder-input">
<mat-select [(ngModel)]="rule.value" multiple (ngModelChange)="onChange()">
<mat-option *ngFor="let opt of options" [value]="opt.value">
{{ opt.name }}
{{ opt.value }}
</mat-option>
</mat-select>
</mat-form-field>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,15 @@ SPDX-License-Identifier: Apache-2.0
*/
import { QueryBuilderWrapperComponent } from './query-builder-wrapper.component';

import {
ComponentHarness,
setupTestModuleForComponent,
} from 'src/app/testing/testing.helpers';
import { ComponentHarness, setupTestModuleForComponent } from 'src/app/testing/testing.helpers';
import { createTerminologyServiceStub } from 'src/app/testing/stubs/terminology.stub';
import { TerminologyService } from 'src/app/mauro/terminology.service';
import { MockComponent } from 'ng-mocks';
import { MeqlOutputComponent } from '../meql-output/meql-output.component';
import { MatCard } from '@angular/material/card';
import { QueryCondition } from '../data-explorer.types';
import { mapModelDataTypeToOptionsArray } from '../query-builder-wrapper.service';
import {
CatalogueItemDomainType,
MdmResourcesConfiguration,
} from '@maurodatamapper/mdm-resources';
import { CatalogueItemDomainType, MdmResourcesConfiguration } from '@maurodatamapper/mdm-resources';
import { of } from 'rxjs';
import { AutocompleteSelectOptionSet } from 'src/app/shared/autocomplete-select/autocomplete-select.component';
import { QueryBuilderComponent } from '../query-builder/query-builder.component';
Expand Down Expand Up @@ -133,12 +127,8 @@ describe('QueryBuilderComponent', () => {
options: [],
};

expect(harness.component.termSearchResults['testField1']).toStrictEqual(
expectedSetup
);
expect(harness.component.termSearchResults['testField2']).toStrictEqual(
expectedSetup
);
expect(harness.component.termSearchResults['testField1']).toStrictEqual(expectedSetup);
expect(harness.component.termSearchResults['testField2']).toStrictEqual(expectedSetup);
});

it('should reset the query when empty', () => {
Expand Down
69 changes: 16 additions & 53 deletions src/app/data-explorer/query-builder/query-builder.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,13 @@
[ngClass]="getClassNames('arrowIconButton', data.collapsed ? 'collapsed' : null)"
>
<ng-container *ngIf="getArrowIconTemplate() as template; else defaultArrowIcon">
<ng-container
*ngTemplateOutlet="template; context: getArrowIconContext()"
></ng-container>
<ng-container *ngTemplateOutlet="template; context: getArrowIconContext()"></ng-container>
</ng-container>
</a>

<ng-container *ngIf="getButtonGroupTemplate() as template; else defaultButtonGroup">
<div [ngClass]="getClassNames('buttonGroup', 'rightAlign')">
<ng-container
*ngTemplateOutlet="template; context: getButtonGroupContext()"
></ng-container>
<ng-container *ngTemplateOutlet="template; context: getButtonGroupContext()"></ng-container>
</div>
</ng-container>

Expand Down Expand Up @@ -80,21 +76,13 @@
</ng-template>

<ng-container *ngIf="getSwitchGroupTemplate() as template; else defaultSwitchGroup">
<ng-container
*ngTemplateOutlet="template; context: getSwitchGroupContext()"
></ng-container>
<ng-container *ngTemplateOutlet="template; context: getSwitchGroupContext()"></ng-container>
</ng-container>

<ng-template #defaultSwitchGroup>
<mat-radio-group
*ngIf="data"
[(ngModel)]="data.condition"
(ngModelChange)="changeConnective()"
>
<mat-radio-group *ngIf="data" [(ngModel)]="data.condition" (ngModelChange)="changeConnective()">
<mat-radio-button color="primary" value="and">AND</mat-radio-button>
<mat-radio-button *ngIf="showOrRadioButton()" color="primary" value="or"
>OR</mat-radio-button
>
<mat-radio-button *ngIf="showOrRadioButton()" color="primary" value="or">OR</mat-radio-button>
</mat-radio-group>
</ng-template>

Expand Down Expand Up @@ -145,9 +133,7 @@
Condition used to be: entities?.length ?? 0 > 0
-->
<div *ngIf="false" class="q-inline-block-display">
<ng-container
*ngIf="getEntityTemplate() as template; else defaultEntity"
>
<ng-container *ngIf="getEntityTemplate() as template; else defaultEntity">
<ng-container
*ngTemplateOutlet="template; context: getEntityContext(rule)"
></ng-container>
Expand Down Expand Up @@ -183,19 +169,14 @@
(ngModelChange)="changeField($event, rule)"
[disabled]="disabled"
>
<option
*ngFor="let field of getFields(rule.entity)"
[ngValue]="field.value"
>
<option *ngFor="let field of getFields(rule.entity)" [ngValue]="field.value">
{{ field.name }}
</option>
</select>
</div>
</ng-template>

<ng-container
*ngIf="getOperatorTemplate() as template; else defaultOperator"
>
<ng-container *ngIf="getOperatorTemplate() as template; else defaultOperator">
<ng-container
*ngTemplateOutlet="template; context: getOperatorContext(rule)"
></ng-container>
Expand All @@ -219,9 +200,7 @@
</div>
</ng-template>

<ng-container
*ngIf="findTemplateForRule(rule) as template; else defaultInput"
>
<ng-container *ngIf="findTemplateForRule(rule) as template; else defaultInput">
<ng-container
*ngTemplateOutlet="template; context: getInputContext(rule)"
></ng-container>
Expand Down Expand Up @@ -271,10 +250,7 @@
[disabled]="disabled"
*ngSwitchCase="'category'"
>
<option
*ngFor="let opt of getOptions(rule.field)"
[ngValue]="opt.value"
>
<option *ngFor="let opt of getOptions(rule.field)" [ngValue]="opt.value">
{{ opt.name }}
</option>
</select>
Expand All @@ -286,10 +262,7 @@
[disabled]="disabled"
multiple
>
<option
*ngFor="let opt of getOptions(rule.field)"
[ngValue]="opt.value"
>
<option *ngFor="let opt of getOptions(rule.field)" [ngValue]="opt.value">
{{ opt.name }}
</option>
</select>
Expand All @@ -315,18 +288,10 @@
[parentOperatorTemplate]="parentOperatorTemplate || operatorTemplate"
[parentFieldTemplate]="parentFieldTemplate || fieldTemplate"
[parentEntityTemplate]="parentEntityTemplate || entityTemplate"
[parentSwitchGroupTemplate]="
parentSwitchGroupTemplate || switchGroupTemplate
"
[parentButtonGroupTemplate]="
parentButtonGroupTemplate || buttonGroupTemplate
"
[parentRemoveButtonTemplate]="
parentRemoveButtonTemplate || removeButtonTemplate
"
[parentEmptyWarningTemplate]="
parentEmptyWarningTemplate || emptyWarningTemplate
"
[parentSwitchGroupTemplate]="parentSwitchGroupTemplate || switchGroupTemplate"
[parentButtonGroupTemplate]="parentButtonGroupTemplate || buttonGroupTemplate"
[parentRemoveButtonTemplate]="parentRemoveButtonTemplate || removeButtonTemplate"
[parentEmptyWarningTemplate]="parentEmptyWarningTemplate || emptyWarningTemplate"
[parentArrowIconTemplate]="parentArrowIconTemplate || arrowIconTemplate"
[parentValue]="data"
[classNames]="classNames"
Expand All @@ -340,9 +305,7 @@
>
</query-builder>

<ng-container
*ngIf="getEmptyWarningTemplate() as template; else defaultEmptyWarning"
>
<ng-container *ngIf="getEmptyWarningTemplate() as template; else defaultEmptyWarning">
<ng-container *ngIf="local.invalid">
<ng-container
*ngTemplateOutlet="template; context: getEmptyWarningContext()"
Expand Down

0 comments on commit 6915230

Please sign in to comment.