From dbd8909aa255ed70c984714774aead6cfb95559f Mon Sep 17 00:00:00 2001 From: Tobias Schweizer Date: Mon, 23 Dec 2019 15:12:09 +0100 Subject: [PATCH 1/6] feature (search): add classes to represent a search query --- src/api/v2/search/search-endpoint.spec.ts | 2 +- src/api/v2/search/search-endpoint.ts | 2 +- src/index.ts | 2 +- src/models/v2/Constants.ts | 35 ++++++++ .../v2/resources/ResourcesConversionUtil.ts | 2 +- .../search/count-query-response.ts | 2 +- .../v2/resources/search/search-resource.ts | 5 ++ .../values/search/comparison-operator.ts | 82 +++++++++++++++++++ .../resources/values/search/search-value.ts | 20 +++++ 9 files changed, 147 insertions(+), 5 deletions(-) rename src/models/v2/{ => resources}/search/count-query-response.ts (83%) create mode 100644 src/models/v2/resources/search/search-resource.ts create mode 100644 src/models/v2/resources/values/search/comparison-operator.ts create mode 100644 src/models/v2/resources/values/search/search-value.ts diff --git a/src/api/v2/search/search-endpoint.spec.ts b/src/api/v2/search/search-endpoint.spec.ts index 3a77f3c18..41d397503 100644 --- a/src/api/v2/search/search-endpoint.spec.ts +++ b/src/api/v2/search/search-endpoint.spec.ts @@ -5,7 +5,7 @@ import { MockAjaxCall } from "../../../../test/mockajaxcall"; import { KnoraApiConfig } from "../../../knora-api-config"; import { KnoraApiConnection } from "../../../knora-api-connection"; import { ReadResource } from "../../../models/v2/resources/read/read-resource"; -import { CountQueryResponse } from "../../../models/v2/search/count-query-response"; +import { CountQueryResponse } from "../../../models/v2/resources/search/count-query-response"; import { SearchEndpoint } from "./search-endpoint"; describe("SearchEndpoint", () => { diff --git a/src/api/v2/search/search-endpoint.ts b/src/api/v2/search/search-endpoint.ts index b1de532c0..3f1bdb480 100644 --- a/src/api/v2/search/search-endpoint.ts +++ b/src/api/v2/search/search-endpoint.ts @@ -5,7 +5,7 @@ import { KnoraApiConfig } from "../../../knora-api-config"; import { ApiResponseError } from "../../../models/api-response-error"; import { ReadResource } from "../../../models/v2/resources/read/read-resource"; import { ResourcesConversionUtil } from "../../../models/v2/resources/ResourcesConversionUtil"; -import { CountQueryResponse } from "../../../models/v2/search/count-query-response"; +import { CountQueryResponse } from "../../../models/v2/resources/search/count-query-response"; import { Endpoint } from "../../endpoint"; import { V2Endpoint } from "../v2-endpoint"; diff --git a/src/index.ts b/src/index.ts index d1f2d498d..4491e087c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -71,7 +71,7 @@ export { ResourcePropertyDefinition } from "./models/v2/ontologies/resource-prop export { ResourceClassDefinition } from "./models/v2/ontologies/resource-class-definition"; export { StandoffClassDefinition } from "./models/v2/ontologies/standoff-class-definition"; export { ReadResource } from "./models/v2/resources/read/read-resource"; -export { CountQueryResponse } from "./models/v2/search/count-query-response"; +export { CountQueryResponse } from "./models/v2/resources/search/count-query-response"; export { UpdateResource } from "./models/v2/resources/update/update-resource"; export { CreateResource } from "./models/v2/resources/create/create-resource"; export { DeleteValue } from "./models/v2/resources/values/delete/delete-value"; diff --git a/src/models/v2/Constants.ts b/src/models/v2/Constants.ts index 79537b991..2f4dcafb4 100644 --- a/src/models/v2/Constants.ts +++ b/src/models/v2/Constants.ts @@ -1,5 +1,6 @@ export class Constants { + // Knora API static KnoraApi = "http://api.knora.org"; static KnoraApiV2 = Constants.KnoraApi + "/ontology/knora-api/v2"; static Delimiter = "#"; @@ -105,6 +106,7 @@ export class Constants { static DeleteComment = Constants.KnoraApiV2 + Constants.Delimiter + "deleteComment"; static Result = Constants.KnoraApiV2 + Constants.Delimiter + "result"; + // Knora Admin static KnoraAdmin = "http://www.knora.org/ontology/knora-admin"; static DefaultSharedOntologyIRI: string = Constants.KnoraAdmin + Constants.Delimiter + "DefaultSharedOntologiesProject"; static SystemProjectIRI: string = Constants.KnoraAdmin + Constants.Delimiter + "SystemProject"; @@ -112,11 +114,13 @@ export class Constants { static ProjectAdminGroupIRI: string = Constants.KnoraAdmin + Constants.Delimiter + "ProjectAdmin"; static ProjectMemberGroupIRI: string = Constants.KnoraAdmin + Constants.Delimiter + "ProjectMember"; + // Salsah GUI static SalsahGui = "http://api.knora.org/ontology/salsah-gui/v2"; static GuiAttribute = Constants.SalsahGui + Constants.Delimiter + "guiAttribute"; static GuiOrder = Constants.SalsahGui + Constants.Delimiter + "guiOrder"; static GuiElement = Constants.SalsahGui + Constants.Delimiter + "guiElement"; + // OWL static Owl = "http://www.w3.org/2002/07/owl"; static Class = Constants.Owl + Constants.Delimiter + "Class"; static Restriction = Constants.Owl + Constants.Delimiter + "Restriction"; @@ -128,15 +132,18 @@ export class Constants { static ObjectProperty = Constants.Owl + Constants.Delimiter + "ObjectProperty"; static AnnotationProperty = Constants.Owl + Constants.Delimiter + "AnnotationProperty"; + // RDF static Rdf = "http://www.w3.org/1999/02/22-rdf-syntax-ns"; static RdfProperty = Constants.Rdf + Constants.Delimiter + "Property"; + // RDF-S static Rdfs = "http://www.w3.org/2000/01/rdf-schema"; static SubClassOf = Constants.Rdfs + Constants.Delimiter + "subClassOf"; static Comment = Constants.Rdfs + Constants.Delimiter + "comment"; static Label = Constants.Rdfs + Constants.Delimiter + "label"; static SubPropertyOf = Constants.Rdfs + Constants.Delimiter + "subPropertyOf"; + // XSD static Xsd = "http://www.w3.org/2001/XMLSchema"; static XsdAnyUri = Constants.Xsd + Constants.Delimiter + "anyURI"; static XsdString = Constants.Xsd + Constants.Delimiter + "string"; @@ -145,6 +152,34 @@ export class Constants { static XsdInteger = Constants.Xsd + Constants.Delimiter + "integer"; static dateTimeStamp = Constants.Xsd + Constants.Delimiter + "dateTimeStamp"; + // schema.org static SchemaNumberOfItems = "http://schema.org/numberOfItems"; + // Gravsearch + static EqualsComparisonOperator: string = "="; + static EqualsComparisonLabel: string = "is equal to"; + + static NotEqualsComparisonOperator: string = "!="; + static NotEqualsComparisonLabel: string = "is not equal to"; + + static GreaterThanComparisonOperator: string = ">"; + static GreaterThanComparisonLabel: string = "is greater than"; + + static GreaterThanEqualsComparisonOperator: string = ">="; + static GreaterThanEqualsComparisonLabel: string = "is greater than equals to"; + + static LessThanComparisonOperator: string = "<"; + static LessThanComparisonLabel: string = "is less than"; + + static LessThanEqualsComparisonOperator: string = "<="; + static LessThanQualsComparisonLabel: string = "is less than equals to"; + + static ExistsComparisonOperator: string = "E"; + static ExistsComparisonLabel: string = "exists"; + + static LikeComparisonOperator: string = "regex"; + static LikeComparisonLabel: string = "is like"; + + static MatchComparisonOperator: string = "contains"; + static MatchComparisonLabel: string = "matches"; } diff --git a/src/models/v2/resources/ResourcesConversionUtil.ts b/src/models/v2/resources/ResourcesConversionUtil.ts index c70122f76..48c640dc7 100644 --- a/src/models/v2/resources/ResourcesConversionUtil.ts +++ b/src/models/v2/resources/ResourcesConversionUtil.ts @@ -5,7 +5,7 @@ import { ListNodeV2Cache } from "../../../cache/ListNodeV2Cache"; import { IResourceClassAndPropertyDefinitions, OntologyCache } from "../../../cache/OntologyCache"; import { Constants } from "../Constants"; import { ResourcePropertyDefinition } from "../ontologies/resource-property-definition"; -import { CountQueryResponse } from "../search/count-query-response"; +import { CountQueryResponse } from "./search/count-query-response"; import { ReadResource } from "./read/read-resource"; import { ReadBooleanValue } from "./values/read/read-boolean-value"; import { ReadColorValue } from "./values/read/read-color-value"; diff --git a/src/models/v2/search/count-query-response.ts b/src/models/v2/resources/search/count-query-response.ts similarity index 83% rename from src/models/v2/search/count-query-response.ts rename to src/models/v2/resources/search/count-query-response.ts index bde1ffca8..88494bba9 100644 --- a/src/models/v2/search/count-query-response.ts +++ b/src/models/v2/resources/search/count-query-response.ts @@ -1,5 +1,5 @@ import { JsonObject, JsonProperty } from "json2typescript"; -import { Constants } from "../Constants"; +import { Constants } from "../../Constants"; @JsonObject("CountQueryResponse") export class CountQueryResponse { diff --git a/src/models/v2/resources/search/search-resource.ts b/src/models/v2/resources/search/search-resource.ts new file mode 100644 index 000000000..1e874dde5 --- /dev/null +++ b/src/models/v2/resources/search/search-resource.ts @@ -0,0 +1,5 @@ +export class SearchResource { + + type?: string; + +} diff --git a/src/models/v2/resources/values/search/comparison-operator.ts b/src/models/v2/resources/values/search/comparison-operator.ts new file mode 100644 index 000000000..e380db47f --- /dev/null +++ b/src/models/v2/resources/values/search/comparison-operator.ts @@ -0,0 +1,82 @@ +import { Constants } from "../../../Constants"; + +export abstract class ComparisonOperator { + + abstract type: string; + + abstract label: string; + +} + +export class Equals extends ComparisonOperator { + + type = Constants.EqualsComparisonOperator; + + label = Constants.EqualsComparisonLabel; + +} + +export class NotEquals extends ComparisonOperator { + + type = Constants.NotEqualsComparisonOperator; + + label = Constants.NotEqualsComparisonLabel; + +} + +export class GreaterThanEquals extends ComparisonOperator { + + type = Constants.GreaterThanEqualsComparisonOperator; + + label = Constants.GreaterThanEqualsComparisonLabel; + +} + +export class GreaterThan extends ComparisonOperator { + + type = Constants.GreaterThanComparisonOperator; + + label = Constants.GreaterThanComparisonLabel; + +} + +export class LessThan extends ComparisonOperator { + + type = Constants.LessThanComparisonOperator; + + label = Constants.LessThanComparisonLabel; + +} + +export class LessThanEquals implements ComparisonOperator { + + type = Constants.LessThanEqualsComparisonOperator; + + label = Constants.LessThanQualsComparisonLabel; + +} + + +export class Exists implements ComparisonOperator { + + type = Constants.ExistsComparisonOperator; + + label = Constants.ExistsComparisonLabel; + +} + +export class Like implements ComparisonOperator { + + type = Constants.LikeComparisonOperator; + + label = Constants.LikeComparisonLabel; + +} + +export class Match implements ComparisonOperator { + + type = Constants.MatchComparisonOperator; + + label = Constants.MatchComparisonLabel; + +} diff --git a/src/models/v2/resources/values/search/search-value.ts b/src/models/v2/resources/values/search/search-value.ts new file mode 100644 index 000000000..f4fd82ea4 --- /dev/null +++ b/src/models/v2/resources/values/search/search-value.ts @@ -0,0 +1,20 @@ +import { PropertyDefinition } from "../../../ontologies/property-definition"; +import { ComparisonOperator } from "./comparison-operator"; + +export abstract class ValueLiteral { + + value: any; + + abstract toSparql(): string; + +} + +export abstract class SearchValue { + + property: PropertyDefinition; + + comparisonOperator: ComparisonOperator; + + valueLiteral?: ValueLiteral; + +} From 4a89852eeb5338b35744daab8d5c7336de55b7f9 Mon Sep 17 00:00:00 2001 From: Tobias Schweizer Date: Mon, 23 Dec 2019 15:53:43 +0100 Subject: [PATCH 2/6] feature (search): add class to search for an integer value --- .../v2/resources/search/search-resource.ts | 4 +++ .../values/search/comparison-operator.ts | 18 ++++++------- .../v2/resources/values/search/expression.ts | 18 +++++++++++++ .../values/search/search-int-value.ts | 27 +++++++++++++++++++ .../resources/values/search/search-value.ts | 11 ++++---- 5 files changed, 64 insertions(+), 14 deletions(-) create mode 100644 src/models/v2/resources/values/search/expression.ts create mode 100644 src/models/v2/resources/values/search/search-int-value.ts diff --git a/src/models/v2/resources/search/search-resource.ts b/src/models/v2/resources/search/search-resource.ts index 1e874dde5..8ff3543be 100644 --- a/src/models/v2/resources/search/search-resource.ts +++ b/src/models/v2/resources/search/search-resource.ts @@ -1,5 +1,9 @@ +import { Expression } from "../values/search/expression"; + export class SearchResource { type?: string; + valueRestriction: Expression; + } diff --git a/src/models/v2/resources/values/search/comparison-operator.ts b/src/models/v2/resources/values/search/comparison-operator.ts index e380db47f..22e2c0746 100644 --- a/src/models/v2/resources/values/search/comparison-operator.ts +++ b/src/models/v2/resources/values/search/comparison-operator.ts @@ -8,7 +8,7 @@ export abstract class ComparisonOperator { } -export class Equals extends ComparisonOperator { +export class EqualsOperator extends ComparisonOperator { type = Constants.EqualsComparisonOperator; @@ -16,7 +16,7 @@ export class Equals extends ComparisonOperator { } -export class NotEquals extends ComparisonOperator { +export class NotEqualsOperator extends ComparisonOperator { type = Constants.NotEqualsComparisonOperator; @@ -24,7 +24,7 @@ export class NotEquals extends ComparisonOperator { } -export class GreaterThanEquals extends ComparisonOperator { +export class GreaterThanEqualsOperator extends ComparisonOperator { type = Constants.GreaterThanEqualsComparisonOperator; @@ -32,7 +32,7 @@ export class GreaterThanEquals extends ComparisonOperator { } -export class GreaterThan extends ComparisonOperator { +export class GreaterThanOperator extends ComparisonOperator { type = Constants.GreaterThanComparisonOperator; @@ -40,7 +40,7 @@ export class GreaterThan extends ComparisonOperator { } -export class LessThan extends ComparisonOperator { +export class LessThanOperator extends ComparisonOperator { type = Constants.LessThanComparisonOperator; @@ -48,7 +48,7 @@ export class LessThan extends ComparisonOperator { } -export class LessThanEquals implements ComparisonOperator { +export class LessThanEqualsOperator implements ComparisonOperator { type = Constants.LessThanEqualsComparisonOperator; @@ -57,7 +57,7 @@ export class LessThanEquals implements ComparisonOperator { } -export class Exists implements ComparisonOperator { +export class ExistsOperator implements ComparisonOperator { type = Constants.ExistsComparisonOperator; @@ -65,7 +65,7 @@ export class Exists implements ComparisonOperator { } -export class Like implements ComparisonOperator { +export class LikeOperator implements ComparisonOperator { type = Constants.LikeComparisonOperator; @@ -73,7 +73,7 @@ export class Like implements ComparisonOperator { } -export class Match implements ComparisonOperator { +export class MatchOperator implements ComparisonOperator { type = Constants.MatchComparisonOperator; diff --git a/src/models/v2/resources/values/search/expression.ts b/src/models/v2/resources/values/search/expression.ts new file mode 100644 index 000000000..f84b3ee90 --- /dev/null +++ b/src/models/v2/resources/values/search/expression.ts @@ -0,0 +1,18 @@ +export abstract class Expression { +} + +export class AndExpression extends Expression { + + leftArg: Expression; + + rightArg: Expression; + +} + +export class OrExpression extends Expression { + + leftArg: Expression; + + rightArg: Expression; + +} diff --git a/src/models/v2/resources/values/search/search-int-value.ts b/src/models/v2/resources/values/search/search-int-value.ts new file mode 100644 index 000000000..bb289b8b6 --- /dev/null +++ b/src/models/v2/resources/values/search/search-int-value.ts @@ -0,0 +1,27 @@ +import { + EqualsOperator, ExistsOperator, + GreaterThanEqualsOperator, GreaterThanOperator, + LessThanEqualsOperator, LessThanOperator, + NotEqualsOperator +} from "./comparison-operator"; +import { SearchValue, ValueLiteral } from "./search-value"; + +export class IntValueLiteral extends ValueLiteral { + + value: number; + + getLiteral(): string { + return String(this.value); + } +} + +export class SearchIntValue extends SearchValue { + + valueLiteral: IntValueLiteral; + + comparisonOperator: EqualsOperator | NotEqualsOperator + | GreaterThanEqualsOperator | LessThanEqualsOperator + | GreaterThanOperator | LessThanOperator + | ExistsOperator; + +} diff --git a/src/models/v2/resources/values/search/search-value.ts b/src/models/v2/resources/values/search/search-value.ts index f4fd82ea4..5cd928985 100644 --- a/src/models/v2/resources/values/search/search-value.ts +++ b/src/models/v2/resources/values/search/search-value.ts @@ -1,20 +1,21 @@ import { PropertyDefinition } from "../../../ontologies/property-definition"; import { ComparisonOperator } from "./comparison-operator"; +import { Expression } from "./expression"; export abstract class ValueLiteral { - value: any; + abstract value: any; - abstract toSparql(): string; + abstract getLiteral(): string; } -export abstract class SearchValue { +export abstract class SearchValue extends Expression { property: PropertyDefinition; - comparisonOperator: ComparisonOperator; + abstract comparisonOperator: ComparisonOperator; - valueLiteral?: ValueLiteral; + abstract valueLiteral?: ValueLiteral; } From e12854826e466ffa1aaba88ed39dcffb9a113590 Mon Sep 17 00:00:00 2001 From: Tobias Schweizer Date: Tue, 24 Dec 2019 11:50:20 +0100 Subject: [PATCH 3/6] feature (search): add method that generates a Gravsearch query string (ongoing) --- .../v2/resources/ResourcesConversionUtil.ts | 2 +- .../search/gravsearch-generator.spec.ts | 58 +++++++++++++ .../resources/search/gravsearch-generator.ts | 86 +++++++++++++++++++ .../v2/resources/search/search-resource.ts | 4 +- .../values/search/search-int-value.ts | 27 ------ .../values/search/search-integer-value.ts | 33 +++++++ .../resources/values/search/search-value.ts | 6 +- 7 files changed, 183 insertions(+), 33 deletions(-) create mode 100644 src/models/v2/resources/search/gravsearch-generator.spec.ts create mode 100644 src/models/v2/resources/search/gravsearch-generator.ts delete mode 100644 src/models/v2/resources/values/search/search-int-value.ts create mode 100644 src/models/v2/resources/values/search/search-integer-value.ts diff --git a/src/models/v2/resources/ResourcesConversionUtil.ts b/src/models/v2/resources/ResourcesConversionUtil.ts index 48c640dc7..75102c784 100644 --- a/src/models/v2/resources/ResourcesConversionUtil.ts +++ b/src/models/v2/resources/ResourcesConversionUtil.ts @@ -5,8 +5,8 @@ import { ListNodeV2Cache } from "../../../cache/ListNodeV2Cache"; import { IResourceClassAndPropertyDefinitions, OntologyCache } from "../../../cache/OntologyCache"; import { Constants } from "../Constants"; import { ResourcePropertyDefinition } from "../ontologies/resource-property-definition"; -import { CountQueryResponse } from "./search/count-query-response"; import { ReadResource } from "./read/read-resource"; +import { CountQueryResponse } from "./search/count-query-response"; import { ReadBooleanValue } from "./values/read/read-boolean-value"; import { ReadColorValue } from "./values/read/read-color-value"; import { ParseReadDateValue, ReadDateValue } from "./values/read/read-date-value"; diff --git a/src/models/v2/resources/search/gravsearch-generator.spec.ts b/src/models/v2/resources/search/gravsearch-generator.spec.ts new file mode 100644 index 000000000..5cef0d5cd --- /dev/null +++ b/src/models/v2/resources/search/gravsearch-generator.spec.ts @@ -0,0 +1,58 @@ +import { IResourceClassAndPropertyDefinitions } from "../../../.."; +import { MockOntology } from "../../../../../test/data/api/v2/mockOntology"; +import { PropertyDefinition } from "../../ontologies/property-definition"; +import { EqualsOperator } from "../values/search/comparison-operator"; +import { IntegerValueLiteral, SearchIntegerValue } from "../values/search/search-integer-value"; +import { GravsearchGenerator } from "./gravsearch-generator"; +import { SearchResource } from "./search-resource"; + +describe("GravsearchGenerator", () => { + + it("should create a Gravsearch query restricted to a certain resource class", () => { + + const searchResource = new SearchResource("http://0.0.0.0:3333/ontology/0001/anything/v2#Thing"); + + const gravsearchQuery = GravsearchGenerator.generateGravsearchQuery(searchResource); + + const expectedQuery = ` +PREFIX knora-api: +CONSTRUCT { + +?mainRes knora-api:isMainResource true . + +} WHERE { + +?mainRes a knora-api:Resource . + +?mainRes a . + + + +} +`; + + expect(gravsearchQuery).toEqual(expectedQuery); + + }); + + it("should create a Gravsearch query searching for a specific integer value", () => { + + const anythingThing = "http://0.0.0.0:3333/ontology/0001/anything/v2#Thing"; + + const mock: IResourceClassAndPropertyDefinitions = MockOntology.mockIResourceClassAndPropertyDefinitions(anythingThing); + + const hasIntegerProp: PropertyDefinition = mock.properties["http://0.0.0.0:3333/ontology/0001/anything/v2#hasInteger"]; + + const integerValueLiteral = new IntegerValueLiteral(5); + + const searchIntegerValue = new SearchIntegerValue(hasIntegerProp, integerValueLiteral, new EqualsOperator()); + + const searchResource = new SearchResource(anythingThing, [searchIntegerValue]); + + const gravsearchQuery = GravsearchGenerator.generateGravsearchQuery(searchResource); + + // console.log(gravsearchQuery); + + }); + +}); diff --git a/src/models/v2/resources/search/gravsearch-generator.ts b/src/models/v2/resources/search/gravsearch-generator.ts new file mode 100644 index 000000000..bffa0415f --- /dev/null +++ b/src/models/v2/resources/search/gravsearch-generator.ts @@ -0,0 +1,86 @@ +import { Constants } from "../../Constants"; +import { AndExpression, Expression, OrExpression } from "../values/search/expression"; +import { SearchValue } from "../values/search/search-value"; +import { SearchResource } from "./search-resource"; + +export namespace GravsearchGenerator { + + const complexTypeToProp = { + "http://api.knora.org/ontology/knora-api/v2#IntValue": Constants.IntValueAsInt, + "http://api.knora.org/ontology/knora-api/v2#DecimalValue": Constants.DecimalValueAsDecimal, + "http://api.knora.org/ontology/knora-api/v2#BooleanValue": Constants.BooleanValueAsBoolean, + "http://api.knora.org/ontology/knora-api/v2#TextValue": Constants.ValueAsString, + "http://api.knora.org/ontology/knora-api/v2#UriValue": Constants.UriValueAsUri, + "http://api.knora.org/ontology/knora-api/v2#ListValue": Constants.ListValueAsListNode + }; + + export const generateGravsearchQuery = (searchResource: SearchResource): string => { + + // class restriction for the resource searched for + let mainResourceClass = ""; + + // if given, create the class restriction for the main resource + if (searchResource.type !== undefined) { + mainResourceClass = `?mainRes a <${searchResource.type}> .`; + } + + const statements: string[] = []; + const filters: string[] = []; + + // create statements for properties (restrictions) + searchResource.properties.forEach( + (prop: Expression, index: number) => { + + const propValue = `?propVal${index}`; + + if (prop instanceof SearchValue) { + // add statement from resource to value object + statements.push(`?mainRes <${prop.property.id}> ${propValue} .`); + + // add statement from value object to value literal variable + + const objectType: string = prop.property.objectType as string; + const propValueLiteral = `${propValue}Literal`; + + // @ts-ignore + statements.push(`${propValue} <${complexTypeToProp[objectType]}> ${propValueLiteral}`); + + // add to FILTER + // TODO: check that comparison operator is not EXISTS + const comparisonOperator = prop.comparisonOperator.type; + filters.push(`FILTER(${propValueLiteral} ${comparisonOperator} "${prop.valueLiteral!.getValue()}"^^<${prop.valueLiteral!.xsdType}>)`); + } else if (prop instanceof AndExpression) { + // handle logical AND + + } else if (prop instanceof OrExpression) { + // handle logical OR + + } else { + throw new Error("Unknown Expression " + prop); + } + + } + ); + + const gravsearchQuery = ` +PREFIX knora-api: +CONSTRUCT { + +?mainRes knora-api:isMainResource true . + +} WHERE { + +?mainRes a knora-api:Resource . + +${mainResourceClass} + +${statements.join("\n")} +${filters.join("\n")} +} +`; + + return gravsearchQuery; + + }; + +} diff --git a/src/models/v2/resources/search/search-resource.ts b/src/models/v2/resources/search/search-resource.ts index 8ff3543be..90d845ff1 100644 --- a/src/models/v2/resources/search/search-resource.ts +++ b/src/models/v2/resources/search/search-resource.ts @@ -2,8 +2,6 @@ import { Expression } from "../values/search/expression"; export class SearchResource { - type?: string; - - valueRestriction: Expression; + constructor(public type?: string, public properties: Expression[] = []) {} } diff --git a/src/models/v2/resources/values/search/search-int-value.ts b/src/models/v2/resources/values/search/search-int-value.ts deleted file mode 100644 index bb289b8b6..000000000 --- a/src/models/v2/resources/values/search/search-int-value.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { - EqualsOperator, ExistsOperator, - GreaterThanEqualsOperator, GreaterThanOperator, - LessThanEqualsOperator, LessThanOperator, - NotEqualsOperator -} from "./comparison-operator"; -import { SearchValue, ValueLiteral } from "./search-value"; - -export class IntValueLiteral extends ValueLiteral { - - value: number; - - getLiteral(): string { - return String(this.value); - } -} - -export class SearchIntValue extends SearchValue { - - valueLiteral: IntValueLiteral; - - comparisonOperator: EqualsOperator | NotEqualsOperator - | GreaterThanEqualsOperator | LessThanEqualsOperator - | GreaterThanOperator | LessThanOperator - | ExistsOperator; - -} diff --git a/src/models/v2/resources/values/search/search-integer-value.ts b/src/models/v2/resources/values/search/search-integer-value.ts new file mode 100644 index 000000000..0c42b213e --- /dev/null +++ b/src/models/v2/resources/values/search/search-integer-value.ts @@ -0,0 +1,33 @@ +import { Constants } from "../../../Constants"; +import { PropertyDefinition } from "../../../ontologies/property-definition"; +import { + EqualsOperator, ExistsOperator, + GreaterThanEqualsOperator, GreaterThanOperator, + LessThanEqualsOperator, LessThanOperator, + NotEqualsOperator +} from "./comparison-operator"; +import { SearchValue, ValueLiteral } from "./search-value"; + +export class IntegerValueLiteral extends ValueLiteral { + + xsdType = Constants.XsdInteger; + + constructor(public value: number) { + super(); + } + + getValue(): string { + return String(this.value); + } +} + +export class SearchIntegerValue extends SearchValue { + + constructor(public property: PropertyDefinition, public valueLiteral: IntegerValueLiteral, public comparisonOperator: EqualsOperator | NotEqualsOperator + | GreaterThanEqualsOperator | LessThanEqualsOperator + | GreaterThanOperator | LessThanOperator + | ExistsOperator) { + super(); + } + +} diff --git a/src/models/v2/resources/values/search/search-value.ts b/src/models/v2/resources/values/search/search-value.ts index 5cd928985..48195b2b9 100644 --- a/src/models/v2/resources/values/search/search-value.ts +++ b/src/models/v2/resources/values/search/search-value.ts @@ -6,13 +6,15 @@ export abstract class ValueLiteral { abstract value: any; - abstract getLiteral(): string; + abstract xsdType: string; + + abstract getValue(): string; } export abstract class SearchValue extends Expression { - property: PropertyDefinition; + abstract property: PropertyDefinition; abstract comparisonOperator: ComparisonOperator; From b7d9aa7a194c3edc24ec94c6df8f274a4d434030 Mon Sep 17 00:00:00 2001 From: Tobias Schweizer Date: Tue, 24 Dec 2019 13:17:29 +0100 Subject: [PATCH 4/6] refactor (search): add some TODOs --- src/models/v2/resources/search/gravsearch-generator.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/models/v2/resources/search/gravsearch-generator.ts b/src/models/v2/resources/search/gravsearch-generator.ts index bffa0415f..98e89acdf 100644 --- a/src/models/v2/resources/search/gravsearch-generator.ts +++ b/src/models/v2/resources/search/gravsearch-generator.ts @@ -27,12 +27,14 @@ export namespace GravsearchGenerator { const statements: string[] = []; const filters: string[] = []; - // create statements for properties (restrictions) + // create statements for non-linking properties (restrictions) searchResource.properties.forEach( (prop: Expression, index: number) => { const propValue = `?propVal${index}`; + // TODO: make sure it is a non-linking property + if (prop instanceof SearchValue) { // add statement from resource to value object statements.push(`?mainRes <${prop.property.id}> ${propValue} .`); From 834f13d16f1eec811adeea50fe404d6e13478127 Mon Sep 17 00:00:00 2001 From: Tobias Schweizer Date: Tue, 24 Dec 2019 13:31:20 +0100 Subject: [PATCH 5/6] refactor (search): change type to ResourcePropertyDefinition --- .../search/gravsearch-generator.spec.ts | 4 +-- .../resources/search/gravsearch-generator.ts | 36 ++++++++++++------- .../values/search/search-integer-value.ts | 13 ++++--- .../resources/values/search/search-value.ts | 7 ++-- 4 files changed, 38 insertions(+), 22 deletions(-) diff --git a/src/models/v2/resources/search/gravsearch-generator.spec.ts b/src/models/v2/resources/search/gravsearch-generator.spec.ts index 5cef0d5cd..8f3eadceb 100644 --- a/src/models/v2/resources/search/gravsearch-generator.spec.ts +++ b/src/models/v2/resources/search/gravsearch-generator.spec.ts @@ -1,4 +1,4 @@ -import { IResourceClassAndPropertyDefinitions } from "../../../.."; +import { IResourceClassAndPropertyDefinitions, ResourcePropertyDefinition } from "../../../.."; import { MockOntology } from "../../../../../test/data/api/v2/mockOntology"; import { PropertyDefinition } from "../../ontologies/property-definition"; import { EqualsOperator } from "../values/search/comparison-operator"; @@ -45,7 +45,7 @@ CONSTRUCT { const integerValueLiteral = new IntegerValueLiteral(5); - const searchIntegerValue = new SearchIntegerValue(hasIntegerProp, integerValueLiteral, new EqualsOperator()); + const searchIntegerValue = new SearchIntegerValue(hasIntegerProp as ResourcePropertyDefinition, integerValueLiteral, new EqualsOperator()); const searchResource = new SearchResource(anythingThing, [searchIntegerValue]); diff --git a/src/models/v2/resources/search/gravsearch-generator.ts b/src/models/v2/resources/search/gravsearch-generator.ts index 98e89acdf..73e636d4a 100644 --- a/src/models/v2/resources/search/gravsearch-generator.ts +++ b/src/models/v2/resources/search/gravsearch-generator.ts @@ -1,6 +1,6 @@ import { Constants } from "../../Constants"; import { AndExpression, Expression, OrExpression } from "../values/search/expression"; -import { SearchValue } from "../values/search/search-value"; +import { SearchValue, ValueLiteral } from "../values/search/search-value"; import { SearchResource } from "./search-resource"; export namespace GravsearchGenerator { @@ -33,24 +33,36 @@ export namespace GravsearchGenerator { const propValue = `?propVal${index}`; - // TODO: make sure it is a non-linking property - if (prop instanceof SearchValue) { // add statement from resource to value object statements.push(`?mainRes <${prop.property.id}> ${propValue} .`); - // add statement from value object to value literal variable + if (!prop.property.isLinkProperty) { + // not a link property + + // add statement from value object to value literal variable + const objectType: string = prop.property.objectType as string; + const propValueLiteral = `${propValue}Literal`; + + // @ts-ignore + statements.push(`${propValue} <${complexTypeToProp[objectType]}> ${propValueLiteral}`); + + // add to FILTER + // TODO: check that comparison operator is not EXISTS + const comparisonOperator = prop.comparisonOperator.type; - const objectType: string = prop.property.objectType as string; - const propValueLiteral = `${propValue}Literal`; + let valueLiteral: ValueLiteral; - // @ts-ignore - statements.push(`${propValue} <${complexTypeToProp[objectType]}> ${propValueLiteral}`); + if (prop.value !== undefined && prop.value instanceof ValueLiteral) { + valueLiteral = prop.value; + } else { + throw new Error("Value is expected to be a literal."); + } - // add to FILTER - // TODO: check that comparison operator is not EXISTS - const comparisonOperator = prop.comparisonOperator.type; - filters.push(`FILTER(${propValueLiteral} ${comparisonOperator} "${prop.valueLiteral!.getValue()}"^^<${prop.valueLiteral!.xsdType}>)`); + filters.push(`FILTER(${propValueLiteral} ${comparisonOperator} "${valueLiteral.getValue()}"^^<${prop.value!.xsdType}>)`); + } else { + // link property + } } else if (prop instanceof AndExpression) { // handle logical AND diff --git a/src/models/v2/resources/values/search/search-integer-value.ts b/src/models/v2/resources/values/search/search-integer-value.ts index 0c42b213e..79db83e3d 100644 --- a/src/models/v2/resources/values/search/search-integer-value.ts +++ b/src/models/v2/resources/values/search/search-integer-value.ts @@ -1,9 +1,12 @@ +import { ResourcePropertyDefinition } from "../../../../.."; import { Constants } from "../../../Constants"; -import { PropertyDefinition } from "../../../ontologies/property-definition"; import { - EqualsOperator, ExistsOperator, - GreaterThanEqualsOperator, GreaterThanOperator, - LessThanEqualsOperator, LessThanOperator, + EqualsOperator, + ExistsOperator, + GreaterThanEqualsOperator, + GreaterThanOperator, + LessThanEqualsOperator, + LessThanOperator, NotEqualsOperator } from "./comparison-operator"; import { SearchValue, ValueLiteral } from "./search-value"; @@ -23,7 +26,7 @@ export class IntegerValueLiteral extends ValueLiteral { export class SearchIntegerValue extends SearchValue { - constructor(public property: PropertyDefinition, public valueLiteral: IntegerValueLiteral, public comparisonOperator: EqualsOperator | NotEqualsOperator + constructor(public property: ResourcePropertyDefinition, public value: IntegerValueLiteral, public comparisonOperator: EqualsOperator | NotEqualsOperator | GreaterThanEqualsOperator | LessThanEqualsOperator | GreaterThanOperator | LessThanOperator | ExistsOperator) { diff --git a/src/models/v2/resources/values/search/search-value.ts b/src/models/v2/resources/values/search/search-value.ts index 48195b2b9..4a57071f0 100644 --- a/src/models/v2/resources/values/search/search-value.ts +++ b/src/models/v2/resources/values/search/search-value.ts @@ -1,4 +1,5 @@ -import { PropertyDefinition } from "../../../ontologies/property-definition"; +import { ResourcePropertyDefinition } from "../../../../.."; +import { SearchResource } from "../../search/search-resource"; import { ComparisonOperator } from "./comparison-operator"; import { Expression } from "./expression"; @@ -14,10 +15,10 @@ export abstract class ValueLiteral { export abstract class SearchValue extends Expression { - abstract property: PropertyDefinition; + abstract property: ResourcePropertyDefinition; abstract comparisonOperator: ComparisonOperator; - abstract valueLiteral?: ValueLiteral; + abstract value?: ValueLiteral | SearchResource; } From 56d41151c186b19ef2561555e90e2a84f9d207f5 Mon Sep 17 00:00:00 2001 From: Tobias Schweizer Date: Tue, 7 Jan 2020 15:55:05 +0100 Subject: [PATCH 6/6] feature (search): make a group pattern that can be combined with the UNION keyword --- .../search/gravsearch-generator.spec.ts | 5 +++-- .../resources/search/gravsearch-generator.ts | 9 +++++---- .../v2/resources/search/search-resource.ts | 4 ++-- .../v2/resources/values/search/expression.ts | 18 ------------------ .../values/search/filter-expression.ts | 18 ++++++++++++++++++ .../resources/values/search/group-pattern.ts | 9 +++++++++ .../v2/resources/values/search/search-value.ts | 4 ++-- 7 files changed, 39 insertions(+), 28 deletions(-) delete mode 100644 src/models/v2/resources/values/search/expression.ts create mode 100644 src/models/v2/resources/values/search/filter-expression.ts create mode 100644 src/models/v2/resources/values/search/group-pattern.ts diff --git a/src/models/v2/resources/search/gravsearch-generator.spec.ts b/src/models/v2/resources/search/gravsearch-generator.spec.ts index 8f3eadceb..a60b37039 100644 --- a/src/models/v2/resources/search/gravsearch-generator.spec.ts +++ b/src/models/v2/resources/search/gravsearch-generator.spec.ts @@ -2,6 +2,7 @@ import { IResourceClassAndPropertyDefinitions, ResourcePropertyDefinition } from import { MockOntology } from "../../../../../test/data/api/v2/mockOntology"; import { PropertyDefinition } from "../../ontologies/property-definition"; import { EqualsOperator } from "../values/search/comparison-operator"; +import { GroupPattern } from "../values/search/group-pattern"; import { IntegerValueLiteral, SearchIntegerValue } from "../values/search/search-integer-value"; import { GravsearchGenerator } from "./gravsearch-generator"; import { SearchResource } from "./search-resource"; @@ -47,11 +48,11 @@ CONSTRUCT { const searchIntegerValue = new SearchIntegerValue(hasIntegerProp as ResourcePropertyDefinition, integerValueLiteral, new EqualsOperator()); - const searchResource = new SearchResource(anythingThing, [searchIntegerValue]); + const searchResource = new SearchResource(anythingThing, [new GroupPattern([searchIntegerValue])]); const gravsearchQuery = GravsearchGenerator.generateGravsearchQuery(searchResource); - // console.log(gravsearchQuery); + console.log(gravsearchQuery); }); diff --git a/src/models/v2/resources/search/gravsearch-generator.ts b/src/models/v2/resources/search/gravsearch-generator.ts index 73e636d4a..3e4831737 100644 --- a/src/models/v2/resources/search/gravsearch-generator.ts +++ b/src/models/v2/resources/search/gravsearch-generator.ts @@ -1,5 +1,6 @@ import { Constants } from "../../Constants"; -import { AndExpression, Expression, OrExpression } from "../values/search/expression"; +import { FilterAndExpression, FilterExpression, FilterOrExpression } from "../values/search/filter-expression"; +import { GroupPattern } from "../values/search/group-pattern"; import { SearchValue, ValueLiteral } from "../values/search/search-value"; import { SearchResource } from "./search-resource"; @@ -29,7 +30,7 @@ export namespace GravsearchGenerator { // create statements for non-linking properties (restrictions) searchResource.properties.forEach( - (prop: Expression, index: number) => { + (prop: GroupPattern, index: number) => { const propValue = `?propVal${index}`; @@ -63,10 +64,10 @@ export namespace GravsearchGenerator { } else { // link property } - } else if (prop instanceof AndExpression) { + } else if (prop instanceof FilterAndExpression) { // handle logical AND - } else if (prop instanceof OrExpression) { + } else if (prop instanceof FilterOrExpression) { // handle logical OR } else { diff --git a/src/models/v2/resources/search/search-resource.ts b/src/models/v2/resources/search/search-resource.ts index 90d845ff1..8da7b79ef 100644 --- a/src/models/v2/resources/search/search-resource.ts +++ b/src/models/v2/resources/search/search-resource.ts @@ -1,7 +1,7 @@ -import { Expression } from "../values/search/expression"; +import { GroupPattern } from "../values/search/group-pattern"; export class SearchResource { - constructor(public type?: string, public properties: Expression[] = []) {} + constructor(public type?: string, public properties: GroupPattern[] = []) {} } diff --git a/src/models/v2/resources/values/search/expression.ts b/src/models/v2/resources/values/search/expression.ts deleted file mode 100644 index f84b3ee90..000000000 --- a/src/models/v2/resources/values/search/expression.ts +++ /dev/null @@ -1,18 +0,0 @@ -export abstract class Expression { -} - -export class AndExpression extends Expression { - - leftArg: Expression; - - rightArg: Expression; - -} - -export class OrExpression extends Expression { - - leftArg: Expression; - - rightArg: Expression; - -} diff --git a/src/models/v2/resources/values/search/filter-expression.ts b/src/models/v2/resources/values/search/filter-expression.ts new file mode 100644 index 000000000..89bcd8432 --- /dev/null +++ b/src/models/v2/resources/values/search/filter-expression.ts @@ -0,0 +1,18 @@ +export abstract class FilterExpression { +} + +export class FilterAndExpression extends FilterExpression { + + leftArg: FilterExpression; + + rightArg: FilterExpression; + +} + +export class FilterOrExpression extends FilterExpression { + + leftArg: FilterExpression; + + rightArg: FilterExpression; + +} diff --git a/src/models/v2/resources/values/search/group-pattern.ts b/src/models/v2/resources/values/search/group-pattern.ts new file mode 100644 index 000000000..31b44d656 --- /dev/null +++ b/src/models/v2/resources/values/search/group-pattern.ts @@ -0,0 +1,9 @@ +import { FilterExpression } from "./filter-expression"; + +export class GroupPattern { + + constructor(public filterExpression: FilterExpression[] = []) { + + } + +} diff --git a/src/models/v2/resources/values/search/search-value.ts b/src/models/v2/resources/values/search/search-value.ts index 4a57071f0..49cf0fcd2 100644 --- a/src/models/v2/resources/values/search/search-value.ts +++ b/src/models/v2/resources/values/search/search-value.ts @@ -1,7 +1,7 @@ import { ResourcePropertyDefinition } from "../../../../.."; import { SearchResource } from "../../search/search-resource"; import { ComparisonOperator } from "./comparison-operator"; -import { Expression } from "./expression"; +import { FilterExpression } from "./filter-expression"; export abstract class ValueLiteral { @@ -13,7 +13,7 @@ export abstract class ValueLiteral { } -export abstract class SearchValue extends Expression { +export abstract class SearchValue extends FilterExpression { abstract property: ResourcePropertyDefinition;