mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-18 18:41:52 +08:00
feat: integrate native indexer for mobile (#14174)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
## Release Notes
* **New Features**
* Added full-text search functionality to mobile apps (Android and iOS),
enabling document indexing and search capabilities.
* Enhanced blob upload support with new GraphQL mutations for creating,
completing, and managing file uploads.
* **Improvements**
* iOS and Android now use SQLite storage backend for improved indexing
performance, aligning with desktop experience.
<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
use affine_common::hashcash::Stamp;
|
||||
use affine_nbstore::pool::SqliteDocStoragePool;
|
||||
use affine_nbstore::{pool::SqliteDocStoragePool, Data};
|
||||
|
||||
#[derive(uniffi::Error, thiserror::Error, Debug)]
|
||||
pub enum UniffiError {
|
||||
@@ -50,9 +50,11 @@ impl TryFrom<DocRecord> for affine_nbstore::DocRecord {
|
||||
fn try_from(record: DocRecord) -> Result<Self> {
|
||||
Ok(Self {
|
||||
doc_id: record.doc_id,
|
||||
bin: base64_simd::STANDARD
|
||||
.decode_to_vec(record.bin)
|
||||
.map_err(|e| UniffiError::Base64DecodingError(e.to_string()))?,
|
||||
bin: Into::<Data>::into(
|
||||
base64_simd::STANDARD
|
||||
.decode_to_vec(record.bin)
|
||||
.map_err(|e| UniffiError::Base64DecodingError(e.to_string()))?,
|
||||
),
|
||||
timestamp: chrono::DateTime::<chrono::Utc>::from_timestamp_millis(record.timestamp)
|
||||
.ok_or(UniffiError::TimestampDecodingError)?
|
||||
.naive_utc(),
|
||||
@@ -156,9 +158,11 @@ impl TryFrom<SetBlob> for affine_nbstore::SetBlob {
|
||||
fn try_from(blob: SetBlob) -> Result<Self> {
|
||||
Ok(Self {
|
||||
key: blob.key,
|
||||
data: base64_simd::STANDARD
|
||||
.decode_to_vec(blob.data)
|
||||
.map_err(|e| UniffiError::Base64DecodingError(e.to_string()))?,
|
||||
data: Into::<Data>::into(
|
||||
base64_simd::STANDARD
|
||||
.decode_to_vec(blob.data)
|
||||
.map_err(|e| UniffiError::Base64DecodingError(e.to_string()))?,
|
||||
),
|
||||
mime: blob.mime,
|
||||
})
|
||||
}
|
||||
@@ -229,6 +233,38 @@ impl From<affine_nbstore::indexer::NativeCrawlResult> for CrawlResult {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(uniffi::Record)]
|
||||
pub struct SearchHit {
|
||||
pub id: String,
|
||||
pub score: f64,
|
||||
pub terms: Vec<String>,
|
||||
}
|
||||
|
||||
impl From<affine_nbstore::indexer::NativeSearchHit> for SearchHit {
|
||||
fn from(value: affine_nbstore::indexer::NativeSearchHit) -> Self {
|
||||
Self {
|
||||
id: value.id,
|
||||
score: value.score,
|
||||
terms: value.terms,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(uniffi::Record)]
|
||||
pub struct MatchRange {
|
||||
pub start: u32,
|
||||
pub end: u32,
|
||||
}
|
||||
|
||||
impl From<affine_nbstore::indexer::NativeMatch> for MatchRange {
|
||||
fn from(value: affine_nbstore::indexer::NativeMatch) -> Self {
|
||||
Self {
|
||||
start: value.start,
|
||||
end: value.end,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(uniffi::Object)]
|
||||
pub struct DocStoragePool {
|
||||
inner: SqliteDocStoragePool,
|
||||
@@ -699,4 +735,100 @@ impl DocStoragePool {
|
||||
.await?;
|
||||
Ok(result.into())
|
||||
}
|
||||
|
||||
pub async fn fts_add_document(
|
||||
&self,
|
||||
universal_id: String,
|
||||
index_name: String,
|
||||
doc_id: String,
|
||||
text: String,
|
||||
index: bool,
|
||||
) -> Result<()> {
|
||||
self
|
||||
.inner
|
||||
.get(universal_id)
|
||||
.await?
|
||||
.fts_add(&index_name, &doc_id, &text, index)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn fts_delete_document(
|
||||
&self,
|
||||
universal_id: String,
|
||||
index_name: String,
|
||||
doc_id: String,
|
||||
) -> Result<()> {
|
||||
self
|
||||
.inner
|
||||
.get(universal_id)
|
||||
.await?
|
||||
.fts_delete(&index_name, &doc_id)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn fts_get_document(
|
||||
&self,
|
||||
universal_id: String,
|
||||
index_name: String,
|
||||
doc_id: String,
|
||||
) -> Result<Option<String>> {
|
||||
Ok(
|
||||
self
|
||||
.inner
|
||||
.get(universal_id)
|
||||
.await?
|
||||
.fts_get(&index_name, &doc_id)
|
||||
.await?,
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn fts_search(
|
||||
&self,
|
||||
universal_id: String,
|
||||
index_name: String,
|
||||
query: String,
|
||||
) -> Result<Vec<SearchHit>> {
|
||||
Ok(
|
||||
self
|
||||
.inner
|
||||
.get(universal_id)
|
||||
.await?
|
||||
.fts_search(&index_name, &query)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn fts_get_matches(
|
||||
&self,
|
||||
universal_id: String,
|
||||
index_name: String,
|
||||
doc_id: String,
|
||||
query: String,
|
||||
) -> Result<Vec<MatchRange>> {
|
||||
Ok(
|
||||
self
|
||||
.inner
|
||||
.get(universal_id)
|
||||
.await?
|
||||
.fts_get_matches(&index_name, &doc_id, &query)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn fts_flush_index(&self, universal_id: String) -> Result<()> {
|
||||
self.inner.get(universal_id).await?.flush_index().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn fts_index_version(&self) -> Result<u32> {
|
||||
Ok(affine_nbstore::storage::SqliteDocStorage::index_version())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user