use std::collections::HashMap;
use std::hash::Hash;
use reqwest::StatusCode;
use serde::Serialize;
use serde_json::{Map, Value};
use crate::{error::EsError, operations::GenericResult, Client, EsResponse};
pub type DocType<'a> = HashMap<&'a str, HashMap<&'a str, &'a str>>;
pub type Mapping<'a> = HashMap<&'a str, DocType<'a>>;
#[derive(Debug, Serialize)]
pub struct Settings {
pub number_of_shards: u32,
pub analysis: Analysis,
}
#[derive(Debug, Serialize)]
pub struct Analysis {
pub filter: Map<String, Value>,
pub analyzer: Map<String, Value>,
}
#[derive(Debug)]
pub struct MappingOperation<'a, 'b> {
client: &'a mut Client,
index: &'b str,
mapping: Option<&'b Mapping<'b>>,
settings: Option<&'b Settings>,
}
impl<'a, 'b> MappingOperation<'a, 'b> {
pub fn new(client: &'a mut Client, index: &'b str) -> MappingOperation<'a, 'b> {
MappingOperation {
client: client,
index: index,
mapping: None,
settings: None,
}
}
pub fn with_mapping(&'b mut self, mapping: &'b Mapping) -> &'b mut Self {
self.mapping = Some(mapping);
self
}
pub fn with_settings(&'b mut self, settings: &'b Settings) -> &'b mut Self {
self.settings = Some(settings);
self
}
pub fn send(&'b mut self) -> Result<MappingResult, EsError> {
if self.mapping.is_none() && self.settings.is_none() {
return Ok(MappingResult);
}
if self.settings.is_some() {
let body = hashmap("settings", self.settings.unwrap());
let url = self.index.to_owned();
let _ = self.client.put_body_op(&url, &body)?;
let _ = self.client.wait_for_status("yellow", "5s");
}
if self.mapping.is_some() {
let _ = self.client.close_index(self.index);
for (entity, properties) in self.mapping.unwrap().iter() {
let body = hashmap("properties", properties);
let url = format!("{}/_mapping/{}", self.index, entity);
let _ = self.client.put_body_op(&url, &body)?;
}
let _ = self.client.open_index(self.index);
}
Ok(MappingResult)
}
}
impl Client {
pub fn open_index<'a>(&'a mut self, index: &'a str) -> Result<GenericResult, EsError> {
let url = format!("{}/_open", index);
let response = self.post_op(&url)?;
match response.status_code() {
StatusCode::OK => Ok(response.read_response()?),
status_code => Err(EsError::EsError(format!(
"Unexpected status: {}",
status_code
))),
}
}
pub fn close_index<'a>(&'a mut self, index: &'a str) -> Result<GenericResult, EsError> {
let url = format!("{}/_close", index);
let response = self.post_op(&url)?;
match response.status_code() {
StatusCode::OK => Ok(response.read_response()?),
status_code => Err(EsError::EsError(format!(
"Unexpected status: {}",
status_code
))),
}
}
pub fn wait_for_status<'a>(
&'a mut self,
status: &'a str,
timeout: &'a str,
) -> Result<(), EsError> {
let url = format!(
"_cluster/health?wait_for_status={}&timeout={}",
status, timeout
);
let response = self.get_op(&url)?;
match response.status_code() {
StatusCode::OK => Ok(()),
status_code => Err(EsError::EsError(format!(
"Unexpected status: {}",
status_code
))),
}
}
}
#[derive(Debug)]
pub struct MappingResult;
#[cfg(test)]
pub mod tests {
use super::*;
#[derive(Debug, Serialize)]
pub struct Author {
pub name: String,
}
#[test]
fn test_mapping() {
let index_name = "tests_test_mapping";
let mut client = crate::tests::make_client();
let _ = client.delete_index(index_name);
let mapping = hashmap2(
"post",
hashmap2(
"created_at",
hashmap2("type", "date", "format", "date_time"),
"title",
hashmap2("type", "string", "index", "not_analyzed"),
),
"author",
hashmap("name", hashmap("type", "string")),
);
let settings = Settings {
number_of_shards: 1,
analysis: Analysis {
filter: serde_json::json! ({
"autocomplete_filter": {
"type": "edge_ngram",
"min_gram": 1,
"max_gram": 2,
}
})
.as_object()
.expect("by construction 'autocomplete_filter' should be a map")
.clone(),
analyzer: serde_json::json! ({
"autocomplete": {
"type": "custom",
"tokenizer": "standard",
"filter": [ "lowercase", "autocomplete_filter"]
}
})
.as_object()
.expect("by construction 'autocomplete' should be a map")
.clone(),
},
};
let result = MappingOperation::new(&mut client, index_name)
.with_mapping(&mapping)
.with_settings(&settings)
.send();
assert!(result.is_ok());
{
let result_wrapped = client
.index(index_name, "post")
.with_doc(&Author {
name: "Homu".to_owned(),
})
.send();
assert!(result_wrapped.is_ok());
let result = result_wrapped.unwrap();
assert!(result.created);
}
}
}
fn hashmap<K, V>(k: K, v: V) -> HashMap<K, V>
where
K: Eq + Hash,
{
let mut m = HashMap::with_capacity(1);
m.insert(k, v);
m
}
#[allow(dead_code)]
fn hashmap2<K, V>(k1: K, v1: V, k2: K, v2: V) -> HashMap<K, V>
where
K: Eq + Hash,
{
let mut m = HashMap::with_capacity(2);
m.insert(k1, v1);
m.insert(k2, v2);
m
}