MCPcopy Create free account
hub / github.com/NodeDB-Lab/nodedb / text_search_impl

Method text_search_impl

nodedb-client/src/remote/client/sql_lifecycle.rs:77–141  ·  view source on GitHub ↗
(
        &self,
        collection: &str,
        field: &str,
        query: &str,
        top_k: usize,
        params: TextSearchParams,
    )

Source from the content-addressed store, hash-verified

75 }
76
77 pub(super) async fn text_search_impl(
78 &self,
79 collection: &str,
80 field: &str,
81 query: &str,
82 top_k: usize,
83 params: TextSearchParams,
84 ) -> NodeDbResult<Vec<SearchResult>> {
85 // Server-side FTS query SQL: `text_match(<field>, '<query>')` in
86 // a WHERE clause selects matching ids; `bm25_score(<field>,
87 // '<query>')` in the SELECT list exposes the score so callers
88 // can order/rank. The planner pattern-matches this shape and
89 // dispatches `SqlPlan::TextSearch`.
90 //
91 // `params` (mode, fuzzy, prefix, etc.) is intentionally ignored
92 // for now — every supported option is also expressible in the
93 // SQL form, but threading them through the DSL string is its
94 // own widening. The defaults (Plain query with fuzzy=true) cover
95 // the common case the trait's spec calls out.
96 let _ = params;
97 let coll = quote_identifier(collection);
98 let field_quoted = quote_identifier(field);
99 let q_lit = quote_string_literal(query);
100 let sql = format!(
101 "SELECT id, bm25_score({field_quoted}, {q_lit}) AS score \
102 FROM {coll} \
103 WHERE text_match({field_quoted}, {q_lit}) \
104 LIMIT {top_k}"
105 );
106
107 let (columns, rows) = self.simple_query_raw(&sql).await?;
108 let id_idx = columns.iter().position(|c| c == "id").unwrap_or(0);
109 let score_idx = columns.iter().position(|c| c == "score").unwrap_or(1);
110
111 let mut results = Vec::with_capacity(rows.len());
112 for row in &rows {
113 let id = row
114 .get(id_idx)
115 .and_then(|v| v.as_str())
116 .unwrap_or("")
117 .to_string();
118 // simple_query returns text — score arrives as a stringified
119 // float. Parse defensively so a missing/malformed score does
120 // not torpedo the whole result set; callers prefer ordered
121 // ids with score 0.0 over an Err.
122 let score = row
123 .get(score_idx)
124 .and_then(|v| v.as_str())
125 .and_then(|s| s.parse::<f32>().ok())
126 .or_else(|| {
127 row.get(score_idx)
128 .and_then(|v| v.as_f64())
129 .map(|f| f as f32)
130 })
131 .unwrap_or(0.0);
132
133 results.push(SearchResult {
134 id,

Callers 1

text_searchMethod · 0.80

Calls 11

quote_identifierFunction · 0.85
quote_string_literalFunction · 0.85
simple_query_rawMethod · 0.80
to_stringMethod · 0.80
iterMethod · 0.45
lenMethod · 0.45
getMethod · 0.45
as_strMethod · 0.45
okMethod · 0.45
as_f64Method · 0.45
pushMethod · 0.45

Tested by

no test coverage detected