Skip to content
This repository was archived by the owner on Jan 20, 2023. It is now read-only.

GROVE-334 : adds 'show more' link to show more facets in the Vue template #8

Open
wants to merge 4 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Copyright © 2018 MarkLogic Corporation.
Copyright © 2019 MarkLogic Corporation.

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

Expand Down
2 changes: 1 addition & 1 deletion NOTICE.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Grove

Copyright © 2018 MarkLogic Corporation.
Copyright © 2019 MarkLogic Corporation.

This project and its code and functionality is not representative of MarkLogic Server and is not supported by MarkLogic.

Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "grove-vue-ui",
"name": "@marklogic-community/grove-vue-ui",
"version": "1.0.0-rc.1",
"private": true,
"grove": {
Expand Down
31 changes: 31 additions & 0 deletions src/api/SearchApi.js
Original file line number Diff line number Diff line change
Expand Up @@ -169,5 +169,36 @@ export default {
return error;
}
);
},
getSimilar(uri) {
let custom = {
"query": {
"queries": [{
"custom-constraint-query": {
"constraint-name": "similar",
"text": uri
}
}]
}
};

return fetch('/api/search/similar', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json'
},
body: JSON.stringify(custom),
credentials: 'same-origin'
}).then(
response => {
return response.json().then(function(json) {
return { response: json };
});
},
error => {
return error;
}
);
}
};
114 changes: 114 additions & 0 deletions src/api/ValuesApi.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { polyfill } from 'es6-promise';
import 'isomorphic-fetch';

polyfill();

