Skip to content

Commit

Permalink
feat: Implement initial subquery implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
jagzmz committed Sep 3, 2024
1 parent ca45a3e commit 8ad392f
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 1 deletion.
9 changes: 9 additions & 0 deletions src/storage/FindOptionsTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,18 @@ export interface FindOptionsWhere {
[k: string]: FindOptionsWhereField | undefined;
}

// Add these new types
export interface SubQuery {
select: Variable[];
where: FindOptionsWhere;
groupBy?: string[];
having?: FindOptionsWhere;
}

export interface FindAllOptions extends FindOneOptions {
offset?: number;
limit?: number;
subQueries?: SubQuery[];
}

export interface FindExistsOptions {
Expand Down
33 changes: 33 additions & 0 deletions src/storage/query-adapter/sparql/SparqlQueryBuilder.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
/* eslint-disable max-len */
/* eslint-disable id-length */
/* eslint-disable arrow-parens */
/* eslint-disable @typescript-eslint/explicit-function-return-type */
import DataFactory from '@rdfjs/data-model';
import type { Variable, NamedNode, Term, Literal } from '@rdfjs/types';
import type {
Expand All @@ -13,6 +17,8 @@ import type {
Pattern,
ConstructQuery,
GraphPattern,
Grouping,
SelectQuery,
} from 'sparqljs';
import {
allTypesAndSuperTypesPath,
Expand Down Expand Up @@ -59,6 +65,7 @@ import type {
FindOptionsWhere,
FindOptionsWhereField,
IdFindOptionsWhereField,
SubQuery,
TypeFindOptionsWhereField,
ValueWhereFieldObject,
} from '../../FindOptionsTypes';
Expand Down Expand Up @@ -104,6 +111,7 @@ export interface SparqlQueryBuilderOptions {
select?: FindOptionsSelect;
order?: FindOptionsOrder;
relations?: FindOptionsRelations;
subQueries?: SubQuery[];
}

export class SparqlQueryBuilder {
Expand All @@ -121,6 +129,11 @@ export class SparqlQueryBuilder {
const whereQueryData = this.createWhereQueryData(subject, options?.where, true);
const orderQueryData = this.createOrderQueryData(subject, options?.order);
const relationsQueryData = this.createRelationsQueryData(subject, relations);
// Handle subqueries
if (options?.subQueries && options.subQueries.length > 0) {
const subQueryPatterns = this.createSubQueryPatterns(options.subQueries);
whereQueryData.values.unshift(...subQueryPatterns as ValuesPattern[]);
}
const patterns: Pattern[] = whereQueryData.values;
if (whereQueryData.triples.length === 0 && (
whereQueryData.filters.length > 0 ||
Expand Down Expand Up @@ -163,6 +176,26 @@ export class SparqlQueryBuilder {
};
}

private createSubQueryPatterns(subQueries: SubQuery[]): Pattern[] {
return subQueries.map((subQuery: SubQuery): Pattern => {
const subQueryWhere = this.createWhereQueryData(entityVariable, subQuery.where);
const selectQuery: SelectQuery = {
type: 'query',
queryType: 'SELECT',
variables: subQuery.select,
where: this.createWherePatternsFromQueryData(
subQueryWhere.values,
subQueryWhere.triples,
subQueryWhere.filters,
),
group: subQuery.groupBy ? subQuery.groupBy.map((g) => ({ expression: DataFactory.variable(g) } as Grouping)) : undefined,
having: subQuery.having ? this.createWhereQueryData(entityVariable, subQuery.having).filters : undefined,
prefixes: {},
};
return createSparqlSelectGroup([ selectQuery ]);
});
}

private createEntityGraphFilterPattern(subject: Variable): GraphPattern {
const entityFilterTriple = { subject, predicate: this.createVariable(), object: this.createVariable() };
return createSparqlGraphPattern(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -706,7 +706,7 @@ describe('a SparqlQueryAdapter', (): void => {
await adapter.findAll({
where: {
type: 'https://schema.org/Place',
'https://standardknowledge.com/ontologies/core/deduplicationGroup': '?deduplicationGroup'
'https://standardknowledge.com/ontologies/core/deduplicationGroup': '?deduplicationGroup',
},
group: DataFactory.variable('deduplicationGroup'),
entitySelectVariable: {
Expand Down Expand Up @@ -736,6 +736,46 @@ describe('a SparqlQueryAdapter', (): void => {
});
});

it('executes a subquery.', async(): Promise<void> => {
await adapter.findAll({
where: {
type: 'https://schema.org/Place',
},
subQueries: [
{
select: [ DataFactory.variable('deduplicationGroup'), {
variable: DataFactory.variable('entity'),
expression: {
type: 'aggregate',
aggregation: 'MIN',
expression: DataFactory.variable('entity'),
},
}],
where: {
'https://standardknowledge.com/ontologies/core/deduplicationGroup': '?deduplicationGroup',
},
groupBy: [ 'deduplicationGroup' ],
},
],
});
expect(select.mock.calls[0][0].split('\n')).toEqual([
'CONSTRUCT { ?subject ?predicate ?object. }',
'WHERE {',
' {',
' SELECT DISTINCT ?entity WHERE {',
' {',
' SELECT ?deduplicationGroup (MIN(?entity) AS ?entity) WHERE { ?entity <https://standardknowledge.com/ontologies/core/deduplicationGroup> ?deduplicationGroup. }',
' GROUP BY ?deduplicationGroup',
' }',
' ?entity (<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>/(<http://www.w3.org/2000/01/rdf-schema#subClassOf>*)) <https://schema.org/Place>.',
' FILTER(EXISTS { GRAPH ?entity { ?entity ?c1 ?c2. } })',
' }',
' }',
' GRAPH ?entity { ?subject ?predicate ?object. }',
'}',
]);
});

describe('findAllBy', (): void => {
it('queries for entities and returns an empty array if there are no results.',
async(): Promise<void> => {
Expand Down

0 comments on commit 8ad392f

Please sign in to comment.