1use std::collections::HashMap;
3use std::sync::Arc;
4
5use datafusion::arrow::array::*;
6use datafusion::arrow::datatypes::SchemaRef;
7use datafusion::arrow::record_batch::RecordBatch;
8use datafusion::error::{DataFusionError, Result as DataFusionResult};
9use qdrant_client::qdrant::{
10 ScoredPoint, SparseVector, VectorOutput, VectorsOutput, point_id, vector_output, vectors_output,
11};
12
13use super::schema::is_multi_vector_field;
14
15pub fn convert_to_multi_vector(
42 data: &[f32],
43 vectors_count: u32,
44) -> DataFusionResult<Vec<Vec<f32>>> {
45 if data.len() % vectors_count as usize != 0 {
46 return Err(DataFusionError::External(Box::new(std::io::Error::new(
47 std::io::ErrorKind::InvalidData,
48 format!(
49 "Malformed multi vector: data length {} is not divisible by vectors count {}",
50 data.len(),
51 vectors_count
52 ),
53 ))));
54 }
55
56 let chunk_size = data.len() / vectors_count as usize;
57 Ok(data.chunks(chunk_size).map(<[f32]>::to_vec).collect())
58}
59
60#[derive(Debug)]
66pub enum Vector {
67 Dense(Vec<f32>),
68 Sparse(SparseVector),
69 MultiDense(Vec<Vec<f32>>),
70}
71
72impl Vector {
73 fn from_vector_output(vector_output: VectorOutput) -> Option<Self> {
75 if let Some(vector) = vector_output.vector {
77 return match vector {
78 vector_output::Vector::Dense(dense) => Some(Self::Dense(dense.data)),
79 vector_output::Vector::Sparse(sparse) => Some(Self::Sparse(sparse)),
80 vector_output::Vector::MultiDense(multi) => {
81 Some(Self::MultiDense(multi.vectors.into_iter().map(|v| v.data).collect()))
82 }
83 };
84 }
85
86 if let Some(vectors_count) = vector_output.vectors_count
88 && let Ok(multi_vectors) = convert_to_multi_vector(&vector_output.data, vectors_count)
89 {
90 return Some(Self::MultiDense(multi_vectors));
92 }
93
94 if let Some(indices) = vector_output.indices {
96 return Some(Self::Sparse(SparseVector {
97 indices: indices.data,
98 values: vector_output.data,
99 }));
100 }
101
102 if vector_output.data.is_empty() {
104 return None;
105 }
106
107 Some(Self::Dense(vector_output.data))
109 }
110}
111
112enum FieldExtractor {
114 Id(StringBuilder),
115 Payload(StringBuilder),
116 DenseVector { name: String, builder: ListBuilder<Float32Builder> },
117 MultiVector { name: String, builder: ListBuilder<ListBuilder<Float32Builder>> },
118 SparseIndices { name: String, builder: ListBuilder<UInt32Builder> },
119 SparseValues { name: String, builder: ListBuilder<Float32Builder> },
120}
121
122impl FieldExtractor {
123 fn from_schema_field(field: &datafusion::arrow::datatypes::Field, capacity: usize) -> Self {
125 match field.name().as_str() {
126 "id" => Self::Id(StringBuilder::with_capacity(capacity, capacity * 16)),
127 "payload" => Self::Payload(StringBuilder::with_capacity(capacity, capacity * 64)),
128 name if name.ends_with("_indices") => Self::SparseIndices {
129 name: name.to_string(),
130 builder: ListBuilder::with_capacity(UInt32Builder::new(), capacity),
131 },
132 name if name.ends_with("_values") => Self::SparseValues {
133 name: name.to_string(),
134 builder: ListBuilder::with_capacity(Float32Builder::new(), capacity),
135 },
136 name if is_multi_vector_field(field) => Self::MultiVector {
137 name: name.to_string(),
138 builder: ListBuilder::with_capacity(
139 ListBuilder::new(Float32Builder::new()),
140 capacity,
141 ),
142 },
143 name => Self::DenseVector {
144 name: name.to_string(),
145 builder: ListBuilder::with_capacity(Float32Builder::new(), capacity),
146 },
147 }
148 }
149}
150
151pub struct QdrantRecordBatchBuilder {
193 schema: SchemaRef,
194 field_extractors: Vec<FieldExtractor>, }
196
197impl QdrantRecordBatchBuilder {
198 pub fn new(schema: SchemaRef, point_count: usize) -> Self {
200 let field_extractors = schema
202 .fields()
203 .iter()
204 .map(|field| FieldExtractor::from_schema_field(field, point_count))
205 .collect();
206
207 Self { schema, field_extractors }
208 }
209
210 pub fn append_point(&mut self, point: ScoredPoint) {
212 let ScoredPoint { id, payload, vectors, .. } = point;
214
215 let vector_lookup = build_vector_lookup(vectors);
217
218 for extractor in &mut self.field_extractors {
220 match extractor {
221 FieldExtractor::Id(builder) => {
222 if let Some(id) = &id {
223 match &id.point_id_options {
224 Some(point_id::PointIdOptions::Num(n)) => {
225 builder.append_value(n.to_string());
226 }
227 Some(point_id::PointIdOptions::Uuid(s)) => builder.append_value(s),
228 None => builder.append_value(""),
229 }
230 } else {
231 builder.append_null();
232 }
233 }
234
235 FieldExtractor::Payload(builder) => {
236 if !payload.is_empty()
237 && let Ok(json) = serde_json::to_string(&payload)
238 {
239 builder.append_value(json);
240 } else {
241 builder.append_null();
242 }
243 }
244
245 FieldExtractor::DenseVector { name, builder } => {
246 if let Some(Vector::Dense(data)) = vector_lookup.get(name) {
247 builder.values().append_slice(data);
248 builder.append(true);
249 } else {
250 builder.append(false);
251 }
252 }
253
254 FieldExtractor::MultiVector { name, builder } => {
255 if let Some(Vector::MultiDense(vectors)) = vector_lookup.get(name) {
256 for vector in vectors {
257 builder.values().values().append_slice(vector);
258 builder.values().append(true);
259 }
260 builder.append(true);
261 } else {
262 builder.append(false);
263 }
264 }
265
266 FieldExtractor::SparseIndices { name, builder } => {
267 let sparse_name = name.trim_end_matches("_indices");
268 if let Some(Vector::Sparse(sparse)) = vector_lookup.get(sparse_name) {
269 builder.values().append_slice(&sparse.indices);
270 builder.append(true);
271 } else {
272 builder.append(false);
273 }
274 }
275
276 FieldExtractor::SparseValues { name, builder } => {
277 let sparse_name = name.trim_end_matches("_values");
278 if let Some(Vector::Sparse(sparse)) = vector_lookup.get(sparse_name) {
279 builder.values().append_slice(&sparse.values);
280 builder.append(true);
281 } else {
282 builder.append(false);
283 }
284 }
285 }
286 }
287 }
288
289 pub fn finish(self) -> DataFusionResult<RecordBatch> {
294 let mut arrays: Vec<ArrayRef> = Vec::with_capacity(self.schema.fields().len());
295
296 for extractor in self.field_extractors {
298 let array: ArrayRef = match extractor {
299 FieldExtractor::Id(mut builder) | FieldExtractor::Payload(mut builder) => {
300 Arc::new(builder.finish())
301 }
302 FieldExtractor::DenseVector { mut builder, .. }
303 | FieldExtractor::SparseValues { mut builder, .. } => Arc::new(builder.finish()),
304 FieldExtractor::MultiVector { mut builder, .. } => Arc::new(builder.finish()),
305 FieldExtractor::SparseIndices { mut builder, .. } => Arc::new(builder.finish()),
306 };
307 arrays.push(array);
308 }
309
310 RecordBatch::try_new(self.schema, arrays)
311 .map_err(|e| DataFusionError::ArrowError(Box::new(e), None))
312 }
313}
314
315fn build_vector_lookup(vectors: Option<VectorsOutput>) -> HashMap<String, Vector> {
317 let mut lookup = HashMap::new();
318
319 if let Some(vectors) = vectors {
320 match vectors.vectors_options {
321 Some(vectors_output::VectorsOptions::Vector(vector_output)) => {
322 if let Some(content) = Vector::from_vector_output(vector_output) {
324 drop(lookup.insert("vector".to_string(), content));
325 }
326 }
327 Some(vectors_output::VectorsOptions::Vectors(named_vectors)) => {
328 for (name, vector_output) in named_vectors.vectors {
330 if let Some(content) = Vector::from_vector_output(vector_output) {
331 drop(lookup.insert(name, content));
332 }
333 }
334 }
335 None => {}
336 }
337 }
338
339 lookup
340}
341
342#[cfg(test)]
343mod tests {
344 use super::*;
345
346 #[test]
347 fn test_convert_to_multi_vector_error() {
348 let data = vec![1.0, 2.0, 3.0]; let vectors_count = 2; let result = convert_to_multi_vector(&data, vectors_count);
353
354 assert!(result.is_err());
355 if let Err(DataFusionError::External(boxed_error)) = result {
356 let error_msg = boxed_error.to_string();
357 assert!(error_msg.contains("Malformed multi vector"));
358 assert!(error_msg.contains("data length 3 is not divisible by vectors count 2"));
359 } else {
360 panic!("Expected DataFusionError::External");
361 }
362 }
363
364 #[test]
365 fn test_vector_from_new_format() {
366 use qdrant_client::qdrant::{DenseVector, MultiDenseVector, SparseVector, vector_output};
367
368 let dense_vector_output = VectorOutput {
370 vector: Some(vector_output::Vector::Dense(DenseVector {
371 data: vec![1.0, 2.0, 3.0],
372 })),
373 data: vec![], indices: None,
375 vectors_count: None,
376 };
377
378 let result = Vector::from_vector_output(dense_vector_output);
379 if let Some(Vector::Dense(data)) = result {
380 assert_eq!(data, vec![1.0, 2.0, 3.0]);
381 } else {
382 panic!("Expected Dense vector");
383 }
384
385 let sparse_vector_output = VectorOutput {
387 vector: Some(vector_output::Vector::Sparse(SparseVector {
388 indices: vec![0, 2, 5],
389 values: vec![0.1, 0.2, 0.3],
390 })),
391 data: vec![], indices: None,
393 vectors_count: None,
394 };
395
396 let result = Vector::from_vector_output(sparse_vector_output);
397 if let Some(Vector::Sparse(sparse)) = result {
398 assert_eq!(sparse.indices, vec![0, 2, 5]);
399 assert_eq!(sparse.values, vec![0.1, 0.2, 0.3]);
400 } else {
401 panic!("Expected Sparse vector");
402 }
403
404 let multi_vector_output = VectorOutput {
406 vector: Some(vector_output::Vector::MultiDense(MultiDenseVector {
407 vectors: vec![DenseVector { data: vec![1.0, 2.0] }, DenseVector {
408 data: vec![3.0, 4.0],
409 }],
410 })),
411 data: vec![], indices: None,
413 vectors_count: None,
414 };
415
416 let result = Vector::from_vector_output(multi_vector_output);
417 if let Some(Vector::MultiDense(multi)) = result {
418 assert_eq!(multi, vec![vec![1.0, 2.0], vec![3.0, 4.0]]);
419 } else {
420 panic!("Expected MultiDense vector");
421 }
422 }
423}