MeiliSearch/meilisearch-http/src/routes/index.rs

426 lines
12 KiB
Rust
Raw Normal View History

2020-04-10 19:05:05 +02:00
use actix_web as aweb;
use actix_web::{delete, get, post, web, HttpResponse};
use chrono::{DateTime, Utc};
2019-11-20 17:28:46 +01:00
use log::error;
2020-04-10 19:05:05 +02:00
use meilisearch_core::UpdateStatus;
use rand::seq::SliceRandom;
use serde::{Deserialize, Serialize};
2019-10-31 15:00:36 +01:00
use crate::error::ResponseError;
use crate::routes::IndexParam;
2020-04-10 19:05:05 +02:00
use crate::Data;
2019-10-31 15:00:36 +01:00
fn generate_uid() -> String {
let mut rng = rand::thread_rng();
let sample = b"abcdefghijklmnopqrstuvwxyz0123456789";
sample
.choose_multiple(&mut rng, 8)
.map(|c| *c as char)
.collect()
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct IndexResponse {
name: String,
uid: String,
created_at: DateTime<Utc>,
updated_at: DateTime<Utc>,
primary_key: Option<String>,
}
2019-11-20 11:24:08 +01:00
#[get("/indexes")]
2020-04-10 19:05:05 +02:00
pub async fn list_indexes(data: web::Data<Data>) -> aweb::Result<web::Json<Vec<IndexResponse>>> {
let reader = data
.db
.main_read_txn()
2020-04-09 10:39:34 +02:00
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let mut response_body = Vec::new();
for index_uid in data.db.indexes_uids() {
let index = data.db.open_index(&index_uid);
2019-11-20 17:28:46 +01:00
match index {
Some(index) => {
2020-04-10 19:05:05 +02:00
let name = index
.main
.name(&reader)
.map_err(|e| ResponseError::Internal(e.to_string()))?
2020-04-10 19:05:05 +02:00
.ok_or(ResponseError::Internal(
"Impossible to get the name of an index".to_string(),
))?;
let created_at = index
.main
.created_at(&reader)
.map_err(|e| ResponseError::Internal(e.to_string()))?
2020-04-10 19:05:05 +02:00
.ok_or(ResponseError::Internal(
"Impossible to get the create date of an index".to_string(),
))?;
let updated_at = index
.main
.updated_at(&reader)
.map_err(|e| ResponseError::Internal(e.to_string()))?
2020-04-10 19:05:05 +02:00
.ok_or(ResponseError::Internal(
"Impossible to get the last update date of an index".to_string(),
))?;
2019-11-20 17:28:46 +01:00
let primary_key = match index.main.schema(&reader) {
Ok(Some(schema)) => match schema.primary_key() {
Some(primary_key) => Some(primary_key.to_owned()),
2020-03-10 11:29:56 +01:00
None => None,
},
2020-02-26 18:49:17 +01:00
_ => None,
};
let index_response = IndexResponse {
2019-11-20 17:28:46 +01:00
name,
uid: index_uid,
created_at,
updated_at,
primary_key,
2019-11-20 17:28:46 +01:00
};
response_body.push(index_response);
2019-11-20 17:28:46 +01:00
}
None => error!(
"Index {} is referenced in the indexes list but cannot be found",
index_uid
),
}
}
Ok(web::Json(response_body))
2019-11-19 17:38:02 +01:00
}
#[get("/indexes/{index_uid}")]
pub async fn get_index(
data: web::Data<Data>,
path: web::Path<IndexParam>,
2020-04-09 10:39:34 +02:00
) -> aweb::Result<web::Json<IndexResponse>> {
2020-04-10 19:05:05 +02:00
let index = data
.db
.open_index(path.index_uid.clone())
.ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?;
2019-11-19 17:38:02 +01:00
2020-04-10 19:05:05 +02:00
let reader = data
.db
.main_read_txn()
2020-04-09 10:39:34 +02:00
.map_err(|err| ResponseError::Internal(err.to_string()))?;
2019-11-19 17:38:02 +01:00
2020-04-10 19:05:05 +02:00
let name = index
.main
.name(&reader)
.map_err(|e| ResponseError::Internal(e.to_string()))?
2020-04-10 19:05:05 +02:00
.ok_or(ResponseError::Internal(
"Impossible to get the name of an index".to_string(),
))?;
let created_at = index
.main
.created_at(&reader)
.map_err(|e| ResponseError::Internal(e.to_string()))?
2020-04-10 19:05:05 +02:00
.ok_or(ResponseError::Internal(
"Impossible to get the create date of an index".to_string(),
))?;
let updated_at = index
.main
.updated_at(&reader)
.map_err(|e| ResponseError::Internal(e.to_string()))?
2020-04-10 19:05:05 +02:00
.ok_or(ResponseError::Internal(
"Impossible to get the last update date of an index".to_string(),
))?;
2019-11-19 17:38:02 +01:00
let primary_key = match index.main.schema(&reader) {
Ok(Some(schema)) => match schema.primary_key() {
Some(primary_key) => Some(primary_key.to_owned()),
2020-03-10 11:29:56 +01:00
None => None,
},
2020-02-26 18:49:17 +01:00
_ => None,
};
Ok(web::Json(IndexResponse {
2019-11-19 17:38:02 +01:00
name,
uid: path.index_uid.clone(),
2019-11-19 17:38:02 +01:00
created_at,
updated_at,
primary_key,
}))
2019-11-19 17:38:02 +01:00
}
2019-11-20 17:28:46 +01:00
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct IndexCreateRequest {
name: Option<String>,
uid: Option<String>,
primary_key: Option<String>,
}
#[post("/indexes")]
pub async fn create_index(
data: web::Data<Data>,
2020-04-10 19:05:05 +02:00
body: web::Json<IndexCreateRequest>,
2020-04-09 10:39:34 +02:00
) -> aweb::Result<web::Json<IndexResponse>> {
if let (None, None) = (body.name.clone(), body.uid.clone()) {
2020-04-10 19:05:05 +02:00
return Err(
ResponseError::BadRequest("Index creation must have an uid".to_string()).into(),
);
}
let uid = match body.uid.clone() {
2020-03-05 11:44:30 +01:00
Some(uid) => {
2020-03-10 11:29:56 +01:00
if uid
.chars()
.all(|x| x.is_ascii_alphanumeric() || x == '-' || x == '_')
{
2020-03-05 11:44:30 +01:00
uid
} else {
2020-04-09 10:39:34 +02:00
return Err(ResponseError::InvalidIndexUid.into());
2020-03-05 11:44:30 +01:00
}
2020-03-10 11:29:56 +01:00
}
None => loop {
let uid = generate_uid();
if data.db.open_index(&uid).is_none() {
break uid;
}
},
};
2020-04-10 19:05:05 +02:00
let created_index = data
.db
.create_index(&uid)
.map_err(|e| ResponseError::CreateIndex(e.to_string()))?;
2020-04-10 19:05:05 +02:00
let mut writer = data
.db
.main_write_txn()
2020-04-09 10:39:34 +02:00
.map_err(|err| ResponseError::Internal(err.to_string()))?;
let name = body.name.clone().unwrap_or(uid.clone());
2020-04-10 19:05:05 +02:00
created_index
.main
.put_name(&mut writer, &name)
.map_err(|e| ResponseError::Internal(e.to_string()))?;
2019-10-31 15:00:36 +01:00
2020-01-23 11:30:18 +01:00
let created_at = created_index
.main
.created_at(&writer)
.map_err(|e| ResponseError::Internal(e.to_string()))?
.ok_or(ResponseError::Internal("".to_string()))?;
2020-01-23 11:30:18 +01:00
let updated_at = created_index
.main
.updated_at(&writer)
.map_err(|e| ResponseError::Internal(e.to_string()))?
.ok_or(ResponseError::Internal("".to_string()))?;
if let Some(id) = body.primary_key.clone() {
2020-04-10 19:05:05 +02:00
if let Some(mut schema) = created_index
.main
.schema(&writer)
.map_err(|e| ResponseError::Internal(e.to_string()))?
{
schema
.set_primary_key(&id)
.map_err(|e| ResponseError::BadRequest(e.to_string()))?;
2020-04-10 19:05:05 +02:00
created_index
.main
.put_schema(&mut writer, &schema)
.map_err(|e| ResponseError::Internal(e.to_string()))?;
}
2020-01-28 17:45:29 +01:00
}
2020-04-10 19:05:05 +02:00
writer
.commit()
2020-04-09 10:39:34 +02:00
.map_err(|err| ResponseError::Internal(err.to_string()))?;
2019-10-31 15:00:36 +01:00
Ok(web::Json(IndexResponse {
2020-01-31 10:50:28 +01:00
name,
uid,
created_at,
updated_at,
primary_key: body.primary_key.clone(),
}))
2019-10-31 15:00:36 +01:00
}
2019-11-20 17:28:46 +01:00
#[derive(Debug, Deserialize)]
2019-11-20 15:00:06 +01:00
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct UpdateIndexRequest {
name: Option<String>,
primary_key: Option<String>,
2019-11-20 15:00:06 +01:00
}
2019-11-20 17:28:46 +01:00
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct UpdateIndexResponse {
2019-11-20 15:00:06 +01:00
name: String,
uid: String,
created_at: DateTime<Utc>,
updated_at: DateTime<Utc>,
primary_key: Option<String>,
2019-11-20 15:00:06 +01:00
}
#[post("/indexes/{index_uid}")]
pub async fn update_index(
data: web::Data<Data>,
path: web::Path<IndexParam>,
2020-04-10 19:05:05 +02:00
body: web::Json<IndexCreateRequest>,
2020-04-09 10:39:34 +02:00
) -> aweb::Result<web::Json<IndexResponse>> {
2020-04-10 19:05:05 +02:00
let index = data
.db
.open_index(path.index_uid.clone())
.ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?;
2019-11-20 15:00:06 +01:00
2020-04-10 19:05:05 +02:00
let mut writer = data
.db
.main_write_txn()
.map_err(|err| ResponseError::Internal(err.to_string()))?;
2020-01-16 16:58:57 +01:00
if let Some(name) = body.name.clone() {
2020-04-10 19:05:05 +02:00
index
.main
.put_name(&mut writer, &name)
.map_err(|e| ResponseError::Internal(e.to_string()))?;
}
2020-01-16 16:58:57 +01:00
if let Some(id) = body.primary_key.clone() {
2020-04-10 19:05:05 +02:00
if let Some(mut schema) = index
.main
.schema(&writer)
.map_err(|e| ResponseError::Internal(e.to_string()))?
{
match schema.primary_key() {
Some(_) => {
2020-04-10 19:05:05 +02:00
return Err(ResponseError::BadRequest(
"The primary key cannot be updated".to_string(),
)
.into());
2020-03-10 11:29:56 +01:00
}
None => {
2020-03-10 15:35:19 +01:00
schema
.set_primary_key(&id)
.map_err(|e| ResponseError::Internal(e.to_string()))?;
2020-04-10 19:05:05 +02:00
index
.main
.put_schema(&mut writer, &schema)
.map_err(|e| ResponseError::Internal(e.to_string()))?;
}
}
}
}
2020-01-16 16:58:57 +01:00
2020-04-10 19:05:05 +02:00
index
.main
.put_updated_at(&mut writer)
.map_err(|e| ResponseError::Internal(e.to_string()))?;
2020-04-10 19:05:05 +02:00
writer
.commit()
2020-04-09 10:39:34 +02:00
.map_err(|err| ResponseError::Internal(err.to_string()))?;
2020-01-16 16:58:57 +01:00
2020-04-10 19:05:05 +02:00
let reader = data
.db
.main_read_txn()
2020-04-09 10:39:34 +02:00
.map_err(|err| ResponseError::Internal(err.to_string()))?;
2020-04-10 19:05:05 +02:00
let name = index
.main
.name(&reader)
.map_err(|e| ResponseError::Internal(e.to_string()))?
2020-04-10 19:05:05 +02:00
.ok_or(ResponseError::Internal(
"Impossible to get the name of an index".to_string(),
))?;
let created_at = index
.main
.created_at(&reader)
.map_err(|e| ResponseError::Internal(e.to_string()))?
2020-04-10 19:05:05 +02:00
.ok_or(ResponseError::Internal(
"Impossible to get the create date of an index".to_string(),
))?;
let updated_at = index
.main
.updated_at(&reader)
.map_err(|e| ResponseError::Internal(e.to_string()))?
2020-04-10 19:05:05 +02:00
.ok_or(ResponseError::Internal(
"Impossible to get the last update date of an index".to_string(),
))?;
2019-11-20 15:00:06 +01:00
let primary_key = match index.main.schema(&reader) {
2020-03-10 11:29:56 +01:00
Ok(Some(schema)) => match schema.primary_key() {
Some(primary_key) => Some(primary_key.to_owned()),
None => None,
},
2020-02-26 18:49:17 +01:00
_ => None,
};
Ok(web::Json(IndexResponse {
name,
uid: path.index_uid.clone(),
2019-11-20 15:00:06 +01:00
created_at,
updated_at,
primary_key,
}))
2019-11-20 15:00:06 +01:00
}
#[delete("/indexes/{index_uid}")]
pub async fn delete_index(
data: web::Data<Data>,
path: web::Path<IndexParam>,
2020-04-09 10:39:34 +02:00
) -> aweb::Result<HttpResponse> {
2020-04-10 19:05:05 +02:00
data.db
.delete_index(&path.index_uid)
.map_err(|e| ResponseError::Internal(e.to_string()))?;
2019-10-31 15:00:36 +01:00
HttpResponse::NoContent().await
2019-10-31 15:00:36 +01:00
}
2020-04-09 10:39:34 +02:00
#[derive(Default, Deserialize)]
pub struct UpdateParam {
index_uid: String,
2020-04-10 19:05:05 +02:00
update_id: u64,
}
2020-04-09 10:39:34 +02:00
#[get("/indexes/{index_uid}/updates/{update_id}")]
pub async fn get_update_status(
data: web::Data<Data>,
path: web::Path<UpdateParam>,
2020-04-09 10:39:34 +02:00
) -> aweb::Result<web::Json<UpdateStatus>> {
2020-04-10 19:05:05 +02:00
let index = data
.db
.open_index(path.index_uid.clone())
.ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?;
2020-04-09 10:39:34 +02:00
2020-04-10 19:05:05 +02:00
let reader = data
.db
.update_read_txn()
2020-04-09 10:39:34 +02:00
.map_err(|err| ResponseError::Internal(err.to_string()))?;
2020-04-10 19:05:05 +02:00
let status = index
.update_status(&reader, path.update_id)
2020-04-09 10:39:34 +02:00
.map_err(|e| ResponseError::Internal(e.to_string()))?;
match status {
Some(status) => Ok(web::Json(status)),
2020-04-10 19:05:05 +02:00
None => Err(ResponseError::NotFound(format!("Update {} not found", path.update_id)).into()),
2020-04-09 10:39:34 +02:00
}
}
#[get("/indexes/{index_uid}/updates")]
pub async fn get_all_updates_status(
data: web::Data<Data>,
path: web::Path<IndexParam>,
2020-04-09 10:39:34 +02:00
) -> aweb::Result<web::Json<Vec<UpdateStatus>>> {
2020-04-10 19:05:05 +02:00
let index = data
.db
.open_index(path.index_uid.clone())
.ok_or(ResponseError::IndexNotFound(path.index_uid.clone()))?;
2020-04-09 10:39:34 +02:00
2020-04-10 19:05:05 +02:00
let reader = data
.db
.update_read_txn()
2020-04-09 10:39:34 +02:00
.map_err(|err| ResponseError::Internal(err.to_string()))?;
2020-04-10 19:05:05 +02:00
let response = index
.all_updates_status(&reader)
2020-04-09 10:39:34 +02:00
.map_err(|err| ResponseError::Internal(err.to_string()))?;
Ok(web::Json(response))
}