- vuejs solr faceted search

- with extra display for active filter items
This commit is contained in:
Arno Kaimbacher 2019-10-10 12:58:13 +02:00
parent c596a620cc
commit a95282e49e
15 changed files with 261 additions and 120 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -21,14 +21,7 @@ section.search {
/* Seasrch items */
.left-bar .panel-title {
text-transform: capitalize;
margin-top: 8px;
margin-bottom: 2px;
color: white;
display: block;
font-weight: bold;
}
.search-items {
@ -116,6 +109,15 @@ section.search {
/* filter items in the left bar */
/*-------------------------------------------------- */
.left-bar .panel-title {
text-transform: capitalize;
margin-top: 8px;
margin-bottom: 2px;
color: white;
display: block;
font-weight: bold;
}
.overflowing {
color: #444444;
list-style: none;
@ -128,7 +130,7 @@ section.search {
cursor: pointer;
}
ul.filterItems li {
ul.filter-items li {
min-height: 15px;
color: #444;
font-size: 12px;
@ -148,21 +150,21 @@ ul.filterItems li {
margin-left: 1em;
}
.filterItems.limited li:nth-of-type(1n+6) {
.filter-items.limited li:nth-of-type(1n+3) {
display: none;
}
ul.filterItems .active {
/* ul.filter-items .active {
background-color: lightgray;
}
ul.filterItems li a {
} */
ul.filter-items li a {
cursor: pointer;
color: lightgray;
text-decoration: none;
font-weight: bold;
font-size: 16px;
}
/* ul.filterItems li a:hover {
/* ul.filter-items li a:hover {
color: #0099cc;
cursor: pointer;
} */
@ -319,7 +321,7 @@ font-size: 16px;
list-style: outside none none;
padding: 0px 0px 12px;
margin: 0px;
max-height: 240px;
/* max-height: 240px; */
position: relative;
overflow-y: auto;
}
@ -342,7 +344,6 @@ font-size: 16px;
width: 1px;
white-space: nowrap;
}
.css-w1gpbi:checked + label::before {
animation: 0s ease 0s 1 normal none running none;
background-color: rgb(255, 255, 255);
@ -396,3 +397,30 @@ vertical-align: middle;
cursor: pointer;
}
.active-filter-items a {
flex-wrap: wrap;
margin: 2px 3px;
padding: 5px 8px;
font-size: 0.95rem;
position: relative;
}
.filter-link {
display: inline-flex;
-moz-box-pack: center;
justify-content: center;
-moz-box-align: center;
align-items: center;
border-radius: 3px;
border: 1px solid transparent;
min-height: 30px;
overflow-wrap: break-word;
padding: 5px 12px;
line-height: 1.2rem;
background-color: rgb(238, 238, 238);
color: rgb(66, 66, 66);
cursor: pointer;
-moz-user-select: none;
transition: all 0.3s ease 0s;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,35 +1,41 @@
<template>
<div class="search-container row">
<div class="search-container row">
<div class="four columns left-bar">
<div id="left-bar" class="sidebar left-bar">
<h2 class="indexheader">DataXplore</h2>
<h2 class="indexheader">DataXplore</h2>
<!-- <div class="card" v-for="item in facets.language" :key="item.id">
<!-- <div class="card" v-for="item in facets.language" :key="item.id">
<span>{{ item }}</span>
</div> -->
</div>-->
<!-- <facet-list v-bind:data="facets"></facet-list> -->
<div class="card" v-for="(valueArray, filterName, index) in facets" :key="index">
<facet-list :data="valueArray" :filterName="filterName"></facet-list>
</div>
<!-- <facet-list v-bind:data="facets"></facet-list> -->
<div class="card" v-for="(item, index) in facets" :key="index">
<facet-list :data="item.values" :filterName="item.filterName" @filter="onFilter"></facet-list>
</div>
</div>
</div>
<div class="eight columns right-bar">
<div id="right-bar" class="sidebar right-bar">
<!-- Search input section -->
<!-- Search input section -->
<div class="row">
<div class="twelve columns">
<vs-input @search="onSearch"></vs-input>
</div>
</div>
<div class="row">
<div class="active-filter-items twelve columns">
<a class="filter-link" v-for="(value, key, index) in activeFilterItems" :key="index">
<span>{{ key + ": " }}</span>
<span v-if="value && value.length > 0">{{ value.join(', ') }}</span>
</a>
</div>
</div>
<!-- Results section -->
<vs-results v-bind:data="results"></vs-results>
</div>
</div>
</div>

View File

@ -3,6 +3,7 @@ import VsInput from './text-search/vs-input.vue';
import VsResults from './search-results/vs-results.vue';
import FacetList from './search-results/facet-list.vue'
import rdrApi from './search-results/dataservice';
import FilterItem from './models/filter-item';
@Component({
components: {
@ -15,14 +16,65 @@ export default class App extends Vue {
results = [];
facets = [];
bar = 'bar';
searchTerm = '';
activeFilterItems = {};
async onFilter(filter) {
// console.log(filter.value);
// if (!this.activeFilterItems.some(e => e.value === filter.value)) {
// this.activeFilterItems.push(filter);
if (!this.activeFilterItems.hasOwnProperty(filter.Category)) {
this.activeFilterItems[filter.Category] = [];
}
if (!this.activeFilterItems[filter.Category].some(e => e === filter.value)) {
this.activeFilterItems[filter.Category].push(filter.value);
var res = await rdrApi.search(this.searchTerm, this.activeFilterItems);
this.results = res.response.docs;
// this.facets = res.facet_counts.facet_fields;
this.facets = [];
var facet_fields = res.facet_counts.facet_fields;
for (var prop in facet_fields) {
var facetValues = facet_fields[prop].map((facet, i) => {
if (i % 2 === 0) {
// var rObj = { value: facet, count: facet_fields[prop][i + 1] };
var rObj = new FilterItem(facet, facet_fields[prop][i + 1]);
return rObj;
}
}).filter(function (el) {
return el != null && el.count > 0;
});
this.facets.push({ filterName: prop, values: facetValues });
}
}
}
async onSearch(term) {
console.log(term);
// this.results = await rdrApi.search(term);
var res = await rdrApi.search(term);
// console.log(term);
// while (this.activeFilterItems.length > 0) {
// this.activeFilterItems.pop();
// }
this.activeFilterItems = {};
while (this.facets.length > 0) {
this.facets.pop();
}
this.searchTerm = term;
var res = await rdrApi.search(this.searchTerm, this.activeFilterItems);
this.results = res.response.docs;
this.facets = res.facet_counts.facet_fields;
var facet_fields = res.facet_counts.facet_fields;
for (var prop in facet_fields) {
var facetValues = facet_fields[prop].map((facet, i) => {
if (i % 2 === 0) {
//var rObj = { value: facet, count: facet_fields[prop][i + 1] };
var rObj = new FilterItem(facet, facet_fields[prop][i + 1])
return rObj;
}
}).filter(function (el) {
return el != null && el.count > 0;
});
this.facets.push({ filterName: prop, values: facetValues });
}
// console.log(this.facets.toString());
}

View File

@ -0,0 +1,25 @@
export default class FilterItem {
category;
value;
count;
active;
constructor(value, count) {
// this.category = category;
this.value = value;
this.count = count;
this.active = false;
this.category = "";
}
get Category() {
return this.category;
}
set Category(theCategory) {
this.category = theCategory;
}
set Active(isActive) {
this.active = isActive;
}
}

View File

@ -2,17 +2,31 @@ import axios from "axios";
export default {
async search(term) {
async search(term, filterItems) {
// solr endpoint
// const host = 'http://voyagerdemo.com/';
const host = 'https://repository.geologie.ac.at/';
const path = 'solr/rdr_data/select';
const fields = 'id,server_date_published,abstract_output,title_output,title_additional,author,subject'; // fields we want returned
const dismaxFields = "title^3 abstract^2 subject^1";
const facetFields = "facet.field=language&facet.field={!key=datatype}doctype";//&fq=year:(2019)";//&facet.query=year:2018";
const facetFields = "facet.field=language&facet.field={!key=datatype}doctype&facet.field=subject";//&fq=year:(2019)";//&facet.query=year:2018";
var filterFields = "";
// filterItems.forEach(function (item) {
// console.log(item.value + " " + item.category);
// filterFields += "&fq=" + item.category +":("+ item.value + ")";
// });
Object.entries(filterItems).forEach(([key, valueArray]) => {
// console.log(`${key} ${valueArray}`);
valueArray.forEach(function (value) {
filterFields += "&fq=" + key +":("+ value + ")";
});
});
// $dismax->setQueryFields('title^3 abstract^2 subject^1');
const api = `${host}${path}?defType=dismax&q=${term}&fl=${fields}&qf=${dismaxFields}&facet=on&${facetFields}&wt=json&rows=20&indent=on`;
const api = `${host}${path}?defType=dismax&q=${term}&fl=${fields}&qf=${dismaxFields}&facet=on&${facetFields}&${filterFields}&wt=json&rows=20&indent=on`;
const res = await axios.get(api);
return res.data;//.response;//.docs;

View File

@ -1,67 +1,79 @@
import { Component, Vue, Prop, Provide } from 'vue-property-decorator';
@Component
export default class FacetList extends Vue {
ITEMS_PER_FILTER = 5;
bar = 'bar';
ITEMS_PER_FILTER = 2;
bar = '';
collapsed = true;
// filterItems = [];
@Prop()
data;
@Prop()
@Prop([String])
filterName;
@Prop([String])
// alias;
get myLanguageFilters() {
// console.log(this.filterName);
// console.log(this.data);
var facetValues = this.data.map((facet, i) => {
if (i % 2 === 0) {
// var rObj = {};
// rObj['value'] = facet;
// rObj['count'] = solrArray[i +1];
var rObj = { value: facet, count: this.data[i + 1] };
return rObj;
}
}).filter(function (el) {
return el != null && el.count > 0;
});
// var facetValues = this.data.language.filter(function(facet, i) {
// return i % 2 === 0;
// }).map(function (facet, i) {
// var rObj = { value: facet, count: this.data.language[i + 1] };
// return rObj;
// }, this);
return facetValues;
};
get facets() {
return this.data;
};
get alias() {
return this.filterName == 'datatype' ? 'doctype' : this.filterName
}
// get filterItems() {
// var facetValues = this.data.map((facet, i) => {
// if (i % 2 === 0) {
// // var rObj = {};
// // rObj['value'] = facet;
// // rObj['count'] = solrArray[i +1];
// var rObj = { value: facet, count: this.data[i + 1], category: this.alias };
// return rObj;
// }
// }).filter(function (el) {
// return el != null && el.count > 0;
// });
// // var facetValues = this.data.language.filter(function(facet, i) {
// // return i % 2 === 0;
// // }).map(function (facet, i) {
// // var rObj = { value: facet, count: this.data.language[i + 1] };
// // return rObj;
// // }, this);
// return facetValues;
// }
get filterItems() {
var facetValues = this.data.map((facet, i) => {
if (i % 2 === 0) {
// var rObj = {};
// rObj['value'] = facet;
// rObj['count'] = solrArray[i +1];
var rObj = { value: facet, count: this.data[i + 1] };
return rObj;
}
}).filter(function (el) {
return el != null && el.count > 0;
});
// var facetValues = this.data.language.filter(function(facet, i) {
// return i % 2 === 0;
// }).map(function (facet, i) {
// var rObj = { value: facet, count: this.data.language[i + 1] };
// return rObj;
// }, this);
return facetValues;
};
return this.data;
}
get overflowing() {
//ko.observable(self.filterItems().length - self.activeFilterItems().length > ITEMS_PER_FILTER);
return (this.filterItems.length) > this.ITEMS_PER_FILTER;
}
get uncollapseLabelText() {
if (this.collapsed == true) {
// return myLabels.viewer.sidePanel.more; //"More results";
return "More results";
}
else {
// return myLabels.viewer.sidePanel.collapse; //"Collapse";
return "Collapse";
}
}
toggle = function () {
if (this.collapsed == true) {
this.collapsed = false;
}
else if (this.collapsed == false) {
this.collapsed = true;
//list.children("li:gt(4)").hide();
}
}
activateItem = function (filterItem) {
filterItem.Category = this.alias;
filterItem.Active = true;
this.$emit("filter", filterItem);
}
mounted() {
};
}
}

View File

@ -5,28 +5,24 @@
<div class="panel panel-primary">
<h3 class="panel-title filterViewModelName">{{ filterName }}</h3>
<!-- e.g.language -->
<ul
class="filter-items"
v-for="(value, index) in myLanguageFilters"
:key="index"
v-bind:class="{'limited':filterItems.length > 1}"
>
<li class="active" role="radio">
<input
<ul class="filter-items" v-bind:class="{'limited':filterItems.length > 1 && collapsed }">
<li v-for="(item, index) in filterItems" :key="index" class="list-group-item">
<!-- <input
class="css-w1gpbi"
name="language"
v-bind:id="value.value"
v-bind:id="item.value"
type="radio"
v-bind:value="value.value"
v-bind:value="item.value"
/>
<label :for="value.value">
<span>{{ value.value }} ({{ value.count }})</span>
</label>
<label :for="item.value">
<span click: @click="activateItem(item)">{{ item.value }} ({{ item.count }})</span>
</label>-->
<a :class="Active ? 'disabled' : ''" @click.prevent="activateItem(item)">{{ item.value }} ({{ item.count }})</a>
</li>
</ul>
<ul class="overflowing">
<ul class="overflowing" v-if="overflowing == true">
<li>
<span @click="toggle()"></span>
<span @click="toggle()">{{ uncollapseLabelText }}</span>
</li>
</ul>
</div>
@ -36,3 +32,11 @@
import FacetList from "./facet-list-class";
export default FacetList;
</script>
<style scoped>
/* local styles */
.disabled {
color: lightgrey;
pointer-events: none;
}
</style>