From f5a3a60fed1d72bc45c258844808388684e1a91c Mon Sep 17 00:00:00 2001 From: Moises Sacal <moisbo@gmail.com> Date: Fri, 4 Oct 2019 19:28:23 +1000 Subject: [PATCH] added faceting ability eresearch/rdm#1303 --- src/components/FacetController.js | 34 ++++++++++++++++++++++ src/components/Router.js | 48 ++++++++++++++++++++++--------- src/components/SolrService.js | 41 +++++++++++--------------- src/components/views/Facets.js | 35 ++++++++++++++++++++++ src/components/views/ListDocs.js | 11 +++++-- src/components/views/Main.js | 21 ++++++-------- src/index.js | 7 +++++ src/styles/media.scss | 2 +- src/styles/theme.scss | 14 +++++---- 9 files changed, 155 insertions(+), 58 deletions(-) create mode 100644 src/components/FacetController.js create mode 100644 src/components/views/Facets.js diff --git a/src/components/FacetController.js b/src/components/FacetController.js new file mode 100644 index 0000000..1795311 --- /dev/null +++ b/src/components/FacetController.js @@ -0,0 +1,34 @@ +const FacetController = { + get: function({data:arr, toJSON:toJSON}) { + const facets = []; + arr.map(function(value, index, Arr) { + if(index % 2 == 0) { + if(toJSON){ + try{ + value = JSON.parse(value) + }catch(e){console.error(e.message);}} + const facet = { + value: value, count: arr[index + 1] + } + facets.push(facet); + } + }); + return facets; + }, + display: function ({data: data, config: config}) { + const displays = []; + data.forEach(function(d){ + const displayFacet = { + count:d.count, + name: config.name, + route: config.route, + searchUrl: d.value[config.searchUrl], + searchText: d.value[config.searchText] + } + displays.push(displayFacet) + }); + return displays; + } + } + + module.exports = FacetController; diff --git a/src/components/Router.js b/src/components/Router.js index 5d14a77..3f4b6ad 100644 --- a/src/components/Router.js +++ b/src/components/Router.js @@ -6,6 +6,8 @@ const Footer = require('./views/footer'); const Search = require('./views/search'); const ViewDoc = require('./views/ViewDoc'); const ViewError = require('./views/ViewError'); +const FacetController = require('./FacetController'); + const solrService = require('./SolrService'); const Router = async function (state) { @@ -30,19 +32,20 @@ const Router = async function (state) { start: 0, page: 1, searchParam: 'author_id%3A', - text: state.main.doc.id + text: state.main.doc.id, + facets: false }); if (status === 200) { state.main.related = res.data.docs || []; } } - app.innerHTML = [Header(state), Menu(state), Container([Search(state), ViewDoc(state)]), Footer(state)].join(''); + app.innerHTML = [Container([Header(state), Menu(state), Search(state), ViewDoc(state), Footer(state)])].join(''); } else { - app.innerHTML = [Header(state), Menu(state), Container([Search(state), ViewError(state)]), Footer(state)].join(''); + app.innerHTML = [Container([Header(state), Menu(state), Search(state), ViewError(state), Footer(state)])].join(''); } } if (verb === '#search/') { - //TODO: make this better + //TODO: make the split better const splits = match[2].split('/'); const start = splits[0] || state.main.start; const page = splits[1] || ''; @@ -51,30 +54,49 @@ const Router = async function (state) { start: start, page: page, searchParam: 'main_search%3A', - text: searchText + text: searchText, + facets: state.facets, + facetLimit: state.facetLimit }); if (status === 200) { state.main.docs = data.docs; state.main.numFound = data.numFound; state.main.searchText = searchText; - app.innerHTML = [Header(state), Menu(state), Container([Search(state), Main(state)]), Footer(state)].join(''); + app.innerHTML = [Container([Header(state), Menu(state), Search(state), Main(state), Footer(state)])].join(''); const input = document.getElementById('text-to-search'); if (input) { input.value = searchText; } } else { - app.innerHTML = [Header(state), Menu(state), Container([Search(state), ViewError(state)]), Footer(state)].join(''); + app.innerHTML = [Container([Header(state), Menu(state), Search(state), ViewError(state), Footer(state)])].join(''); } } } else { - const {data, status} = await solrService.searchAll({api: state.config.api}); - if (status === 200) { - state.main.docs = data.docs; - state.main.numFound = data.numFound; + const res = await solrService.search({api: state.config.api}, { + start: state.main.start, + page: state.main.page, + searchParam: '*%3A', + text: '*', + facets: state.facets, + facetLimit: state.facetLimit + }); + if (res.status === 200) { + state.main.docs = res.data.docs; + state.main.numFound = res.data.numFound; state.main.searchText = ''; - app.innerHTML = [Header(state), Menu(state), Container([Search(state), Main(state)]), Footer(state)].join(''); + state.facetResult = res.facets; + const facetContent = FacetController.get({data: state.facetResult['facet_fields'][state.facets[0]], toJSON: true}); + const facetData = FacetController.display({data: facetContent, config: { + name: 'Dataset_author_facetmulti', + route: '#view/', + searchUrl: '@id', + searchText: 'name' + } + }); + state.facetData = facetData; + app.innerHTML = [Container([Header(state), Menu(state), Search(state), Main(state), Footer(state)])].join(''); } else { - app.innerHTML = [Header(state), Menu(state), Container([Search(state), ViewError(state)]), Footer(state)].join(''); + app.innerHTML = [Container([Header(state), Menu(state), Search(state), ViewError(state), Footer(state)])].join(''); } } }; diff --git a/src/components/SolrService.js b/src/components/SolrService.js index 1567a10..502132a 100644 --- a/src/components/SolrService.js +++ b/src/components/SolrService.js @@ -1,43 +1,36 @@ const axios = require('axios'); const SolrService = { - searchAll: async function (config) { - try { - const req = await axios.get(`${config.api}/select?q=*%3A*`); - if (req.data) { - return {data: req.data['response'], status: req.status}; - } else { - return {data: [], status: req.status}; - } - } catch (e) { - return {data: [], status: e.message}; - } - }, get: async function (config, data) { try { let param = `get?id=`; data = encodeURIComponent(`${data}`); - const req = await axios.get(`${config.api}/${param}${data}`); - if (req.data) { - return {data: req.data['doc'], status: req.status}; + const res = await axios.get(`${config.api}/${param}${data}`); + if (res.data) { + return {data: res.data['doc'], status: res.status}; } else { - return {data: {}, status: req.status}; + return {data: {}, status: res.status}; } } catch (e) { return {data: {}, status: e.message}; } }, - search: async function (config, {start: start, page: page, searchParam: searchParam, text: text}) { + search: async function (config, {start: start, page: page, searchParam: searchParam, text: text, facets: facets, facetLimit: facetLimit}) { try { - let param = `select?q=`; - if (text === '' || !text) { + let param = `select?q=`; + if (text === '' || !text ) { text = '*'; } - const req = await axios.get(`${config.api}/${param}${searchParam}${text}&start=${start}&page=${page}`); - if (req.data) { - return {data: req.data['response'], status: req.status}; + let query = `${param}${searchParam}${text}&start=${start}&page=${page}`; + + if(facets) { + query += `&facet=true%20&facet.field=${[...facets].join('&facet.field')}&facet.limit=${facetLimit || 5}`; + } + const res = await axios.get(`${config.api}/${query}`); + if (res.data) { + return {data: res.data['response'], facets: res.data['facet_counts'], status: res.status}; } else { - return {data: [], status: req.status}; + return {data: [], status: res.status}; } } catch (e) { return {data: [], status: e.message}; @@ -45,4 +38,4 @@ const SolrService = { } }; -module.exports = SolrService; \ No newline at end of file +module.exports = SolrService; diff --git a/src/components/views/Facets.js b/src/components/views/Facets.js new file mode 100644 index 0000000..d381228 --- /dev/null +++ b/src/components/views/Facets.js @@ -0,0 +1,35 @@ +const $ = require("jquery"); +const isIterable = require('../isIterable'); + +const Facets = function (data) { + let html = `<ul class="list-group col-sm-4">`; + + if(isIterable(data.facetsDisplay)){ + for(let fd of data.facetsDisplay){ + html += `<li class="list-group-item"> + <div> + <h4>${fd.displayText}</h4> + <hr/> + <ul class="list-group">`; + const currentFacet = data.facetData.filter((f)=>{ + return f.name === fd.name; + }); + if(isIterable(currentFacet)){ + for(let facet of currentFacet){ + html += `<li class="row"> + <div class="col-sm-4">${facet['count']}</div> + <div class="col-sm-8"><a href="/${facet['route']}${facet['searchUrl']}">${facet['searchText']}</a></div> + </li>`; + } + } + html += `</ul> + </div> + </li>` + } + }; + + html += `</ul>`; + return html; +}; + +module.exports = Facets; diff --git a/src/components/views/ListDocs.js b/src/components/views/ListDocs.js index 9e85d11..00ce87d 100644 --- a/src/components/views/ListDocs.js +++ b/src/components/views/ListDocs.js @@ -1,7 +1,11 @@ +const Facets = require('./Facets'); + const ListDocs = function (data) { var html = ''; const docs = data.main.docs; - html += `<ul class="list-group">`; + html += `<div class="container col-sm-12"><div class="row">` + html += Facets(data); + html += `<ul class="list-group col-sm-8">`; if (docs.length > 0) { docs.forEach((d) => { var url = `${data.config.repo}${d['uri_id']}/`; @@ -13,8 +17,9 @@ const ListDocs = function (data) { } else { html += `<div class="text-center"> No data found</div>`; } - html += `<div></div><br/></div></ul>`; + html += `</ul><div><br/></div>`; + html += `</div></div>`; return html; }; -module.exports = ListDocs; \ No newline at end of file +module.exports = ListDocs; diff --git a/src/components/views/Main.js b/src/components/views/Main.js index d312824..4fbf293 100644 --- a/src/components/views/Main.js +++ b/src/components/views/Main.js @@ -3,23 +3,20 @@ const ListDocs = require('./ListDocs'); const Pagination = require('./Pagination'); const Main = function (data) { - let html = `<div class="maincontent-body"> -<div><br/></div> - -`; - html = [ListDocs(data), Pagination(data)].join(''); - + let html = `<div><br/></div>`; + html = [ListDocs(data), '<div><br/></div>' ,Pagination(data)].join(''); html += ` - <div class="container"> - <div class="text-center"> - <p>Results: ${data.main.numFound}</p> + <div class="row"> + <div class="container"> + <div class="text-center"> + <p>Results: ${data.main.numFound}</p> + </div> </div> </div> <div><br/></div> - </div> -`; + `; return html; }; -module.exports = Main; \ No newline at end of file +module.exports = Main; diff --git a/src/index.js b/src/index.js index 5ad51be..ec3816d 100644 --- a/src/index.js +++ b/src/index.js @@ -43,6 +43,13 @@ let state = { {display: "SubDoc", field: "contactPoint", fieldName: 'Contact Point', template: '${item.name} ${item.email}'}, ] }, + facets: ['Dataset_author_facetmulti'], + facetsDisplay: [ + {name: 'Dataset_author_facetmulti', displayText: 'Top Authors'}, + //{name: 'Keywords', displayText: 'Top Keywords'} + ], + facetData: [], + facetLimit: 5, footer:{ text: '2019 University of Technology Sydney' }, diff --git a/src/styles/media.scss b/src/styles/media.scss index 0ce0b90..dfd5913 100644 --- a/src/styles/media.scss +++ b/src/styles/media.scss @@ -13,7 +13,7 @@ } } -@media only screen and (max-width: 430px) { +@media only screen and (max-width: 490px) { .header-main a { font-size: 23px !important; bottom: 23px !important; diff --git a/src/styles/theme.scss b/src/styles/theme.scss index 1cccca2..c569751 100644 --- a/src/styles/theme.scss +++ b/src/styles/theme.scss @@ -22,15 +22,20 @@ html, body { } .content-inside { - //padding: 20px; - padding-bottom: 70px; + min-height: 100vh; /* will cover the 100% of viewport */ + overflow: hidden; + display: block; + position: relative; + padding-bottom: 100px; /* height of your footer */ } .footer { - height: 70px; - margin-top: -70px; + position: absolute; + bottom: 0; + width: 100%; background-color: #000; color: #fff; + height: 70px; } .footer p { @@ -93,4 +98,3 @@ html, body { width: 500px; background-color: $refiner-card-bg-color; } - -- GitLab