use std::collections::HashSet; use crate::database::*; use crate::util::result::{Error, Result}; use futures::StreamExt; use mongodb::{ bson::{doc, from_document}, options::FindOptions, }; use rocket_contrib::json::{Json, JsonValue}; use serde::{Deserialize, Serialize}; use validator::Validate; #[derive(Serialize, Deserialize, FromFormValue)] pub enum Sort { Relevance, Latest, Oldest, } #[derive(Validate, Serialize, Deserialize, FromForm)] pub struct Options { #[validate(length(min = 1, max = 64))] query: String, #[validate(range(min = 1, max = 100))] limit: Option, #[validate(length(min = 26, max = 26))] before: Option, #[validate(length(min = 26, max = 26))] after: Option, sort: Option, include_users: Option, } #[post("//search", data = "")] pub async fn req(user: User, target: Ref, options: Json) -> Result { options .validate() .map_err(|error| Error::FailedValidation { error })?; let target = target.fetch_channel().await?; target.has_messaging()?; let perm = permissions::PermissionCalculator::new(&user) .with_channel(&target) .for_channel() .await?; if !perm.get_view() { Err(Error::MissingPermission)? } let mut messages = vec![]; let limit = options.limit.unwrap_or(50); let mut cursor = get_collection("messages") .find( doc! { "channel": target.id(), "$text": { "$search": &options.query } }, FindOptions::builder() .projection(doc! { "score": { "$meta": "textScore" } }) .limit(limit) .sort(doc! { "score": { "$meta": "textScore" } }) .build(), ) .await .map_err(|_| Error::DatabaseError { operation: "find", with: "messages", })?; while let Some(result) = cursor.next().await { if let Ok(doc) = result { messages.push( from_document::(doc).map_err(|_| Error::DatabaseError { operation: "from_document", with: "message", })?, ); } } if options.include_users.unwrap_or_else(|| false) { let mut ids = HashSet::new(); for message in &messages { ids.insert(message.author.clone()); } ids.remove(&user.id); let user_ids = ids.into_iter().collect(); let users = user.fetch_multiple_users(user_ids).await?; if let Channel::TextChannel { server, .. } = target { Ok(json!({ "messages": messages, "users": users, "members": Server::fetch_members(&server).await? })) } else { Ok(json!({ "messages": messages, "users": users, })) } } else { Ok(json!(messages)) } }