feat: search by message components
Signed-off-by: Zomatree <me@zomatree.live>
This commit is contained in:
@@ -10,12 +10,12 @@ use elasticsearch::{
|
||||
indices::{IndicesCreateParts, IndicesDeleteParts},
|
||||
};
|
||||
use elasticsearch_dsl::{FieldSort, Query, Search, SearchResponse, Sort};
|
||||
use revolt_database::Message;
|
||||
use revolt_database::{Database, Message};
|
||||
use serde_json::{Map, Value, json};
|
||||
|
||||
pub use elasticsearch;
|
||||
|
||||
use crate::SearchTerms;
|
||||
use crate::{MessageComponent, MetadataFile, SearchTerms};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
@@ -87,12 +87,6 @@ impl ElasticsearchClient {
|
||||
.indices()
|
||||
.create(IndicesCreateParts::Index("messages"))
|
||||
.body(json!({
|
||||
// "settings": {
|
||||
// "index": {
|
||||
// "sort.field": "_id",
|
||||
// "sort.order": ["asc", "desc"],
|
||||
// },
|
||||
// },
|
||||
"mappings": {
|
||||
"properties": {
|
||||
"content": {"type": "text"},
|
||||
@@ -105,9 +99,13 @@ impl ElasticsearchClient {
|
||||
"embeds": {
|
||||
"properties": {}
|
||||
},
|
||||
"attachments": { // TODO: images, videos, files
|
||||
"attachments": {
|
||||
"type": "nested",
|
||||
"properties": {}
|
||||
"properties": {
|
||||
"metadata.type": {
|
||||
"type": "keyword"
|
||||
}
|
||||
}
|
||||
},
|
||||
// TODO: links
|
||||
}
|
||||
@@ -152,15 +150,27 @@ impl ElasticsearchClient {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: component filtering
|
||||
// Need to pass FileHash to this to extract metadata
|
||||
if let Some(components) = terms.filters.components {
|
||||
let mut components_query = Query::bool();
|
||||
|
||||
for component in components {
|
||||
match component {
|
||||
MessageComponent::Image => components_query = components_query.should(Query::term("attachments.metadata.type", "Image")),
|
||||
MessageComponent::Video => components_query = components_query.should(Query::term("attachments.metadata.type", "Video")),
|
||||
MessageComponent::File => components_query = components_query.should(Query::exists("attachments.file._id")),
|
||||
MessageComponent::Embed => query = query.filter(Query::exists("embeds")),
|
||||
};
|
||||
}
|
||||
|
||||
query = query.filter(Query::nested("attachments", components_query));
|
||||
}
|
||||
|
||||
let search = Search::new()
|
||||
.query(query)
|
||||
.stats(false)
|
||||
.sort([Sort::FieldSort(
|
||||
.sort(Sort::FieldSort(
|
||||
FieldSort::new("_id".to_string()).order(terms.sort.unwrap_or_default().into()),
|
||||
)]);
|
||||
));
|
||||
|
||||
let response = self
|
||||
.inner
|
||||
@@ -184,7 +194,7 @@ impl ElasticsearchClient {
|
||||
}
|
||||
}
|
||||
|
||||
fn create_message_source(&self, message: Message) -> Value {
|
||||
fn create_message_source(&self, _db: &Database, message: Message) -> Value {
|
||||
let mut map = Map::new();
|
||||
|
||||
map.insert("channel".to_string(), Value::String(message.channel));
|
||||
@@ -196,9 +206,21 @@ impl ElasticsearchClient {
|
||||
}
|
||||
|
||||
if let Some(attachments) = message.attachments {
|
||||
let mut files = Vec::new();
|
||||
|
||||
for attachment in attachments {
|
||||
// TODO: swap this out for fetching the metadata from FileHash because of deprecation
|
||||
// let metadata = attachment.as_hash(db).await.expect("Failed to fetch FileHash").metadata;
|
||||
|
||||
files.push(MetadataFile {
|
||||
metadata: attachment.metadata.clone(),
|
||||
file: attachment,
|
||||
})
|
||||
}
|
||||
|
||||
map.insert(
|
||||
"attachments".to_string(),
|
||||
serde_json::to_value(attachments).unwrap(),
|
||||
serde_json::to_value(files).unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -240,9 +262,9 @@ impl ElasticsearchClient {
|
||||
Value::Object(map)
|
||||
}
|
||||
|
||||
pub async fn index_message(&self, message: Message) -> Result<(), Error> {
|
||||
pub async fn index_message(&self, db: &Database, message: Message) -> Result<(), Error> {
|
||||
let id = message.id.clone();
|
||||
let source = self.create_message_source(message);
|
||||
let source = self.create_message_source(db, message);
|
||||
|
||||
let exception = self
|
||||
.inner
|
||||
@@ -260,9 +282,9 @@ impl ElasticsearchClient {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn edit_message(&self, message: Message) -> Result<(), Error> {
|
||||
pub async fn edit_message(&self, db: &Database, message: Message) -> Result<(), Error> {
|
||||
let id = message.id.clone();
|
||||
let source = self.create_message_source(message);
|
||||
let source = self.create_message_source(db, message);
|
||||
|
||||
let exception = self
|
||||
.inner
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
use iso8601_timestamp::Timestamp;
|
||||
use revolt_models::v0;
|
||||
use serde::Serialize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use revolt_database::{File, Metadata};
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Serialize)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize)]
|
||||
pub enum AuthorType {
|
||||
User,
|
||||
// Bot,
|
||||
Webhook,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum MessageComponent {
|
||||
Image,
|
||||
Video,
|
||||
@@ -18,7 +19,7 @@ pub enum MessageComponent {
|
||||
Embed,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, PartialEq)]
|
||||
#[derive(Debug, Clone, Default, PartialEq)]
|
||||
pub struct SearchFilters {
|
||||
pub content: Option<String>,
|
||||
pub author: Option<Vec<String>>,
|
||||
@@ -31,14 +32,14 @@ pub struct SearchFilters {
|
||||
pub components: Option<Vec<MessageComponent>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Default, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
||||
pub enum SortOrder {
|
||||
Asc,
|
||||
#[default]
|
||||
Desc,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct SearchTerms {
|
||||
pub channels: Vec<String>,
|
||||
pub filters: SearchFilters,
|
||||
@@ -47,6 +48,12 @@ pub struct SearchTerms {
|
||||
pub sort: Option<SortOrder>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct MetadataFile {
|
||||
pub file: File,
|
||||
pub metadata: Metadata
|
||||
}
|
||||
|
||||
impl From<v0::AuthorType> for AuthorType {
|
||||
fn from(value: v0::AuthorType) -> Self {
|
||||
match value {
|
||||
|
||||
Reference in New Issue
Block a user