use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Document {
pub id: String,
pub content: String,
#[serde(default)]
pub namespace: Option<String>,
#[serde(default)]
pub metadata: HashMap<String, Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub embedding: Option<Vec<f32>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub source: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub created_at: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub updated_at: Option<i64>,
}
impl Document {
pub fn new(id: impl Into<String>, content: impl Into<String>) -> Self {
Self {
id: id.into(),
content: content.into(),
namespace: None,
metadata: HashMap::new(),
embedding: None,
source: None,
created_at: None,
updated_at: None,
}
}
pub fn namespace(mut self, namespace: impl Into<String>) -> Self {
self.namespace = Some(namespace.into());
self
}
pub fn metadata(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
self.metadata.insert(key.into(), value.into());
self
}
pub fn embedding(mut self, embedding: Vec<f32>) -> Self {
self.embedding = Some(embedding);
self
}
pub fn source(mut self, source: impl Into<String>) -> Self {
self.source = Some(source.into());
self
}
pub fn created_at(mut self, timestamp: i64) -> Self {
self.created_at = Some(timestamp);
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SearchResult {
pub document: Document,
pub score: f32,
#[serde(default)]
pub highlights: Vec<String>,
}
impl SearchResult {
pub fn new(document: Document, score: f32) -> Self {
Self {
document,
score,
highlights: Vec::new(),
}
}
pub fn with_highlights(mut self, highlights: Vec<String>) -> Self {
self.highlights = highlights;
self
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct SearchOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub limit: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub namespace: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub min_score: Option<f32>,
#[serde(default)]
pub include_highlights: bool,
#[serde(default)]
pub filters: HashMap<String, Value>,
}
impl SearchOptions {
pub fn new() -> Self {
Self::default()
}
pub fn limit(mut self, limit: usize) -> Self {
self.limit = Some(limit);
self
}
pub fn namespace(mut self, namespace: impl Into<String>) -> Self {
self.namespace = Some(namespace.into());
self
}
pub fn min_score(mut self, score: f32) -> Self {
self.min_score = Some(score);
self
}
pub fn include_highlights(mut self, include: bool) -> Self {
self.include_highlights = include;
self
}
pub fn filter(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
self.filters.insert(key.into(), value.into());
self
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ListOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub limit: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub offset: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub namespace: Option<String>,
}
impl ListOptions {
pub fn new() -> Self {
Self::default()
}
pub fn limit(mut self, limit: usize) -> Self {
self.limit = Some(limit);
self
}
pub fn offset(mut self, offset: usize) -> Self {
self.offset = Some(offset);
self
}
pub fn namespace(mut self, namespace: impl Into<String>) -> Self {
self.namespace = Some(namespace.into());
self
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct KnowledgeStats {
pub document_count: usize,
pub namespace_count: usize,
pub namespaces: Vec<String>,
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_document_builder() {
let doc = Document::new("doc1", "Hello world")
.namespace("test")
.metadata("author", json!("alice"))
.source("test.txt");
assert_eq!(doc.id, "doc1");
assert_eq!(doc.content, "Hello world");
assert_eq!(doc.namespace, Some("test".to_string()));
assert_eq!(doc.metadata.get("author"), Some(&json!("alice")));
assert_eq!(doc.source, Some("test.txt".to_string()));
}
#[test]
fn test_search_options() {
let opts = SearchOptions::new()
.limit(10)
.namespace("docs")
.min_score(0.5)
.include_highlights(true)
.filter("type", json!("article"));
assert_eq!(opts.limit, Some(10));
assert_eq!(opts.namespace, Some("docs".to_string()));
assert_eq!(opts.min_score, Some(0.5));
assert!(opts.include_highlights);
assert_eq!(opts.filters.get("type"), Some(&json!("article")));
}
#[test]
fn test_document_serialization() {
let doc = Document::new("doc1", "Test content")
.namespace("test")
.metadata("key", json!("value"));
let json = serde_json::to_string(&doc).unwrap();
let parsed: Document = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.id, doc.id);
assert_eq!(parsed.content, doc.content);
assert_eq!(parsed.namespace, doc.namespace);
}
}