Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Internal Representation of a Search Query #133

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/api/v2/search/search-endpoint.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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", () => {
Expand Down
2 changes: 1 addition & 1 deletion src/api/v2/search/search-endpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
35 changes: 35 additions & 0 deletions src/models/v2/Constants.ts
Original file line number Diff line number Diff line change
@@ -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 = "#";
Expand Down Expand Up @@ -105,18 +106,21 @@ 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";
static SystemAdminGroupIRI: string = Constants.KnoraAdmin + Constants.Delimiter + "SystemAdmin";
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";
Expand All @@ -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";
Expand All @@ -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";
}
2 changes: 1 addition & 1 deletion src/models/v2/resources/ResourcesConversionUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { JsonObject, JsonProperty } from "json2typescript";
import { Constants } from "../Constants";
import { Constants } from "../../Constants";

@JsonObject("CountQueryResponse")
export class CountQueryResponse {
Expand Down
59 changes: 59 additions & 0 deletions src/models/v2/resources/search/gravsearch-generator.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
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";

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: <http://api.knora.org/ontology/knora-api/v2#>
CONSTRUCT {

?mainRes knora-api:isMainResource true .

} WHERE {

?mainRes a knora-api:Resource .

?mainRes a <http://0.0.0.0:3333/ontology/0001/anything/v2#Thing> .



}
`;

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 as ResourcePropertyDefinition, integerValueLiteral, new EqualsOperator());

const searchResource = new SearchResource(anythingThing, [new GroupPattern([searchIntegerValue])]);

const gravsearchQuery = GravsearchGenerator.generateGravsearchQuery(searchResource);

console.log(gravsearchQuery);

});

});
101 changes: 101 additions & 0 deletions src/models/v2/resources/search/gravsearch-generator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { Constants } from "../../Constants";
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";

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 non-linking properties (restrictions)
searchResource.properties.forEach(
(prop: GroupPattern, index: number) => {

const propValue = `?propVal${index}`;

if (prop instanceof SearchValue) {
// add statement from resource to value object
statements.push(`?mainRes <${prop.property.id}> ${propValue} .`);

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;

let valueLiteral: ValueLiteral;

if (prop.value !== undefined && prop.value instanceof ValueLiteral) {
valueLiteral = prop.value;
} else {
throw new Error("Value is expected to be a literal.");
}

filters.push(`FILTER(${propValueLiteral} ${comparisonOperator} "${valueLiteral.getValue()}"^^<${prop.value!.xsdType}>)`);
} else {
// link property
}
} else if (prop instanceof FilterAndExpression) {
// handle logical AND

} else if (prop instanceof FilterOrExpression) {
// handle logical OR

} else {
throw new Error("Unknown Expression " + prop);
}

}
);

const gravsearchQuery = `
PREFIX knora-api: <http://api.knora.org/ontology/knora-api/v2#>
CONSTRUCT {

?mainRes knora-api:isMainResource true .

} WHERE {

?mainRes a knora-api:Resource .

${mainResourceClass}

${statements.join("\n")}
${filters.join("\n")}
}
`;

return gravsearchQuery;

};

}
7 changes: 7 additions & 0 deletions src/models/v2/resources/search/search-resource.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { GroupPattern } from "../values/search/group-pattern";

export class SearchResource {

constructor(public type?: string, public properties: GroupPattern[] = []) {}

}
82 changes: 82 additions & 0 deletions src/models/v2/resources/values/search/comparison-operator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { Constants } from "../../../Constants";

export abstract class ComparisonOperator {

abstract type: string;

abstract label: string;

}

export class EqualsOperator extends ComparisonOperator {

type = Constants.EqualsComparisonOperator;

label = Constants.EqualsComparisonLabel;

}

export class NotEqualsOperator extends ComparisonOperator {

type = Constants.NotEqualsComparisonOperator;

label = Constants.NotEqualsComparisonLabel;

}

export class GreaterThanEqualsOperator extends ComparisonOperator {

type = Constants.GreaterThanEqualsComparisonOperator;

label = Constants.GreaterThanEqualsComparisonLabel;

}

export class GreaterThanOperator extends ComparisonOperator {

type = Constants.GreaterThanComparisonOperator;

label = Constants.GreaterThanComparisonLabel;

}

export class LessThanOperator extends ComparisonOperator {

type = Constants.LessThanComparisonOperator;

label = Constants.LessThanComparisonLabel;

}

export class LessThanEqualsOperator implements ComparisonOperator {

type = Constants.LessThanEqualsComparisonOperator;

label = Constants.LessThanQualsComparisonLabel;

}


export class ExistsOperator implements ComparisonOperator {

type = Constants.ExistsComparisonOperator;

label = Constants.ExistsComparisonLabel;

}

export class LikeOperator implements ComparisonOperator {

type = Constants.LikeComparisonOperator;

label = Constants.LikeComparisonLabel;

}

export class MatchOperator implements ComparisonOperator {

type = Constants.MatchComparisonOperator;

label = Constants.MatchComparisonLabel;

}
18 changes: 18 additions & 0 deletions src/models/v2/resources/values/search/filter-expression.ts
Original file line number Diff line number Diff line change
@@ -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;

}
Loading