Skip to main content

mf_search/
state_plugin.rs

1use std::sync::Arc;
2
3use anyhow::Result;
4use async_trait::async_trait;
5use mf_model::node_pool::NodePool;
6use mf_state::plugin::{
7    Plugin, PluginMetadata, PluginSpec, PluginTrait, StateField,
8};
9use mf_state::state::State;
10use mf_state::transaction::Transaction;
11
12use crate::backend::SqliteBackend;
13use crate::service::{IndexEvent, IndexService};
14use crate::step_registry::ensure_default_step_indexers;
15
16// Resource wrappers
17pub struct SearchIndexResource {
18    pub service: Arc<IndexService>,
19}
20impl mf_state::resource::Resource for SearchIndexResource {}
21
22struct SearchIndexStateField {
23    service: Arc<IndexService>,
24}
25
26impl std::fmt::Debug for SearchIndexStateField {
27    fn fmt(
28        &self,
29        f: &mut std::fmt::Formatter<'_>,
30    ) -> std::fmt::Result {
31        f.debug_struct("SearchIndexStateField").finish()
32    }
33}
34
35#[async_trait]
36impl StateField for SearchIndexStateField {
37    type Value = SearchIndexResource;
38
39    async fn init(
40        &self,
41        _config: &mf_state::state::StateConfig,
42        instance: &State,
43    ) -> Arc<Self::Value> {
44        let service =
45            Arc::new(SearchIndexResource { service: self.service.clone() });
46        let service_ref = self.service.clone();
47        let node_pool_ref = instance.node_pool.clone();
48        tokio::spawn(async move {
49            let _ = service_ref
50                .handle(IndexEvent::Rebuild {
51                    pool: node_pool_ref,
52                    scope: RebuildScope::Full,
53                })
54                .await;
55        });
56        service
57    }
58
59    async fn apply(
60        &self,
61        tr: &Transaction,
62        value: Arc<Self::Value>,
63        old_state: &State,
64        new_state: &State,
65    ) -> Arc<Self::Value> {
66        let svc = value.service.clone();
67        let steps: Vec<Arc<dyn mf_transform::step::Step>> =
68            tr.steps.iter().cloned().collect();
69        let pool_before: Arc<NodePool> = old_state.doc();
70        let pool_after: Arc<NodePool> = new_state.doc();
71
72        // 异步处理索引更新(不阻塞事务)
73        tokio::spawn(async move {
74            let _ = svc
75                .handle(IndexEvent::TransactionCommitted {
76                    pool_before: Some(pool_before),
77                    pool_after,
78                    steps,
79                })
80                .await;
81        });
82
83        value
84    }
85}
86
87#[derive(Debug)]
88struct SearchIndexPluginTrait {}
89
90impl PluginTrait for SearchIndexPluginTrait {
91    fn metadata(&self) -> PluginMetadata {
92        PluginMetadata {
93            name: "search_index".to_string(),
94            version: "2.0.0".to_string(),
95            description: "SQLite 搜索索引插件".to_string(),
96            author: "ModuForge".to_string(),
97            dependencies: vec![],
98            conflicts: vec![],
99            state_fields: vec![],
100            tags: vec![],
101        }
102    }
103}
104
105/// 创建搜索索引插件(使用 SQLite 后端)
106pub async fn create_search_index_plugin(
107    index_dir: &std::path::Path
108) -> Result<Arc<Plugin>> {
109    ensure_default_step_indexers();
110    let backend = Arc::new(SqliteBackend::new_in_dir(index_dir).await?);
111    let service = Arc::new(IndexService::new(backend));
112
113    let field = Arc::new(SearchIndexStateField { service });
114    let spec = PluginSpec {
115        state_field: Some(field),
116        tr: Arc::new(SearchIndexPluginTrait {}),
117    };
118    Ok(Arc::new(Plugin::new(spec)))
119}
120
121/// 创建临时搜索索引插件(用于测试)
122pub async fn create_temp_search_index_plugin() -> Result<Arc<Plugin>> {
123    ensure_default_step_indexers();
124    let backend = Arc::new(SqliteBackend::new_in_system_temp().await?);
125    let service = Arc::new(IndexService::new(backend));
126
127    let field = Arc::new(SearchIndexStateField { service });
128    let spec = PluginSpec {
129        state_field: Some(field),
130        tr: Arc::new(SearchIndexPluginTrait {}),
131    };
132    Ok(Arc::new(Plugin::new(spec)))
133}
134
135// 向后兼容的别名
136pub use create_search_index_plugin as SearchPlugin;
137use crate::RebuildScope;