From 604dc9a81bbd0027fd583f46c54d31ef6865c225 Mon Sep 17 00:00:00 2001
From: esikkala <esko.ikkala@aalto.fi>
Date: Thu, 27 Jun 2019 12:01:58 +0300
Subject: [PATCH] Use facet priorities, refactor filter constructing

---
 src/client/reducers/manuscriptsFacets.js |  20 +-
 src/server/index.js                      |  26 +--
 src/server/sparql/FacetResults.js        |  95 ++------
 src/server/sparql/FacetValues.js         | 117 ++++------
 src/server/sparql/Filters.js             | 264 ++++++++++++++---------
 5 files changed, 251 insertions(+), 271 deletions(-)

diff --git a/src/client/reducers/manuscriptsFacets.js b/src/client/reducers/manuscriptsFacets.js
index 16058f3e..964eb520 100644
--- a/src/client/reducers/manuscriptsFacets.js
+++ b/src/client/reducers/manuscriptsFacets.js
@@ -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
     },
   }
 };
diff --git a/src/server/index.js b/src/server/index.js
index 25a5ef43..2d0a4168 100644
--- a/src/server/index.js
+++ b/src/server/index.js
@@ -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) {
diff --git a/src/server/sparql/FacetResults.js b/src/server/sparql/FacetResults.js
index 68815fb9..65b427d4 100644
--- a/src/server/sparql/FacetResults.js
+++ b/src/server/sparql/FacetResults.js
@@ -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}));
   }
diff --git a/src/server/sparql/FacetValues.js b/src/server/sparql/FacetValues.js
index ec2c81db..10f065ef 100644
--- a/src/server/sparql/FacetValues.js
+++ b/src/server/sparql/FacetValues.js
@@ -1,4 +1,3 @@
-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
diff --git a/src/server/sparql/Filters.js b/src/server/sparql/Filters.js
index b6e2a373..cb323cc4 100644
--- a/src/server/sparql/Filters.js
+++ b/src/server/sparql/Filters.js
@@ -1,120 +1,188 @@
 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('>, <')}> ))
   `);
 };
-- 
GitLab