use crate::{search::*, util::*};
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
#[serde(remote = "Self")]
pub struct MoreLikeThisQuery {
#[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
fields: Option<Vec<String>>,
like: Vec<Like>,
#[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
unlike: Option<Vec<Like>>,
#[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
min_term_freq: Option<i64>,
#[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
max_query_terms: Option<i64>,
#[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
min_doc_freq: Option<i64>,
#[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
max_doc_freq: Option<i64>,
#[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
min_word_length: Option<i64>,
#[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
max_word_length: Option<i64>,
#[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
stop_words: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
analyzer: Option<String>,
#[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
minimum_should_match: Option<String>,
#[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
fail_on_unsupported_field: Option<bool>,
#[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
boost_terms: Option<f64>,
#[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
include: Option<bool>,
#[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
boost: Option<f32>,
#[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
_name: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(untagged)]
pub enum Like {
String(String),
Document(Document),
}
impl From<String> for Like {
fn from(value: String) -> Self {
Self::String(value)
}
}
impl<'a> From<&'a str> for Like {
fn from(value: &'a str) -> Self {
Self::String(value.into())
}
}
impl From<Document> for Like {
fn from(value: Document) -> Self {
Self::Document(value)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct Document {
_id: String,
#[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
_index: Option<String>,
#[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
_routing: Option<String>,
#[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
_source: Option<SourceFilter>,
#[serde(default, skip_serializing_if = "ShouldSkip::should_skip")]
_stored_fields: StoredFields,
}
impl Document {
pub fn new<T>(id: T) -> Self
where
T: ToString,
{
Self {
_id: id.to_string(),
_stored_fields: Default::default(),
_index: None,
_routing: None,
_source: None,
}
}
pub fn index<T>(mut self, index: T) -> Self
where
T: ToString,
{
self._index = Some(index.to_string());
self
}
pub fn routing<T>(mut self, routing: T) -> Self
where
T: ToString,
{
self._routing = Some(routing.to_string());
self
}
pub fn source<T>(mut self, source: T) -> Self
where
T: Into<SourceFilter>,
{
self._source = Some(source.into());
self
}
pub fn stored_fields<T>(mut self, stored_fields: T) -> Self
where
T: Into<StoredFields>,
{
self._stored_fields = stored_fields.into();
self
}
}
impl Query {
pub fn more_like_this<I>(like: I) -> MoreLikeThisQuery
where
I: IntoIterator,
I::Item: Into<Like>,
{
MoreLikeThisQuery {
like: like.into_iter().map(Into::into).collect(),
fields: None,
unlike: None,
min_term_freq: None,
max_query_terms: None,
min_doc_freq: None,
max_doc_freq: None,
min_word_length: None,
max_word_length: None,
stop_words: None,
analyzer: None,
minimum_should_match: None,
fail_on_unsupported_field: None,
boost_terms: None,
include: None,
boost: None,
_name: None,
}
}
}
impl MoreLikeThisQuery {
add_boost_and_name!();
pub fn fields<I>(mut self, fields: I) -> Self
where
I: IntoIterator,
I::Item: ToString,
{
self.fields = Some(fields.into_iter().map(|x| x.to_string()).collect());
self
}
pub fn unlike<I>(mut self, unlike: I) -> Self
where
I: IntoIterator,
I::Item: Into<Like>,
{
self.unlike = Some(unlike.into_iter().map(Into::into).collect());
self
}
pub fn max_query_terms(mut self, max_query_terms: i64) -> Self {
self.max_query_terms = Some(max_query_terms);
self
}
pub fn min_term_freq(mut self, min_term_freq: i64) -> Self {
self.min_term_freq = Some(min_term_freq);
self
}
pub fn min_doc_freq(mut self, min_doc_freq: i64) -> Self {
self.min_doc_freq = Some(min_doc_freq);
self
}
pub fn max_doc_freq(mut self, max_doc_freq: i64) -> Self {
self.max_doc_freq = Some(max_doc_freq);
self
}
pub fn min_word_length(mut self, min_word_length: i64) -> Self {
self.min_word_length = Some(min_word_length);
self
}
pub fn max_word_length(mut self, max_word_length: i64) -> Self {
self.max_word_length = Some(max_word_length);
self
}
pub fn stop_words<T>(mut self, stop_words: T) -> Self
where
T: IntoIterator,
T::Item: ToString,
{
self.stop_words = Some(stop_words.into_iter().map(|x| x.to_string()).collect());
self
}
pub fn analyzer<T>(mut self, analyzer: T) -> Self
where
T: ToString,
{
self.analyzer = Some(analyzer.to_string());
self
}
pub fn minimum_should_match<T>(mut self, minimum_should_match: T) -> Self
where
T: ToString,
{
self.minimum_should_match = Some(minimum_should_match.to_string());
self
}
pub fn fail_on_unsupported_field(mut self, fail_on_unsupported_field: bool) -> Self {
self.fail_on_unsupported_field = Some(fail_on_unsupported_field);
self
}
pub fn boost_terms<T>(mut self, boost_terms: T) -> Self
where
T: Into<f64>,
{
self.boost_terms = Some(boost_terms.into());
self
}
pub fn include(mut self, include: bool) -> Self {
self.include = Some(include);
self
}
}
impl ShouldSkip for MoreLikeThisQuery {
fn should_skip(&self) -> bool {
self.like.is_empty()
}
}
serialize_with_root!("more_like_this": MoreLikeThisQuery);
deserialize_with_root!("more_like_this": MoreLikeThisQuery);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn serialization() {
assert_serialize_query(
Query::more_like_this(["test"]).fields(["title"]),
json!({
"more_like_this": {
"fields": ["title"],
"like": [
"test"
]
}
}),
);
assert_serialize_query(
Query::more_like_this(["test"])
.fields(["title", "description"])
.min_term_freq(1)
.max_query_terms(12)
.boost(1.2)
.name("more_like_this"),
json!({
"more_like_this": {
"fields": ["title", "description"],
"like": [
"test"
],
"min_term_freq": 1,
"max_query_terms": 12,
"boost": 1.2,
"_name": "more_like_this"
}
}),
);
assert_serialize_query(
Query::more_like_this([Document::new("123")]).fields(["title"]),
json!({
"more_like_this": {
"fields": ["title"],
"like": [
{
"_id": "123"
}
]
}
}),
);
assert_serialize_query(
Query::more_like_this([Document::new("123")])
.fields(["title", "description"])
.min_term_freq(1)
.max_query_terms(12)
.boost(1.2)
.name("more_like_this"),
json!({
"more_like_this": {
"fields": ["title", "description"],
"like": [
{
"_id": "123"
}
],
"min_term_freq": 1,
"max_query_terms": 12,
"boost": 1.2,
"_name": "more_like_this"
}
}),
);
assert_serialize_query(
Query::more_like_this([Like::from(Document::new("123")), Like::from("test")])
.fields(["title"]),
json!({
"more_like_this": {
"fields": ["title"],
"like": [
{
"_id": "123"
},
"test"
]
}
}),
);
assert_serialize_query(
Query::more_like_this([
Like::from(
Document::new("123")
.index("test_index")
.routing("test_routing")
.source(false),
),
Like::from("test"),
])
.fields(["title", "description"])
.min_term_freq(1)
.max_query_terms(12)
.boost(1.2)
.name("more_like_this"),
json!({
"more_like_this": {
"fields": ["title", "description"],
"like": [
{
"_id": "123",
"_index": "test_index",
"_routing": "test_routing",
"_source": false
},
"test"
],
"min_term_freq": 1,
"max_query_terms": 12,
"boost": 1.2,
"_name": "more_like_this"
}
}),
);
}
}