diff --git a/src/client/epics/index.js b/src/client/epics/index.js index a4a98cf55924455b2962832ea238909fd41dad42..51931e6a25886571b429002423bfb8f641880928 100644 --- a/src/client/epics/index.js +++ b/src/client/epics/index.js @@ -21,25 +21,14 @@ const fetchResultsEpic = (action$, state$) => action$.pipe( withLatestFrom(state$), mergeMap(([action, state]) => { const { resultClass } = action; - const resultState = resultStateToUrl(state[resultClass], state[`${resultClass}Facets`]); - const requestUrl = `${apiUrl + resultClass}?${resultState}`; + const params = stateSliceToUrl(state[resultClass], state[`${resultClass}Facets`]); + const requestUrl = `${apiUrl}${resultClass}/results?${params}`; return ajax.getJSON(requestUrl).pipe( map(response => updateResults({ data: response })) ); }) ); -// const getPlaces = action$ => action$.pipe( -// ofType(FETCH_PLACES), -// mergeMap(action => { -// const searchUrl = apiUrl + 'places'; -// const requestUrl = `${searchUrl}?variant=${action.variant}`; -// return ajax.getJSON(requestUrl).pipe( -// map(response => updatePlaces({ places: response })) -// ); -// }) -// ); - const fetchByURIEpic = action$ => action$.pipe( ofType(FETCH_BY_URI), mergeMap(action => { @@ -84,12 +73,12 @@ const fetchFacetEpic = (action$, state$) => action$.pipe( }) ); -export const resultStateToUrl = (data, facets) => { +export const stateSliceToUrl = (stateSlice, facets) => { let params = { - page: data.page, - pagesize: data.pagesize, - sortBy: data.sortBy, - sortDirection: data.sortDirection + page: stateSlice.page, + pagesize: stateSlice.pagesize, + sortBy: stateSlice.sortBy, + sortDirection: stateSlice.sortDirection }; let filters = {}; let activeFilters = false; diff --git a/src/server/index.js b/src/server/index.js index b4935a3c75020d73355dd8ad51cbff391f6dc377..f21dbfca0e74b2fbe62108949bca8e120c8a977e 100644 --- a/src/server/index.js +++ b/src/server/index.js @@ -1,12 +1,8 @@ import express from 'express'; import path from 'path'; import bodyParser from 'body-parser'; -import { - getManuscripts, - getPlaces, - getPlace, - getFacet -} from './sparql/Manuscripts'; +import { getManuscripts } from './sparql/Manuscripts'; +import { getFacet } from './sparql/Facets'; const DEFAULT_PORT = 3001; const app = express(); const apiPath = '/api'; @@ -24,13 +20,18 @@ app.use(function(req, res, next) { // Serve the static files from the React app app.use(express.static(path.join(__dirname, './../public/'))); -app.get(`${apiPath}/manuscripts`, (req, res) => { +app.get(`${apiPath}/:resultClass/results`, (req, res) => { const page = parseInt(req.query.page) || 0; const pagesize = parseInt(req.query.pagesize) || 5; const sortBy = req.query.sortBy; const sortDirection = req.query.sortDirection; const filters = req.query.filters == null ? null : JSON.parse(req.query.filters); - return getManuscripts(page, pagesize, filters, sortBy, sortDirection).then(data => { + let getResults = null; + switch (req.params.resultClass) { + case 'manuscripts': + getResults = getManuscripts; + } + return getResults(page, pagesize, filters, sortBy, sortDirection).then(data => { res.json(data); }) .catch(err => { @@ -39,27 +40,6 @@ app.get(`${apiPath}/manuscripts`, (req, res) => { }); }); -app.get(`${apiPath}/places/:placeId?`, (req, res) => { - if (req.params.placeId) { - return getPlace(req.params.placeId).then(data => { - res.json(data[0]); - }) - .catch((err) => { - console.log(err); - return res.sendStatus(500); - }); - } else { - const variant = req.query.variant ? req.query.variant : 'productionPlaces'; - return getPlaces(variant).then((data) => { - res.json(data); - }) - .catch((err) => { - console.log(err); - return res.sendStatus(500); - }); - } -}); - app.get(`${apiPath}/facet/:id`, (req, res) => { const filters = req.query.filters == null ? null : JSON.parse(req.query.filters); return getFacet(req.params.id, req.query.sortBy, req.query.sortDirection, filters).then(data => { @@ -71,6 +51,27 @@ app.get(`${apiPath}/facet/:id`, (req, res) => { }); }); +// app.get(`${apiPath}/places/:placeId?`, (req, res) => { +// if (req.params.placeId) { +// return getPlace(req.params.placeId).then(data => { +// res.json(data[0]); +// }) +// .catch((err) => { +// console.log(err); +// return res.sendStatus(500); +// }); +// } else { +// const variant = req.query.variant ? req.query.variant : 'productionPlaces'; +// return getPlaces(variant).then((data) => { +// res.json(data); +// }) +// .catch((err) => { +// console.log(err); +// return res.sendStatus(500); +// }); +// } +// }); + /* Routes are matched to a url in order of their definition Redirect all the the rest for react-router to handle */ app.get('*', function(request, response) { diff --git a/src/server/sparql/FacetConfigs.js b/src/server/sparql/FacetConfigs.js new file mode 100644 index 0000000000000000000000000000000000000000..8c4711d4f13a94ce8d6e5973a8e5532b6cf08f0c --- /dev/null +++ b/src/server/sparql/FacetConfigs.js @@ -0,0 +1,49 @@ +export const facetConfigs = { + productionPlace: { + id: 'productionPlace', + label: 'Production place', + labelPath: '^crm:P108_has_produced/crm:P7_took_place_at/skos:prefLabel', + predicate: '^crm:P108_has_produced/crm:P7_took_place_at', + parentPredicate: '^crm:P108_has_produced/crm:P7_took_place_at/gvp:broaderPreferred+', + type: 'hierarchical', + }, + author: { + id: 'author', + label: 'Author', + labelPath: 'mmm-schema:manuscript_author/skos:prefLabel', + predicate: 'mmm-schema:manuscript_author', + type: 'list' + }, + source: { + id: 'source', + label: 'Source', + labelPath: 'dct:source/skos:prefLabel', + predicate: 'dct:source', + type: 'list', + }, + language: { + id: 'language', + label: 'Language', + labelPath: 'crm:P128_carries/crm:P72_has_language', + predicate: 'crm:P128_carries/crm:P72_has_language', + type: 'list', + }, + productionTimespan: { + id: 'productionTimespan', + label: 'Production Date', + labelPath: '^crm:P108_has_produced/crm:P4_has_time-span/skos:prefLabel', + type: 'list', + }, + prefLabel: { + id: 'prefLabel', + label: 'Title', + labelPath: 'skos:prefLabel', + type: 'list', + }, + event: { + id: 'event', + label: 'Event', + labelPath: '^mmm-schema:observed_manuscript/mmm-schema:observed_time-span', + type: 'list', + }, +}; diff --git a/src/server/sparql/Facets.js b/src/server/sparql/Facets.js new file mode 100644 index 0000000000000000000000000000000000000000..557eabc1f9e962978c0ff2d87edd11159385099b --- /dev/null +++ b/src/server/sparql/Facets.js @@ -0,0 +1,82 @@ +import { has } from 'lodash'; +import SparqlSearchEngine from './SparqlSearchEngine'; +import datasetConfig from './Datasets'; +import { facetConfigs } from './FacetConfigs'; +import { + mapFacet, + mapHierarchicalFacet, +} from './Mappers'; + +const sparqlSearchEngine = new SparqlSearchEngine(); + +export const getFacet = (id, sortBy, sortDirection, filters) => { + let { endpoint, facetQuery } = datasetConfig['mmm']; + const facetConfig = facetConfigs[id]; + let selectedBlock = '# no selections'; + let filterBlock = '# no filters'; + let parentBlock = '# no parents'; + let mapper = mapFacet; + if (filters !== null) { + filterBlock = generateFacetFilter(id, filters); + if (has(filters, id)) { + selectedBlock = ` + OPTIONAL { + FILTER(?id IN ( <${filters[id].join('>, <')}> )) + BIND(true AS ?selected_) + } + `; + } + } + if (facetConfig.type === 'hierarchical') { + mapper = mapHierarchicalFacet; + parentBlock = ` + UNION + { + ${generateFacetFilterParents(id, filters)} + ?instance ${facetConfig.parentPredicate} ?id . + BIND(COALESCE(?selected_, false) as ?selected) + OPTIONAL { ?id skos:prefLabel ?prefLabel_ } + BIND(COALESCE(STR(?prefLabel_), STR(?id)) AS ?prefLabel) + OPTIONAL { ?id dct:source ?source } + OPTIONAL { ?id gvp:broaderPreferred ?parent_ } + BIND(COALESCE(?parent_, '0') as ?parent) + } + `; + } + facetQuery = facetQuery.replace(/<FILTER>/g, filterBlock ); + facetQuery = facetQuery.replace(/<PREDICATE>/g, facetConfig.predicate); + facetQuery = facetQuery.replace('<SELECTED_VALUES>', selectedBlock); + facetQuery = facetQuery.replace('<PARENTS>', parentBlock); + facetQuery = facetQuery.replace('<ORDER_BY>', `ORDER BY ${sortDirection}(?${sortBy})` ); + // if (id == 'productionPlace') { + // //console.log(filters) + // console.log(facetQuery) + // } + return sparqlSearchEngine.doSearch(facetQuery, endpoint, mapper); +}; + +const generateFacetFilter = (facetId, filters) => { + let filterStr = ''; + for (let property in filters) { + if (property !== facetId) { + filterStr += ` + VALUES ?${property}Filter { <${filters[property].join('> <')}> } + ?instance ${facetConfigs[property].predicate} ?${property}Filter . + `; + } + } + return filterStr; +}; + +const generateFacetFilterParents = (facetId, filters) => { + let filterStr = ''; + for (let property in filters) { + if (property !== facetId) { + filterStr += ` + VALUES ?${property}FilterParents { <${filters[property].join('> <')}> } + ?instance ${facetConfigs[property].predicate} ?${property}FilterParents . + `; + } + } + return filterStr; +}; diff --git a/src/server/sparql/Manuscripts.js b/src/server/sparql/Manuscripts.js index f6d6f829445fb68406d50cb8bbdd962e15ee2972..b7b23d8b4c6bbbeb971fa7dd10ba3625632c0268 100644 --- a/src/server/sparql/Manuscripts.js +++ b/src/server/sparql/Manuscripts.js @@ -1,65 +1,11 @@ -import { has } from 'lodash'; import SparqlSearchEngine from './SparqlSearchEngine'; import datasetConfig from './Datasets'; -import { - mapFacet, - mapHierarchicalFacet, - mapCount, -} from './Mappers'; +import { mapCount } from './Mappers'; import { makeObjectList } from './SparqlObjectMapper'; +import { facetConfigs } from './FacetConfigs'; const sparqlSearchEngine = new SparqlSearchEngine(); -const facetConfigs = { - productionPlace: { - id: 'productionPlace', - label: 'Production place', - labelPath: '^crm:P108_has_produced/crm:P7_took_place_at/skos:prefLabel', - predicate: '^crm:P108_has_produced/crm:P7_took_place_at', - parentPredicate: '^crm:P108_has_produced/crm:P7_took_place_at/gvp:broaderPreferred+', - type: 'hierarchical', - }, - author: { - id: 'author', - label: 'Author', - labelPath: 'mmm-schema:manuscript_author/skos:prefLabel', - predicate: 'mmm-schema:manuscript_author', - type: 'list' - }, - source: { - id: 'source', - label: 'Source', - labelPath: 'dct:source/skos:prefLabel', - predicate: 'dct:source', - type: 'list', - }, - language: { - id: 'language', - label: 'Language', - labelPath: 'crm:P128_carries/crm:P72_has_language', - predicate: 'crm:P128_carries/crm:P72_has_language', - type: 'list', - }, - productionTimespan: { - id: 'productionTimespan', - label: 'Production Date', - labelPath: '^crm:P108_has_produced/crm:P4_has_time-span/skos:prefLabel', - type: 'list', - }, - prefLabel: { - id: 'prefLabel', - label: 'Title', - labelPath: 'skos:prefLabel', - type: 'list', - }, - event: { - id: 'event', - label: 'Event', - labelPath: '^mmm-schema:observed_manuscript/mmm-schema:observed_time-span', - type: 'list', - }, -}; - export const getManuscripts = (page, pagesize, filters, sortBy, sortDirection) => { return Promise.all([ getManuscriptCount(filters), @@ -110,79 +56,6 @@ export const getPlace = id => { return sparqlSearchEngine.doSearch(placeQuery, endpoint, makeObjectList); }; - -export const getFacet = (id, sortBy, sortDirection, filters) => { - let { endpoint, facetQuery } = datasetConfig['mmm']; - const facetConfig = facetConfigs[id]; - let selectedBlock = '# no selections'; - let filterBlock = '# no filters'; - let parentBlock = '# no parents'; - let mapper = mapFacet; - if (filters !== null) { - filterBlock = generateFacetFilter(id, filters); - if (has(filters, id)) { - selectedBlock = ` - OPTIONAL { - FILTER(?id IN ( <${filters[id].join('>, <')}> )) - BIND(true AS ?selected_) - } - `; - } - } - if (facetConfig.type === 'hierarchical') { - mapper = mapHierarchicalFacet; - parentBlock = ` - UNION - { - ${generateFacetFilterParents(id, filters)} - ?instance ${facetConfig.parentPredicate} ?id . - BIND(COALESCE(?selected_, false) as ?selected) - OPTIONAL { ?id skos:prefLabel ?prefLabel_ } - BIND(COALESCE(STR(?prefLabel_), STR(?id)) AS ?prefLabel) - OPTIONAL { ?id dct:source ?source } - OPTIONAL { ?id gvp:broaderPreferred ?parent_ } - BIND(COALESCE(?parent_, '0') as ?parent) - } - `; - } - facetQuery = facetQuery.replace(/<FILTER>/g, filterBlock ); - facetQuery = facetQuery.replace(/<PREDICATE>/g, facetConfig.predicate); - facetQuery = facetQuery.replace('<SELECTED_VALUES>', selectedBlock); - facetQuery = facetQuery.replace('<PARENTS>', parentBlock); - facetQuery = facetQuery.replace('<ORDER_BY>', `ORDER BY ${sortDirection}(?${sortBy})` ); - // if (id == 'productionPlace') { - // //console.log(filters) - // console.log(facetQuery) - // } - return sparqlSearchEngine.doSearch(facetQuery, endpoint, mapper); -}; - -const generateFacetFilter = (facetId, filters) => { - let filterStr = ''; - for (let property in filters) { - if (property !== facetId) { - filterStr += ` - VALUES ?${property}Filter { <${filters[property].join('> <')}> } - ?instance ${facetConfigs[property].predicate} ?${property}Filter . - `; - } - } - return filterStr; -}; - -const generateFacetFilterParents = (facetId, filters) => { - let filterStr = ''; - for (let property in filters) { - if (property !== facetId) { - filterStr += ` - VALUES ?${property}FilterParents { <${filters[property].join('> <')}> } - ?instance ${facetConfigs[property].predicate} ?${property}FilterParents . - `; - } - } - return filterStr; -}; - const generateResultFilter = filters => { let filterStr = ''; for (let property in filters) {