1use serde::{Deserialize, Serialize};
7
8use crate::value::Value;
9
10#[repr(u8)]
18#[derive(
19 Debug,
20 Clone,
21 Copy,
22 PartialEq,
23 Eq,
24 Hash,
25 Serialize,
26 Deserialize,
27 zerompk::ToMessagePack,
28 zerompk::FromMessagePack,
29)]
30#[serde(try_from = "u8", into = "u8")]
31#[msgpack(c_enum)]
32pub enum OpCode {
33 Auth = 0x01,
35 Ping = 0x02,
36 Status = 0x03,
39
40 PointGet = 0x10,
42 PointPut = 0x11,
43 PointDelete = 0x12,
44 VectorSearch = 0x13,
45 RangeScan = 0x14,
46 CrdtRead = 0x15,
47 CrdtApply = 0x16,
48 GraphRagFusion = 0x17,
49 AlterCollectionPolicy = 0x18,
50
51 Sql = 0x20,
53 Ddl = 0x21,
54 Explain = 0x22,
55 CopyFrom = 0x23,
56
57 Set = 0x30,
59 Show = 0x31,
60 Reset = 0x32,
61
62 Begin = 0x40,
64 Commit = 0x41,
65 Rollback = 0x42,
66
67 GraphHop = 0x50,
69 GraphNeighbors = 0x51,
70 GraphPath = 0x52,
71 GraphSubgraph = 0x53,
72 EdgePut = 0x54,
73 EdgeDelete = 0x55,
74 GraphAlgo = 0x56,
75 GraphMatch = 0x57,
76
77 SpatialScan = 0x19,
79
80 TimeseriesScan = 0x1A,
82 TimeseriesIngest = 0x1B,
83
84 TextSearch = 0x60,
86 HybridSearch = 0x61,
87
88 VectorBatchInsert = 0x70,
90 DocumentBatchInsert = 0x71,
91
92 KvScan = 0x72,
94 KvExpire = 0x73,
95 KvPersist = 0x74,
96 KvGetTtl = 0x75,
97 KvBatchGet = 0x76,
98 KvBatchPut = 0x77,
99 KvFieldGet = 0x78,
100 KvFieldSet = 0x79,
101
102 DocumentUpdate = 0x7A,
104 DocumentScan = 0x7B,
105 DocumentUpsert = 0x7C,
106 DocumentBulkUpdate = 0x7D,
107 DocumentBulkDelete = 0x7E,
108
109 VectorInsert = 0x7F,
111 VectorMultiSearch = 0x80,
112 VectorDelete = 0x81,
113
114 ColumnarScan = 0x82,
116 ColumnarInsert = 0x83,
117
118 RecursiveScan = 0x84,
120
121 DocumentTruncate = 0x85,
123 DocumentEstimateCount = 0x86,
124 DocumentInsertSelect = 0x87,
125 DocumentRegister = 0x88,
126 DocumentDropIndex = 0x89,
127
128 KvRegisterIndex = 0x8A,
130 KvDropIndex = 0x8B,
131 KvTruncate = 0x8C,
132
133 VectorSetParams = 0x8D,
135
136 KvIncr = 0x8E,
138 KvIncrFloat = 0x8F,
139 KvCas = 0x90,
140 KvGetSet = 0x91,
141
142 KvRegisterSortedIndex = 0x92,
144 KvDropSortedIndex = 0x93,
145 KvSortedIndexRank = 0x94,
146 KvSortedIndexTopK = 0x95,
147 KvSortedIndexRange = 0x96,
148 KvSortedIndexCount = 0x97,
149 KvSortedIndexScore = 0x98,
150}
151
152impl OpCode {
153 pub fn is_write(&self) -> bool {
155 matches!(
156 self,
157 OpCode::PointPut
158 | OpCode::PointDelete
159 | OpCode::CrdtApply
160 | OpCode::EdgePut
161 | OpCode::EdgeDelete
162 | OpCode::VectorBatchInsert
163 | OpCode::DocumentBatchInsert
164 | OpCode::AlterCollectionPolicy
165 | OpCode::TimeseriesIngest
166 | OpCode::KvExpire
167 | OpCode::KvPersist
168 | OpCode::KvBatchPut
169 | OpCode::KvFieldSet
170 | OpCode::DocumentUpdate
171 | OpCode::DocumentUpsert
172 | OpCode::DocumentBulkUpdate
173 | OpCode::DocumentBulkDelete
174 | OpCode::VectorInsert
175 | OpCode::VectorDelete
176 | OpCode::ColumnarInsert
177 | OpCode::DocumentTruncate
178 | OpCode::DocumentInsertSelect
179 | OpCode::DocumentRegister
180 | OpCode::DocumentDropIndex
181 | OpCode::KvRegisterIndex
182 | OpCode::KvDropIndex
183 | OpCode::KvTruncate
184 | OpCode::VectorSetParams
185 | OpCode::KvIncr
186 | OpCode::KvIncrFloat
187 | OpCode::KvCas
188 | OpCode::KvGetSet
189 | OpCode::KvRegisterSortedIndex
190 | OpCode::KvDropSortedIndex
191 )
192 }
193}
194
195impl From<OpCode> for u8 {
196 fn from(op: OpCode) -> u8 {
197 op as u8
198 }
199}
200
201impl TryFrom<u8> for OpCode {
202 type Error = String;
203
204 fn try_from(value: u8) -> Result<Self, Self::Error> {
205 match value {
206 0x01 => Ok(OpCode::Auth),
207 0x02 => Ok(OpCode::Ping),
208 0x03 => Ok(OpCode::Status),
209 0x10 => Ok(OpCode::PointGet),
210 0x11 => Ok(OpCode::PointPut),
211 0x12 => Ok(OpCode::PointDelete),
212 0x13 => Ok(OpCode::VectorSearch),
213 0x14 => Ok(OpCode::RangeScan),
214 0x15 => Ok(OpCode::CrdtRead),
215 0x16 => Ok(OpCode::CrdtApply),
216 0x17 => Ok(OpCode::GraphRagFusion),
217 0x18 => Ok(OpCode::AlterCollectionPolicy),
218 0x19 => Ok(OpCode::SpatialScan),
219 0x1A => Ok(OpCode::TimeseriesScan),
220 0x1B => Ok(OpCode::TimeseriesIngest),
221 0x20 => Ok(OpCode::Sql),
222 0x21 => Ok(OpCode::Ddl),
223 0x22 => Ok(OpCode::Explain),
224 0x23 => Ok(OpCode::CopyFrom),
225 0x30 => Ok(OpCode::Set),
226 0x31 => Ok(OpCode::Show),
227 0x32 => Ok(OpCode::Reset),
228 0x40 => Ok(OpCode::Begin),
229 0x41 => Ok(OpCode::Commit),
230 0x42 => Ok(OpCode::Rollback),
231 0x50 => Ok(OpCode::GraphHop),
232 0x51 => Ok(OpCode::GraphNeighbors),
233 0x52 => Ok(OpCode::GraphPath),
234 0x53 => Ok(OpCode::GraphSubgraph),
235 0x54 => Ok(OpCode::EdgePut),
236 0x55 => Ok(OpCode::EdgeDelete),
237 0x56 => Ok(OpCode::GraphAlgo),
238 0x57 => Ok(OpCode::GraphMatch),
239 0x60 => Ok(OpCode::TextSearch),
240 0x61 => Ok(OpCode::HybridSearch),
241 0x70 => Ok(OpCode::VectorBatchInsert),
242 0x71 => Ok(OpCode::DocumentBatchInsert),
243 0x72 => Ok(OpCode::KvScan),
244 0x73 => Ok(OpCode::KvExpire),
245 0x74 => Ok(OpCode::KvPersist),
246 0x75 => Ok(OpCode::KvGetTtl),
247 0x76 => Ok(OpCode::KvBatchGet),
248 0x77 => Ok(OpCode::KvBatchPut),
249 0x78 => Ok(OpCode::KvFieldGet),
250 0x79 => Ok(OpCode::KvFieldSet),
251 0x7A => Ok(OpCode::DocumentUpdate),
252 0x7B => Ok(OpCode::DocumentScan),
253 0x7C => Ok(OpCode::DocumentUpsert),
254 0x7D => Ok(OpCode::DocumentBulkUpdate),
255 0x7E => Ok(OpCode::DocumentBulkDelete),
256 0x7F => Ok(OpCode::VectorInsert),
257 0x80 => Ok(OpCode::VectorMultiSearch),
258 0x81 => Ok(OpCode::VectorDelete),
259 0x82 => Ok(OpCode::ColumnarScan),
260 0x83 => Ok(OpCode::ColumnarInsert),
261 0x84 => Ok(OpCode::RecursiveScan),
262 0x85 => Ok(OpCode::DocumentTruncate),
263 0x86 => Ok(OpCode::DocumentEstimateCount),
264 0x87 => Ok(OpCode::DocumentInsertSelect),
265 0x88 => Ok(OpCode::DocumentRegister),
266 0x89 => Ok(OpCode::DocumentDropIndex),
267 0x8A => Ok(OpCode::KvRegisterIndex),
268 0x8B => Ok(OpCode::KvDropIndex),
269 0x8C => Ok(OpCode::KvTruncate),
270 0x8D => Ok(OpCode::VectorSetParams),
271 0x8E => Ok(OpCode::KvIncr),
272 0x8F => Ok(OpCode::KvIncrFloat),
273 0x90 => Ok(OpCode::KvCas),
274 0x91 => Ok(OpCode::KvGetSet),
275 0x92 => Ok(OpCode::KvRegisterSortedIndex),
276 0x93 => Ok(OpCode::KvDropSortedIndex),
277 0x94 => Ok(OpCode::KvSortedIndexRank),
278 0x95 => Ok(OpCode::KvSortedIndexTopK),
279 0x96 => Ok(OpCode::KvSortedIndexRange),
280 0x97 => Ok(OpCode::KvSortedIndexCount),
281 0x98 => Ok(OpCode::KvSortedIndexScore),
282 other => Err(format!("unknown OpCode byte: 0x{other:02X}")),
283 }
284 }
285}
286
287#[repr(u8)]
291#[derive(
292 Debug,
293 Clone,
294 Copy,
295 PartialEq,
296 Eq,
297 Serialize,
298 Deserialize,
299 zerompk::ToMessagePack,
300 zerompk::FromMessagePack,
301)]
302#[msgpack(c_enum)]
303pub enum ResponseStatus {
304 Ok = 0,
306 Partial = 1,
308 Error = 2,
310}
311
312#[derive(
316 Debug, Clone, Serialize, Deserialize, zerompk::ToMessagePack, zerompk::FromMessagePack,
317)]
318#[serde(tag = "method", rename_all = "snake_case")]
319pub enum AuthMethod {
320 Trust {
321 #[serde(default = "default_username")]
322 username: String,
323 },
324 Password {
325 username: String,
326 password: String,
327 },
328 ApiKey {
329 token: String,
330 },
331}
332
333fn default_username() -> String {
334 "admin".into()
335}
336
337#[derive(
339 Debug, Clone, Serialize, Deserialize, zerompk::ToMessagePack, zerompk::FromMessagePack,
340)]
341pub struct AuthResponse {
342 pub username: String,
343 pub tenant_id: u32,
344}
345
346#[derive(Debug, Clone, Serialize, Deserialize)]
353pub struct NativeRequest {
354 pub op: OpCode,
356 pub seq: u64,
358 #[serde(flatten)]
360 pub fields: RequestFields,
361}
362
363impl zerompk::ToMessagePack for NativeRequest {
364 fn write<W: zerompk::Write>(&self, writer: &mut W) -> zerompk::Result<()> {
365 writer.write_array_len(3)?;
366 self.op.write(writer)?;
367 writer.write_u64(self.seq)?;
368 self.fields.write(writer)
369 }
370}
371
372impl<'a> zerompk::FromMessagePack<'a> for NativeRequest {
373 fn read<R: zerompk::Read<'a>>(reader: &mut R) -> zerompk::Result<Self> {
374 let len = reader.read_array_len()?;
375 if len != 3 {
376 return Err(zerompk::Error::ArrayLengthMismatch {
377 expected: 3,
378 actual: len,
379 });
380 }
381 let op = OpCode::read(reader)?;
382 let seq = reader.read_u64()?;
383 let fields = RequestFields::read(reader)?;
384 Ok(Self { op, seq, fields })
385 }
386}
387
388#[derive(
393 Debug, Clone, Serialize, Deserialize, zerompk::ToMessagePack, zerompk::FromMessagePack,
394)]
395#[serde(untagged)]
396pub enum RequestFields {
397 Text(TextFields),
400}
401
402#[derive(Debug, Clone, Default, Serialize, Deserialize)]
406pub struct TextFields {
407 #[serde(skip_serializing_if = "Option::is_none")]
409 pub auth: Option<AuthMethod>,
410
411 #[serde(skip_serializing_if = "Option::is_none")]
413 pub sql: Option<String>,
414 #[serde(skip_serializing_if = "Option::is_none")]
415 pub key: Option<String>,
416 #[serde(skip_serializing_if = "Option::is_none")]
417 pub value: Option<String>,
418
419 #[serde(skip_serializing_if = "Option::is_none")]
421 pub collection: Option<String>,
422 #[serde(skip_serializing_if = "Option::is_none")]
423 pub document_id: Option<String>,
424 #[serde(skip_serializing_if = "Option::is_none")]
425 pub data: Option<Vec<u8>>,
426
427 #[serde(skip_serializing_if = "Option::is_none")]
429 pub query_vector: Option<Vec<f32>>,
430 #[serde(skip_serializing_if = "Option::is_none")]
431 pub top_k: Option<u64>,
432
433 #[serde(skip_serializing_if = "Option::is_none")]
435 pub field: Option<String>,
436 #[serde(skip_serializing_if = "Option::is_none")]
437 pub limit: Option<u64>,
438
439 #[serde(skip_serializing_if = "Option::is_none")]
441 pub delta: Option<Vec<u8>>,
442 #[serde(skip_serializing_if = "Option::is_none")]
443 pub peer_id: Option<u64>,
444
445 #[serde(skip_serializing_if = "Option::is_none")]
447 pub vector_top_k: Option<u64>,
448 #[serde(skip_serializing_if = "Option::is_none")]
449 pub edge_label: Option<String>,
450 #[serde(skip_serializing_if = "Option::is_none")]
451 pub direction: Option<String>,
452 #[serde(skip_serializing_if = "Option::is_none")]
453 pub expansion_depth: Option<u64>,
454 #[serde(skip_serializing_if = "Option::is_none")]
455 pub final_top_k: Option<u64>,
456 #[serde(skip_serializing_if = "Option::is_none")]
457 pub vector_k: Option<f64>,
458 #[serde(skip_serializing_if = "Option::is_none")]
459 pub graph_k: Option<f64>,
460 #[serde(skip_serializing_if = "Option::is_none")]
461 pub vector_field: Option<String>,
462
463 #[serde(skip_serializing_if = "Option::is_none")]
465 pub start_node: Option<String>,
466 #[serde(skip_serializing_if = "Option::is_none")]
467 pub end_node: Option<String>,
468 #[serde(skip_serializing_if = "Option::is_none")]
469 pub depth: Option<u64>,
470 #[serde(skip_serializing_if = "Option::is_none")]
471 pub from_node: Option<String>,
472 #[serde(skip_serializing_if = "Option::is_none")]
473 pub to_node: Option<String>,
474 #[serde(skip_serializing_if = "Option::is_none")]
475 pub edge_type: Option<String>,
476 #[serde(skip_serializing_if = "Option::is_none")]
477 pub properties: Option<serde_json::Value>,
478
479 #[serde(skip_serializing_if = "Option::is_none")]
481 pub query_text: Option<String>,
482 #[serde(skip_serializing_if = "Option::is_none")]
483 pub vector_weight: Option<f64>,
484 #[serde(skip_serializing_if = "Option::is_none")]
485 pub fuzzy: Option<bool>,
486
487 #[serde(skip_serializing_if = "Option::is_none")]
489 pub ef_search: Option<u64>,
490 #[serde(skip_serializing_if = "Option::is_none")]
492 pub field_name: Option<String>,
493
494 #[serde(skip_serializing_if = "Option::is_none")]
496 pub lower_bound: Option<Vec<u8>>,
497 #[serde(skip_serializing_if = "Option::is_none")]
498 pub upper_bound: Option<Vec<u8>>,
499
500 #[serde(skip_serializing_if = "Option::is_none")]
503 pub mutation_id: Option<u64>,
504
505 #[serde(skip_serializing_if = "Option::is_none")]
507 pub vectors: Option<Vec<BatchVector>>,
508 #[serde(skip_serializing_if = "Option::is_none")]
509 pub documents: Option<Vec<BatchDocument>>,
510
511 #[serde(skip_serializing_if = "Option::is_none")]
514 pub query_geometry: Option<Vec<u8>>,
515 #[serde(skip_serializing_if = "Option::is_none")]
517 pub spatial_predicate: Option<String>,
518 #[serde(skip_serializing_if = "Option::is_none")]
520 pub distance_meters: Option<f64>,
521
522 #[serde(skip_serializing_if = "Option::is_none")]
525 pub payload: Option<Vec<u8>>,
526 #[serde(skip_serializing_if = "Option::is_none")]
528 pub format: Option<String>,
529 #[serde(skip_serializing_if = "Option::is_none")]
531 pub time_range_start: Option<i64>,
532 #[serde(skip_serializing_if = "Option::is_none")]
534 pub time_range_end: Option<i64>,
535 #[serde(skip_serializing_if = "Option::is_none")]
537 pub bucket_interval: Option<String>,
538
539 #[serde(skip_serializing_if = "Option::is_none")]
542 pub ttl_ms: Option<u64>,
543 #[serde(skip_serializing_if = "Option::is_none")]
545 pub cursor: Option<Vec<u8>>,
546 #[serde(skip_serializing_if = "Option::is_none")]
548 pub match_pattern: Option<String>,
549 #[serde(skip_serializing_if = "Option::is_none")]
551 pub keys: Option<Vec<Vec<u8>>>,
552 #[serde(skip_serializing_if = "Option::is_none")]
554 pub entries: Option<Vec<(Vec<u8>, Vec<u8>)>>,
555 #[serde(skip_serializing_if = "Option::is_none")]
557 pub fields: Option<Vec<String>>,
558
559 #[serde(skip_serializing_if = "Option::is_none")]
562 pub incr_delta: Option<i64>,
563 #[serde(skip_serializing_if = "Option::is_none")]
565 pub incr_float_delta: Option<f64>,
566 #[serde(skip_serializing_if = "Option::is_none")]
568 pub expected: Option<Vec<u8>>,
569 #[serde(skip_serializing_if = "Option::is_none")]
571 pub new_value: Option<Vec<u8>>,
572
573 #[serde(skip_serializing_if = "Option::is_none")]
576 pub index_name: Option<String>,
577 #[serde(skip_serializing_if = "Option::is_none")]
579 pub sort_columns: Option<Vec<(String, String)>>,
580 #[serde(skip_serializing_if = "Option::is_none")]
582 pub key_column: Option<String>,
583 #[serde(skip_serializing_if = "Option::is_none")]
585 pub window_type: Option<String>,
586 #[serde(skip_serializing_if = "Option::is_none")]
588 pub window_timestamp_column: Option<String>,
589 #[serde(skip_serializing_if = "Option::is_none")]
591 pub window_start_ms: Option<u64>,
592 #[serde(skip_serializing_if = "Option::is_none")]
594 pub window_end_ms: Option<u64>,
595 #[serde(skip_serializing_if = "Option::is_none")]
597 pub top_k_count: Option<u32>,
598 #[serde(skip_serializing_if = "Option::is_none")]
600 pub score_min: Option<Vec<u8>>,
601 #[serde(skip_serializing_if = "Option::is_none")]
603 pub score_max: Option<Vec<u8>>,
604
605 #[serde(skip_serializing_if = "Option::is_none")]
608 pub updates: Option<Vec<(String, Vec<u8>)>>,
609 #[serde(skip_serializing_if = "Option::is_none")]
611 pub filters: Option<Vec<u8>>,
612
613 #[serde(skip_serializing_if = "Option::is_none")]
616 pub vector: Option<Vec<f32>>,
617 #[serde(skip_serializing_if = "Option::is_none")]
619 pub vector_id: Option<u32>,
620
621 #[serde(skip_serializing_if = "Option::is_none")]
623 pub policy: Option<serde_json::Value>,
624
625 #[serde(skip_serializing_if = "Option::is_none")]
628 pub algorithm: Option<String>,
629 #[serde(skip_serializing_if = "Option::is_none")]
631 pub match_query: Option<String>,
632 #[serde(skip_serializing_if = "Option::is_none")]
634 pub algo_params: Option<serde_json::Value>,
635
636 #[serde(skip_serializing_if = "Option::is_none")]
639 pub index_paths: Option<Vec<String>>,
640 #[serde(skip_serializing_if = "Option::is_none")]
642 pub source_collection: Option<String>,
643
644 #[serde(skip_serializing_if = "Option::is_none")]
647 pub field_position: Option<u64>,
648 #[serde(skip_serializing_if = "Option::is_none")]
650 pub backfill: Option<bool>,
651
652 #[serde(skip_serializing_if = "Option::is_none")]
655 pub m: Option<u64>,
656 #[serde(skip_serializing_if = "Option::is_none")]
658 pub ef_construction: Option<u64>,
659 #[serde(skip_serializing_if = "Option::is_none")]
661 pub metric: Option<String>,
662 #[serde(skip_serializing_if = "Option::is_none")]
664 pub index_type: Option<String>,
665}
666
667impl zerompk::ToMessagePack for TextFields {
668 fn write<W: zerompk::Write>(&self, writer: &mut W) -> zerompk::Result<()> {
669 use crate::json_msgpack::JsonValue;
670 writer.write_array_len(81)?;
671 self.auth.write(writer)?;
672 self.sql.write(writer)?;
673 self.key.write(writer)?;
674 self.value.write(writer)?;
675 self.collection.write(writer)?;
676 self.document_id.write(writer)?;
677 self.data.write(writer)?;
678 self.query_vector.write(writer)?;
679 self.top_k.write(writer)?;
680 self.field.write(writer)?;
681 self.limit.write(writer)?;
682 self.delta.write(writer)?;
683 self.peer_id.write(writer)?;
684 self.vector_top_k.write(writer)?;
685 self.edge_label.write(writer)?;
686 self.direction.write(writer)?;
687 self.expansion_depth.write(writer)?;
688 self.final_top_k.write(writer)?;
689 self.vector_k.write(writer)?;
690 self.graph_k.write(writer)?;
691 self.vector_field.write(writer)?;
692 self.start_node.write(writer)?;
693 self.end_node.write(writer)?;
694 self.depth.write(writer)?;
695 self.from_node.write(writer)?;
696 self.to_node.write(writer)?;
697 self.edge_type.write(writer)?;
698 self.properties
699 .as_ref()
700 .map(|v| JsonValue(v.clone()))
701 .write(writer)?;
702 self.query_text.write(writer)?;
703 self.vector_weight.write(writer)?;
704 self.fuzzy.write(writer)?;
705 self.ef_search.write(writer)?;
706 self.field_name.write(writer)?;
707 self.lower_bound.write(writer)?;
708 self.upper_bound.write(writer)?;
709 self.mutation_id.write(writer)?;
710 self.vectors.write(writer)?;
711 self.documents.write(writer)?;
712 self.query_geometry.write(writer)?;
713 self.spatial_predicate.write(writer)?;
714 self.distance_meters.write(writer)?;
715 self.payload.write(writer)?;
716 self.format.write(writer)?;
717 self.time_range_start.write(writer)?;
718 self.time_range_end.write(writer)?;
719 self.bucket_interval.write(writer)?;
720 self.ttl_ms.write(writer)?;
721 self.cursor.write(writer)?;
722 self.match_pattern.write(writer)?;
723 self.keys.write(writer)?;
724 self.entries.write(writer)?;
725 self.fields.write(writer)?;
726 self.incr_delta.write(writer)?;
727 self.incr_float_delta.write(writer)?;
728 self.expected.write(writer)?;
729 self.new_value.write(writer)?;
730 self.index_name.write(writer)?;
731 self.sort_columns.write(writer)?;
732 self.key_column.write(writer)?;
733 self.window_type.write(writer)?;
734 self.window_timestamp_column.write(writer)?;
735 self.window_start_ms.write(writer)?;
736 self.window_end_ms.write(writer)?;
737 self.top_k_count.write(writer)?;
738 self.score_min.write(writer)?;
739 self.score_max.write(writer)?;
740 self.updates.write(writer)?;
741 self.filters.write(writer)?;
742 self.vector.write(writer)?;
743 self.vector_id.write(writer)?;
744 self.policy
745 .as_ref()
746 .map(|v| JsonValue(v.clone()))
747 .write(writer)?;
748 self.algorithm.write(writer)?;
749 self.match_query.write(writer)?;
750 self.algo_params
751 .as_ref()
752 .map(|v| JsonValue(v.clone()))
753 .write(writer)?;
754 self.index_paths.write(writer)?;
755 self.source_collection.write(writer)?;
756 self.field_position.write(writer)?;
757 self.backfill.write(writer)?;
758 self.m.write(writer)?;
759 self.ef_construction.write(writer)?;
760 self.metric.write(writer)?;
761 self.index_type.write(writer)
762 }
763}
764
765impl<'a> zerompk::FromMessagePack<'a> for TextFields {
766 fn read<R: zerompk::Read<'a>>(reader: &mut R) -> zerompk::Result<Self> {
767 use crate::json_msgpack::JsonValue;
768 let len = reader.read_array_len()?;
769 if len != 81 {
770 return Err(zerompk::Error::ArrayLengthMismatch {
771 expected: 81,
772 actual: len,
773 });
774 }
775 Ok(Self {
776 auth: Option::<AuthMethod>::read(reader)?,
777 sql: Option::<String>::read(reader)?,
778 key: Option::<String>::read(reader)?,
779 value: Option::<String>::read(reader)?,
780 collection: Option::<String>::read(reader)?,
781 document_id: Option::<String>::read(reader)?,
782 data: Option::<Vec<u8>>::read(reader)?,
783 query_vector: Option::<Vec<f32>>::read(reader)?,
784 top_k: Option::<u64>::read(reader)?,
785 field: Option::<String>::read(reader)?,
786 limit: Option::<u64>::read(reader)?,
787 delta: Option::<Vec<u8>>::read(reader)?,
788 peer_id: Option::<u64>::read(reader)?,
789 vector_top_k: Option::<u64>::read(reader)?,
790 edge_label: Option::<String>::read(reader)?,
791 direction: Option::<String>::read(reader)?,
792 expansion_depth: Option::<u64>::read(reader)?,
793 final_top_k: Option::<u64>::read(reader)?,
794 vector_k: Option::<f64>::read(reader)?,
795 graph_k: Option::<f64>::read(reader)?,
796 vector_field: Option::<String>::read(reader)?,
797 start_node: Option::<String>::read(reader)?,
798 end_node: Option::<String>::read(reader)?,
799 depth: Option::<u64>::read(reader)?,
800 from_node: Option::<String>::read(reader)?,
801 to_node: Option::<String>::read(reader)?,
802 edge_type: Option::<String>::read(reader)?,
803 properties: Option::<JsonValue>::read(reader)?.map(|v| v.0),
804 query_text: Option::<String>::read(reader)?,
805 vector_weight: Option::<f64>::read(reader)?,
806 fuzzy: Option::<bool>::read(reader)?,
807 ef_search: Option::<u64>::read(reader)?,
808 field_name: Option::<String>::read(reader)?,
809 lower_bound: Option::<Vec<u8>>::read(reader)?,
810 upper_bound: Option::<Vec<u8>>::read(reader)?,
811 mutation_id: Option::<u64>::read(reader)?,
812 vectors: Option::<Vec<BatchVector>>::read(reader)?,
813 documents: Option::<Vec<BatchDocument>>::read(reader)?,
814 query_geometry: Option::<Vec<u8>>::read(reader)?,
815 spatial_predicate: Option::<String>::read(reader)?,
816 distance_meters: Option::<f64>::read(reader)?,
817 payload: Option::<Vec<u8>>::read(reader)?,
818 format: Option::<String>::read(reader)?,
819 time_range_start: Option::<i64>::read(reader)?,
820 time_range_end: Option::<i64>::read(reader)?,
821 bucket_interval: Option::<String>::read(reader)?,
822 ttl_ms: Option::<u64>::read(reader)?,
823 cursor: Option::<Vec<u8>>::read(reader)?,
824 match_pattern: Option::<String>::read(reader)?,
825 keys: Option::<Vec<Vec<u8>>>::read(reader)?,
826 entries: Option::<Vec<(Vec<u8>, Vec<u8>)>>::read(reader)?,
827 fields: Option::<Vec<String>>::read(reader)?,
828 incr_delta: Option::<i64>::read(reader)?,
829 incr_float_delta: Option::<f64>::read(reader)?,
830 expected: Option::<Vec<u8>>::read(reader)?,
831 new_value: Option::<Vec<u8>>::read(reader)?,
832 index_name: Option::<String>::read(reader)?,
833 sort_columns: Option::<Vec<(String, String)>>::read(reader)?,
834 key_column: Option::<String>::read(reader)?,
835 window_type: Option::<String>::read(reader)?,
836 window_timestamp_column: Option::<String>::read(reader)?,
837 window_start_ms: Option::<u64>::read(reader)?,
838 window_end_ms: Option::<u64>::read(reader)?,
839 top_k_count: Option::<u32>::read(reader)?,
840 score_min: Option::<Vec<u8>>::read(reader)?,
841 score_max: Option::<Vec<u8>>::read(reader)?,
842 updates: Option::<Vec<(String, Vec<u8>)>>::read(reader)?,
843 filters: Option::<Vec<u8>>::read(reader)?,
844 vector: Option::<Vec<f32>>::read(reader)?,
845 vector_id: Option::<u32>::read(reader)?,
846 policy: Option::<JsonValue>::read(reader)?.map(|v| v.0),
847 algorithm: Option::<String>::read(reader)?,
848 match_query: Option::<String>::read(reader)?,
849 algo_params: Option::<JsonValue>::read(reader)?.map(|v| v.0),
850 index_paths: Option::<Vec<String>>::read(reader)?,
851 source_collection: Option::<String>::read(reader)?,
852 field_position: Option::<u64>::read(reader)?,
853 backfill: Option::<bool>::read(reader)?,
854 m: Option::<u64>::read(reader)?,
855 ef_construction: Option::<u64>::read(reader)?,
856 metric: Option::<String>::read(reader)?,
857 index_type: Option::<String>::read(reader)?,
858 })
859 }
860}
861
862#[derive(Debug, Clone, Serialize, Deserialize)]
864pub struct BatchVector {
865 pub id: String,
866 pub embedding: Vec<f32>,
867 #[serde(skip_serializing_if = "Option::is_none")]
868 pub metadata: Option<serde_json::Value>,
869}
870
871impl zerompk::ToMessagePack for BatchVector {
872 fn write<W: zerompk::Write>(&self, writer: &mut W) -> zerompk::Result<()> {
873 use crate::json_msgpack::JsonValue;
874 writer.write_array_len(3)?;
875 writer.write_string(&self.id)?;
876 self.embedding.write(writer)?;
877 self.metadata
878 .as_ref()
879 .map(|v| JsonValue(v.clone()))
880 .write(writer)
881 }
882}
883
884impl<'a> zerompk::FromMessagePack<'a> for BatchVector {
885 fn read<R: zerompk::Read<'a>>(reader: &mut R) -> zerompk::Result<Self> {
886 use crate::json_msgpack::JsonValue;
887 let len = reader.read_array_len()?;
888 if len != 3 {
889 return Err(zerompk::Error::ArrayLengthMismatch {
890 expected: 3,
891 actual: len,
892 });
893 }
894 let id = reader.read_string()?.into_owned();
895 let embedding = Vec::<f32>::read(reader)?;
896 let metadata = Option::<JsonValue>::read(reader)?.map(|v| v.0);
897 Ok(Self {
898 id,
899 embedding,
900 metadata,
901 })
902 }
903}
904
905#[derive(Debug, Clone, Serialize, Deserialize)]
907pub struct BatchDocument {
908 pub id: String,
909 pub fields: serde_json::Value,
910}
911
912impl zerompk::ToMessagePack for BatchDocument {
913 fn write<W: zerompk::Write>(&self, writer: &mut W) -> zerompk::Result<()> {
914 use crate::json_msgpack::JsonValue;
915 writer.write_array_len(2)?;
916 writer.write_string(&self.id)?;
917 JsonValue(self.fields.clone()).write(writer)
918 }
919}
920
921impl<'a> zerompk::FromMessagePack<'a> for BatchDocument {
922 fn read<R: zerompk::Read<'a>>(reader: &mut R) -> zerompk::Result<Self> {
923 use crate::json_msgpack::JsonValue;
924 let len = reader.read_array_len()?;
925 if len != 2 {
926 return Err(zerompk::Error::ArrayLengthMismatch {
927 expected: 2,
928 actual: len,
929 });
930 }
931 let id = reader.read_string()?.into_owned();
932 let fields = JsonValue::read(reader)?.0;
933 Ok(Self { id, fields })
934 }
935}
936
937#[derive(
941 Debug, Clone, Serialize, Deserialize, zerompk::ToMessagePack, zerompk::FromMessagePack,
942)]
943pub struct NativeResponse {
944 pub seq: u64,
946 pub status: ResponseStatus,
948 #[serde(skip_serializing_if = "Option::is_none")]
950 pub columns: Option<Vec<String>>,
951 #[serde(skip_serializing_if = "Option::is_none")]
953 pub rows: Option<Vec<Vec<Value>>>,
954 #[serde(skip_serializing_if = "Option::is_none")]
956 pub rows_affected: Option<u64>,
957 pub watermark_lsn: u64,
959 #[serde(skip_serializing_if = "Option::is_none")]
961 pub error: Option<ErrorPayload>,
962 #[serde(skip_serializing_if = "Option::is_none")]
964 pub auth: Option<AuthResponse>,
965}
966
967#[derive(
969 Debug, Clone, Serialize, Deserialize, zerompk::ToMessagePack, zerompk::FromMessagePack,
970)]
971pub struct ErrorPayload {
972 pub code: String,
974 pub message: String,
976}
977
978impl NativeResponse {
979 pub fn ok(seq: u64) -> Self {
981 Self {
982 seq,
983 status: ResponseStatus::Ok,
984 columns: None,
985 rows: None,
986 rows_affected: None,
987 watermark_lsn: 0,
988 error: None,
989 auth: None,
990 }
991 }
992
993 pub fn from_query_result(seq: u64, qr: crate::result::QueryResult, lsn: u64) -> Self {
995 Self {
996 seq,
997 status: ResponseStatus::Ok,
998 columns: Some(qr.columns),
999 rows: Some(qr.rows),
1000 rows_affected: Some(qr.rows_affected),
1001 watermark_lsn: lsn,
1002 error: None,
1003 auth: None,
1004 }
1005 }
1006
1007 pub fn error(seq: u64, code: impl Into<String>, message: impl Into<String>) -> Self {
1009 Self {
1010 seq,
1011 status: ResponseStatus::Error,
1012 columns: None,
1013 rows: None,
1014 rows_affected: None,
1015 watermark_lsn: 0,
1016 error: Some(ErrorPayload {
1017 code: code.into(),
1018 message: message.into(),
1019 }),
1020 auth: None,
1021 }
1022 }
1023
1024 pub fn auth_ok(seq: u64, username: String, tenant_id: u32) -> Self {
1026 Self {
1027 seq,
1028 status: ResponseStatus::Ok,
1029 columns: None,
1030 rows: None,
1031 rows_affected: None,
1032 watermark_lsn: 0,
1033 error: None,
1034 auth: Some(AuthResponse {
1035 username,
1036 tenant_id,
1037 }),
1038 }
1039 }
1040
1041 pub fn status_row(seq: u64, message: impl Into<String>) -> Self {
1043 Self {
1044 seq,
1045 status: ResponseStatus::Ok,
1046 columns: Some(vec!["status".into()]),
1047 rows: Some(vec![vec![Value::String(message.into())]]),
1048 rows_affected: Some(1),
1049 watermark_lsn: 0,
1050 error: None,
1051 auth: None,
1052 }
1053 }
1054}
1055
1056pub const MAX_FRAME_SIZE: u32 = 16 * 1024 * 1024;
1060
1061pub const FRAME_HEADER_LEN: usize = 4;
1063
1064pub const DEFAULT_NATIVE_PORT: u16 = 6433;
1066
1067#[cfg(test)]
1068mod tests {
1069 use super::*;
1070
1071 #[test]
1072 fn opcode_repr() {
1073 assert_eq!(OpCode::Auth as u8, 0x01);
1074 assert_eq!(OpCode::Sql as u8, 0x20);
1075 assert_eq!(OpCode::Begin as u8, 0x40);
1076 assert_eq!(OpCode::GraphHop as u8, 0x50);
1077 assert_eq!(OpCode::TextSearch as u8, 0x60);
1078 assert_eq!(OpCode::VectorBatchInsert as u8, 0x70);
1079 }
1080
1081 #[test]
1082 fn opcode_is_write() {
1083 assert!(OpCode::PointPut.is_write());
1084 assert!(OpCode::PointDelete.is_write());
1085 assert!(OpCode::CrdtApply.is_write());
1086 assert!(OpCode::EdgePut.is_write());
1087 assert!(!OpCode::PointGet.is_write());
1088 assert!(!OpCode::Sql.is_write());
1089 assert!(!OpCode::VectorSearch.is_write());
1090 assert!(!OpCode::Ping.is_write());
1091 }
1092
1093 #[test]
1094 fn response_status_repr() {
1095 assert_eq!(ResponseStatus::Ok as u8, 0);
1096 assert_eq!(ResponseStatus::Partial as u8, 1);
1097 assert_eq!(ResponseStatus::Error as u8, 2);
1098 }
1099
1100 #[test]
1101 fn native_response_ok() {
1102 let r = NativeResponse::ok(42);
1103 assert_eq!(r.seq, 42);
1104 assert_eq!(r.status, ResponseStatus::Ok);
1105 assert!(r.error.is_none());
1106 }
1107
1108 #[test]
1109 fn native_response_error() {
1110 let r = NativeResponse::error(1, "42P01", "collection not found");
1111 assert_eq!(r.status, ResponseStatus::Error);
1112 let e = r.error.unwrap();
1113 assert_eq!(e.code, "42P01");
1114 assert_eq!(e.message, "collection not found");
1115 }
1116
1117 #[test]
1118 fn native_response_from_query_result() {
1119 let qr = crate::result::QueryResult {
1120 columns: vec!["id".into(), "name".into()],
1121 rows: vec![vec![
1122 Value::String("u1".into()),
1123 Value::String("Alice".into()),
1124 ]],
1125 rows_affected: 0,
1126 };
1127 let r = NativeResponse::from_query_result(5, qr, 100);
1128 assert_eq!(r.seq, 5);
1129 assert_eq!(r.watermark_lsn, 100);
1130 assert_eq!(r.columns.as_ref().unwrap().len(), 2);
1131 assert_eq!(r.rows.as_ref().unwrap().len(), 1);
1132 }
1133
1134 #[test]
1135 fn native_response_status_row() {
1136 let r = NativeResponse::status_row(3, "OK");
1137 assert_eq!(r.columns.as_ref().unwrap(), &["status"]);
1138 assert_eq!(r.rows.as_ref().unwrap()[0][0].as_str(), Some("OK"));
1139 }
1140
1141 #[test]
1142 fn msgpack_roundtrip_request() {
1143 let req = NativeRequest {
1144 op: OpCode::Sql,
1145 seq: 1,
1146 fields: RequestFields::Text(TextFields {
1147 sql: Some("SELECT 1".into()),
1148 ..Default::default()
1149 }),
1150 };
1151 let bytes = zerompk::to_msgpack_vec(&req).unwrap();
1152 let decoded: NativeRequest = zerompk::from_msgpack(&bytes).unwrap();
1153 assert_eq!(decoded.op, OpCode::Sql);
1154 assert_eq!(decoded.seq, 1);
1155 }
1156
1157 #[test]
1158 fn msgpack_roundtrip_response() {
1159 let resp = NativeResponse::from_query_result(
1160 7,
1161 crate::result::QueryResult {
1162 columns: vec!["x".into()],
1163 rows: vec![vec![Value::Integer(42)]],
1164 rows_affected: 0,
1165 },
1166 99,
1167 );
1168 let bytes = zerompk::to_msgpack_vec(&resp).unwrap();
1169 let decoded: NativeResponse = zerompk::from_msgpack(&bytes).unwrap();
1170 assert_eq!(decoded.seq, 7);
1171 assert_eq!(decoded.watermark_lsn, 99);
1172 assert_eq!(decoded.rows.unwrap()[0][0].as_i64(), Some(42));
1173 }
1174
1175 #[test]
1176 fn auth_method_variants() {
1177 let trust = AuthMethod::Trust {
1178 username: "admin".into(),
1179 };
1180 let bytes = zerompk::to_msgpack_vec(&trust).unwrap();
1181 let decoded: AuthMethod = zerompk::from_msgpack(&bytes).unwrap();
1182 match decoded {
1183 AuthMethod::Trust { username } => assert_eq!(username, "admin"),
1184 _ => panic!("expected Trust variant"),
1185 }
1186
1187 let pw = AuthMethod::Password {
1188 username: "user".into(),
1189 password: "secret".into(),
1190 };
1191 let bytes = zerompk::to_msgpack_vec(&pw).unwrap();
1192 let decoded: AuthMethod = zerompk::from_msgpack(&bytes).unwrap();
1193 match decoded {
1194 AuthMethod::Password { username, password } => {
1195 assert_eq!(username, "user");
1196 assert_eq!(password, "secret");
1197 }
1198 _ => panic!("expected Password variant"),
1199 }
1200 }
1201}