diff --git a/src/client/actions/index.js b/src/client/actions/index.js index 0f6ae83fc34ee91d7fc4f6f3764686418b81270c..83b4477ea2f92f638b9ede3090b32581cc3895f3 100644 --- a/src/client/actions/index.js +++ b/src/client/actions/index.js @@ -99,9 +99,9 @@ export const fetchFacetFailed = (facetClass, id, error, message) => ({ type: FETCH_FACET_FAILED, facetClass, id, error, message }); -export const updateFacetValues = ({ facetClass, id, distinctValueCount, values, flatValues }) => ({ +export const updateFacetValues = ({ facetClass, id, distinctValueCount, values, flatValues, min, max }) => ({ type: UPDATE_FACET_VALUES, - facetClass, id, distinctValueCount, values, flatValues + facetClass, id, distinctValueCount, values, flatValues, min, max }); export const updateFacetOption = ({ facetClass, facetID, option, value }) => ({ type: UPDATE_FACET_OPTION, diff --git a/src/client/components/facet_bar/DateSliderFacet.js b/src/client/components/facet_bar/DateSliderFacet.js new file mode 100644 index 0000000000000000000000000000000000000000..91ce8732eddd7122f7987b985259434db17cd270 --- /dev/null +++ b/src/client/components/facet_bar/DateSliderFacet.js @@ -0,0 +1,148 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import CircularProgress from '@material-ui/core/CircularProgress'; +import purple from '@material-ui/core/colors/purple'; +import { withStyles } from '@material-ui/core/styles'; +import { Slider, Rail, Handles, Tracks, Ticks } from 'react-compound-slider'; +import { Handle, Track, Tick, TooltipRail } from './SliderComponents'; + +const sliderRootStyle = { + position: 'relative', + width: '100%', +}; + +const styles = theme => ({ + root: { + height: '100%', + display: 'flex', + alignItems: 'center', + paddingLeft: theme.spacing(4), + paddingRight: theme.spacing(2) + }, + spinnerContainer: { + display: 'flex', + width: '100%', + height: '100%', + alignItems: 'center', + justifyContent: 'center' + }, +}); + +class DateSliderFacet extends Component { + + componentDidMount = () => { + this.props.fetchFacet({ + facetClass: this.props.facetClass, + facetID: this.props.facetID, + }); + } + + handleSliderOnChange = values => { + this.props.updateFacetOption({ + facetClass: this.props.facetClass, + facetID: this.props.facetID, + option: this.props.facet.filterType, + value: values + }); + } + + ISOStringToYear = str => { + let year = null; + if (str.charAt(0) == '-') { + year = parseInt(str.substring(0,5)); + } else { + year = parseInt(str.substring(0,4)); + } + return year; + } + + render() { + const { classes, someFacetIsFetching } = this.props; + const { isFetching, min, max } = this.props.facet; + if (isFetching || min == null || max == null) { + return( + <div className={classes.spinnerContainer}> + <CircularProgress style={{ color: purple[500] }} thickness={5} /> + </div> + ); + } else { + const minYear = this.ISOStringToYear(min); + const maxYear = this.ISOStringToYear(max); + const domain = [ minYear, maxYear ]; // use as default values + + // https://github.com/sghall/react-compound-slider + return ( + <div className={classes.root}> + <Slider + mode={1} + step={1} + domain={domain} + disabled={someFacetIsFetching} + reversed={false} + rootStyle={sliderRootStyle} + onChange={this.handleSliderOnChange} + values={domain} + > + <Rail>{railProps => <TooltipRail {...railProps} />}</Rail> + <Handles> + {({ handles, activeHandleID, getHandleProps }) => ( + <div className="slider-handles"> + {handles.map(handle => ( + <Handle + key={handle.id} + handle={handle} + domain={domain} + isActive={handle.id === activeHandleID} + getHandleProps={getHandleProps} + /> + ))} + </div> + )} + </Handles> + <Tracks left={false} right={false}> + {({ tracks, getTrackProps }) => ( + <div className="slider-tracks"> + {tracks.map(({ id, source, target }) => ( + <Track + key={id} + source={source} + target={target} + getTrackProps={getTrackProps} + /> + ))} + </div> + )} + </Tracks> + <Ticks count={10}> + {({ ticks }) => ( + <div className="slider-ticks"> + {ticks.map(tick => ( + <Tick key={tick.id} tick={tick} count={ticks.length} /> + ))} + </div> + )} + </Ticks> + </Slider> + </div> + ); + } + + + } +} + +DateSliderFacet.propTypes = { + classes: PropTypes.object.isRequired, + facetID: PropTypes.string.isRequired, + facet: PropTypes.object.isRequired, + facetClass: PropTypes.string, + resultClass: PropTypes.string, + fetchFacet: PropTypes.func, + someFacetIsFetching: PropTypes.bool.isRequired, + updateFacetOption: PropTypes.func, + facetUpdateID: PropTypes.number, + updatedFilter: PropTypes.oneOfType([PropTypes.object, PropTypes.string]), + updatedFacet: PropTypes.string, +}; + +export default withStyles(styles)(DateSliderFacet); diff --git a/src/client/components/facet_bar/FacetBar.js b/src/client/components/facet_bar/FacetBar.js index b43e61e1918f2752f9a91b10751897eeb47eaf3d..4a2f115e8ccce9fd7b016a256a41a5eadca38904 100644 --- a/src/client/components/facet_bar/FacetBar.js +++ b/src/client/components/facet_bar/FacetBar.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import { withStyles } from '@material-ui/core/styles'; import HierarchicalFacet from './HierarchicalFacet'; import TextFacet from './TextFacet'; -import DateSlider from './slider/DateSlider'; +import DateSliderFacet from './DateSliderFacet'; import Paper from '@material-ui/core/Paper'; import FacetHeader from './FacetHeader'; import FacetInfo from './FacetInfo'; @@ -99,17 +99,24 @@ class FacetBar extends React.Component { facetClass={this.props.facetClass} resultClass={this.props.resultClass} facetUpdateID={facetUpdateID} - updatedFacet={updatedFacet} - updatedFilter={updatedFilter} fetchFacet={this.props.fetchFacet} someFacetIsFetching={someFacetIsFetching} updateFacetOption={this.props.updateFacetOption} /> ); break; - case 'timespan': + case 'timespanFilter': facetComponent = ( - <DateSlider /> + <DateSliderFacet + facetID={facetID} + facet={facet} + facetClass={this.props.facetClass} + resultClass={this.props.resultClass} + facetUpdateID={facetUpdateID} + fetchFacet={this.props.fetchFacet} + someFacetIsFetching={someFacetIsFetching} + updateFacetOption={this.props.updateFacetOption} + /> ); break; default: diff --git a/src/client/components/facet_bar/HierarchicalFacet.js b/src/client/components/facet_bar/HierarchicalFacet.js index dc694a97c74add594d0481bd559323a885188473..6d9e1d4478a3be0d0d755b4b3b7296c20c08f407 100644 --- a/src/client/components/facet_bar/HierarchicalFacet.js +++ b/src/client/components/facet_bar/HierarchicalFacet.js @@ -376,7 +376,10 @@ HierarchicalFacet.propTypes = { someFacetIsFetching: PropTypes.bool.isRequired, updateFacetOption: PropTypes.func, facetUpdateID: PropTypes.number, - updatedFilter: PropTypes.oneOfType([PropTypes.object, PropTypes.string]), + updatedFilter: PropTypes.oneOfType([ + PropTypes.object, + PropTypes.string, + PropTypes.array]), updatedFacet: PropTypes.string, }; diff --git a/src/client/components/facet_bar/slider/SliderComponents.js b/src/client/components/facet_bar/SliderComponents.js similarity index 100% rename from src/client/components/facet_bar/slider/SliderComponents.js rename to src/client/components/facet_bar/SliderComponents.js diff --git a/src/client/components/facet_bar/TextFacet.js b/src/client/components/facet_bar/TextFacet.js index 51a9bc8e36f96e83c606e3d361994ab16002741e..4fb8c37c1972d2c009e7e1b18e1752f99ae666f6 100644 --- a/src/client/components/facet_bar/TextFacet.js +++ b/src/client/components/facet_bar/TextFacet.js @@ -119,8 +119,6 @@ TextFacet.propTypes = { someFacetIsFetching: PropTypes.bool.isRequired, updateFacetOption: PropTypes.func, facetUpdateID: PropTypes.number, - updatedFilter: PropTypes.oneOfType([PropTypes.object, PropTypes.string]), - updatedFacet: PropTypes.string, }; export default withStyles(styles)(TextFacet); diff --git a/src/client/components/facet_bar/slider/DateSlider.js b/src/client/components/facet_bar/slider/DateSlider.js deleted file mode 100644 index 24a234b548069d5060bda8f2301f1a4a7b890040..0000000000000000000000000000000000000000 --- a/src/client/components/facet_bar/slider/DateSlider.js +++ /dev/null @@ -1,115 +0,0 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { withStyles } from '@material-ui/core/styles'; -import { Slider, Rail, Handles, Tracks, Ticks } from 'react-compound-slider'; -import { Handle, Track, Tick, TooltipRail } from './SliderComponents'; - -const sliderRootStyle = { - position: 'relative', - width: '100%', -}; - -const styles = theme => ({ - root: { - height: '100%', - display: 'flex', - alignItems: 'center', - paddingLeft: theme.spacing(4), - paddingRight: theme.spacing(2) - } -}); - -const defaultValues = [-1000, 1975]; - -class DateSlider extends Component { - state = { - domain: [-1000, 1975], - values: defaultValues.slice(), - update: defaultValues.slice(), - reversed: false, - } - - onUpdate = update => { - this.setState({ update }); - } - - onChange = values => { - this.setState({ values }); - } - - setDomain = domain => { - this.setState({ domain }); - } - - toggleReverse = () => { - this.setState(prev => ({ reversed: !prev.reversed })); - } - - render() { - const { - state: { domain, values, reversed }, - } = this; - const { classes } = this.props; - - return ( - <div className={classes.root}> - <Slider - mode={1} - step={1} - domain={domain} - reversed={reversed} - rootStyle={sliderRootStyle} - onUpdate={this.onUpdate} - onChange={this.onChange} - values={values} - > - <Rail>{railProps => <TooltipRail {...railProps} />}</Rail> - <Handles> - {({ handles, activeHandleID, getHandleProps }) => ( - <div className="slider-handles"> - {handles.map(handle => ( - <Handle - key={handle.id} - handle={handle} - domain={domain} - isActive={handle.id === activeHandleID} - getHandleProps={getHandleProps} - /> - ))} - </div> - )} - </Handles> - <Tracks left={false} right={false}> - {({ tracks, getTrackProps }) => ( - <div className="slider-tracks"> - {tracks.map(({ id, source, target }) => ( - <Track - key={id} - source={source} - target={target} - getTrackProps={getTrackProps} - /> - ))} - </div> - )} - </Tracks> - <Ticks count={10}> - {({ ticks }) => ( - <div className="slider-ticks"> - {ticks.map(tick => ( - <Tick key={tick.id} tick={tick} count={ticks.length} /> - ))} - </div> - )} - </Ticks> - </Slider> - </div> - ); - } -} - -DateSlider.propTypes = { - classes: PropTypes.object.isRequired, -}; - -export default withStyles(styles)(DateSlider); diff --git a/src/client/components/facet_results/ResultTableCell.js b/src/client/components/facet_results/ResultTableCell.js index f670c21b9ececf565b28ddd8a2d9152ad39efd60..d0023ababebbcc767035f1100b32685341242ba1 100644 --- a/src/client/components/facet_results/ResultTableCell.js +++ b/src/client/components/facet_results/ResultTableCell.js @@ -27,12 +27,14 @@ const ResultTableCell = props => { let year; let month; let day; + /* TODO: remove this when data has been fixed problematic example http://ldf.fi/mmm/time/production_229499 */ if (Array.isArray(str)) { str = str[0]; } + if (str.charAt(0) == '-') { year = parseInt(str.substring(0,5)); month = parseInt(str.substring(7,8)); diff --git a/src/client/epics/index.js b/src/client/epics/index.js index 1d59ed41d6dd5d58dfc71758dde6ea962d587d30..56d5982f9b2fe8291d3254ebd444ca9b7ae23d87 100644 --- a/src/client/epics/index.js +++ b/src/client/epics/index.js @@ -41,7 +41,7 @@ const fetchPaginatedResultsEpic = (action$, state$) => action$.pipe( ofType(FETCH_PAGINATED_RESULTS), withLatestFrom(state$), mergeMap(([action, state]) => { - const { resultClass, facetClass, sortBy, variant } = action; + const { resultClass, facetClass, sortBy } = action; const { page, pagesize, sortDirection } = state[resultClass]; const params = stateToUrl({ facets: state[`${facetClass}Facets`].facets, @@ -50,7 +50,7 @@ const fetchPaginatedResultsEpic = (action$, state$) => action$.pipe( pagesize: pagesize, sortBy: sortBy, sortDirection: sortDirection, - variant: variant + variant: null }); const requestUrl = `${apiUrl}${resultClass}/paginated?${params}`; // https://rxjs-dev.firebaseapp.com/api/ajax/ajax @@ -112,6 +112,7 @@ const fetchResultCountEpic = (action$, state$) => action$.pipe( pagesize: null, sortBy: null, sortDirection: null, + variant: null }); const requestUrl = `${apiUrl}${resultClass}/count?${params}`; return ajax.getJSON(requestUrl).pipe( @@ -214,9 +215,11 @@ const fetchFacetEpic = (action$, state$) => action$.pipe( map(res => updateFacetValues({ facetClass: facetClass, id: facetID, - distinctValueCount: res.distinctValueCount, - values: res.values, - flatValues: res.flatValues + distinctValueCount: res.distinctValueCount || null, + values: res.values || null, + flatValues: res.flatValues || null, + min: res.min || null, + max: res.max || null })), catchError(error => of({ type: FETCH_FACET_FAILED, @@ -251,9 +254,11 @@ export const stateToUrl = ({ let uriFilters = {}; let spatialFilters = {}; let textFilters = {}; + let timespanFilters = {}; let activeUriFilters = false; let activeSpatialFilters = false; let activeTextFilters = false; + let activeTimespanFilters = false; for (const [key, value] of Object.entries(facets)) { if (has(value, 'uriFilter') && value.uriFilter !== null) { activeUriFilters = true; @@ -264,6 +269,9 @@ export const stateToUrl = ({ } else if (has(value, 'textFilter') && value.textFilter !== null) { activeTextFilters = true; textFilters[key] = value.textFilter; + } else if (has(value, 'timespanFilter') && value.timespanFilter !== null) { + activeTimespanFilters = true; + timespanFilters[key] = value.timespanFilter; } } if (activeUriFilters) { @@ -275,7 +283,9 @@ export const stateToUrl = ({ if (activeTextFilters) { params.textFilters = JSON.stringify(textFilters); } - + if (activeTimespanFilters) { + params.timespanFilters = JSON.stringify(timespanFilters); + } return querystring.stringify(params); }; diff --git a/src/client/reducers/helpers.js b/src/client/reducers/helpers.js index 0954da13a62148748d2ee99d206dc30dbdcfde1d..5daaec158941826114e5e9826f99dd1fc0ce439f 100644 --- a/src/client/reducers/helpers.js +++ b/src/client/reducers/helpers.js @@ -61,7 +61,12 @@ export const updateSortBy = (state, action) => { export const updateFacetOption = (state, action) => { const { facetID, option, value } = action; - const filterTypes = [ 'uriFilter', 'spatialFilter', 'textFilter' ]; + const filterTypes = [ + 'uriFilter', + 'spatialFilter', + 'textFilter', + 'timespanFilter' + ]; if (filterTypes.includes(action.option)) { return updateFacetFilter(state, action); } else { @@ -78,8 +83,6 @@ export const updateFacetOption = (state, action) => { } }; - - const updateFacetFilter = (state, action) => { const { facetID, value } = action; const oldFacet = state.facets[facetID]; @@ -111,6 +114,14 @@ const updateFacetFilter = (state, action) => { ...state.facets[facetID], textFilter: value }; + } else if (oldFacet.filterType === 'timespanFilter') { + newFacet = { + ...state.facets[facetID], + timespanFilter: { + start: value[0], + end: value[1] + } + }; } return { ...state, @@ -178,17 +189,32 @@ export const fetchFacetFailed = (state, action) => { }; export const updateFacetValues = (state, action) => { - return { - ...state, - facets: { - ...state.facets, - [ action.id ]: { - ...state.facets[action.id], - distinctValueCount: action.distinctValueCount, - values: action.values, - flatValues: action.flatValues || [], - isFetching: false + if (state.facets[action.id].type === 'timespan') { + return { + ...state, + facets: { + ...state.facets, + [ action.id ]: { + ...state.facets[action.id], + min: action.min || null, + max: action.max || null, + isFetching: false + } } - } - }; + }; + } else { + return { + ...state, + facets: { + ...state.facets, + [ action.id ]: { + ...state.facets[action.id], + distinctValueCount: action.distinctValueCount || null, + values: action.values || null, + flatValues: action.flatValues || [], + isFetching: false + } + } + }; + } }; diff --git a/src/client/reducers/manuscriptsFacets.js b/src/client/reducers/manuscriptsFacets.js index 8a6097f4203aef806d08f8c97c44b59868953de2..369f4e96da47171155dfb7f0f2733f82e28c2913 100644 --- a/src/client/reducers/manuscriptsFacets.js +++ b/src/client/reducers/manuscriptsFacets.js @@ -33,7 +33,6 @@ export const INITIAL_STATE = { filterType: 'textFilter', textFilter: null, }, - productionPlace: { id: 'productionPlace', label: 'Production place', @@ -53,24 +52,26 @@ export const INITIAL_STATE = { spatialFilter: null, type: 'hierarchical', }, - // productionDate: { - // id: 'productionDate', - // label: 'Production date', - // //predicate: defined in backend - // distinctValueCount: 0, - // values: [], - // flatValues: [], - // sortBy: 'prefLabel', - // sortDirection: 'asc', - // sortButton: false, - // spatialFilterButton: false, - // isFetching: false, - // searchField: false, - // containerClass: 'ten', - // filterType: 'timespan', - // startValue: null, - // endValue: null - // }, + productionTimespan: { + id: 'productionTimespan', + label: 'Production date', + //predicate: defined in backend + distinctValueCount: null, + values: null, + flatValues: null, + sortBy: null, + sortDirection: null, + sortButton: false, + spatialFilterButton: false, + isFetching: false, + searchField: false, + containerClass: 'three', + filterType: 'timespanFilter', + min: null, + max: null, + timespanFilter: null, + type: 'timespan' + }, author: { id: 'author', label: 'Author', diff --git a/src/server/index.js b/src/server/index.js index bc6e02cac2e359bbf732fafae8182590b80f9fda..622c83d4d8852c76a1c6f6599d3c3cce9ba2d9b6 100644 --- a/src/server/index.js +++ b/src/server/index.js @@ -106,13 +106,12 @@ app.get(`${apiPath}/:resultClass/instance/:uri`, async (req, res, next) => { }); app.get(`${apiPath}/:facetClass/facet/:id`, async (req, res, next) => { - req.setTimeout(300000); // 5 minutes to avoid 504 errors try { const data = await getFacet({ facetClass: req.params.facetClass, facetID: req.params.id, - sortBy: req.query.sortBy, - sortDirection: req.query.sortDirection, + 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) diff --git a/src/server/sparql/FacetConfigs.js b/src/server/sparql/FacetConfigs.js index 34f7b4926c658cb5e029fc759f3460a24cff06ae..1a90c95dbecda73940ac4518e5b62d3197fa5d19 100644 --- a/src/server/sparql/FacetConfigs.js +++ b/src/server/sparql/FacetConfigs.js @@ -33,9 +33,10 @@ export const facetConfigs = { productionTimespan: { id: 'productionTimespan', facetValueFilter: '', + labelPath: '^crm:P108_has_produced/crm:P4_has_time-span/crm:P82a_begin_of_the_begin', + predicate: '^crm:P108_has_produced/crm:P4_has_time-span', startProperty: 'crm:P82a_begin_of_the_begin', endProperty: 'crm:P82b_end_of_the_end', - labelPath: '^crm:P108_has_produced/crm:P4_has_time-span/crm:P82a_begin_of_the_begin', type: 'timespan', }, language: { diff --git a/src/server/sparql/FacetValues.js b/src/server/sparql/FacetValues.js index 0729eeb7fddea29566b54e11630cbb20c8885e6d..cbfb7b5c5dcec6b49128d1bee6a6966b1455a3ab 100644 --- a/src/server/sparql/FacetValues.js +++ b/src/server/sparql/FacetValues.js @@ -11,6 +11,7 @@ import { generateFilter, generateSelectedFilter } from './Filters'; import { mapFacet, mapHierarchicalFacet, + mapTimespanFacet } from './Mappers'; export const getFacet = ({ @@ -23,24 +24,30 @@ export const getFacet = ({ textFilters }) => { const facetConfig = facetConfigs[facetClass][facetID]; - // choose a query template: + // choose query template and result mapper: let q = ''; + let mapper = null; switch(facetConfig.type) { case 'list': + q = facetValuesQuery; + mapper = mapFacet; + break; case 'hierarchical': q = facetValuesQuery; + mapper = mapHierarchicalFacet; break; case 'timespan': q = facetValuesQueryTimespan; + mapper = mapTimespanFacet; break; default: q = facetValuesQuery; + mapper = mapFacet; } let selectedBlock = '# no selections'; let selectedNoHitsBlock = '# no filters from other facets'; let filterBlock = '# no filters'; let parentBlock = '# no parents'; - let mapper = mapFacet; const hasFilters = uriFilters !== null || spatialFilters !== null || textFilters !== null; @@ -61,16 +68,21 @@ export const getFacet = ({ facetID, uriFilters }); - selectedNoHitsBlock = generateSelectedNoHitsBlock({ - facetClass, - facetID, - uriFilters, - spatialFilters, - textFilters - }); + /* + 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, + facetID, + uriFilters, + spatialFilters, + textFilters + }); + } } if (facetConfig.type === 'hierarchical') { - mapper = mapHierarchicalFacet; const { parentPredicate } = facetConfig; parentBlock = generateParentBlock({ facetClass, @@ -85,10 +97,22 @@ export const getFacet = ({ q = q.replace('<SELECTED_VALUES_NO_HITS>', selectedNoHitsBlock); q = q.replace('<FACET_VALUE_FILTER>', facetConfig.facetValueFilter); q = q.replace('<PARENTS>', parentBlock); - q = q.replace('<ORDER_BY>', `ORDER BY ${sortDirection}(?${sortBy})` ); + // TODO: order only when facet type is list + if (facetConfig.type === 'list') { + q = q.replace('<ORDER_BY>', `ORDER BY ${sortDirection}(?${sortBy})` ); + } else { + q = q.replace('<ORDER_BY>', '# no need for ordering'); + } q = q.replace(/<FACET_CLASS>/g, facetConfigs[facetClass].facetClass); q = q.replace(/<FILTER>/g, filterBlock ); q = q.replace(/<PREDICATE>/g, facetConfig.predicate); + if (facetConfig.type === 'timespan') { + q = q.replace('<START_PROPERTY>', facetConfig.startProperty); + q = q.replace('<END_PROPERTY>', facetConfig.endProperty); + } + // if (facetID == 'productionPlace') { + // console.log(prefixes + q) + // } return runSelectQuery(prefixes + q, endpoint, mapper); }; @@ -115,20 +139,16 @@ const generateSelectedNoHitsBlock = ({ spatialFilters, textFilters }) => { - const facetIDs = Object.keys(uriFilters); - // get selected values with no hits, only when there are filters from - // other facets - if (facetIDs.length > 1) { - const noHitsFilter = generateFilter({ - facetClass: facetClass, - uriFilters: uriFilters, - spatialFilters: spatialFilters, - textFilters: textFilters, - filterTarget: 'instance', - facetID: facetID, - inverse: true, - }); - return ` + const noHitsFilter = generateFilter({ + facetClass: facetClass, + uriFilters: uriFilters, + spatialFilters: spatialFilters, + textFilters: textFilters, + filterTarget: 'instance', + facetID: facetID, + inverse: true, + }); + return ` UNION { # facet values that have been selected but return no results @@ -137,7 +157,6 @@ const generateSelectedNoHitsBlock = ({ BIND(true AS ?selected_) } `; - } }; const generateParentBlock = ({ diff --git a/src/server/sparql/Mappers.js b/src/server/sparql/Mappers.js index a6dc4785f1eff0a1e91c9633c9b3691250e418cb..c68982b8f286514924169011fdf7edb7c2db3109 100644 --- a/src/server/sparql/Mappers.js +++ b/src/server/sparql/Mappers.js @@ -22,23 +22,6 @@ export const mapCount = sparqlBindings => { return sparqlBindings[0].count.value; }; -export const mapFacetValues = sparqlBindings => { - const results = sparqlBindings.map(b => { - try { - return { - id: b.id.value, - prefLabel: b.prefLabel.value, - selected: b.selected.value, - parent: b.parent.value, - instanceCount: b.instanceCount.value - }; - } catch(err) { - console.log(err); - } - }); - return results; -}; - export const mapFacet = sparqlBindings => { const results = mapFacetValues(sparqlBindings); return { @@ -65,6 +48,31 @@ export const mapHierarchicalFacet = sparqlBindings => { }; }; +export const mapTimespanFacet = sparqlBindings => { + const b = sparqlBindings[0]; + return { + min: b.min.value, + max: b.max.value + }; +}; + +const mapFacetValues = sparqlBindings => { + const results = sparqlBindings.map(b => { + try { + return { + id: b.id.value, + prefLabel: b.prefLabel.value, + selected: b.selected.value, + parent: b.parent.value, + instanceCount: b.instanceCount.value + }; + } catch(err) { + console.log(err); + } + }); + return results; +}; + const comparator = (a, b) => { if (Array.isArray(a.prefLabel)) { a.prefLabel = a.prefLabel[0]; diff --git a/src/server/sparql/SparqlQueriesGeneral.js b/src/server/sparql/SparqlQueriesGeneral.js index 6142c5ea9ae7fa3f1afde918fd212d30d47cc305..a673daccb25eb0cf25cf1eeb1a0975f99c526a48 100644 --- a/src/server/sparql/SparqlQueriesGeneral.js +++ b/src/server/sparql/SparqlQueriesGeneral.js @@ -98,19 +98,22 @@ export const facetValuesQuery = ` `; export const facetValuesQueryTimespan = ` + # ignore selections from other facets SELECT ?min ?max { - <FILTER> - ?instance <PREDICATE> ?timespan . - VALUES ?facetClass { <FACET_CLASS> } - ?instance a ?facetClass . { - SELECT (MIN(?start)) AS ?min) { + SELECT (MIN(?start) AS ?min) { + ?instance <PREDICATE> ?timespan . + VALUES ?facetClass { <FACET_CLASS> } + ?instance a ?facetClass . ?timespan <START_PROPERTY> ?start . } } { - SELECT (MAX(?end)) AS ?max) { - ?timespan <END_PROPERTY> ?start . + SELECT (MAX(?end) AS ?max) { + ?instance <PREDICATE> ?timespan . + VALUES ?facetClass { <FACET_CLASS> } + ?instance a ?facetClass . + ?timespan <END_PROPERTY> ?end . } } }