- vuejs solr faceted search
- with extra display for active filter items
This commit is contained in:
parent
c596a620cc
commit
a95282e49e
15 changed files with 261 additions and 120 deletions
|
@ -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>
|
||||
|
||||
<!-- <div class="card" v-for="item in facets.language" :key="item.id">
|
||||
<span>{{ item }}</span>
|
||||
</div> -->
|
||||
<h2 class="indexheader">DataXplore</h2>
|
||||
|
||||
<!-- <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>
|
||||
<!-- <div class="card" v-for="item in facets.language" :key="item.id">
|
||||
<span>{{ item }}</span>
|
||||
</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 -->
|
||||
|
||||
<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>
|
||||
|
|
|
@ -3,9 +3,10 @@ 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: {
|
||||
components: {
|
||||
VsInput,
|
||||
VsResults,
|
||||
FacetList
|
||||
|
@ -15,16 +16,67 @@ 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());
|
||||
}
|
||||
|
||||
|
||||
|
||||
mounted() {
|
||||
// console.log('Component mounted.')
|
||||
|
|
25
resources/assets/js/search/models/filter-item.js
Normal file
25
resources/assets/js/search/models/filter-item.js
Normal 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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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';
|
||||
// filterItems = [];
|
||||
|
||||
ITEMS_PER_FILTER = 2;
|
||||
bar = '';
|
||||
collapsed = true;
|
||||
// filterItems = [];
|
||||
|
||||
@Prop()
|
||||
data;
|
||||
@Prop()
|
||||
@Prop([String])
|
||||
filterName;
|
||||
|
||||
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;
|
||||
};
|
||||
@Prop([String])
|
||||
// alias;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
mounted() {
|
||||
};
|
||||
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() {
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
@ -35,4 +31,12 @@
|
|||
<script lang="ts">
|
||||
import FacetList from "./facet-list-class";
|
||||
export default FacetList;
|
||||
</script>
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* local styles */
|
||||
.disabled {
|
||||
color: lightgrey;
|
||||
pointer-events: none;
|
||||
}
|
||||
</style>
|
Loading…
Add table
editor.link_modal.header
Reference in a new issue