Skip to content
Snippets Groups Projects
Commit 604dc9a8 authored by esikkala's avatar esikkala
Browse files

Use facet priorities, refactor filter constructing

parent 27f32cda
No related branches found
No related tags found
No related merge requests found
......@@ -32,6 +32,7 @@ export const INITIAL_STATE = {
containerClass: 'one',
filterType: 'textFilter',
textFilter: null,
priority: 1
},
productionPlace: {
id: 'productionPlace',
......@@ -51,6 +52,7 @@ export const INITIAL_STATE = {
uriFilter: null,
spatialFilter: null,
type: 'hierarchical',
priority: 4
},
productionTimespan: {
id: 'productionTimespan',
......@@ -70,7 +72,8 @@ export const INITIAL_STATE = {
min: null,
max: null,
timespanFilter: null,
type: 'timespan'
type: 'timespan',
priority: 7
},
author: {
id: 'author',
......@@ -87,7 +90,8 @@ export const INITIAL_STATE = {
searchField: true,
containerClass: 'ten',
filterType: 'uriFilter',
uriFilter: null
uriFilter: null,
priority: 2
},
language: {
id: 'owner',
......@@ -104,7 +108,8 @@ export const INITIAL_STATE = {
searchField: true,
containerClass: 'ten',
filterType: 'uriFilter',
uriFilter: null
uriFilter: null,
priority: 5
},
collection: {
id: 'collection',
......@@ -121,7 +126,8 @@ export const INITIAL_STATE = {
searchField: true,
containerClass: 'ten',
filterType: 'uriFilter',
uriFilter: null
uriFilter: null,
priority: 6
},
owner: {
id: 'owner',
......@@ -138,7 +144,8 @@ export const INITIAL_STATE = {
searchField: true,
containerClass: 'ten',
filterType: 'uriFilter',
uriFilter: null
uriFilter: null,
priority: 3
},
source: {
id: 'source',
......@@ -155,7 +162,8 @@ export const INITIAL_STATE = {
searchField: false,
containerClass: 'three',
filterType: 'uriFilter',
uriFilter: null
uriFilter: null,
priority: 8
},
}
};
......
......@@ -43,13 +43,9 @@ app.get(`${apiPath}/:resultClass/paginated`, async (req, res, next) => {
resultClass: req.params.resultClass,
page: parseInt(req.query.page) || null,
pagesize: parseInt(req.query.pagesize) || null,
uriFilters: req.query.uriFilters == null ? null : JSON.parse(req.query.uriFilters),
spatialFilters: req.query.spatialFilters == null ? null : JSON.parse(req.query.spatialFilters),
textFilters: req.query.textFilters == null ? null : JSON.parse(req.query.textFilters),
timespanFilters: req.query.timespanFilters == null ? null : JSON.parse(req.query.timespanFilters),
sortBy: req.query.sortBy || null,
sortDirection: req.query.sortDirection || null,
constraints: req.query.constraints
constraints: req.query.constraints == null ? null : JSON.parse(req.query.constraints),
});
res.json(data);
} catch(error) {
......@@ -62,10 +58,7 @@ app.get(`${apiPath}/:resultClass/all`, async (req, res, next) => {
const data = await getAllResults({
resultClass: req.params.resultClass,
facetClass: req.query.facetClass || null,
uriFilters: req.query.uriFilters == null ? null : JSON.parse(req.query.uriFilters),
spatialFilters: req.query.spatialFilters == null ? null : JSON.parse(req.query.spatialFilters),
textFilters: req.query.textFilters == null ? null : JSON.parse(req.query.textFilters),
timespanFilters: req.query.timespanFilters == null ? null : JSON.parse(req.query.timespanFilters),
constraints: req.query.constraints == null ? null : JSON.parse(req.query.constraints),
variant: req.query.variant || null,
});
res.json({
......@@ -80,10 +73,7 @@ app.get(`${apiPath}/:resultClass/count`, async (req, res, next) => {
try {
const count = await getResultCount({
resultClass: req.params.resultClass,
uriFilters: req.query.uriFilters == null ? null : JSON.parse(req.query.uriFilters),
spatialFilters: req.query.spatialFilters == null ? null : JSON.parse(req.query.spatialFilters),
textFilters: req.query.textFilters == null ? null : JSON.parse(req.query.textFilters),
timespanFilters: req.query.timespanFilters == null ? null : JSON.parse(req.query.timespanFilters),
constraints: req.query.constraints == null ? null : JSON.parse(req.query.constraints),
});
res.json({ count });
} catch(error) {
......@@ -96,10 +86,7 @@ app.get(`${apiPath}/:resultClass/instance/:uri`, async (req, res, next) => {
const data = await getByURI({
resultClass: req.params.resultClass,
facetClass: req.query.facetClass || null,
uriFilters: req.query.uriFilters == null ? null : JSON.parse(req.query.uriFilters),
spatialFilters: req.query.spatialFilters == null ? null : JSON.parse(req.query.spatialFilters),
textFilters: req.query.textFilters == null ? null : JSON.parse(req.query.textFilters),
timespanFilters: req.query.timespanFilters == null ? null : JSON.parse(req.query.timespanFilters),
constraints: req.query.constraints == null ? null : JSON.parse(req.query.constraints),
variant: req.query.variant || null,
uri: req.params.uri
});
......@@ -117,10 +104,7 @@ app.get(`${apiPath}/:facetClass/facet/:id`, async (req, res, next) => {
facetID: req.params.id,
sortBy: req.query.sortBy || null,
sortDirection: req.query.sortDirection || null,
uriFilters: req.query.uriFilters == null ? null : JSON.parse(req.query.uriFilters),
spatialFilters: req.query.spatialFilters == null ? null : JSON.parse(req.query.spatialFilters),
textFilters: req.query.textFilters == null ? null : JSON.parse(req.query.textFilters),
timespanFilters: req.query.timespanFilters == null ? null : JSON.parse(req.query.timespanFilters),
constraints: req.query.constraints == null ? null : JSON.parse(req.query.constraints),
});
res.json(data);
} catch(error) {
......
......@@ -15,18 +15,14 @@ import { facetConfigs } from './FacetConfigs';
import { mapCount } from './Mappers';
import { makeObjectList } from './SparqlObjectMapper';
import {
generateFilter,
hasFilters
generateConstraintsBlock,
} from './Filters';
export const getPaginatedResults = async ({
resultClass,
page,
pagesize,
uriFilters,
spatialFilters,
textFilters,
timespanFilters,
constraints,
sortBy,
sortDirection
}) => {
......@@ -34,10 +30,7 @@ export const getPaginatedResults = async ({
resultClass,
page,
pagesize,
uriFilters,
spatialFilters,
textFilters,
timespanFilters,
constraints,
sortBy,
sortDirection
});
......@@ -51,10 +44,7 @@ export const getPaginatedResults = async ({
export const getAllResults = ({
// resultClass, // TODO: handle other classes than manuscripts
facetClass,
uriFilters,
spatialFilters,
textFilters,
timespanFilters,
constraints,
variant
}) => {
let q = '';
......@@ -77,21 +67,12 @@ export const getAllResults = ({
filterTarget = 'manuscript__id';
break;
}
const hasActiveFilters = hasFilters({
uriFilters,
spatialFilters,
textFilters,
timespanFilters,
});
if (!hasActiveFilters) {
if (constraints == null) {
q = q.replace('<FILTER>', '# no filters');
} else {
q = q.replace('<FILTER>', generateFilter({
q = q.replace('<FILTER>', generateConstraintsBlock({
facetClass: facetClass,
uriFilters: uriFilters,
spatialFilters: spatialFilters,
textFilters: textFilters,
timespanFilters: timespanFilters,
constraints: constraints,
filterTarget: filterTarget,
facetID: null
}));
......@@ -104,29 +85,17 @@ export const getAllResults = ({
export const getResultCount = ({
resultClass,
uriFilters,
spatialFilters,
textFilters,
timespanFilters,
constraints
}) => {
let q = countQuery;
q = q.replace('<FACET_CLASS>', facetConfigs[resultClass].facetClass);
const hasActiveFilters = hasFilters({
uriFilters,
spatialFilters,
textFilters,
timespanFilters,
});
if (!hasActiveFilters) {
if (constraints == null) {
q = q.replace('<FILTER>', '# no filters');
} else {
q = q.replace('<FILTER>', generateFilter({
q = q.replace('<FILTER>', generateConstraintsBlock({
resultClass: resultClass,
facetClass: resultClass,
uriFilters: uriFilters,
spatialFilters: spatialFilters,
textFilters: textFilters,
timespanFilters: timespanFilters,
constraints: constraints,
filterTarget: 'id',
facetID: null
}));
......@@ -138,31 +107,19 @@ const getPaginatedData = ({
resultClass,
page,
pagesize,
uriFilters,
spatialFilters,
textFilters,
timespanFilters,
constraints,
sortBy,
sortDirection
}) => {
let q = facetResultSetQuery;
const facetConfig = facetConfigs[resultClass];
const hasActiveFilters = hasFilters({
uriFilters,
spatialFilters,
textFilters,
timespanFilters,
});
if (!hasActiveFilters) {
if (constraints == null) {
q = q.replace('<FILTER>', '# no filters');
} else {
q = q.replace('<FILTER>', generateFilter({
q = q.replace('<FILTER>', generateConstraintsBlock({
resultClass: resultClass,
facetClass: resultClass,
uriFilters: uriFilters,
spatialFilters: spatialFilters,
textFilters: textFilters,
timespanFilters: timespanFilters,
constraints: constraints,
filterTarget: 'id',
facetID: null}));
}
......@@ -206,17 +163,14 @@ const getPaginatedData = ({
resultSetProperties = '';
}
q = q.replace('<RESULT_SET_PROPERTIES>', resultSetProperties);
console.log(prefixes + q)
// console.log(prefixes + q)
return runSelectQuery(prefixes + q, endpoint, makeObjectList);
};
export const getByURI = ({
resultClass,
facetClass,
uriFilters,
spatialFilters,
textFilters,
timespanFilters,
constraints,
//variant,
uri
}) => {
......@@ -226,22 +180,13 @@ export const getByURI = ({
q = placeQuery;
break;
}
const hasActiveFilters = hasFilters({
uriFilters,
spatialFilters,
textFilters,
timespanFilters,
});
if (!hasActiveFilters) {
if (constraints == null) {
q = q.replace('<FILTER>', '# no filters');
} else {
q = q.replace('<FILTER>', generateFilter({
q = q.replace('<FILTER>', generateConstraintsBlock({
resultClass: resultClass,
facetClass: facetClass,
uriFilters: uriFilters,
spatialFilters: spatialFilters,
textFilters: textFilters,
timespanFilters: timespanFilters,
constraints: constraints,
filterTarget: 'manuscript__id',
facetID: null}));
}
......
import { has } from 'lodash';
import { runSelectQuery } from './SparqlApi';
import {
endpoint,
......@@ -8,8 +7,10 @@ import {
import { prefixes } from './SparqlQueriesPrefixes';
import { facetConfigs } from './FacetConfigs';
import {
hasFilters,
generateFilter,
hasPreviousSelections,
hasPreviousSelectionsFromOtherFacets,
getUriFilters,
generateConstraintsBlock,
generateSelectedFilter
} from './Filters';
import {
......@@ -23,10 +24,7 @@ export const getFacet = ({
facetID,
sortBy,
sortDirection,
uriFilters,
spatialFilters,
textFilters,
timespanFilters,
constraints,
}) => {
const facetConfig = facetConfigs[facetClass][facetID];
// choose query template and result mapper:
......@@ -53,42 +51,29 @@ export const getFacet = ({
let selectedNoHitsBlock = '# no filters from other facets';
let filterBlock = '# no filters';
let parentBlock = '# no parents';
const hasActiveFilters = hasFilters({
uriFilters,
spatialFilters,
textFilters,
timespanFilters,
});
if (hasActiveFilters) {
filterBlock = generateFilter({
if (constraints !== null) {
filterBlock = generateConstraintsBlock({
facetClass: facetClass,
uriFilters: uriFilters,
spatialFilters: spatialFilters,
textFilters: textFilters,
timespanFilters: timespanFilters,
constraints: constraints,
filterTarget: 'instance',
facetID: facetID,
inverse: false,
});
}
// if this facet has previous selections, include them in the query
if (uriFilters !== null && has(uriFilters, facetID)) {
selectedBlock = generateSelectedBlock({
facetID,
uriFilters
});
/*
if there are also filters from other facets, we need this
additional block for facet values that return 0 hits
*/
if (Object.keys(uriFilters).length > 1) {
selectedNoHitsBlock = generateSelectedNoHitsBlock({
facetClass,
// if this facet has previous selections, include them in the query
if (hasPreviousSelections(constraints, facetID)) {
selectedBlock = generateSelectedBlock({
facetID,
uriFilters,
spatialFilters,
textFilters
constraints
});
/* if there are also filters from other facets, we need this
additional block for facet values that return 0 hits */
if (hasPreviousSelectionsFromOtherFacets(constraints, facetID)) {
selectedNoHitsBlock = generateSelectedNoHitsBlock({
facetClass,
facetID,
constraints
});
}
}
}
if (facetConfig.type === 'hierarchical') {
......@@ -96,10 +81,7 @@ export const getFacet = ({
parentBlock = generateParentBlock({
facetClass,
facetID,
uriFilters,
spatialFilters,
textFilters,
timespanFilters,
constraints,
parentPredicate
});
}
......@@ -127,10 +109,11 @@ export const getFacet = ({
const generateSelectedBlock = ({
facetID,
uriFilters,
constraints,
}) => {
const selectedFilter = generateSelectedFilter({
selectedValues: uriFilters[facetID],
facetID,
constraints,
inverse: false
});
return `
......@@ -144,17 +127,11 @@ const generateSelectedBlock = ({
const generateSelectedNoHitsBlock = ({
facetClass,
facetID,
uriFilters,
spatialFilters,
textFilters,
timespanFilters,
constraints
}) => {
const noHitsFilter = generateFilter({
const noHitsFilter = generateConstraintsBlock({
facetClass: facetClass,
uriFilters: uriFilters,
spatialFilters: spatialFilters,
textFilters: textFilters,
timespanFilters: timespanFilters,
constraints: constraints,
filterTarget: 'instance',
facetID: facetID,
inverse: true,
......@@ -163,7 +140,7 @@ const generateSelectedNoHitsBlock = ({
UNION
{
# facet values that have been selected but return no results
VALUES ?id { <${uriFilters[facetID].join('> <')}> }
VALUES ?id { <${getUriFilters(constraints, facetID).join('> <')}> }
${noHitsFilter}
BIND(true AS ?selected_)
}
......@@ -173,28 +150,26 @@ const generateSelectedNoHitsBlock = ({
const generateParentBlock = ({
facetClass,
facetID,
uriFilters,
spatialFilters,
textFilters,
timespanFilters,
constraints,
parentPredicate
}) => {
const parentFilterStr = generateFilter({
facetClass: facetClass,
uriFilters: uriFilters,
spatialFilters: spatialFilters,
textFilters: textFilters,
timespanFilters: timespanFilters,
filterTarget: 'instance2',
facetID: facetID,
inverse: false
});
let ignoreSelectedValues = '';
if (uriFilters !== null && has(uriFilters, facetID)) {
ignoreSelectedValues = generateSelectedFilter({
selectedValues:uriFilters[facetID],
inverse: true
let parentFilterStr = '# no filters';
let ignoreSelectedValues = '# no selected values';
if (constraints !== null) {
parentFilterStr = generateConstraintsBlock({
facetClass: facetClass,
constraints: constraints,
filterTarget: 'instance2',
facetID: facetID,
inverse: false
});
if (hasPreviousSelections) {
ignoreSelectedValues = generateSelectedFilter({
facetID: facetID,
constraints: constraints,
inverse: true
});
}
}
return `
UNION
......
import { facetConfigs } from './FacetConfigs';
export const hasFilters = ({
uriFilters,
spatialFilters,
textFilters,
timespanFilters,
}) => {
return uriFilters !== null
|| spatialFilters !== null
|| textFilters !== null
|| timespanFilters !== null;
export const hasPreviousSelections = (constraints, facetID) => {
let hasPreviousSelections = false;
for (const [key, value] of Object.entries(constraints)) {
if (key === facetID && value.filterType === 'uriFilter') {
hasPreviousSelections = true;
}
}
return hasPreviousSelections;
};
export const hasPreviousSelectionsFromOtherFacets = (constraints, facetID) => {
for (const [key, value] of Object.entries(constraints)) {
if (key !== facetID && value.filterType === 'uriFilter') {
return true;
}
}
return false;
};
export const getUriFilters = (constraints, facetID) => {
for (const [key, value] of Object.entries(constraints)) {
if (key === facetID && value.filterType === 'uriFilter') {
return value.values;
}
}
return [];
};
export const generateFilter = ({
export const generateConstraintsBlock = ({
facetClass,
uriFilters,
spatialFilters,
textFilters,
timespanFilters,
constraints,
filterTarget,
facetID,
inverse,
}) => {
delete constraints[facetID]; // use only constraints from other facets
let filterStr = '';
let facetProperty = facetID !== null ? facetID : '';
if (textFilters !== null) {
for (let property in textFilters) {
if (property !== facetProperty) {
const queryString = textFilters[property];
filterStr += `
?${filterTarget} text:query (${facetConfigs[facetClass][property].textQueryProperty} '${queryString}') .
`;
}
}
}
if (spatialFilters !== null) {
for (let property in spatialFilters) {
if (property !== facetProperty) {
const { latMin, longMin, latMax, longMax } = spatialFilters[property];
filterStr += `
?${property}Filter spatial:withinBox (${latMin} ${longMin} ${latMax} ${longMax} 1000000) .
?${filterTarget} ${facetConfigs[facetClass][property].predicate} ?${property}Filter .
`;
}
}
let constraintsArr = [];
for (const [key, value] of Object.entries(constraints)) {
constraintsArr.push({
id: key,
filterType: value.filterType,
priority: value.priority,
values: value.values,
});
}
if (timespanFilters !== null) {
for (let property in timespanFilters) {
if (property !== facetProperty) {
const facetConfig = facetConfigs[facetClass][property];
const { start, end } = timespanFilters[property];
const selectionStart = start;
const selectionEnd = end;
// filterStr += `
// ?${filterTarget} ${facetConfig.predicate} ?timespan .
// ?timespan ${facetConfig.startProperty} ?start .
// ?timespan ${facetConfig.endProperty} ?end .
// # both start and end is in selected range
// FILTER(?start >= "${start}"^^xsd:date)
// FILTER(?end <= "${end}"^^xsd:date)
// `;
filterStr += `
?${filterTarget} ${facetConfig.predicate} ?timespan .
?timespan ${facetConfig.startProperty} ?timespanStart .
?timespan ${facetConfig.endProperty} ?timespanEnd .
# either start or end is in selected range
FILTER(
?timespanStart >= "${selectionStart}"^^xsd:date && ?timespanStart <= "${selectionEnd}"^^xsd:date
||
?timespanEnd >= "${selectionStart}"^^xsd:date && ?timespanEnd <= "${selectionEnd}"^^xsd:date
)
`;
}
constraintsArr.sort((a, b) => a.priority - b.priority);
constraintsArr.map(c => {
switch (c.filterType) {
case 'textFilter':
filterStr += generateTextFilter({
facetClass: facetClass,
facetID: c.id,
filterTarget: filterTarget,
queryString: c.values
});
break;
case 'uriFilter':
filterStr += generateUriFilter({
facetClass: facetClass,
facetID: c.id,
filterTarget: filterTarget,
values: c.values,
inverse: inverse
});
break;
case 'spatialFilter':
filterStr += generateSpatialFilter({
facetClass: facetClass,
facetID: c.id,
filterTarget: filterTarget,
values: c.values,
});
break;
case 'timespanFilter':
filterStr += generateTimespanFilter({
facetClass: facetClass,
facetID: c.id,
filterTarget: filterTarget,
values: c.values,
});
break;
}
});
return filterStr;
};
const generateTextFilter = ({
facetClass,
facetID,
filterTarget,
queryString
}) => {
return `?${filterTarget} text:query (${facetConfigs[facetClass][facetID].textQueryProperty} '${queryString}') . `;
};
const generateSpatialFilter = ({
facetClass,
facetID,
filterTarget,
values
}) => {
const { latMin, longMin, latMax, longMax } = values;
return `
?${facetID}Filter spatial:withinBox (${latMin} ${longMin} ${latMax} ${longMax} 1000000) .
?${filterTarget} ${facetConfigs[facetClass][facetID].predicate} ?${facetID}Filter .
`;
};
const generateTimespanFilter = ({
facetClass,
facetID,
filterTarget,
values
}) => {
const facetConfig = facetConfigs[facetClass][facetID];
const { start, end } = values;
const selectionStart = start;
const selectionEnd = end;
// return `
// ?${filterTarget} ${facetConfig.predicate} ?timespan .
// ?timespan ${facetConfig.startProperty} ?start .
// ?timespan ${facetConfig.endProperty} ?end .
// # both start and end is in selected range
// FILTER(?start >= "${start}"^^xsd:date)
// FILTER(?end <= "${end}"^^xsd:date)
// `;
return `
?${filterTarget} ${facetConfig.predicate} ?timespan .
?timespan ${facetConfig.startProperty} ?timespanStart .
?timespan ${facetConfig.endProperty} ?timespanEnd .
# either start or end is in selected range
FILTER(
?timespanStart >= "${selectionStart}"^^xsd:date && ?timespanStart <= "${selectionEnd}"^^xsd:date
||
?timespanEnd >= "${selectionStart}"^^xsd:date && ?timespanEnd <= "${selectionEnd}"^^xsd:date
)
`;
};
const generateUriFilter = ({
facetClass,
facetID,
filterTarget,
values,
inverse
}) => {
let s = '';
let addChildren = facetConfigs[facetClass][facetID].type == 'hierarchical';
if (addChildren) {
s = `
VALUES ?${facetID}Filter { <${values.join('> <')}> }
?${facetID}FilterWithChildren gvp:broaderPreferred* ?${facetID}Filter .
`;
} else {
s = `
VALUES ?${facetID}Filter { <${values.join('> <')}> }
`;
}
if (uriFilters !== null) {
for (let property in uriFilters) {
// when filtering facet values, apply filters only from other facets
if (property !== facetProperty) {
let addChildren = facetConfigs[facetClass][property].type == 'hierarchical';
if (addChildren) {
filterStr += `
VALUES ?${property}Filter { <${uriFilters[property].join('> <')}> }
?${property}FilterWithChildren gvp:broaderPreferred* ?${property}Filter .
`;
} else {
filterStr += `
VALUES ?${property}Filter { <${uriFilters[property].join('> <')}> }
`;
}
if (inverse) {
filterStr += `
FILTER NOT EXISTS {
?${filterTarget} ${facetConfigs[facetClass][property].predicate} ?${property}Filter .
?${filterTarget} ${facetConfigs[facetClass][facetID].predicate} ?id .
}
`;
} else {
const filterValue = addChildren
? `?${property}FilterWithChildren`
: `?${property}Filter`;
filterStr += `
?${filterTarget} ${facetConfigs[facetClass][property].predicate} ${filterValue} .
`;
}
}
}
if (inverse) {
s += `
FILTER NOT EXISTS {
?${filterTarget} ${facetConfigs[facetClass][facetID].predicate} ?${facetID}Filter .
?${filterTarget} ${facetConfigs[facetClass][facetID].predicate} ?id .
}
`;
} else {
const filterValue = addChildren
? `?${facetID}FilterWithChildren`
: `?${facetID}Filter`;
s += `
?${filterTarget} ${facetConfigs[facetClass][facetID].predicate} ${filterValue} .
`;
}
return filterStr;
return s;
};
export const generateSelectedFilter = ({
selectedValues,
facetID,
constraints,
inverse
}) => {
return (`
FILTER(?id ${inverse ? 'NOT' : ''} IN ( <${selectedValues.join('>, <')}> ))
FILTER(?id ${inverse ? 'NOT' : ''} IN ( <${getUriFilters(constraints, facetID).join('>, <')}> ))
`);
};
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment