1use std::any::Any;
3use std::sync::Arc;
4
5use datafusion::arrow::array::*;
6use datafusion::arrow::datatypes::*;
7use datafusion::catalog::{Session, TableProvider};
8use datafusion::datasource::TableType;
9use datafusion::error::{DataFusionError, Result as DataFusionResult};
10use datafusion::execution::{SendableRecordBatchStream, TaskContext};
11use datafusion::logical_expr::dml::InsertOp;
12use datafusion::physical_plan::execution_plan::Boundedness;
13use datafusion::physical_plan::{DisplayAs, DisplayFormatType, ExecutionPlan, PlanProperties};
14use datafusion::prelude::Expr;
15use datafusion::sql::TableReference;
16use qdrant_client::Qdrant;
17use qdrant_client::qdrant::{QueryPointsBuilder, VectorsSelector};
18
19use crate::arrow::deserialize::QdrantRecordBatchBuilder;
20use crate::arrow::schema::collection_to_arrow_schema;
21use crate::error::{Error, Result};
22use crate::stream::QdrantQueryStream;
23use crate::utils;
24
25#[derive(Clone)]
82pub struct QdrantTableProvider {
83 table: TableReference,
84 client: Arc<Qdrant>,
85 schema: Arc<Schema>,
86}
87
88impl std::fmt::Debug for QdrantTableProvider {
89 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
90 f.debug_struct("QdrantTableProvider")
91 .field("table", &self.table)
92 .field("client", &"Qdrant")
93 .field("schema", &self.schema)
94 .finish()
95 }
96}
97
98impl QdrantTableProvider {
99 pub async fn try_new(client: Qdrant, collection: &str) -> Result<Self> {
133 let info = client.collection_info(collection).await?;
134 let config = info
136 .result
137 .ok_or(Error::MissingCollectionInfo(collection.into()))?
138 .config
139 .ok_or(Error::MissingCollectionInfo(collection.into()))?;
140 let schema = collection_to_arrow_schema(collection, &config)?;
141 Ok(Self {
142 table: TableReference::bare(collection),
143 client: Arc::new(client),
144 schema: Arc::new(schema),
145 })
146 }
147}
148
149#[async_trait::async_trait]
150impl TableProvider for QdrantTableProvider {
151 fn as_any(&self) -> &dyn Any { self }
152
153 fn schema(&self) -> SchemaRef { Arc::clone(&self.schema) }
154
155 fn table_type(&self) -> TableType { TableType::Base }
156
157 async fn scan(
158 &self,
159 _state: &dyn Session,
160 projection: Option<&Vec<usize>>,
161 filters: &[Expr],
162 limit: Option<usize>,
163 ) -> DataFusionResult<Arc<dyn ExecutionPlan>> {
164 let projected_schema = match projection {
166 Some(indices) if !indices.is_empty() => Arc::new(self.schema.project(indices)?),
167 _ => Arc::clone(&self.schema),
168 };
169
170 let vector_selector = utils::build_vector_selector(&projected_schema);
172 let payload_selector = utils::build_payload_selector(&projected_schema);
173
174 Ok(Arc::new(QdrantScanExec::new(
176 Arc::clone(&self.client),
177 self.table.table().to_string(),
178 projected_schema,
179 vector_selector,
180 payload_selector,
181 filters,
182 limit,
183 )))
184 }
185
186 async fn insert_into(
187 &self,
188 _state: &dyn Session,
189 _input: Arc<dyn ExecutionPlan>,
190 _insert_op: InsertOp,
191 ) -> DataFusionResult<Arc<dyn ExecutionPlan>> {
192 todo!()
193 }
194}
195
196#[derive(Clone)]
211pub struct QdrantScanExec {
212 client: Arc<Qdrant>,
213 collection: String,
214 schema: SchemaRef, vector_selector: utils::VectorSelectorSpec,
216 payload_selector: bool,
217 filter: Arc<[Expr]>,
218 limit: Option<usize>,
219 properties: PlanProperties,
220}
221
222impl std::fmt::Debug for QdrantScanExec {
223 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
224 f.debug_struct("QdrantScanExec")
225 .field("client", &"Qdrant")
226 .field("collection", &self.collection)
227 .field("schema", &self.schema)
228 .field("vector_selector", &self.vector_selector)
229 .field("payload_selector", &self.payload_selector)
230 .field("limit", &self.limit)
231 .finish_non_exhaustive()
232 }
233}
234
235impl QdrantScanExec {
236 pub fn new(
237 client: Arc<Qdrant>,
238 collection: String,
239 schema: SchemaRef,
240 vector_selector: utils::VectorSelectorSpec,
241 payload_selector: bool,
242 filter: &[Expr],
243 limit: Option<usize>,
244 ) -> Self {
245 let properties = PlanProperties::new(
246 datafusion::physical_expr::EquivalenceProperties::new(Arc::clone(&schema)),
247 datafusion::physical_plan::Partitioning::UnknownPartitioning(1),
248 datafusion::physical_plan::execution_plan::EmissionType::Final,
249 Boundedness::Bounded,
250 );
251
252 Self {
253 client,
254 collection,
255 schema,
256 vector_selector,
257 payload_selector,
258 filter: Arc::from(filter),
259 limit,
260 properties,
261 }
262 }
263}
264
265pub(crate) async fn execute_qdrant_query(
270 client: Arc<Qdrant>,
271 collection: String,
272 schema: SchemaRef,
273 vector_selector: utils::VectorSelectorSpec,
274 payload_selector: bool,
275 _filters: &[Expr],
276 limit: Option<usize>,
277) -> DataFusionResult<RecordBatch> {
278 let mut query_builder = QueryPointsBuilder::new(&collection);
280
281 match vector_selector {
283 utils::VectorSelectorSpec::None => {
284 query_builder = query_builder.with_vectors(false);
285 }
286 utils::VectorSelectorSpec::All => {
287 query_builder = query_builder.with_vectors(true);
288 }
289 utils::VectorSelectorSpec::Named(names) => {
290 query_builder = query_builder.with_vectors(VectorsSelector { names });
291 }
292 }
293
294 query_builder = query_builder.with_payload(payload_selector);
295
296 if let Some(limit_val) = limit {
297 query_builder = query_builder.limit(limit_val as u64);
298 }
299
300 let response =
302 client.query(query_builder).await.map_err(|e| DataFusionError::External(Box::new(e)))?;
303
304 let points = response.result;
306
307 if points.is_empty() {
308 return Ok(RecordBatch::new_empty(schema));
309 }
310
311 let mut builder = QdrantRecordBatchBuilder::new(schema, points.len());
313
314 for point in points {
316 builder.append_point(point); }
318
319 builder.finish()
320}
321
322impl ExecutionPlan for QdrantScanExec {
323 fn name(&self) -> &'static str { "QdrantScanExec" }
324
325 fn as_any(&self) -> &dyn Any { self }
326
327 fn properties(&self) -> &PlanProperties { &self.properties }
328
329 fn children(&self) -> Vec<&Arc<dyn ExecutionPlan>> { vec![] }
330
331 fn with_new_children(
332 self: Arc<Self>,
333 _children: Vec<Arc<dyn ExecutionPlan>>,
334 ) -> DataFusionResult<Arc<dyn ExecutionPlan>> {
335 Ok(self)
336 }
337
338 fn execute(
339 &self,
340 _partition: usize,
341 _context: Arc<TaskContext>,
342 ) -> DataFusionResult<SendableRecordBatchStream> {
343 let client = Arc::clone(&self.client);
344 let collection = self.collection.clone();
345 let schema = Arc::clone(&self.schema);
346 let vector_selector = self.vector_selector.clone();
347 let payload_selector = self.payload_selector;
348 let filter = Arc::clone(&self.filter);
349 let limit = self.limit;
350 let inner = Box::pin(futures_util::stream::once(async move {
351 execute_qdrant_query(
352 client,
353 collection,
354 schema,
355 vector_selector,
356 payload_selector,
357 &filter,
358 limit,
359 )
360 .await
361 }));
362 let stream = QdrantQueryStream::new(Arc::clone(&self.schema), inner);
363 Ok(Box::pin(stream))
364 }
365}
366
367impl DisplayAs for QdrantScanExec {
368 fn fmt_as(&self, t: DisplayFormatType, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
369 match t {
370 DisplayFormatType::Default | DisplayFormatType::Verbose => {
371 write!(f, "QdrantScanExec: collection={}", self.collection)?;
372 if let Some(limit) = self.limit {
373 write!(f, ", limit={limit}")?;
374 }
375 Ok(())
376 }
377 DisplayFormatType::TreeRender => {
378 write!(f, "QdrantScanExec: collection={}", self.collection)
379 }
380 }
381 }
382}