mirror of
https://github.com/meilisearch/MeiliSearch
synced 2024-11-27 07:14:26 +01:00
Introduce a new facet filters query field
This commit is contained in:
parent
afa86d8a45
commit
33945a3115
627
http-ui/Cargo.lock
generated
627
http-ui/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -22,6 +22,7 @@ tempfile = "3.1.0"
|
|||||||
askama = "0.10.1"
|
askama = "0.10.1"
|
||||||
askama_warp = "0.10.0"
|
askama_warp = "0.10.0"
|
||||||
bytes = "0.5.6"
|
bytes = "0.5.6"
|
||||||
|
either = "1.6.1"
|
||||||
flate2 = "1.0.19"
|
flate2 = "1.0.19"
|
||||||
futures = "0.3.6"
|
futures = "0.3.6"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
@ -2,9 +2,10 @@ var request = null;
|
|||||||
var timeoutID = null;
|
var timeoutID = null;
|
||||||
var selected_facets = {};
|
var selected_facets = {};
|
||||||
|
|
||||||
$('#query, #facet').on('input', function () {
|
$('#query, #filters').on('input', function () {
|
||||||
var query = $('#query').val();
|
var query = $('#query').val();
|
||||||
var facet = $('#facet').val();
|
var filters = $('#filters').val();
|
||||||
|
var facet_filters = selectedFacetsToArray(selected_facets);
|
||||||
var timeoutMs = 100;
|
var timeoutMs = 100;
|
||||||
|
|
||||||
if (timeoutID !== null) {
|
if (timeoutID !== null) {
|
||||||
@ -17,7 +18,10 @@ $('#query, #facet').on('input', function () {
|
|||||||
url: "query",
|
url: "query",
|
||||||
contentType: 'application/json',
|
contentType: 'application/json',
|
||||||
data: JSON.stringify({
|
data: JSON.stringify({
|
||||||
'query': query, 'facetCondition': facet, "facetDistribution": true
|
'query': query,
|
||||||
|
'filters': filters,
|
||||||
|
'facetFilters': facet_filters,
|
||||||
|
"facetDistribution": true,
|
||||||
}),
|
}),
|
||||||
contentType: 'application/json',
|
contentType: 'application/json',
|
||||||
success: function (data, textStatus, request) {
|
success: function (data, textStatus, request) {
|
||||||
@ -41,7 +45,20 @@ $('#query, #facet').on('input', function () {
|
|||||||
|
|
||||||
// Create the select element
|
// Create the select element
|
||||||
let select = $(`<select data-facet-name='${facet_name}' multiple size=\"8\"></select>`);
|
let select = $(`<select data-facet-name='${facet_name}' multiple size=\"8\"></select>`);
|
||||||
for (value of data.facets[facet_name]) {
|
let selected_values = selected_facets[facet_name] || [];
|
||||||
|
// Create the previously selected facets (mark them as selected)
|
||||||
|
for (value of selected_values) {
|
||||||
|
let option = $('<option></option>')
|
||||||
|
.text(value)
|
||||||
|
.attr('selected', "selected")
|
||||||
|
.attr('value', value)
|
||||||
|
.attr('title', value);
|
||||||
|
select.append(option);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the newly discovered facets
|
||||||
|
let diff = diffArray(data.facets[facet_name], selected_values);
|
||||||
|
for (value of diff) {
|
||||||
let option = $('<option></option>')
|
let option = $('<option></option>')
|
||||||
.text(value)
|
.text(value)
|
||||||
.attr('value', value)
|
.attr('value', value)
|
||||||
@ -53,7 +70,6 @@ $('#query, #facet').on('input', function () {
|
|||||||
$('#facets').append(div);
|
$('#facets').append(div);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
for (element of data.documents) {
|
for (element of data.documents) {
|
||||||
const elem = document.createElement('li');
|
const elem = document.createElement('li');
|
||||||
elem.classList.add("document");
|
elem.classList.add("document");
|
||||||
@ -87,8 +103,8 @@ $('#query, #facet').on('input', function () {
|
|||||||
$('#facets select').on('change', function(e) {
|
$('#facets select').on('change', function(e) {
|
||||||
let facet_name = $(this).attr('data-facet-name');
|
let facet_name = $(this).attr('data-facet-name');
|
||||||
selected_facets[facet_name] = $(this).val();
|
selected_facets[facet_name] = $(this).val();
|
||||||
|
$('#query').trigger('input');
|
||||||
});
|
});
|
||||||
|
|
||||||
},
|
},
|
||||||
beforeSend: function () {
|
beforeSend: function () {
|
||||||
if (request !== null) {
|
if (request !== null) {
|
||||||
@ -100,6 +116,25 @@ $('#query, #facet').on('input', function () {
|
|||||||
}, timeoutMs);
|
}, timeoutMs);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function diffArray(arr1, arr2) {
|
||||||
|
return arr1.concat(arr2).filter(function (val) {
|
||||||
|
if (!(arr1.includes(val) && arr2.includes(val)))
|
||||||
|
return val;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectedFacetsToArray(facets_obj) {
|
||||||
|
var array = [];
|
||||||
|
for (const facet_name in facets_obj) {
|
||||||
|
var subarray = [];
|
||||||
|
for (const facet_value of facets_obj[facet_name]) {
|
||||||
|
subarray.push(`${facet_name}:${facet_value}`);
|
||||||
|
}
|
||||||
|
array.push(subarray);
|
||||||
|
}
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
// Make the number of document a little bit prettier
|
// Make the number of document a little bit prettier
|
||||||
$('#docs-count').text(function(index, text) {
|
$('#docs-count').text(function(index, text) {
|
||||||
return parseInt(text).toLocaleString()
|
return parseInt(text).toLocaleString()
|
||||||
|
@ -11,6 +11,7 @@ use std::{mem, io};
|
|||||||
|
|
||||||
use askama_warp::Template;
|
use askama_warp::Template;
|
||||||
use byte_unit::Byte;
|
use byte_unit::Byte;
|
||||||
|
use either::Either;
|
||||||
use flate2::read::GzDecoder;
|
use flate2::read::GzDecoder;
|
||||||
use futures::stream;
|
use futures::stream;
|
||||||
use futures::{FutureExt, StreamExt};
|
use futures::{FutureExt, StreamExt};
|
||||||
@ -620,12 +621,29 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
.body(include_str!("../public/logo-black.svg"))
|
.body(include_str!("../public/logo-black.svg"))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
enum UntaggedEither<L, R> {
|
||||||
|
Left(L),
|
||||||
|
Right(R),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L, R> From<UntaggedEither<L, R>> for Either<L, R> {
|
||||||
|
fn from(value: UntaggedEither<L, R>) -> Either<L, R> {
|
||||||
|
match value {
|
||||||
|
UntaggedEither::Left(left) => Either::Left(left),
|
||||||
|
UntaggedEither::Right(right) => Either::Right(right),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
struct QueryBody {
|
struct QueryBody {
|
||||||
query: Option<String>,
|
query: Option<String>,
|
||||||
facet_condition: Option<String>,
|
filters: Option<String>,
|
||||||
|
facet_filters: Option<Vec<UntaggedEither<Vec<String>, String>>>,
|
||||||
facet_distribution: Option<bool>,
|
facet_distribution: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -651,12 +669,33 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
if let Some(query) = query.query {
|
if let Some(query) = query.query {
|
||||||
search.query(query);
|
search.query(query);
|
||||||
}
|
}
|
||||||
if let Some(condition) = query.facet_condition {
|
|
||||||
if !condition.trim().is_empty() {
|
let filters = match query.filters {
|
||||||
let condition = FacetCondition::from_str(&rtxn, &index, &condition).unwrap();
|
Some(condition) if !condition.trim().is_empty() => {
|
||||||
|
Some(FacetCondition::from_str(&rtxn, &index, &condition).unwrap())
|
||||||
|
},
|
||||||
|
_otherwise => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let facet_filters = match query.facet_filters {
|
||||||
|
Some(array) => {
|
||||||
|
let eithers = array.into_iter().map(Into::into);
|
||||||
|
FacetCondition::from_array(&rtxn, &index, eithers).unwrap()
|
||||||
|
},
|
||||||
|
_otherwise => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let condition = match (filters, facet_filters) {
|
||||||
|
(Some(filters), Some(facet_filters)) => {
|
||||||
|
Some(FacetCondition::And(Box::new(filters), Box::new(facet_filters)))
|
||||||
|
},
|
||||||
|
(Some(condition), None) | (None, Some(condition)) => Some(condition),
|
||||||
|
_otherwise => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(condition) = condition {
|
||||||
search.facet_condition(condition);
|
search.facet_condition(condition);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let SearchResult { found_words, candidates, documents_ids } = search.execute().unwrap();
|
let SearchResult { found_words, candidates, documents_ids } = search.execute().unwrap();
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@
|
|||||||
<div class="level-item">
|
<div class="level-item">
|
||||||
<div class="field has-addons has-addons-right">
|
<div class="field has-addons has-addons-right">
|
||||||
<input id="query" class="input" type="text" autofocus placeholder="e.g. George Clooney">
|
<input id="query" class="input" type="text" autofocus placeholder="e.g. George Clooney">
|
||||||
<input id="facet" class="input" type="text" placeholder="facet filter like released >= 1577836800">
|
<input id="filters" class="input" type="text" placeholder="filters like released >= 1577836800">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="level-item"></div>
|
<div class="level-item"></div>
|
||||||
|
Loading…
Reference in New Issue
Block a user