export default {
name: 'ValuesApi',

getValues(name, params, options, searchState, qtext, facetObject) {
//get options
return fetch('/v1/config/query/all?format=json', {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should make a call to /search/{type}/config.. (see comments in grove-node PR)

method: 'GET',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json'
},
credentials: 'same-origin'
}).then(
response => {
return response.json().then(function(json) {
let options = json.options || {};

let path = '';
let collation = '';
for (let i=0; i < options.constraint.length; i++) {
if (options.constraint[i].name === name){
path = options.constraint[i].range['path-index'].text;
collation = options.constraint[i].range.collation;
}
}

options.values = {
name: name,
range: {
collation: collation,
type: 'xs:string',
'path-index': {
text: path,
ns: ''
}
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is better to just copy the entire range object from the constraint. Might not be string, might not be path index..

'values-option' : ['frequency-order']
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

copy these from facet-options.. some facets use item-order for sure..

};
//combine with search
let searchType = 'all';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

searchType should be provided as param, like in searchApi..

// let searchType = searchType !== undefined ? searchType : 'all';
let start = facetObject.facetValues.length +1 || 1;
// let start = 1;
let pageLength = 10;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how about using facet limit as page-length?

// var limit = 100;
var limit = start + pageLength - 1;

let facets = Object.keys(searchState.activeFacets || {}).map(function(facetName) {
let constraintType = searchState.activeFacets[facetName].type;
if (constraintType && constraintType.substring(0, 3) === 'xs:') {
constraintType = 'range';
}
let temp = {
'range-constraint-query' :{
'constraint-name': facetName,
constraintType: constraintType
}
};
searchState.activeFacets[facetName].values.map(function(facetValue) {
temp.value = [facetValue.value];
if (facetValue.negated) {
temp['range-operator'] = 'NE';
}
});

return temp;
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think latest searchApi in development branch has captured this in a helper function that you could expose and reuse here..

let valuesParams = new URLSearchParams();
valuesParams.append('q', qtext);
valuesParams.append('start', start);
valuesParams.append('pageLength', pageLength);
valuesParams.append('limit', limit);
valuesParams.append('direction', 'descending');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

direction should match direction of facet..

let valuesParamsString = valuesParams.toString();

return fetch('/v1/values/' + name + '?'+valuesParamsString, {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

similar to getting of config, make a call to /search/{type}/values instead. Consider passing above params through via post body rather than request params, and rewrite them in middle-tier to request params, though perhaps something for bonus..

method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json'
},
body: JSON.stringify({
search: {
query: {
queries: {
and: [].concat(facets)
}},
options: options
}}),
credentials: 'same-origin'
}).then(
response => {
return response.json().then(function(json) {

return { response: json };
});
},
error => {
return error;
}
);
});
},
error => {
return error;
}
);
}
};
4 changes: 2 additions & 2 deletions src/components/ml-search/ml-facets.vue
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
</span>
<i v-if="!!negate" class="fa fa-ban facet-add-neg" v-on:click.prevent="negate(facetName, facet.type, value.name)" :title="value.name"></i>
</div>
<div v-if="!!showMore && !facet.displayingAll">
<a href v-on:click.prevent="showMore(facetName)">see more ...</a>
<div v-if="!!showMore && !facet.displayingAll && facet.type.startsWith('xs:')">
<a href v-on:click.prevent="showMore(facet, facetName)">show more ...</a>
</div>
</div>
</div>
Expand Down
32 changes: 13 additions & 19 deletions src/components/ml-similar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
</template>

<script>
import SearchApi from "../api/SearchApi";

export default {
name: 'ml-similar',
props: {
Expand All @@ -35,28 +37,20 @@ export default {
updateSimilar() {
if (this.uri) {
this.loading = true;
// TODO: fetching similar docs needs a better middleware/backend solution
this.$http({
method: 'GET',
url: '/v1/resources/extsimilar',
params: {
'rs:uri': this.uri
},
auth: {
username: this.$store.state.auth.username,
password: this.$store.state.auth.password,
sendImmediately: true
}
}).then(
response => {
this.similar = response.data.similar;

SearchApi.getSimilar(this.uri).then(result => {
console.log('getSimilar',result);

if (result.response) {
this.similar = result.response.results;
this.loading = false;
},
error => {
console.log(error);
} else {
// error
this.loading = false;
return result;
}
);
});

}
},
resultLabel(result) {
Expand Down
48 changes: 48 additions & 0 deletions src/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Vuex from 'vuex';
import authApi from './api/AuthApi';
import searchApi from './api/SearchApi';
import crudApi from './api/CRUDApi';
import ValuesApi from './api/ValuesApi.js';

Vue.use(Vuex);

Expand Down Expand Up @@ -156,6 +157,38 @@ function typedSearchState(searchType) {
state.facets = response.facets;
}
},
setFacetValue(state, { facetName, facets }) {
if (facets) {
let values = state.facets[facetName].facetValues;

if (facets) {
facets.forEach(function(newFacetValue) {
//check if not existing
for (let i = 0; i < values.length; i++) {
if (values[i].name === newFacetValue._value) {
state.facets[facetName].displayingAll = true;
return;
}
}
values.push({
name: newFacetValue._value || 'blank',
count: facetName.endsWith('*')?'':newFacetValue.frequency,
value: newFacetValue._value || ''
});
});
}

state.facets[facetName].facetValues = values;
} else {
state.facets[facetName].displayingAll = true;
return;
}
},
setFacetDisplayingAll(state, { facetName }) {
state.facets[facetName].displayingAll = true;
//cloning data to force UI update
state.facets[facetName] = {...state.facets[facetName]};
},
setText(state, { qtext }) {
if (qtext !== undefined) {
state.qtext = qtext;
Expand Down Expand Up @@ -298,6 +331,21 @@ function typedSearchState(searchType) {
return result;
}
});
},
showMore({ commit, state }, {facet, facetName}) {
let params = {};
let options = {};
ValuesApi.getValues(facetName, params, options,
state, state.qtext, facet).then( response => {
let newFacets = response.response && response.response['values-response'] && response.response['values-response']['distinct-value'];

if (newFacets) {
commit('setFacetValue', {facetName, facets: newFacets});
} else {
commit('setFacetDisplayingAll', {facetName});
}

});
}
},
getters: {
Expand Down
12 changes: 11 additions & 1 deletion src/views/SearchPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<ml-input :qtext="qtext" :search="search" :suggest="suggest" class="search"></ml-input>
</div>
<div class="col-xs-12 col-sm-4 col-md-3 facets-col">
<ml-facets v-if="facets" :facets="facets" :toggle="toggleFacet" :active-facets="activeFacets" :negate="toggleNegatedFacet"></ml-facets>
<ml-facets v-if="facets" :facets="facets" :toggle="toggleFacet" :active-facets="activeFacets" :negate="toggleNegatedFacet" :showMore="showMore"></ml-facets>
</div>
<div class="col-xs-12 col-sm-8 col-md-9 results-col">
<i class="fa fa-refresh pull-right" :class="searchPending ? 'fa-spin' : ''"
Expand Down Expand Up @@ -109,6 +109,16 @@ export default {
}
},
methods: {
showMore(facet, facetName) {
if (facet.displayingAll) {
return;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be unnecessary I think. the button should simply not show if displayingAll is true..

this.$store
.dispatch('search/' + this.type + '/showMore', {facet, facetName})
.then(() => {
this.searchPending = false;
});
},
toggleFacet(facet, type, value) {
console.log('Toggle ' + facet + ' ' + type + ' ' + value);
this.searchPending = true;
Expand Down