From 42e33118306b8eca5f3bf56313efc784221542a2 Mon Sep 17 00:00:00 2001 From: Tobias Schweizer Date: Tue, 25 May 2021 10:43:27 +0200 Subject: [PATCH] feat(advanced search): specify linked resource (DSP-1587) (#293) * feat(advanced search): make component to search for a resource * feat(advanced search): get class of linked resource * feat(advanced search): distinguish between specifying and searching for a linked resource * feat(advanced search): distinguish between specifying and searching for a linked resource * feat(gravsearch generation): prepare adaption * refactor(gravsearch service): make method for props handling * refactor(gravsearch service): make method for props handling * refactor(gravsearch service): generate method for props handling * refactor(gravsearch service): avoid redundant statements * test(gravsearch service): adapt expectation * refactor(gravsearch service): make var names unique * refactor(gravsearch service): make var names unique * refactor(gravsearch service): improve type checking * refactor(gravsearch service): improve comments * feature(advanced search): introduce top level flag * test(advanced search): add test for comp op * test(advanced search): add new Input in mocked components * test(advanced search): add test for SearchResourceComponent * test(gravsearch service): add test for Gravsearch service * feat(gravsearch service): add variable for rec. call count * feat(gravsearch service): add optional res. class restriction on linked res. * feat(advanced search): only show sort criterion for main resource on top level * docs(advanced search): adapt design docs (ongoing) * refactor(advanced search): move select res. class comp. * refactor(advanced search): move select prop. comp. * feat(advanced search): add fallback for object class constraint * feat(advanced search): determine ontology from resource class * docs(advanced search): adapt design docs * docs(gravsearch service): add comment --- .../how-to-contribute/design-documentation.md | 38 ++- .../advanced-search.component.html | 2 +- .../advanced-search.component.spec.ts | 2 + .../advanced-search.component.ts | 2 +- ...urce-and-property-selection.component.html | 2 +- ...e-and-property-selection.component.spec.ts | 2 + ...source-and-property-selection.component.ts | 6 +- .../select-property.component.html | 2 +- .../select-property.component.scss | 0 .../select-property.component.spec.ts | 40 ++- .../select-property.component.ts | 9 +- .../operator-constants.ts | 0 .../specify-property-value/operator.ts | 20 ++ .../search-boolean-value.component.html | 0 .../search-boolean-value.component.scss | 0 .../search-boolean-value.component.spec.ts | 0 .../search-boolean-value.component.ts | 0 .../search-date-value.component.html | 0 .../search-date-value.component.scss | 0 .../search-date-value.component.spec.ts | 2 +- .../search-date-value.component.ts | 2 +- .../search-decimal-value.component.html | 0 .../search-decimal-value.component.scss | 0 .../search-decimal-value.component.spec.ts | 0 .../search-decimal-value.component.ts | 0 .../search-int-value.component.html | 0 .../search-int-value.component.scss | 0 .../search-int-value.component.spec.ts | 0 .../search-int-value.component.ts | 0 .../search-link-value.component.html | 0 .../search-link-value.component.scss | 0 .../search-link-value.component.spec.ts | 2 +- .../search-link-value.component.ts | 2 +- .../search-display-list.component.html | 0 .../search-display-list.component.scss | 0 .../search-display-list.component.spec.ts | 0 .../search-display-list.component.ts | 0 .../search-list-value.component.html | 0 .../search-list-value.component.scss | 0 .../search-list-value.component.spec.ts | 2 +- .../search-list-value.component.ts | 4 +- .../search-resource.component.html | 2 + .../search-resource.component.scss} | 0 .../search-resource.component.spec.ts | 125 +++++++++ .../search-resource.component.ts | 65 +++++ .../search-text-value.component.html | 0 .../search-text-value.component.scss} | 0 .../search-text-value.component.spec.ts | 0 .../search-text-value.component.ts | 0 .../search-uri-value.component.html | 0 .../search-uri-value.component.scss | 0 .../search-uri-value.component.spec.ts | 0 .../search-uri-value.component.ts | 2 +- .../specify-property-value.component.html | 12 +- .../specify-property-value.component.scss | 0 .../specify-property-value.component.spec.ts | 68 ++++- .../specify-property-value.component.ts | 18 +- .../select-resource-class.component.html | 0 .../select-resource-class.component.scss | 0 .../select-resource-class.component.spec.ts | 0 .../select-resource-class.component.ts | 0 projects/dsp-ui/src/lib/search/index.ts | 2 +- .../dsp-ui/src/lib/search/search.module.ts | 28 +- .../gravsearch-generation.service.spec.ts | 236 +++++++++++++---- .../services/gravsearch-generation.service.ts | 245 +++++++++++------- .../advanced-search-playground.component.ts | 2 +- 66 files changed, 753 insertions(+), 191 deletions(-) rename projects/dsp-ui/src/lib/search/advanced-search/{ => resource-and-property-selection}/select-property/select-property.component.html (85%) rename projects/dsp-ui/src/lib/search/advanced-search/{ => resource-and-property-selection}/select-property/select-property.component.scss (100%) rename projects/dsp-ui/src/lib/search/advanced-search/{ => resource-and-property-selection}/select-property/select-property.component.spec.ts (88%) rename projects/dsp-ui/src/lib/search/advanced-search/{ => resource-and-property-selection}/select-property/select-property.component.ts (92%) rename projects/dsp-ui/src/lib/search/advanced-search/{ => resource-and-property-selection}/select-property/specify-property-value/operator-constants.ts (100%) rename projects/dsp-ui/src/lib/search/advanced-search/{ => resource-and-property-selection}/select-property/specify-property-value/operator.ts (92%) rename projects/dsp-ui/src/lib/search/advanced-search/{ => resource-and-property-selection}/select-property/specify-property-value/search-boolean-value/search-boolean-value.component.html (100%) rename projects/dsp-ui/src/lib/search/advanced-search/{ => resource-and-property-selection}/select-property/specify-property-value/search-boolean-value/search-boolean-value.component.scss (100%) rename projects/dsp-ui/src/lib/search/advanced-search/{ => resource-and-property-selection}/select-property/specify-property-value/search-boolean-value/search-boolean-value.component.spec.ts (100%) rename projects/dsp-ui/src/lib/search/advanced-search/{ => resource-and-property-selection}/select-property/specify-property-value/search-boolean-value/search-boolean-value.component.ts (100%) rename projects/dsp-ui/src/lib/search/advanced-search/{ => resource-and-property-selection}/select-property/specify-property-value/search-date-value/search-date-value.component.html (100%) rename projects/dsp-ui/src/lib/search/advanced-search/{ => resource-and-property-selection}/select-property/specify-property-value/search-date-value/search-date-value.component.scss (100%) rename projects/dsp-ui/src/lib/search/advanced-search/{ => resource-and-property-selection}/select-property/specify-property-value/search-date-value/search-date-value.component.spec.ts (97%) rename projects/dsp-ui/src/lib/search/advanced-search/{ => resource-and-property-selection}/select-property/specify-property-value/search-date-value/search-date-value.component.ts (94%) rename projects/dsp-ui/src/lib/search/advanced-search/{ => resource-and-property-selection}/select-property/specify-property-value/search-decimal-value/search-decimal-value.component.html (100%) rename projects/dsp-ui/src/lib/search/advanced-search/{ => resource-and-property-selection}/select-property/specify-property-value/search-decimal-value/search-decimal-value.component.scss (100%) rename projects/dsp-ui/src/lib/search/advanced-search/{ => resource-and-property-selection}/select-property/specify-property-value/search-decimal-value/search-decimal-value.component.spec.ts (100%) rename projects/dsp-ui/src/lib/search/advanced-search/{ => resource-and-property-selection}/select-property/specify-property-value/search-decimal-value/search-decimal-value.component.ts (100%) rename projects/dsp-ui/src/lib/search/advanced-search/{ => resource-and-property-selection}/select-property/specify-property-value/search-int-value/search-int-value.component.html (100%) rename projects/dsp-ui/src/lib/search/advanced-search/{ => resource-and-property-selection}/select-property/specify-property-value/search-int-value/search-int-value.component.scss (100%) rename projects/dsp-ui/src/lib/search/advanced-search/{ => resource-and-property-selection}/select-property/specify-property-value/search-int-value/search-int-value.component.spec.ts (100%) rename projects/dsp-ui/src/lib/search/advanced-search/{ => resource-and-property-selection}/select-property/specify-property-value/search-int-value/search-int-value.component.ts (100%) rename projects/dsp-ui/src/lib/search/advanced-search/{ => resource-and-property-selection}/select-property/specify-property-value/search-link-value/search-link-value.component.html (100%) rename projects/dsp-ui/src/lib/search/advanced-search/{ => resource-and-property-selection}/select-property/specify-property-value/search-link-value/search-link-value.component.scss (100%) rename projects/dsp-ui/src/lib/search/advanced-search/{ => resource-and-property-selection}/select-property/specify-property-value/search-link-value/search-link-value.component.spec.ts (98%) rename projects/dsp-ui/src/lib/search/advanced-search/{ => resource-and-property-selection}/select-property/specify-property-value/search-link-value/search-link-value.component.ts (97%) rename projects/dsp-ui/src/lib/search/advanced-search/{ => resource-and-property-selection}/select-property/specify-property-value/search-list-value/search-display-list/search-display-list.component.html (100%) rename projects/dsp-ui/src/lib/search/advanced-search/{ => resource-and-property-selection}/select-property/specify-property-value/search-list-value/search-display-list/search-display-list.component.scss (100%) rename projects/dsp-ui/src/lib/search/advanced-search/{ => resource-and-property-selection}/select-property/specify-property-value/search-list-value/search-display-list/search-display-list.component.spec.ts (100%) rename projects/dsp-ui/src/lib/search/advanced-search/{ => resource-and-property-selection}/select-property/specify-property-value/search-list-value/search-display-list/search-display-list.component.ts (100%) rename projects/dsp-ui/src/lib/search/advanced-search/{ => resource-and-property-selection}/select-property/specify-property-value/search-list-value/search-list-value.component.html (100%) rename projects/dsp-ui/src/lib/search/advanced-search/{ => resource-and-property-selection}/select-property/specify-property-value/search-list-value/search-list-value.component.scss (100%) rename projects/dsp-ui/src/lib/search/advanced-search/{ => resource-and-property-selection}/select-property/specify-property-value/search-list-value/search-list-value.component.spec.ts (98%) rename projects/dsp-ui/src/lib/search/advanced-search/{ => resource-and-property-selection}/select-property/specify-property-value/search-list-value/search-list-value.component.ts (94%) create mode 100644 projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-resource/search-resource.component.html rename projects/dsp-ui/src/lib/search/advanced-search/{select-property/specify-property-value/search-text-value/search-text-value.component.scss => resource-and-property-selection/select-property/specify-property-value/search-resource/search-resource.component.scss} (100%) create mode 100644 projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-resource/search-resource.component.spec.ts create mode 100644 projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-resource/search-resource.component.ts rename projects/dsp-ui/src/lib/search/advanced-search/{ => resource-and-property-selection}/select-property/specify-property-value/search-text-value/search-text-value.component.html (100%) rename projects/dsp-ui/src/lib/search/advanced-search/{select-property/specify-property-value/search-uri-value/search-uri-value.component.scss => resource-and-property-selection/select-property/specify-property-value/search-text-value/search-text-value.component.scss} (100%) rename projects/dsp-ui/src/lib/search/advanced-search/{ => resource-and-property-selection}/select-property/specify-property-value/search-text-value/search-text-value.component.spec.ts (100%) rename projects/dsp-ui/src/lib/search/advanced-search/{ => resource-and-property-selection}/select-property/specify-property-value/search-text-value/search-text-value.component.ts (100%) rename projects/dsp-ui/src/lib/search/advanced-search/{ => resource-and-property-selection}/select-property/specify-property-value/search-uri-value/search-uri-value.component.html (100%) create mode 100644 projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-uri-value/search-uri-value.component.scss rename projects/dsp-ui/src/lib/search/advanced-search/{ => resource-and-property-selection}/select-property/specify-property-value/search-uri-value/search-uri-value.component.spec.ts (100%) rename projects/dsp-ui/src/lib/search/advanced-search/{ => resource-and-property-selection}/select-property/specify-property-value/search-uri-value/search-uri-value.component.ts (95%) rename projects/dsp-ui/src/lib/search/advanced-search/{ => resource-and-property-selection}/select-property/specify-property-value/specify-property-value.component.html (78%) rename projects/dsp-ui/src/lib/search/advanced-search/{ => resource-and-property-selection}/select-property/specify-property-value/specify-property-value.component.scss (100%) rename projects/dsp-ui/src/lib/search/advanced-search/{ => resource-and-property-selection}/select-property/specify-property-value/specify-property-value.component.spec.ts (70%) rename projects/dsp-ui/src/lib/search/advanced-search/{ => resource-and-property-selection}/select-property/specify-property-value/specify-property-value.component.ts (90%) rename projects/dsp-ui/src/lib/search/advanced-search/{ => resource-and-property-selection}/select-resource-class/select-resource-class.component.html (100%) rename projects/dsp-ui/src/lib/search/advanced-search/{ => resource-and-property-selection}/select-resource-class/select-resource-class.component.scss (100%) rename projects/dsp-ui/src/lib/search/advanced-search/{ => resource-and-property-selection}/select-resource-class/select-resource-class.component.spec.ts (100%) rename projects/dsp-ui/src/lib/search/advanced-search/{ => resource-and-property-selection}/select-resource-class/select-resource-class.component.ts (100%) diff --git a/docs/how-to-contribute/design-documentation.md b/docs/how-to-contribute/design-documentation.md index 740ed914a..ed1176ac5 100644 --- a/docs/how-to-contribute/design-documentation.md +++ b/docs/how-to-contribute/design-documentation.md @@ -208,18 +208,20 @@ The advanced search consists of the following components: - `AdvancedSearchComponent`: Main form: Reset and submit buttons, buttons to add and remove properties. - `SelectOntologyComponent`: Select an ontology from a list. - - `SelectResourceClassComponent`: Select a resource class from a list. - - `SelectPropertyComponent`: Select a property from a list. - - `SpecifyPropertyValueComponent`: Specify a comparison operator and a value for a chosen property. - - `SearchBooleanValueComponent`: Specify a Boolean value. - - `SearchDateValueComponent`: Specify a date value. - - `SearchDecimalValueComponent`: Specify a decimal value. - - `SearchIntegerValueComponent`: Specify an integer value. - - `SearchLinkValueComponent`: Specify the target of a link property. - - `SearchListValueComponent`: Specify a list value. - - `SearchDisplayListComponent`: Displays the children of a list node recursively. - - `TextValueComponent`: Specify a text value. - - `UriValueComponent`: Specify a URI value. + - `ResourceAndPropertySelectionComponent`: Parent component that contains `SelectResourceClassComponent` and `SelectPropertyComponent` for recursive reuse. + - `SelectResourceClassComponent`: Select a resource class from a list. + - `SelectPropertyComponent`: Select a property from a list. + - `SpecifyPropertyValueComponent`: Specify a comparison operator and a value for a chosen property. + - `SearchBooleanValueComponent`: Specify a Boolean value. + - `SearchDateValueComponent`: Specify a date value. + - `SearchDecimalValueComponent`: Specify a decimal value. + - `SearchIntegerValueComponent`: Specify an integer value. + - `SearchLinkValueComponent`: Specify the target of a link property. + - `SearchResourceComponent`: Specify the class and/or properties of a linked resource (uses `ResourceAndPropertySelectionComponent`, see below). + - `SearchListValueComponent`: Specify a list value. + - `SearchDisplayListComponent`: Displays the children of a list node recursively. + - `TextValueComponent`: Specify a text value. + - `UriValueComponent`: Specify a URI value. #### Component Interaction @@ -235,6 +237,18 @@ When a property is chosen, a comparison operator can be specified. Once a comparison operator is specified other than "EXISTS", a value can be specified using `SpecifyPropertyValueComponent`. Depending on the value type of the property, `SpecifyPropertyValueComponent` chooses the apt component to let the user enter a value. +##### Recursive Use of ResourceAndPropertySelectionComponent + +`ResourceAndPropertySelectionComponent` is used in the main form and in `SearchResourceComponent`'s template (if the user chooses the operator `Match` for a linking property) +to allow for searching linked resources by specifying their class and/or properties. + +Only one level of recursion is allowed, i.e. linking properties on the linked resource **cannot** use the `Match` operator. + +Sort criteria can only be chosen on the level of the main resource. +The boolean `@Input` `toplevel` distinguishes the top level from the level below. + + + #### Form Validation `AdvancedSearchComponent` creates the main form that is then passed down to the child components. diff --git a/projects/dsp-ui/src/lib/search/advanced-search/advanced-search.component.html b/projects/dsp-ui/src/lib/search/advanced-search/advanced-search.component.html index 884ab26b4..6faa5c9b7 100644 --- a/projects/dsp-ui/src/lib/search/advanced-search/advanced-search.component.html +++ b/projects/dsp-ui/src/lib/search/advanced-search/advanced-search.component.html @@ -5,7 +5,7 @@ (ontologySelected)="setActiveOntology($event)"> - +
diff --git a/projects/dsp-ui/src/lib/search/advanced-search/advanced-search.component.spec.ts b/projects/dsp-ui/src/lib/search/advanced-search/advanced-search.component.spec.ts index 1b26aaa40..cdc666122 100644 --- a/projects/dsp-ui/src/lib/search/advanced-search/advanced-search.component.spec.ts +++ b/projects/dsp-ui/src/lib/search/advanced-search/advanced-search.component.spec.ts @@ -48,6 +48,8 @@ class TestSelectResourceClassAndPropertyComponent { @Input() resClassRestriction?: string; + @Input() topLevel: boolean; + } /** diff --git a/projects/dsp-ui/src/lib/search/advanced-search/advanced-search.component.ts b/projects/dsp-ui/src/lib/search/advanced-search/advanced-search.component.ts index 4a8268221..172ef22f3 100644 --- a/projects/dsp-ui/src/lib/search/advanced-search/advanced-search.component.ts +++ b/projects/dsp-ui/src/lib/search/advanced-search/advanced-search.component.ts @@ -17,7 +17,7 @@ import { DspApiConnectionToken } from '../../core/core.module'; import { SearchParams } from '../../viewer/views/list-view/list-view.component'; import { GravsearchGenerationService } from '../services/gravsearch-generation.service'; import { ResourceAndPropertySelectionComponent } from './resource-and-property-selection/resource-and-property-selection.component'; -import { PropertyWithValue } from './select-property/specify-property-value/operator'; +import { PropertyWithValue } from './resource-and-property-selection/select-property/specify-property-value/operator'; @Component({ selector: 'dsp-advanced-search', diff --git a/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/resource-and-property-selection.component.html b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/resource-and-property-selection.component.html index 7592823b2..41e8c20b8 100644 --- a/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/resource-and-property-selection.component.html +++ b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/resource-and-property-selection.component.html @@ -13,7 +13,7 @@
+ [properties]="properties" [topLevel]="topLevel">
diff --git a/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/resource-and-property-selection.component.spec.ts b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/resource-and-property-selection.component.spec.ts index 84282e2c4..74aa8d19e 100644 --- a/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/resource-and-property-selection.component.spec.ts +++ b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/resource-and-property-selection.component.spec.ts @@ -50,6 +50,8 @@ class TestSelectPropertyComponent { @Input() activeResourceClass: ResourceClassDefinition; + @Input() topLevel: boolean; + } /** diff --git a/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/resource-and-property-selection.component.ts b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/resource-and-property-selection.component.ts index 73504c7b8..23620bef2 100644 --- a/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/resource-and-property-selection.component.ts +++ b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/resource-and-property-selection.component.ts @@ -16,8 +16,8 @@ import { ResourceClassDefinition, ResourcePropertyDefinition } from '@dasch-swiss/dsp-js'; -import { SelectResourceClassComponent } from '../select-resource-class/select-resource-class.component'; -import { SelectPropertyComponent } from '../select-property/select-property.component'; +import { SelectResourceClassComponent } from './select-resource-class/select-resource-class.component'; +import { SelectPropertyComponent } from './select-property/select-property.component'; import { DspApiConnectionToken } from '../../../core/core.module'; @@ -34,6 +34,8 @@ export class ResourceAndPropertySelectionComponent implements OnInit, OnChanges @Input() resourceClassRestriction?: string; + @Input() topLevel; + form: FormGroup; activeResourceClass: ResourceClassDefinition; diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-property/select-property.component.html b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/select-property.component.html similarity index 85% rename from projects/dsp-ui/src/lib/search/advanced-search/select-property/select-property.component.html rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/select-property.component.html index 196f8aade..d61ff1712 100644 --- a/projects/dsp-ui/src/lib/search/advanced-search/select-property/select-property.component.html +++ b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/select-property.component.html @@ -6,7 +6,7 @@ + [property]="propertySelected" [topLevel]="topLevel"> diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-property/select-property.component.scss b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/select-property.component.scss similarity index 100% rename from projects/dsp-ui/src/lib/search/advanced-search/select-property/select-property.component.scss rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/select-property.component.scss diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-property/select-property.component.spec.ts b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/select-property.component.spec.ts similarity index 88% rename from projects/dsp-ui/src/lib/search/advanced-search/select-property/select-property.component.spec.ts rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/select-property.component.spec.ts index 811798b3f..05d1169e5 100644 --- a/projects/dsp-ui/src/lib/search/advanced-search/select-property/select-property.component.spec.ts +++ b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/select-property.component.spec.ts @@ -26,7 +26,7 @@ import { ComparisonOperatorAndValue, Equals, ValueLiteral } from './specify-prop */ @Component({ template: ` - ` + ` }) class TestHostComponent implements OnInit { @@ -38,6 +38,8 @@ class TestHostComponent implements OnInit { activeResourceClass: ResourceClassDefinition; + topLevel: boolean; + constructor(@Inject(FormBuilder) private _fb: FormBuilder) { } @@ -47,6 +49,8 @@ class TestHostComponent implements OnInit { const resProps = MockOntology.mockReadOntology('http://0.0.0.0:3333/ontology/0001/anything/v2').getPropertyDefinitionsByType(ResourcePropertyDefinition); this.propertyDefs = resProps; + + this.topLevel = true; } } @@ -64,12 +68,13 @@ class TestSpecifyPropertyValueComponent implements OnInit { @Input() property: ResourcePropertyDefinition; + @Input() topLevel: boolean; + getComparisonOperatorAndValueLiteralForProperty(): ComparisonOperatorAndValue { return new ComparisonOperatorAndValue(new Equals(), new ValueLiteral('1', 'http://www.w3.org/2001/XMLSchema#integer')); } ngOnInit() { - } } @@ -174,7 +179,7 @@ describe('SelectPropertyComponent', () => { }); - it('should show the sort checkbox when a property with cardinality 1 is selected', async () => { + it('should show the sort checkbox when a property with cardinality 1 is selected for the top level resource', async () => { const select = await loader.getHarness(MatSelectHarness); await select.open(); @@ -201,6 +206,35 @@ describe('SelectPropertyComponent', () => { }); + it('should not show the sort checkbox when a property with cardinality 1 is selected for a linked resource', async () => { + + testHostComponent.topLevel = false; + + const select = await loader.getHarness(MatSelectHarness); + await select.open(); + + const options = await select.getOptions(); + expect(await options[4].getText()).toEqual('Date'); + + await options[4].click(); + + const resClass = new ResourceClassDefinition(); + resClass.propertiesList = [{ + propertyIndex: 'http://0.0.0.0:3333/ontology/0001/anything/v2#hasDate', + cardinality: Cardinality._1, + isInherited: true + }]; + + testHostComponent.activeResourceClass = resClass; + + testHostFixture.detectChanges(); + + const checkbox = await loader.getAllHarnesses(MatCheckboxHarness); + + expect(checkbox.length).toEqual(0); + + }); + it('should get the specified value for the selected property', async () => { const select = await loader.getHarness(MatSelectHarness); diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-property/select-property.component.ts b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/select-property.component.ts similarity index 92% rename from projects/dsp-ui/src/lib/search/advanced-search/select-property/select-property.component.ts rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/select-property.component.ts index 0c504a66a..3b9ba5e95 100644 --- a/projects/dsp-ui/src/lib/search/advanced-search/select-property/select-property.component.ts +++ b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/select-property.component.ts @@ -2,7 +2,7 @@ import { Component, Inject, Input, OnDestroy, OnInit, ViewChild } from '@angular import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { Cardinality, IHasProperty, ResourceClassDefinition, ResourcePropertyDefinition } from '@dasch-swiss/dsp-js'; import { Subscription } from 'rxjs'; -import { SortingService } from '../../../action/services/sorting.service'; +import { SortingService } from '../../../../action/services/sorting.service'; import { ComparisonOperatorAndValue, PropertyWithValue } from './specify-property-value/operator'; import { SpecifyPropertyValueComponent } from './specify-property-value/specify-property-value.component'; @@ -22,6 +22,8 @@ export class SelectPropertyComponent implements OnInit, OnDestroy { // index of the given property (unique) @Input() index: number; + @Input() topLevel: boolean; + // properties that can be selected from private _properties: ResourcePropertyDefinition[]; @@ -107,12 +109,13 @@ export class SelectPropertyComponent implements OnInit, OnDestroy { */ sortCriterion(): boolean { - // TODO: this method is called from teh template. It is called on each change detection cycle. + // TODO: this method is called from the template. It is called on each change detection cycle. // TODO: this is acceptable because this method has no side-effects // TODO: find a better way: evaluate once and store the result in a class member // check if a resource class is selected and if the property's cardinality is 1 for the selected resource class - if (this._activeResourceClass !== undefined && this.propertySelected !== undefined && !this.propertySelected.isLinkProperty) { + // sort criterion is only available for main resource on top level + if (this.topLevel && this._activeResourceClass !== undefined && this.propertySelected !== undefined && !this.propertySelected.isLinkProperty) { const cardinalities: IHasProperty[] = this._activeResourceClass.propertiesList.filter( (card: IHasProperty) => { diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/operator-constants.ts b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/operator-constants.ts similarity index 100% rename from projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/operator-constants.ts rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/operator-constants.ts diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/operator.ts b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/operator.ts similarity index 92% rename from projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/operator.ts rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/operator.ts index 97593c0c2..efcedc0a8 100644 --- a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/operator.ts +++ b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/operator.ts @@ -213,6 +213,26 @@ export class IRI implements Value { } +/** + * Represents a linked resource. + */ +export class LinkedResource implements Value { + + /** + * Constructs a [LinkedResource]. + * + * @param properties the properties of the linked resource. + * @param resourceClass the class of the linked resource, if any. + */ + constructor(public properties: PropertyWithValue[], public resourceClass?: string) { + } + + public toSparql(): string { + throw Error('invalid call of toSparql'); + } + +} + /** * An abstract interface that represents a value. * This interface has to be implemented for all value types (value component classes). diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-boolean-value/search-boolean-value.component.html b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-boolean-value/search-boolean-value.component.html similarity index 100% rename from projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-boolean-value/search-boolean-value.component.html rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-boolean-value/search-boolean-value.component.html diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-boolean-value/search-boolean-value.component.scss b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-boolean-value/search-boolean-value.component.scss similarity index 100% rename from projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-boolean-value/search-boolean-value.component.scss rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-boolean-value/search-boolean-value.component.scss diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-boolean-value/search-boolean-value.component.spec.ts b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-boolean-value/search-boolean-value.component.spec.ts similarity index 100% rename from projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-boolean-value/search-boolean-value.component.spec.ts rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-boolean-value/search-boolean-value.component.spec.ts diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-boolean-value/search-boolean-value.component.ts b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-boolean-value/search-boolean-value.component.ts similarity index 100% rename from projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-boolean-value/search-boolean-value.component.ts rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-boolean-value/search-boolean-value.component.ts diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-date-value/search-date-value.component.html b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-date-value/search-date-value.component.html similarity index 100% rename from projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-date-value/search-date-value.component.html rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-date-value/search-date-value.component.html diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-date-value/search-date-value.component.scss b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-date-value/search-date-value.component.scss similarity index 100% rename from projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-date-value/search-date-value.component.scss rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-date-value/search-date-value.component.scss diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-date-value/search-date-value.component.spec.ts b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-date-value/search-date-value.component.spec.ts similarity index 97% rename from projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-date-value/search-date-value.component.spec.ts rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-date-value/search-date-value.component.spec.ts index 02f399b44..cb9299464 100644 --- a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-date-value/search-date-value.component.spec.ts +++ b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-date-value/search-date-value.component.spec.ts @@ -7,7 +7,7 @@ import { MatDatepickerModule } from '@angular/material/datepicker'; import { MatInputModule } from '@angular/material/input'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { CalendarDate, CalendarPeriod, GregorianCalendarDate } from 'jdnconvertiblecalendar'; -import { DspViewerModule } from '../../../../../viewer/viewer.module'; +import { DspViewerModule } from '../../../../../../viewer/viewer.module'; import { ValueLiteral } from '../operator'; import { SearchDateValueComponent } from './search-date-value.component'; diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-date-value/search-date-value.component.ts b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-date-value/search-date-value.component.ts similarity index 94% rename from projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-date-value/search-date-value.component.ts rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-date-value/search-date-value.component.ts index e5b7691b4..54f4ae1cb 100644 --- a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-date-value/search-date-value.component.ts +++ b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-date-value/search-date-value.component.ts @@ -2,7 +2,7 @@ import { Component, Inject, Input, OnDestroy, OnInit } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { Constants } from '@dasch-swiss/dsp-js'; import { JDNConvertibleCalendar } from 'jdnconvertiblecalendar'; -import { CalendarHeaderComponent } from '../../../../../viewer/values/date-value/calendar-header/calendar-header.component'; +import { CalendarHeaderComponent } from '../../../../../../viewer/values/date-value/calendar-header/calendar-header.component'; import { PropertyValue, Value, ValueLiteral } from '../operator'; // https://stackoverflow.com/questions/45661010/dynamic-nested-reactive-form-expressionchangedafterithasbeencheckederror diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-decimal-value/search-decimal-value.component.html b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-decimal-value/search-decimal-value.component.html similarity index 100% rename from projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-decimal-value/search-decimal-value.component.html rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-decimal-value/search-decimal-value.component.html diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-decimal-value/search-decimal-value.component.scss b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-decimal-value/search-decimal-value.component.scss similarity index 100% rename from projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-decimal-value/search-decimal-value.component.scss rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-decimal-value/search-decimal-value.component.scss diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-decimal-value/search-decimal-value.component.spec.ts b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-decimal-value/search-decimal-value.component.spec.ts similarity index 100% rename from projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-decimal-value/search-decimal-value.component.spec.ts rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-decimal-value/search-decimal-value.component.spec.ts diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-decimal-value/search-decimal-value.component.ts b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-decimal-value/search-decimal-value.component.ts similarity index 100% rename from projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-decimal-value/search-decimal-value.component.ts rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-decimal-value/search-decimal-value.component.ts diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-int-value/search-int-value.component.html b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-int-value/search-int-value.component.html similarity index 100% rename from projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-int-value/search-int-value.component.html rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-int-value/search-int-value.component.html diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-int-value/search-int-value.component.scss b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-int-value/search-int-value.component.scss similarity index 100% rename from projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-int-value/search-int-value.component.scss rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-int-value/search-int-value.component.scss diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-int-value/search-int-value.component.spec.ts b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-int-value/search-int-value.component.spec.ts similarity index 100% rename from projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-int-value/search-int-value.component.spec.ts rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-int-value/search-int-value.component.spec.ts diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-int-value/search-int-value.component.ts b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-int-value/search-int-value.component.ts similarity index 100% rename from projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-int-value/search-int-value.component.ts rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-int-value/search-int-value.component.ts diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-link-value/search-link-value.component.html b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-link-value/search-link-value.component.html similarity index 100% rename from projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-link-value/search-link-value.component.html rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-link-value/search-link-value.component.html diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-link-value/search-link-value.component.scss b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-link-value/search-link-value.component.scss similarity index 100% rename from projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-link-value/search-link-value.component.scss rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-link-value/search-link-value.component.scss diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-link-value/search-link-value.component.spec.ts b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-link-value/search-link-value.component.spec.ts similarity index 98% rename from projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-link-value/search-link-value.component.spec.ts rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-link-value/search-link-value.component.spec.ts index 99fc9a468..045d2500e 100644 --- a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-link-value/search-link-value.component.spec.ts +++ b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-link-value/search-link-value.component.spec.ts @@ -14,7 +14,7 @@ import { SearchEndpointV2 } from '@dasch-swiss/dsp-js'; import { of } from 'rxjs'; -import { DspApiConnectionToken } from '../../../../../core/core.module'; +import { DspApiConnectionToken } from '../../../../../../core/core.module'; import { IRI } from '../operator'; import { SearchLinkValueComponent } from './search-link-value.component'; diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-link-value/search-link-value.component.ts b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-link-value/search-link-value.component.ts similarity index 97% rename from projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-link-value/search-link-value.component.ts rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-link-value/search-link-value.component.ts index e18b9b346..b6e658616 100644 --- a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-link-value/search-link-value.component.ts +++ b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-link-value/search-link-value.component.ts @@ -1,7 +1,7 @@ import { Component, Inject, Input, OnDestroy, OnInit } from '@angular/core'; import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; import { Constants, KnoraApiConnection, ReadResource, ReadResourceSequence } from '@dasch-swiss/dsp-js'; -import { DspApiConnectionToken } from '../../../../../core/core.module'; +import { DspApiConnectionToken } from '../../../../../../core/core.module'; import { IRI, PropertyValue, Value } from '../operator'; // https://stackoverflow.com/questions/45661010/dynamic-nested-reactive-form-expressionchangedafterithasbeencheckederror diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-list-value/search-display-list/search-display-list.component.html b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-list-value/search-display-list/search-display-list.component.html similarity index 100% rename from projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-list-value/search-display-list/search-display-list.component.html rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-list-value/search-display-list/search-display-list.component.html diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-list-value/search-display-list/search-display-list.component.scss b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-list-value/search-display-list/search-display-list.component.scss similarity index 100% rename from projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-list-value/search-display-list/search-display-list.component.scss rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-list-value/search-display-list/search-display-list.component.scss diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-list-value/search-display-list/search-display-list.component.spec.ts b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-list-value/search-display-list/search-display-list.component.spec.ts similarity index 100% rename from projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-list-value/search-display-list/search-display-list.component.spec.ts rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-list-value/search-display-list/search-display-list.component.spec.ts diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-list-value/search-display-list/search-display-list.component.ts b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-list-value/search-display-list/search-display-list.component.ts similarity index 100% rename from projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-list-value/search-display-list/search-display-list.component.ts rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-list-value/search-display-list/search-display-list.component.ts diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-list-value/search-list-value.component.html b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-list-value/search-list-value.component.html similarity index 100% rename from projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-list-value/search-list-value.component.html rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-list-value/search-list-value.component.html diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-list-value/search-list-value.component.scss b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-list-value/search-list-value.component.scss similarity index 100% rename from projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-list-value/search-list-value.component.scss rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-list-value/search-list-value.component.scss diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-list-value/search-list-value.component.spec.ts b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-list-value/search-list-value.component.spec.ts similarity index 98% rename from projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-list-value/search-list-value.component.spec.ts rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-list-value/search-list-value.component.spec.ts index bef293f12..d12623bbe 100644 --- a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-list-value/search-list-value.component.spec.ts +++ b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-list-value/search-list-value.component.spec.ts @@ -11,7 +11,7 @@ import { By } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { ListNodeV2, ListsEndpointV2, MockList, MockOntology, ResourcePropertyDefinition } from '@dasch-swiss/dsp-js'; import { of } from 'rxjs'; -import { DspApiConnectionToken } from '../../../../../core/core.module'; +import { DspApiConnectionToken } from '../../../../../../core/core.module'; import { IRI } from '../operator'; import { SearchListValueComponent } from './search-list-value.component'; diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-list-value/search-list-value.component.ts b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-list-value/search-list-value.component.ts similarity index 94% rename from projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-list-value/search-list-value.component.ts rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-list-value/search-list-value.component.ts index cad35f4b6..a20e3639c 100644 --- a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-list-value/search-list-value.component.ts +++ b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-list-value/search-list-value.component.ts @@ -8,8 +8,8 @@ import { ListNodeV2, ResourcePropertyDefinition } from '@dasch-swiss/dsp-js'; -import { NotificationService } from '../../../../../action/services/notification.service'; -import { DspApiConnectionToken } from '../../../../../core/core.module'; +import { NotificationService } from '../../../../../../action/services/notification.service'; +import { DspApiConnectionToken } from '../../../../../../core/core.module'; import { IRI, PropertyValue, Value } from '../operator'; // https://stackoverflow.com/questions/45661010/dynamic-nested-reactive-form-expressionchangedafterithasbeencheckederror diff --git a/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-resource/search-resource.component.html b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-resource/search-resource.component.html new file mode 100644 index 000000000..929004fcd --- /dev/null +++ b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-resource/search-resource.component.html @@ -0,0 +1,2 @@ + diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-text-value/search-text-value.component.scss b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-resource/search-resource.component.scss similarity index 100% rename from projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-text-value/search-text-value.component.scss rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-resource/search-resource.component.scss diff --git a/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-resource/search-resource.component.spec.ts b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-resource/search-resource.component.spec.ts new file mode 100644 index 000000000..ae5d7be3f --- /dev/null +++ b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-resource/search-resource.component.spec.ts @@ -0,0 +1,125 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import { SearchResourceComponent } from './search-resource.component'; +import { Component, Inject, Input, OnInit, ViewChild } from '@angular/core'; +import { SearchLinkValueComponent } from '../search-link-value/search-link-value.component'; +import { FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { ComparisonOperatorAndValue, GreaterThan, LinkedResource, PropertyWithValue, ValueLiteral } from '../operator'; +import { MockOntology, ResourcePropertyDefinition } from '@dasch-swiss/dsp-js'; + +/** + * Test host component to simulate parent component. + */ +@Component({ + template: ` + ` +}) +class TestHostComponent implements OnInit { + + form; + + @ViewChild('searchRes', { static: false }) searchResource: SearchResourceComponent; + + resClass: string; + + constructor(@Inject(FormBuilder) private _fb: FormBuilder) { + } + + ngOnInit() { + this.form = this._fb.group({}); + this.resClass = 'http://0.0.0.0:3333/ontology/0001/anything/v2#Thing'; + } +} + +/** + * Test component to simulate ResourceAndPropertySelectionComponent. + */ +@Component({ + selector: 'dsp-resource-and-property-selection', + template: `` +}) +class TestResourceAndPropertySelectionComponent implements OnInit { + + @Input() formGroup: FormGroup; + + @Input() activeOntology: string; + + @Input() resourceClassRestriction?: string; + + @Input() topLevel; + + ngOnInit() { + + const anythingOnto = MockOntology.mockReadOntology('http://0.0.0.0:3333/ontology/0001/anything/v2'); + + const linkedResValue = new ComparisonOperatorAndValue(new GreaterThan(), new ValueLiteral('0.5', 'http://www.w3.org/2001/XMLSchema#decimal')); + + const hasDecimal = anythingOnto.properties['http://0.0.0.0:3333/ontology/0001/anything/v2#hasDecimal']; + + const linkedResourceWithVal = new PropertyWithValue(hasDecimal as ResourcePropertyDefinition, linkedResValue, false); + + this.propertyComponents = [{ + getPropertySelectedWithValue: () => linkedResourceWithVal + }]; + } + + // mock ref to child comp. + resourceClassComponent = { + selectedResourceClassIri: 'http://0.0.0.0:3333/ontology/0001/anything/v2#Thing' + }; + + // mock ref to child comp. + propertyComponents = []; + +} + +describe('SearchLinkValueComponent', () => { + let testHostComponent: TestHostComponent; + let testHostFixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + + TestBed.configureTestingModule({ + imports: [ + BrowserAnimationsModule, + ReactiveFormsModule + ], + declarations: [ + SearchResourceComponent, + TestResourceAndPropertySelectionComponent, + TestHostComponent + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + testHostFixture = TestBed.createComponent(TestHostComponent); + testHostComponent = testHostFixture.componentInstance; + + testHostFixture.detectChanges(); + }); + + it('should create', () => { + expect(testHostComponent).toBeTruthy(); + expect(testHostComponent.searchResource).toBeTruthy(); + }); + + it('should correctly determine the ontology from the resource class constraint', () => { + expect(testHostComponent.searchResource.restrictResourceClass).toEqual('http://0.0.0.0:3333/ontology/0001/anything/v2#Thing'); + }); + + it('should return a specified resource', () => { + + const linkedRes = testHostComponent.searchResource.getValue(); + + expect(linkedRes instanceof LinkedResource).toBeTrue(); + expect((linkedRes as LinkedResource).resourceClass).toEqual('http://0.0.0.0:3333/ontology/0001/anything/v2#Thing'); + expect((linkedRes as LinkedResource).properties.length).toEqual(1); + + expect((linkedRes as LinkedResource).properties[0].property.id).toEqual('http://0.0.0.0:3333/ontology/0001/anything/v2#hasDecimal'); + + }); + +}); diff --git a/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-resource/search-resource.component.ts b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-resource/search-resource.component.ts new file mode 100644 index 000000000..581132f68 --- /dev/null +++ b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-resource/search-resource.component.ts @@ -0,0 +1,65 @@ +import { Component, Inject, Input, OnInit, ViewChild } from '@angular/core'; +import { FormBuilder, FormGroup } from '@angular/forms'; +import { LinkedResource, PropertyValue, PropertyWithValue, Value } from '../operator'; +import { ResourceAndPropertySelectionComponent } from '../../../resource-and-property-selection.component'; +import { Constants } from '@dasch-swiss/dsp-js'; + +// https://stackoverflow.com/questions/45661010/dynamic-nested-reactive-form-expressionchangedafterithasbeencheckederror +const resolvedPromise = Promise.resolve(null); + +@Component({ + selector: 'dsp-search-resource', + templateUrl: './search-resource.component.html', + styleUrls: ['./search-resource.component.scss'] +}) +export class SearchResourceComponent implements OnInit, PropertyValue { + + // parent FormGroup + @Input() formGroup: FormGroup; + + @Input() restrictResourceClass: string; + + type = Constants.Resource; + + form: FormGroup; + + ontology: string; + + constructor(@Inject(FormBuilder) private _fb: FormBuilder) { + } + + // reference to the component that controls the resource class selection + @ViewChild('resAndPropSel') resourceAndPropertySelection: ResourceAndPropertySelectionComponent; + + ngOnInit(): void { + + this.form = this._fb.group({}); + + resolvedPromise.then(() => { + this.formGroup.addControl('propValue', this.form); + }); + + // get ontology from restriction + this.ontology = this.restrictResourceClass.split('#')[0]; + } + + getValue(): Value { + + const resClassOption = this.resourceAndPropertySelection.resourceClassComponent.selectedResourceClassIri; + + let resClass; + + if (resClassOption !== false) { + resClass = resClassOption; + } + + const properties: PropertyWithValue[] = this.resourceAndPropertySelection.propertyComponents.map( + (propComp) => { + return propComp.getPropertySelectedWithValue(); + } + ); + + return new LinkedResource(properties, resClass); + } + +} diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-text-value/search-text-value.component.html b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-text-value/search-text-value.component.html similarity index 100% rename from projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-text-value/search-text-value.component.html rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-text-value/search-text-value.component.html diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-uri-value/search-uri-value.component.scss b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-text-value/search-text-value.component.scss similarity index 100% rename from projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-uri-value/search-uri-value.component.scss rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-text-value/search-text-value.component.scss diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-text-value/search-text-value.component.spec.ts b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-text-value/search-text-value.component.spec.ts similarity index 100% rename from projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-text-value/search-text-value.component.spec.ts rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-text-value/search-text-value.component.spec.ts diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-text-value/search-text-value.component.ts b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-text-value/search-text-value.component.ts similarity index 100% rename from projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-text-value/search-text-value.component.ts rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-text-value/search-text-value.component.ts diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-uri-value/search-uri-value.component.html b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-uri-value/search-uri-value.component.html similarity index 100% rename from projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-uri-value/search-uri-value.component.html rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-uri-value/search-uri-value.component.html diff --git a/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-uri-value/search-uri-value.component.scss b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-uri-value/search-uri-value.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-uri-value/search-uri-value.component.spec.ts b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-uri-value/search-uri-value.component.spec.ts similarity index 100% rename from projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-uri-value/search-uri-value.component.spec.ts rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-uri-value/search-uri-value.component.spec.ts diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-uri-value/search-uri-value.component.ts b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-uri-value/search-uri-value.component.ts similarity index 95% rename from projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-uri-value/search-uri-value.component.ts rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-uri-value/search-uri-value.component.ts index 9d5f06521..e9e2b9c67 100644 --- a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/search-uri-value/search-uri-value.component.ts +++ b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/search-uri-value/search-uri-value.component.ts @@ -1,7 +1,7 @@ import { Component, Inject, Input, OnInit, OnDestroy } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { Constants } from '@dasch-swiss/dsp-js'; -import { CustomRegex } from '../../../../../viewer/values/custom-regex'; +import { CustomRegex } from '../../../../../../viewer/values/custom-regex'; import { PropertyValue, Value, ValueLiteral } from '../operator'; // https://stackoverflow.com/questions/45661010/dynamic-nested-reactive-form-expressionchangedafterithasbeencheckederror diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/specify-property-value.component.html b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/specify-property-value.component.html similarity index 78% rename from projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/specify-property-value.component.html rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/specify-property-value.component.html index 3760dfee6..b81ba144f 100644 --- a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/specify-property-value.component.html +++ b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/specify-property-value.component.html @@ -13,8 +13,16 @@ - + + + + + + + + + + diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/specify-property-value.component.scss b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/specify-property-value.component.scss similarity index 100% rename from projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/specify-property-value.component.scss rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/specify-property-value.component.scss diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/specify-property-value.component.spec.ts b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/specify-property-value.component.spec.ts similarity index 70% rename from projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/specify-property-value.component.spec.ts rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/specify-property-value.component.spec.ts index a8de4bc8c..921f87428 100644 --- a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/specify-property-value.component.spec.ts +++ b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/specify-property-value.component.spec.ts @@ -17,7 +17,7 @@ import { IRI, Value, ValueLiteral } from './operator'; */ @Component({ template: ` - ` + ` }) class TestHostComponent implements OnInit { @@ -27,6 +27,8 @@ class TestHostComponent implements OnInit { propertyDef: ResourcePropertyDefinition; + topLevel = true; + constructor(@Inject(FormBuilder) private _fb: FormBuilder) { } @@ -259,6 +261,42 @@ describe('SpecifyPropertyValueComponent', () => { expect(testHostComponent.specifyProperty.formGroup).toBeDefined(); expect(testHostComponent.specifyProperty.property).toBeDefined(); expect(testHostComponent.specifyProperty.property.id).toEqual('http://0.0.0.0:3333/ontology/0001/anything/v2#hasInteger'); + expect(testHostComponent.specifyProperty.topLevel).toBeTrue(); + }); + + it('should initialise the Inputs correctly for a linking prop with object class constraint', () => { + + const resProps = MockOntology.mockReadOntology('http://0.0.0.0:3333/ontology/0001/anything/v2').getPropertyDefinitionsByType(ResourcePropertyDefinition); + + testHostComponent.propertyDef = resProps.filter(propDef => propDef.id === 'http://0.0.0.0:3333/ontology/0001/anything/v2#hasOtherThing')[0]; + + testHostFixture.detectChanges(); + + expect(testHostComponent.specifyProperty.formGroup).toBeDefined(); + expect(testHostComponent.specifyProperty.property).toBeDefined(); + expect(testHostComponent.specifyProperty.property.id).toEqual('http://0.0.0.0:3333/ontology/0001/anything/v2#hasOtherThing'); + expect(testHostComponent.specifyProperty.topLevel).toBeTrue(); + + expect(testHostComponent.specifyProperty.objectClassConstraint).toEqual('http://0.0.0.0:3333/ontology/0001/anything/v2#Thing'); + + }); + + it('should initialise the Inputs correctly for a linking prop without object class constraint', () => { + + const resProps = MockOntology.mockReadOntology('http://0.0.0.0:3333/ontology/0001/anything/v2').getPropertyDefinitionsByType(ResourcePropertyDefinition); + + testHostComponent.propertyDef = resProps.filter(propDef => propDef.id === 'http://0.0.0.0:3333/ontology/0001/anything/v2#hasOtherThing')[0]; + testHostComponent.propertyDef.objectType = undefined; + + testHostFixture.detectChanges(); + + expect(testHostComponent.specifyProperty.formGroup).toBeDefined(); + expect(testHostComponent.specifyProperty.property).toBeDefined(); + expect(testHostComponent.specifyProperty.property.id).toEqual('http://0.0.0.0:3333/ontology/0001/anything/v2#hasOtherThing'); + expect(testHostComponent.specifyProperty.topLevel).toBeTrue(); + + expect(testHostComponent.specifyProperty.objectClassConstraint).toEqual('http://api.knora.org/ontology/knora-api/v2#Resource'); + }); it('should add a new control to the parent form', waitForAsync(() => { @@ -277,6 +315,32 @@ describe('SpecifyPropertyValueComponent', () => { expect(testHostComponent.specifyProperty.comparisonOperators.length).toEqual(7); }); + it('should set the correct comparison operators for a linking property type (on top level)', () => { + + const resProps = MockOntology.mockReadOntology('http://0.0.0.0:3333/ontology/0001/anything/v2').getPropertyDefinitionsByType(ResourcePropertyDefinition); + + testHostComponent.propertyDef = resProps.filter(propDef => propDef.id === 'http://0.0.0.0:3333/ontology/0001/anything/v2#hasOtherThing')[0]; + + testHostFixture.detectChanges(); + + expect(testHostComponent.specifyProperty.comparisonOperators.length).toEqual(4); + expect(testHostComponent.topLevel).toBeTrue(); + }); + + it('should set the correct comparison operators for a linking property type (not on top level)', () => { + + const resProps = MockOntology.mockReadOntology('http://0.0.0.0:3333/ontology/0001/anything/v2').getPropertyDefinitionsByType(ResourcePropertyDefinition); + + testHostComponent.propertyDef = resProps.filter(propDef => propDef.id === 'http://0.0.0.0:3333/ontology/0001/anything/v2#hasOtherThing')[0]; + + testHostComponent.topLevel = false; + + testHostFixture.detectChanges(); + + expect(testHostComponent.specifyProperty.comparisonOperators.length).toEqual(3); + expect(testHostComponent.topLevel).toBeFalse(); + }); + it('should init the MatSelect and MatOptions correctly', async () => { const select = await loader.getHarness(MatSelectHarness); @@ -293,7 +357,7 @@ describe('SpecifyPropertyValueComponent', () => { }); - it('should set the fom to valid when an comparison operator has been chosen', async () => { + it('should set the form to valid when an comparison operator has been chosen', async () => { expect(testHostComponent.specifyProperty.form.valid).toBe(false); diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/specify-property-value.component.ts b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/specify-property-value.component.ts similarity index 90% rename from projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/specify-property-value.component.ts rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/specify-property-value.component.ts index 2ef71663b..76fe9fe56 100644 --- a/projects/dsp-ui/src/lib/search/advanced-search/select-property/specify-property-value/specify-property-value.component.ts +++ b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-property/specify-property-value/specify-property-value.component.ts @@ -30,14 +30,16 @@ export class SpecifyPropertyValueComponent implements OnChanges, OnDestroy { // parent FormGroup @Input() formGroup: FormGroup; + @Input() topLevel: boolean; + @ViewChild('propertyValue', { static: false }) propertyValueComponent: PropertyValue; + objectClassConstraint: string; + // setter method for the property chosen by the user @Input() set property(prop: ResourcePropertyDefinition) { - this.comparisonOperatorSelected = undefined; // reset to initial state this._property = prop; - this.resetComparisonOperators(); // reset comparison operators for given property (overwriting any previous selection) } // getter method for this._property @@ -77,6 +79,13 @@ export class SpecifyPropertyValueComponent implements OnChanges, OnDestroy { this.comparisonOperatorSelected = data.comparisonOperator; }); + // comparison operator selection + this.comparisonOperatorSelected = undefined; // reset to initial state + this.resetComparisonOperators(); // reset comparison operators for given property (overwriting any previous selection) + + // use knora-api:Resource as a fallback + this.objectClassConstraint = (this.property.isLinkProperty && this.property.objectType !== undefined) ? this.property.objectType : Constants.Resource; + resolvedPromise.then(() => { // remove from the parent form group (clean reset) @@ -111,11 +120,14 @@ export class SpecifyPropertyValueComponent implements OnChanges, OnDestroy { break; case Constants.BooleanValue: - case Constants.Resource: case Constants.UriValue: this.comparisonOperators = [new Equals(), new NotEquals(), new Exists()]; break; + case Constants.Resource: // TODO: Match is only available on top level + this.comparisonOperators = this.topLevel ? [new Equals(), new NotEquals(), new Exists(), new Match()] : [new Equals(), new NotEquals(), new Exists()]; + break; + case Constants.IntValue: case Constants.DecimalValue: case Constants.DateValue: diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-resource-class/select-resource-class.component.html b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-resource-class/select-resource-class.component.html similarity index 100% rename from projects/dsp-ui/src/lib/search/advanced-search/select-resource-class/select-resource-class.component.html rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-resource-class/select-resource-class.component.html diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-resource-class/select-resource-class.component.scss b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-resource-class/select-resource-class.component.scss similarity index 100% rename from projects/dsp-ui/src/lib/search/advanced-search/select-resource-class/select-resource-class.component.scss rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-resource-class/select-resource-class.component.scss diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-resource-class/select-resource-class.component.spec.ts b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-resource-class/select-resource-class.component.spec.ts similarity index 100% rename from projects/dsp-ui/src/lib/search/advanced-search/select-resource-class/select-resource-class.component.spec.ts rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-resource-class/select-resource-class.component.spec.ts diff --git a/projects/dsp-ui/src/lib/search/advanced-search/select-resource-class/select-resource-class.component.ts b/projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-resource-class/select-resource-class.component.ts similarity index 100% rename from projects/dsp-ui/src/lib/search/advanced-search/select-resource-class/select-resource-class.component.ts rename to projects/dsp-ui/src/lib/search/advanced-search/resource-and-property-selection/select-resource-class/select-resource-class.component.ts diff --git a/projects/dsp-ui/src/lib/search/index.ts b/projects/dsp-ui/src/lib/search/index.ts index f1f8a7ab4..ef2a2a817 100644 --- a/projects/dsp-ui/src/lib/search/index.ts +++ b/projects/dsp-ui/src/lib/search/index.ts @@ -3,6 +3,6 @@ export * from './search-panel/search-panel.component'; export * from './fulltext-search/fulltext-search.component'; export * from './expert-search/expert-search.component'; export * from './advanced-search/advanced-search.component'; -export * from './advanced-search/select-property/specify-property-value/operator'; +export * from './advanced-search/resource-and-property-selection/select-property/specify-property-value/operator'; export * from './services/advanced-search-params.service'; export * from './services/gravsearch-generation.service'; diff --git a/projects/dsp-ui/src/lib/search/search.module.ts b/projects/dsp-ui/src/lib/search/search.module.ts index ee96699bc..ea54648a2 100644 --- a/projects/dsp-ui/src/lib/search/search.module.ts +++ b/projects/dsp-ui/src/lib/search/search.module.ts @@ -22,20 +22,21 @@ import { ExpertSearchComponent } from './expert-search/expert-search.component'; import { FulltextSearchComponent } from './fulltext-search/fulltext-search.component'; import { AdvancedSearchComponent } from './advanced-search/advanced-search.component'; import { SelectOntologyComponent } from './advanced-search/select-ontology/select-ontology.component'; -import { SelectResourceClassComponent } from './advanced-search/select-resource-class/select-resource-class.component'; -import { SelectPropertyComponent } from './advanced-search/select-property/select-property.component'; -import { SpecifyPropertyValueComponent } from './advanced-search/select-property/specify-property-value/specify-property-value.component'; -import { SearchIntValueComponent } from './advanced-search/select-property/specify-property-value/search-int-value/search-int-value.component'; -import { SearchBooleanValueComponent } from './advanced-search/select-property/specify-property-value/search-boolean-value/search-boolean-value.component'; -import { SearchDateValueComponent } from './advanced-search/select-property/specify-property-value/search-date-value/search-date-value.component'; -import { SearchDecimalValueComponent } from './advanced-search/select-property/specify-property-value/search-decimal-value/search-decimal-value.component'; -import { SearchLinkValueComponent } from './advanced-search/select-property/specify-property-value/search-link-value/search-link-value.component'; -import { SearchListValueComponent } from './advanced-search/select-property/specify-property-value/search-list-value/search-list-value.component'; -import { SearchDisplayListComponent } from './advanced-search/select-property/specify-property-value/search-list-value/search-display-list/search-display-list.component'; -import { SearchTextValueComponent } from './advanced-search/select-property/specify-property-value/search-text-value/search-text-value.component'; -import { SearchUriValueComponent } from './advanced-search/select-property/specify-property-value/search-uri-value/search-uri-value.component'; +import { SelectResourceClassComponent } from './advanced-search/resource-and-property-selection/select-resource-class/select-resource-class.component'; +import { SelectPropertyComponent } from './advanced-search/resource-and-property-selection/select-property/select-property.component'; +import { SpecifyPropertyValueComponent } from './advanced-search/resource-and-property-selection/select-property/specify-property-value/specify-property-value.component'; +import { SearchIntValueComponent } from './advanced-search/resource-and-property-selection/select-property/specify-property-value/search-int-value/search-int-value.component'; +import { SearchBooleanValueComponent } from './advanced-search/resource-and-property-selection/select-property/specify-property-value/search-boolean-value/search-boolean-value.component'; +import { SearchDateValueComponent } from './advanced-search/resource-and-property-selection/select-property/specify-property-value/search-date-value/search-date-value.component'; +import { SearchDecimalValueComponent } from './advanced-search/resource-and-property-selection/select-property/specify-property-value/search-decimal-value/search-decimal-value.component'; +import { SearchLinkValueComponent } from './advanced-search/resource-and-property-selection/select-property/specify-property-value/search-link-value/search-link-value.component'; +import { SearchListValueComponent } from './advanced-search/resource-and-property-selection/select-property/specify-property-value/search-list-value/search-list-value.component'; +import { SearchDisplayListComponent } from './advanced-search/resource-and-property-selection/select-property/specify-property-value/search-list-value/search-display-list/search-display-list.component'; +import { SearchTextValueComponent } from './advanced-search/resource-and-property-selection/select-property/specify-property-value/search-text-value/search-text-value.component'; +import { SearchUriValueComponent } from './advanced-search/resource-and-property-selection/select-property/specify-property-value/search-uri-value/search-uri-value.component'; import { DspActionModule } from '../action'; import { ResourceAndPropertySelectionComponent } from './advanced-search/resource-and-property-selection/resource-and-property-selection.component'; +import { SearchResourceComponent } from './advanced-search/resource-and-property-selection/select-property/specify-property-value/search-resource/search-resource.component'; @@ -58,7 +59,8 @@ import { ResourceAndPropertySelectionComponent } from './advanced-search/resourc SearchDisplayListComponent, SearchTextValueComponent, SearchUriValueComponent, - ResourceAndPropertySelectionComponent + ResourceAndPropertySelectionComponent, + SearchResourceComponent ], imports: [ CommonModule, diff --git a/projects/dsp-ui/src/lib/search/services/gravsearch-generation.service.spec.ts b/projects/dsp-ui/src/lib/search/services/gravsearch-generation.service.spec.ts index be9806d6b..3fff9cf47 100644 --- a/projects/dsp-ui/src/lib/search/services/gravsearch-generation.service.spec.ts +++ b/projects/dsp-ui/src/lib/search/services/gravsearch-generation.service.spec.ts @@ -4,10 +4,10 @@ import { TestBed } from '@angular/core/testing'; import { MockOntology, ResourcePropertyDefinition } from '@dasch-swiss/dsp-js'; import { ComparisonOperatorAndValue, - Equals, GreaterThan, GreaterThanEquals, IRI, LessThan, LessThanEquals, Like, Match, NotEquals, + Equals, GreaterThan, GreaterThanEquals, IRI, LessThan, LessThanEquals, Like, LinkedResource, Match, NotEquals, PropertyWithValue, ValueLiteral -} from '../advanced-search/select-property/specify-property-value/operator'; +} from '../advanced-search/resource-and-property-selection/select-property/specify-property-value/operator'; describe('GravsearchGenerationService', () => { @@ -707,50 +707,6 @@ CONSTRUCT { FILTER(?propVal0Literal < "1"^^) -} - -OFFSET 0 -`; - - expect(gravsearch).toEqual(expectedGravsearch); - - expect(searchParamsServiceSpy.changeSearchParamsMsg).toHaveBeenCalledTimes(1); - - }); - - it('should create a Gravsearch query string with a linking property matching a resource', () => { - - const prop = MockOntology.mockReadOntology('http://0.0.0.0:3333/ontology/0001/anything/v2').properties['http://0.0.0.0:3333/ontology/0001/anything/v2#hasOtherThing'] as ResourcePropertyDefinition; - - - const value = new ComparisonOperatorAndValue(new Equals(), new IRI('http://rdfh.ch/0001/0C-0L1kORryKzJAJxxRyRQ')); - - const propWithVal = new PropertyWithValue(prop, value, false); - - const gravsearch = gravSearchGenerationServ.createGravsearchQuery([propWithVal], undefined, 0); - - const expectedGravsearch = ` -PREFIX knora-api: -CONSTRUCT { - -?mainRes knora-api:isMainResource true . - -?mainRes . - -} WHERE { - -?mainRes a knora-api:Resource . - - - - -?mainRes . - - - - - - } OFFSET 0 @@ -992,6 +948,194 @@ FILTER(?propVal1Literal > "0.1"^^) ORDER BY ?propVal0 ?propVal1 +OFFSET 0 +`; + + expect(gravsearch).toEqual(expectedGravsearch); + + expect(searchParamsServiceSpy.changeSearchParamsMsg).toHaveBeenCalledTimes(1); + + }); + + it('should create a Gravsearch query string with a linking property matching a resource', () => { + + const prop = MockOntology.mockReadOntology('http://0.0.0.0:3333/ontology/0001/anything/v2').properties['http://0.0.0.0:3333/ontology/0001/anything/v2#hasOtherThing'] as ResourcePropertyDefinition; + + + const value = new ComparisonOperatorAndValue(new Equals(), new IRI('http://rdfh.ch/0001/0C-0L1kORryKzJAJxxRyRQ')); + + const propWithVal = new PropertyWithValue(prop, value, false); + + const gravsearch = gravSearchGenerationServ.createGravsearchQuery([propWithVal], undefined, 0); + + const expectedGravsearch = ` +PREFIX knora-api: +CONSTRUCT { + +?mainRes knora-api:isMainResource true . + +?mainRes . + +} WHERE { + +?mainRes a knora-api:Resource . + + + + +?mainRes . + + + + + + +} + +OFFSET 0 +`; + + expect(gravsearch).toEqual(expectedGravsearch); + + expect(searchParamsServiceSpy.changeSearchParamsMsg).toHaveBeenCalledTimes(1); + + }); + + it('search for a specified linked resource specified by one prop', () => { + + const anythingOnto = MockOntology.mockReadOntology('http://0.0.0.0:3333/ontology/0001/anything/v2'); + + const hasOtherThingProp = anythingOnto.properties['http://0.0.0.0:3333/ontology/0001/anything/v2#hasOtherThing']; + + const linkedResValue = new ComparisonOperatorAndValue(new GreaterThan(), new ValueLiteral('0.5', 'http://www.w3.org/2001/XMLSchema#decimal')); + + const hasDecimal = anythingOnto.properties['http://0.0.0.0:3333/ontology/0001/anything/v2#hasDecimal']; + + const linkedResourceWithVal = new PropertyWithValue(hasDecimal as ResourcePropertyDefinition, linkedResValue, false); + + const linkedResource = new LinkedResource([linkedResourceWithVal], 'http://0.0.0.0:3333/ontology/0001/anything/v2#Thing'); + + const mainResValue = new ComparisonOperatorAndValue(new Match(), linkedResource); + + const mainResPropWithVal = new PropertyWithValue(hasOtherThingProp as ResourcePropertyDefinition, mainResValue, false); + + const gravsearch = gravSearchGenerationServ.createGravsearchQuery([mainResPropWithVal], 'http://0.0.0.0:3333/ontology/0001/anything/v2#Thing', 0); + + const expectedGravsearch = ` +PREFIX knora-api: +CONSTRUCT { + +?mainRes knora-api:isMainResource true . + +?mainRes ?linkedRes00 . +?linkedRes00 ?propVallinkedRes000 . + + + + +} WHERE { + +?mainRes a knora-api:Resource . + +?mainRes a . + + +?mainRes ?linkedRes00 . +?linkedRes00 ?propVallinkedRes000 . + + + + + + + +?linkedRes00 a . +?propVallinkedRes000 ?propVallinkedRes000Literal +FILTER(?propVallinkedRes000Literal > "0.5"^^) + + +} + +OFFSET 0 +`; + + expect(gravsearch).toEqual(expectedGravsearch); + + expect(searchParamsServiceSpy.changeSearchParamsMsg).toHaveBeenCalledTimes(1); + + }); + + it('search for a specified linked resource specified by two props', () => { + + const anythingOnto = MockOntology.mockReadOntology('http://0.0.0.0:3333/ontology/0001/anything/v2'); + + const hasOtherThingProp = anythingOnto.properties['http://0.0.0.0:3333/ontology/0001/anything/v2#hasOtherThing']; + + const linkedResDecValue = new ComparisonOperatorAndValue(new GreaterThan(), new ValueLiteral('0.5', 'http://www.w3.org/2001/XMLSchema#decimal')); + + const hasDecimal = anythingOnto.properties['http://0.0.0.0:3333/ontology/0001/anything/v2#hasDecimal']; + + const linkedResourceWithDecVal = new PropertyWithValue(hasDecimal as ResourcePropertyDefinition, linkedResDecValue, false); + + const linkedResIntValue = new ComparisonOperatorAndValue(new Equals(), new ValueLiteral('1', 'http://www.w3.org/2001/XMLSchema#integer')); + + const hasInt = anythingOnto.properties['http://0.0.0.0:3333/ontology/0001/anything/v2#hasInteger']; + + const linkedResourceWithIntVal = new PropertyWithValue(hasInt as ResourcePropertyDefinition, linkedResIntValue, false); + + const linkedResource = new LinkedResource([linkedResourceWithDecVal, linkedResourceWithIntVal], 'http://0.0.0.0:3333/ontology/0001/anything/v2#Thing'); + + const mainResValue = new ComparisonOperatorAndValue(new Match(), linkedResource); + + const mainResPropWithVal = new PropertyWithValue(hasOtherThingProp as ResourcePropertyDefinition, mainResValue, false); + + const gravsearch = gravSearchGenerationServ.createGravsearchQuery([mainResPropWithVal], 'http://0.0.0.0:3333/ontology/0001/anything/v2#Thing', 0); + + const expectedGravsearch = ` +PREFIX knora-api: +CONSTRUCT { + +?mainRes knora-api:isMainResource true . + +?mainRes ?linkedRes00 . +?linkedRes00 ?propVallinkedRes000 . + + + +?linkedRes00 ?propVallinkedRes001 . + + + + +} WHERE { + +?mainRes a knora-api:Resource . + +?mainRes a . + + +?mainRes ?linkedRes00 . +?linkedRes00 ?propVallinkedRes000 . + + + +?linkedRes00 ?propVallinkedRes001 . + + + + + + + +?linkedRes00 a . +?propVallinkedRes000 ?propVallinkedRes000Literal +FILTER(?propVallinkedRes000Literal > "0.5"^^) +?propVallinkedRes001 ?propVallinkedRes001Literal +FILTER(?propVallinkedRes001Literal = "1"^^) + + +} + OFFSET 0 `; diff --git a/projects/dsp-ui/src/lib/search/services/gravsearch-generation.service.ts b/projects/dsp-ui/src/lib/search/services/gravsearch-generation.service.ts index 4d2dd3b3e..ee949d310 100644 --- a/projects/dsp-ui/src/lib/search/services/gravsearch-generation.service.ts +++ b/projects/dsp-ui/src/lib/search/services/gravsearch-generation.service.ts @@ -5,8 +5,8 @@ import { Injectable } from '@angular/core'; import { Constants } from '@dasch-swiss/dsp-js'; import { AdvancedSearchParams, AdvancedSearchParamsService } from './advanced-search-params.service'; -import { PropertyWithValue } from '../advanced-search/select-property/specify-property-value/operator'; -import { ComparisonOperatorConstants } from '../advanced-search/select-property/specify-property-value/operator-constants'; +import { LinkedResource, PropertyWithValue } from '../advanced-search/resource-and-property-selection/select-property/specify-property-value/operator'; +import { ComparisonOperatorConstants } from '../advanced-search/resource-and-property-selection/select-property/specify-property-value/operator-constants'; @Injectable({ providedIn: 'root' @@ -22,121 +22,184 @@ export class GravsearchGenerationService { [Constants.ListValue]: Constants.ListValueAsListNode }; - constructor(private _searchParamsService: AdvancedSearchParamsService) { } + // criteria for the order by statement + private orderByCriteria = []; + + // statements to be returned in query results + private returnStatements = []; + + constructor(private _searchParamsService: AdvancedSearchParamsService) { + } /** + * Factory method returning a property handling method. * - * Will be replaced by `@knora/api` (github:knora-api-js-lib) - * - * Generates a Gravsearch query from the provided arguments. - * - * @param properties the properties specified by the user. - * @param mainResourceClassOption the class of the main resource, if any. - * @param offset the offset to be used (nth page of results). + * @param resourceVar Name of the variable identifying the resource. + * @param topLevel Flag indicating if the top level is affected (main resource). + * @param callCounter Inidcates the number of recursive calls of this method. */ - createGravsearchQuery(properties: PropertyWithValue[], mainResourceClassOption?: string, offset: number = 0): string { - - // class restriction for the resource searched for - let mainResourceClass = ''; - - // if given, create the class restriction for the main resource - if (mainResourceClassOption !== undefined) { - mainResourceClass = `?mainRes a <${mainResourceClassOption}> .`; - } - - // criteria for the order by statement - const orderByCriteria = []; - - // statements to be returned in query results - const returnStatements = []; - - // loop over given properties and create statements and filters from them - const props: string[] = properties.map( - (propWithVal: PropertyWithValue, index: number) => { - - // represents the object of a statement - let propValue; - if (!propWithVal.property.isLinkProperty || propWithVal.valueLiteral.comparisonOperator.getClassName() === 'Exists') { - // it is not a linking property, create a variable for the value (to be used by a subsequent FILTER) - // OR the comparison operator Exists is used in which case we do not need to specify the object any further - propValue = `?propVal${index}`; + private makeHandlePropsMethod(resourceVar: string, topLevel = true, callCounter = 0): (propWithVal: PropertyWithValue, index: number) => [string, string] { + + /** + * Converts a [PropertyWithValue] into a tuple of statements and restrictions. + * + * @param propWithVal property with value to be converted. + * @param index index identifying the current prop. + */ + const handleProps = (propWithVal: PropertyWithValue, index: number): [string, string] => { + + let linkedResStatementsAndRestrictions: [string, string][] = []; + // represents the object of a statement + let object; + if (!propWithVal.property.isLinkProperty || propWithVal.valueLiteral.comparisonOperator.getClassName() === 'Exists') { + // it is not a linking property, create a variable for the value (to be used by a subsequent FILTER) + // OR the comparison operator Exists is used in which case we do not need to specify the object any further + if (topLevel) { + object = `?propVal${index}`; } else { - // it is a linking property and the comparison operator is not Exists, use its IRI - propValue = propWithVal.valueLiteral.value.toSparql(); + object = `?propVal${resourceVar}${index}`; } + } else { + // it is a linking property and the comparison operator is not Exists, + if (!(propWithVal.valueLiteral.value instanceof LinkedResource)) { + // use its IRI + object = propWithVal.valueLiteral.value.toSparql(); + } else { + // specify the resource's properties + const linkedResVarName = `linkedRes${callCounter}${index}`; - // generate statement - let statement = `?mainRes <${propWithVal.property.id}> ${propValue} .`; + object = `?${linkedResVarName}`; + // recursively call this method to handle the linked resource's properties + linkedResStatementsAndRestrictions = propWithVal.valueLiteral.value.properties.map(this.makeHandlePropsMethod(linkedResVarName, false, callCounter + 1)); - // check if it is a linking property that has to be wrapped in a FILTER NOT EXISTS (comparison operator NOT_EQUALS) to negate it - if (propWithVal.property.isLinkProperty && propWithVal.valueLiteral.comparisonOperator.getClassName() === 'NotEquals') { - // do not include statement in results, because the query checks for the absence of this statement - statement = `FILTER NOT EXISTS { + } + } + + // generate statement + let statement = `?${resourceVar} <${propWithVal.property.id}> ${object} .`; + + if (linkedResStatementsAndRestrictions.length > 0) { + // get statements from two-tuple + statement += linkedResStatementsAndRestrictions + .map(statAndRestr => statAndRestr[0]) + .reduce((acc: string, stat: string) => acc + stat); + } + + // check if it is a linking property that has to be wrapped in a FILTER NOT EXISTS (comparison operator NOT_EQUALS) to negate it + if (propWithVal.property.isLinkProperty && propWithVal.valueLiteral.comparisonOperator.getClassName() === 'NotEquals') { + // do not include statement in results, because the query checks for the absence of this statement + statement = `FILTER NOT EXISTS { ${statement} }`; - } else { - // TODO: check if statement should be returned returned in results (Boolean flag from checkbox) - returnStatements.push(statement); - statement = ` + } else { + // TODO: check if statement should be returned returned in results (Boolean flag from checkbox) + if (topLevel) { + this.returnStatements.push(statement); + } + statement = ` ${statement} `; - } - - // generate restricting expression (e.g., a FILTER) if comparison operator is not Exists - let restriction = ''; - // only create a FILTER if the comparison operator is not EXISTS and it is not a linking property - if (!propWithVal.property.isLinkProperty && propWithVal.valueLiteral.comparisonOperator.getClassName() !== 'Exists') { - // generate variable for value literal - const propValueLiteral = `${propValue}Literal`; - - if (propWithVal.valueLiteral.comparisonOperator.getClassName() === 'Like') { - // generate statement to value literal - restriction = `${propValue} <${this.complexTypeToProp[propWithVal.property.objectType]}> ${propValueLiteral}` + '\n'; - // use regex function for LIKE - restriction += `FILTER regex(${propValueLiteral}, ${propWithVal.valueLiteral.value.toSparql()}, "i")`; - } else if (propWithVal.valueLiteral.comparisonOperator.getClassName() === 'Match') { - // use Gravsearch function for MATCH - restriction += `FILTER <${ComparisonOperatorConstants.MatchFunction}>(${propValue}, ${propWithVal.valueLiteral.value.toSparql()})`; - } else if (propWithVal.property.objectType === Constants.DateValue) { - // handle date property - restriction = `FILTER(knora-api:toSimpleDate(${propValue}) ${propWithVal.valueLiteral.comparisonOperator.type} ${propWithVal.valueLiteral.value.toSparql()})`; - } else if (propWithVal.property.objectType === Constants.ListValue) { - // handle list node - restriction = `${propValue} <${this.complexTypeToProp[propWithVal.property.objectType]}> ${propWithVal.valueLiteral.value.toSparql()}` + '\n'; - // check for comparison operator "not equals" - if (propWithVal.valueLiteral.comparisonOperator.getClassName() === 'NotEquals') { - restriction = `FILTER NOT EXISTS { + } + + // generate restricting expression (e.g., a FILTER) if comparison operator is not Exists + let restriction = ''; + // only create a FILTER if the comparison operator is not EXISTS and it is not a linking property + if (!propWithVal.property.isLinkProperty && propWithVal.valueLiteral.comparisonOperator.getClassName() !== 'Exists') { + // generate variable for value literal + const propValueLiteral = `${object}Literal`; + + if (propWithVal.valueLiteral.comparisonOperator.getClassName() === 'Like') { + // generate statement to value literal + restriction = `${object} <${this.complexTypeToProp[propWithVal.property.objectType]}> ${propValueLiteral}` + '\n'; + // use regex function for LIKE + restriction += `FILTER regex(${propValueLiteral}, ${propWithVal.valueLiteral.value.toSparql()}, "i")`; + } else if (propWithVal.valueLiteral.comparisonOperator.getClassName() === 'Match') { + // use Gravsearch function for MATCH + restriction += `FILTER <${ComparisonOperatorConstants.MatchFunction}>(${object}, ${propWithVal.valueLiteral.value.toSparql()})`; + } else if (propWithVal.property.objectType === Constants.DateValue) { + // handle date property + restriction = `FILTER(knora-api:toSimpleDate(${object}) ${propWithVal.valueLiteral.comparisonOperator.type} ${propWithVal.valueLiteral.value.toSparql()})`; + } else if (propWithVal.property.objectType === Constants.ListValue) { + // handle list node + restriction = `${object} <${this.complexTypeToProp[propWithVal.property.objectType]}> ${propWithVal.valueLiteral.value.toSparql()}` + '\n'; + // check for comparison operator "not equals" + if (propWithVal.valueLiteral.comparisonOperator.getClassName() === 'NotEquals') { + restriction = `FILTER NOT EXISTS { ${restriction} }`; - } - } else { - // generate statement to value literal - restriction = `${propValue} <${this.complexTypeToProp[propWithVal.property.objectType]}> ${propValueLiteral}` + '\n'; - // generate filter expression - restriction += `FILTER(${propValueLiteral} ${propWithVal.valueLiteral.comparisonOperator.type} ${propWithVal.valueLiteral.value.toSparql()})`; } + } else { + // generate statement to value literal + restriction = `${object} <${this.complexTypeToProp[propWithVal.property.objectType]}> ${propValueLiteral}` + '\n'; + // generate filter expression + restriction += `FILTER(${propValueLiteral} ${propWithVal.valueLiteral.comparisonOperator.type} ${propWithVal.valueLiteral.value.toSparql()})`; } + } - // check if current value is a sort criterion - if (propWithVal.isSortCriterion) { - orderByCriteria.push(propValue); - } + // Check for class restriction on linked resource, if any + if ((propWithVal.valueLiteral.value instanceof LinkedResource) && propWithVal.valueLiteral.value.resourceClass !== undefined) { + restriction += `\n${object} a <${propWithVal.valueLiteral.value.resourceClass}> .\n`; + } - return `${statement} -${restriction} -`; + if (linkedResStatementsAndRestrictions.length > 0) { + // get restriction from two-tuple + restriction += linkedResStatementsAndRestrictions + .map(statAndRestr => statAndRestr[1]) + .reduce((acc: string, restr: string) => acc + '\n' + restr); + } + + // check if current value is a sort criterion + if (propWithVal.isSortCriterion) { + this.orderByCriteria.push(object); + } + + return [statement, restriction]; + + }; + + return handleProps; + } + + /** + * + * Will be replaced by `@knora/api` (github:knora-api-js-lib) + * + * Generates a Gravsearch query from the provided arguments. + * + * @param properties the properties specified by the user. + * @param mainResourceClassOption the class of the main resource, if any. + * @param offset the offset to be used (nth page of results). + */ + createGravsearchQuery(properties: PropertyWithValue[], mainResourceClassOption?: string, offset: number = 0): string { + + // reinit for each Gravsearch query since this service is a singleton + this.orderByCriteria = []; + this.returnStatements = []; - }); + // class restriction for the resource searched for + let mainResourceClass = ''; + + // if given, create the class restriction for the main resource + if (mainResourceClassOption !== undefined) { + mainResourceClass = `?mainRes a <${mainResourceClassOption}> .`; + } + + // loop over given properties and create statements and filters from them + const props: string[] = properties.map(this.makeHandlePropsMethod('mainRes')).map((statementAndRestriction) => + `${statementAndRestriction[0]} +${statementAndRestriction[1]} +` + ); let orderByStatement = ''; - if (orderByCriteria.length > 0) { + if (this.orderByCriteria.length > 0) { orderByStatement = ` -ORDER BY ${orderByCriteria.join(' ')} +ORDER BY ${this.orderByCriteria.join(' ')} `; } @@ -147,7 +210,7 @@ CONSTRUCT { ?mainRes knora-api:isMainResource true . -${returnStatements.join('\n')} +${this.returnStatements.join('\n')} } WHERE { diff --git a/src/app/advanced-search-playground/advanced-search-playground.component.ts b/src/app/advanced-search-playground/advanced-search-playground.component.ts index 1920dce60..d08ff7e6b 100644 --- a/src/app/advanced-search-playground/advanced-search-playground.component.ts +++ b/src/app/advanced-search-playground/advanced-search-playground.component.ts @@ -29,7 +29,7 @@ export class AdvancedSearchPlaygroundComponent implements OnInit { submitQuery(gravsearch: SearchParams) { - console.log('search params', this._advancedSearchParamsService.getSearchParams().generateGravsearch(1)); + console.log('search params', this._advancedSearchParamsService.getSearchParams().generateGravsearch(0)); } }