mirror of
https://github.com/qdrant/qdrant.git
synced 2026-08-06 10:00:58 -05:00
More type conversion for Qdrant on Edge Python bindings (#7372)
* Cleanup `edge::Shard::retrieve` * Cleanup Python errors * Cleanup `PointId` conversions * Rename `ids` into `point_ids` in `PyShard::retrieve` * WIP: Prototype native conversions for more types
This commit is contained in:
@@ -26,12 +26,9 @@ mod qdrant_edge {
|
||||
PyVectorStorageDatatype, PyVectorStorageType,
|
||||
};
|
||||
#[pymodule_export]
|
||||
use super::search::{
|
||||
PyFilter, PyQuery, PyQueryVector, PyScoredPoint, PySearchParams, PySearchRequest,
|
||||
PyWithPayload, PyWithVector,
|
||||
};
|
||||
use super::search::{PyScoredPoint, PySearchParams, PySearchRequest};
|
||||
#[pymodule_export]
|
||||
use super::types::{PyRecord, PyVector};
|
||||
use super::types::{PyRecord, PySparseVector};
|
||||
#[pymodule_export]
|
||||
use super::update::{PyPoint, PyUpdateOperation};
|
||||
}
|
||||
@@ -55,29 +52,23 @@ impl PyShard {
|
||||
|
||||
pub fn search(&self, search: PySearchRequest) -> Result<Vec<PyScoredPoint>> {
|
||||
let points = self.0.search(search.into())?;
|
||||
let points = points.into_iter().map(PyScoredPoint).collect();
|
||||
let points = PyScoredPoint::from_rust_vec(points);
|
||||
Ok(points)
|
||||
}
|
||||
|
||||
pub fn retrieve(
|
||||
&self,
|
||||
ids: Vec<PyPointId>,
|
||||
point_ids: Vec<PyPointId>,
|
||||
with_payload: Option<PyWithPayload>,
|
||||
with_vector: Option<PyWithVector>,
|
||||
) -> Result<Vec<PyRecord>, PyErr> {
|
||||
let ids_res: Result<Vec<_>, _> = ids.into_iter().map(PointIdType::try_from).collect();
|
||||
let ids = ids_res?;
|
||||
|
||||
let records = self
|
||||
.0
|
||||
.retrieve(
|
||||
&ids,
|
||||
with_payload.map(WithPayloadInterface::from),
|
||||
with_vector.map(WithVector::from),
|
||||
)
|
||||
.map_err(PyError::from)?;
|
||||
|
||||
let points = records.into_iter().map(PyRecord).collect();
|
||||
) -> Result<Vec<PyRecord>> {
|
||||
let point_ids = PyPointId::into_rust_vec(point_ids);
|
||||
let points = self.0.retrieve(
|
||||
&point_ids,
|
||||
with_payload.map(WithPayloadInterface::from),
|
||||
with_vector.map(WithVector::from),
|
||||
)?;
|
||||
let points = PyRecord::from_rust_vec(points);
|
||||
Ok(points)
|
||||
}
|
||||
}
|
||||
@@ -95,6 +86,6 @@ impl From<OperationError> for PyError {
|
||||
|
||||
impl From<PyError> for PyErr {
|
||||
fn from(err: PyError) -> Self {
|
||||
PyErr::new::<PyException, _>(err.0.to_string())
|
||||
PyException::new_err(err.0.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
use std::mem;
|
||||
|
||||
use derive_more::Into;
|
||||
use pyo3::IntoPyObjectExt as _;
|
||||
use pyo3::exceptions::PyValueError;
|
||||
use pyo3::prelude::*;
|
||||
use segment::data_types::vectors::*;
|
||||
use segment::json_path::JsonPath;
|
||||
use segment::data_types::vectors::NamedQuery;
|
||||
use segment::types::*;
|
||||
use shard::query::query_enum::QueryEnum;
|
||||
use shard::search::*;
|
||||
@@ -39,67 +42,30 @@ impl PySearchRequest {
|
||||
}
|
||||
}
|
||||
|
||||
#[pyclass(name = "Query")]
|
||||
#[derive(Clone, Debug, Into)]
|
||||
pub struct PyQuery(QueryEnum);
|
||||
|
||||
#[pymethods]
|
||||
impl PyQuery {
|
||||
#[staticmethod]
|
||||
pub fn nearest(query: PyQueryVector, using: Option<String>) -> Self {
|
||||
Self(QueryEnum::Nearest(NamedQuery {
|
||||
query: query.into(),
|
||||
using,
|
||||
}))
|
||||
impl<'py> FromPyObject<'py> for PyQuery {
|
||||
fn extract_bound(query: &Bound<'py, PyAny>) -> PyResult<Self> {
|
||||
let query = if let Ok(single) = query.extract() {
|
||||
QueryEnum::Nearest(NamedQuery::default_dense(single))
|
||||
} else {
|
||||
return Err(PyValueError::new_err(format!(
|
||||
"failed to convert Python object {query} into query"
|
||||
)));
|
||||
};
|
||||
|
||||
Ok(Self(query))
|
||||
}
|
||||
}
|
||||
|
||||
impl PyQuery {
|
||||
fn _variants(query: QueryEnum) {
|
||||
match query {
|
||||
QueryEnum::Nearest(_) => (),
|
||||
QueryEnum::RecommendBestScore(_) => todo!(), // TODO!
|
||||
QueryEnum::RecommendSumScores(_) => todo!(), // TODO!
|
||||
QueryEnum::Discover(_) => todo!(), // TODO!
|
||||
QueryEnum::Context(_) => todo!(), // TODO!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pyclass(name = "QueryVector")]
|
||||
#[derive(Clone, Debug, Into)]
|
||||
pub struct PyQueryVector(VectorInternal);
|
||||
|
||||
#[pymethods]
|
||||
impl PyQueryVector {
|
||||
#[staticmethod]
|
||||
pub fn dense(vector: Vec<f32>) -> Self {
|
||||
Self(VectorInternal::Dense(vector))
|
||||
}
|
||||
|
||||
#[staticmethod]
|
||||
pub fn sparse(vector: PySparseVector) -> Self {
|
||||
Self(VectorInternal::Sparse(vector.into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl PyQueryVector {
|
||||
fn _variants(query: VectorInternal) {
|
||||
match query {
|
||||
VectorInternal::Dense(_) => (),
|
||||
VectorInternal::Sparse(_) => (),
|
||||
VectorInternal::MultiDense(_) => todo!(), // TODO!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pyclass(name = "Filter")]
|
||||
#[derive(Clone, Debug, Into)]
|
||||
pub struct PyFilter(Filter);
|
||||
|
||||
#[pymethods]
|
||||
impl PyFilter {
|
||||
// TODO!
|
||||
impl<'py> FromPyObject<'py> for PyFilter {
|
||||
fn extract_bound(_filter: &Bound<'py, PyAny>) -> PyResult<Self> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[pyclass(name = "SearchParams")]
|
||||
@@ -140,124 +106,110 @@ impl PyQuantizationSearchParams {
|
||||
}
|
||||
}
|
||||
|
||||
#[pyclass(name = "WithVector")]
|
||||
#[derive(Clone, Debug, Into)]
|
||||
pub struct PyWithVector(WithVector);
|
||||
|
||||
#[pymethods]
|
||||
impl PyWithVector {
|
||||
#[new]
|
||||
pub fn new(with_vector: bool) -> Self {
|
||||
Self(WithVector::Bool(with_vector))
|
||||
}
|
||||
impl<'py> FromPyObject<'py> for PyWithVector {
|
||||
fn extract_bound(with_vector: &Bound<'py, PyAny>) -> PyResult<Self> {
|
||||
#[derive(FromPyObject)]
|
||||
enum Helper {
|
||||
Bool(bool),
|
||||
Selector(Vec<String>),
|
||||
}
|
||||
|
||||
#[staticmethod]
|
||||
pub fn selector(vectors: Vec<VectorNameBuf>) -> Self {
|
||||
Self(WithVector::Selector(vectors))
|
||||
let with_vector = match with_vector.extract()? {
|
||||
Helper::Bool(bool) => WithVector::Bool(bool),
|
||||
Helper::Selector(vectors) => WithVector::Selector(vectors),
|
||||
};
|
||||
|
||||
Ok(Self(with_vector))
|
||||
}
|
||||
}
|
||||
|
||||
impl PyWithVector {
|
||||
fn _variants(with_vector: WithVector) {
|
||||
match with_vector {
|
||||
WithVector::Bool(_) => (),
|
||||
WithVector::Selector(_) => (),
|
||||
impl<'py> IntoPyObject<'py> for PyWithVector {
|
||||
type Target = PyAny;
|
||||
type Output = Bound<'py, Self::Target>;
|
||||
type Error = PyErr; // Infallible?
|
||||
|
||||
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
|
||||
IntoPyObject::into_pyobject(&self, py)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'py> IntoPyObject<'py> for &PyWithVector {
|
||||
type Target = PyAny;
|
||||
type Output = Bound<'py, Self::Target>;
|
||||
type Error = PyErr; // Infallible?
|
||||
|
||||
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
|
||||
match &self.0 {
|
||||
WithVector::Bool(bool) => bool.into_bound_py_any(py),
|
||||
WithVector::Selector(vectors) => vectors.into_bound_py_any(py),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pyclass(name = "WithPayload")]
|
||||
#[derive(Clone, Debug, Into)]
|
||||
pub struct PyWithPayload(WithPayloadInterface);
|
||||
|
||||
#[pymethods]
|
||||
impl PyWithPayload {
|
||||
#[new]
|
||||
pub fn new(with_payload: bool) -> Self {
|
||||
Self(WithPayloadInterface::Bool(with_payload))
|
||||
}
|
||||
|
||||
#[staticmethod]
|
||||
pub fn fields(fields: Vec<PyJsonPath>) -> Self {
|
||||
let fields = fields.into_iter().map(Into::into).collect(); // TODO: Transmute!?
|
||||
Self(WithPayloadInterface::Fields(fields))
|
||||
}
|
||||
|
||||
#[staticmethod]
|
||||
pub fn selector(selector: PyPayloadSelector) -> Self {
|
||||
Self(WithPayloadInterface::Selector(selector.into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl PyWithPayload {
|
||||
fn _variants(with_payload: WithPayloadInterface) {
|
||||
match with_payload {
|
||||
WithPayloadInterface::Bool(_) => (),
|
||||
WithPayloadInterface::Fields(_) => (),
|
||||
WithPayloadInterface::Selector(_) => (),
|
||||
impl<'py> FromPyObject<'py> for PyWithPayload {
|
||||
fn extract_bound(with_payload: &Bound<'py, PyAny>) -> PyResult<Self> {
|
||||
#[derive(FromPyObject)]
|
||||
enum Helper {
|
||||
Bool(bool),
|
||||
// TODO: `Fields(Vec<JsonPath>)`!
|
||||
// TODO: `Selector(PayloadSelector)`!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pyclass(name = "PayloadSelector")]
|
||||
#[derive(Clone, Debug, Into)]
|
||||
pub struct PyPayloadSelector(PayloadSelector);
|
||||
|
||||
#[pymethods]
|
||||
impl PyPayloadSelector {
|
||||
#[staticmethod]
|
||||
pub fn include(fields: Vec<PyJsonPath>) -> Self {
|
||||
let include = PayloadSelectorInclude {
|
||||
include: fields.into_iter().map(Into::into).collect(), // TODO: Transmute!?
|
||||
let with_payload = match with_payload.extract()? {
|
||||
Helper::Bool(bool) => WithPayloadInterface::Bool(bool),
|
||||
};
|
||||
|
||||
Self(PayloadSelector::Include(include))
|
||||
}
|
||||
|
||||
#[staticmethod]
|
||||
pub fn exclude(fields: Vec<PyJsonPath>) -> Self {
|
||||
let exclude = PayloadSelectorExclude {
|
||||
exclude: fields.into_iter().map(Into::into).collect(), // TODO: Transmute!?
|
||||
};
|
||||
|
||||
Self(PayloadSelector::Exclude(exclude))
|
||||
Ok(Self(with_payload))
|
||||
}
|
||||
}
|
||||
|
||||
impl PyPayloadSelector {
|
||||
fn _variants(payload_selector: PayloadSelector) {
|
||||
match payload_selector {
|
||||
PayloadSelector::Include(_) => (),
|
||||
PayloadSelector::Exclude(_) => (),
|
||||
impl<'py> IntoPyObject<'py> for PyWithPayload {
|
||||
type Target = PyAny;
|
||||
type Output = Bound<'py, Self::Target>;
|
||||
type Error = PyErr; // Infallible?
|
||||
|
||||
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
|
||||
IntoPyObject::into_pyobject(&self, py)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'py> IntoPyObject<'py> for &PyWithPayload {
|
||||
type Target = PyAny;
|
||||
type Output = Bound<'py, Self::Target>;
|
||||
type Error = PyErr; // Infallible?
|
||||
|
||||
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
|
||||
match &self.0 {
|
||||
WithPayloadInterface::Bool(bool) => bool.into_bound_py_any(py),
|
||||
WithPayloadInterface::Fields(_fields) => todo!(),
|
||||
WithPayloadInterface::Selector(_selector) => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pyclass(name = "JsonPath")]
|
||||
#[derive(Clone, Debug, Into)]
|
||||
pub struct PyJsonPath(JsonPath);
|
||||
|
||||
#[pymethods]
|
||||
impl PyJsonPath {
|
||||
#[new]
|
||||
pub fn new(json_path: &str) -> super::Result<Self> {
|
||||
let json_path = json_path.parse().map_err(|()| {
|
||||
OperationError::validation_error(format!("{json_path} is not a valid JSON path"))
|
||||
})?;
|
||||
|
||||
Ok(Self(json_path))
|
||||
}
|
||||
}
|
||||
|
||||
#[pyclass(name = "ScoredPoint")]
|
||||
#[derive(Clone, Debug, Into)]
|
||||
#[repr(transparent)]
|
||||
pub struct PyScoredPoint(pub ScoredPoint);
|
||||
|
||||
impl PyScoredPoint {
|
||||
pub fn from_rust_vec(points: Vec<ScoredPoint>) -> Vec<Self> {
|
||||
// `PyScoredPoint` has transparent representation, so transmuting is safe
|
||||
unsafe { mem::transmute(points) }
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyScoredPoint {
|
||||
#[getter]
|
||||
pub fn id(&self) -> PyPointId {
|
||||
PyPointId::from(self.0.id)
|
||||
PyPointId(self.0.id)
|
||||
}
|
||||
|
||||
#[getter]
|
||||
@@ -271,15 +223,8 @@ impl PyScoredPoint {
|
||||
}
|
||||
|
||||
#[getter]
|
||||
pub fn vector(&self) -> Option<DenseVector> {
|
||||
let Some(vector) = &self.0.vector else {
|
||||
return None;
|
||||
};
|
||||
|
||||
match vector {
|
||||
VectorStructInternal::Single(vec) => Some(vec.clone()),
|
||||
_ => None, // TODO!
|
||||
}
|
||||
pub fn vector(&self) -> Option<&PyVectorInternal> {
|
||||
self.0.vector.as_ref().map(PyVectorInternal::from_ref)
|
||||
}
|
||||
|
||||
#[getter]
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::mem;
|
||||
|
||||
use derive_more::Into;
|
||||
use pyo3::IntoPyObjectExt as _;
|
||||
use pyo3::exceptions::PyException;
|
||||
use pyo3::exceptions::PyValueError;
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::{PyDict, PyFloat, PyInt, PyList};
|
||||
use segment::types::*;
|
||||
@@ -62,8 +62,8 @@ fn value_from_py(val: &Bound<PyAny>) -> PyResult<serde_json::Value> {
|
||||
let obj = object_from_py(dict)?;
|
||||
serde_json::Value::Object(obj)
|
||||
} else {
|
||||
return Err(PyErr::new::<PyException, _>(format!(
|
||||
"failed to convert Python object {val} into payload value"
|
||||
return Err(PyValueError::new_err(format!(
|
||||
"failed to convert Python object {val} into payload value type"
|
||||
)));
|
||||
};
|
||||
|
||||
@@ -88,11 +88,13 @@ fn number_from_py(num: &Bound<PyAny>) -> PyResult<serde_json::Number> {
|
||||
serde_json::Number::from(int)
|
||||
} else if let Ok(float) = num.extract() {
|
||||
serde_json::Number::from_f64(float).ok_or_else(|| {
|
||||
PyErr::new::<PyException, _>(format!("failed to convert {float} into payload number"))
|
||||
PyValueError::new_err(format!(
|
||||
"failed to convert {float} into payload number type"
|
||||
))
|
||||
})?
|
||||
} else {
|
||||
return Err(PyErr::new::<PyException, _>(format!(
|
||||
"failed to convert Python object {num} into payload number"
|
||||
return Err(PyValueError::new_err(format!(
|
||||
"failed to convert Python object {num} into payload number type"
|
||||
)));
|
||||
};
|
||||
|
||||
|
||||
@@ -1,38 +1,67 @@
|
||||
use pyo3::exceptions::PyException;
|
||||
use std::mem;
|
||||
|
||||
use derive_more::Into;
|
||||
use pyo3::IntoPyObjectExt as _;
|
||||
use pyo3::exceptions::PyValueError;
|
||||
use pyo3::prelude::*;
|
||||
use segment::types::PointIdType;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Clone, Debug, IntoPyObject, FromPyObject)]
|
||||
pub enum PyPointId {
|
||||
NumId(u64),
|
||||
UuidString(String),
|
||||
Uuid(Uuid),
|
||||
#[derive(Copy, Clone, Debug, Into)]
|
||||
#[repr(transparent)]
|
||||
pub struct PyPointId(pub PointIdType);
|
||||
|
||||
impl PyPointId {
|
||||
pub fn into_rust_vec(point_ids: Vec<PyPointId>) -> Vec<PointIdType> {
|
||||
// `PyPointId` has transparent representation, so transmuting is safe
|
||||
unsafe { mem::transmute(point_ids) }
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<PyPointId> for PointIdType {
|
||||
type Error = PyErr;
|
||||
fn try_from(value: PyPointId) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
PyPointId::NumId(id) => Ok(PointIdType::NumId(id)),
|
||||
PyPointId::UuidString(uuid_string) => {
|
||||
let uuid = Uuid::parse_str(&uuid_string).map_err(|_| {
|
||||
PyErr::new::<PyException, _>(format!(
|
||||
"failed to parse string {uuid_string} into UUID for point ID"
|
||||
))
|
||||
impl<'py> FromPyObject<'py> for PyPointId {
|
||||
fn extract_bound(point_id: &Bound<'py, PyAny>) -> PyResult<Self> {
|
||||
#[derive(FromPyObject)]
|
||||
enum Helper {
|
||||
NumId(u64),
|
||||
Uuid(Uuid),
|
||||
UuidStr(String),
|
||||
}
|
||||
|
||||
let point_id = match point_id.extract()? {
|
||||
Helper::NumId(id) => PointIdType::NumId(id),
|
||||
Helper::Uuid(uuid) => PointIdType::Uuid(uuid),
|
||||
Helper::UuidStr(uuid_str) => {
|
||||
let uuid = Uuid::parse_str(&uuid_str).map_err(|err| {
|
||||
PyValueError::new_err(format!("failed to parse {uuid_str} as UUID: {err}"))
|
||||
})?;
|
||||
Ok(PointIdType::Uuid(uuid))
|
||||
|
||||
PointIdType::Uuid(uuid)
|
||||
}
|
||||
PyPointId::Uuid(uuid) => Ok(PointIdType::Uuid(uuid)),
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Self(point_id))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PointIdType> for PyPointId {
|
||||
fn from(value: PointIdType) -> Self {
|
||||
match value {
|
||||
PointIdType::NumId(id) => PyPointId::NumId(id),
|
||||
PointIdType::Uuid(uuid) => PyPointId::Uuid(uuid),
|
||||
impl<'py> IntoPyObject<'py> for PyPointId {
|
||||
type Target = PyAny;
|
||||
type Output = Bound<'py, PyAny>;
|
||||
type Error = PyErr; // Infallible
|
||||
|
||||
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
|
||||
IntoPyObject::into_pyobject(&self, py)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'py> IntoPyObject<'py> for &PyPointId {
|
||||
type Target = PyAny;
|
||||
type Output = Bound<'py, PyAny>;
|
||||
type Error = PyErr; // Infallible
|
||||
|
||||
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
|
||||
match &self.0 {
|
||||
PointIdType::NumId(id) => id.into_bound_py_any(py),
|
||||
PointIdType::Uuid(uuid) => uuid.into_bound_py_any(py),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,31 +1,34 @@
|
||||
use std::mem;
|
||||
|
||||
use derive_more::Into;
|
||||
use pyo3::prelude::*;
|
||||
use segment::data_types::order_by::OrderValue;
|
||||
use shard::operations::point_ops::VectorStructPersisted;
|
||||
use shard::retrieve::record_internal::RecordInternal;
|
||||
|
||||
use crate::*;
|
||||
|
||||
#[pyclass(name = "Record")]
|
||||
#[derive(Clone, Debug, Into)]
|
||||
#[repr(transparent)]
|
||||
pub struct PyRecord(pub RecordInternal);
|
||||
|
||||
impl PyRecord {
|
||||
pub fn from_rust_vec(records: Vec<RecordInternal>) -> Vec<Self> {
|
||||
// `PyRecord` has transparent representation, so transmuting is safe
|
||||
unsafe { mem::transmute(records) }
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyRecord {
|
||||
#[getter]
|
||||
pub fn id(&self) -> PyPointId {
|
||||
PyPointId::from(self.0.id)
|
||||
PyPointId(self.0.id)
|
||||
}
|
||||
|
||||
#[getter]
|
||||
pub fn vector(&self) -> Option<PyVector> {
|
||||
let Some(vector) = &self.0.vector else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let vector_persisted = VectorStructPersisted::from(vector.clone());
|
||||
|
||||
Some(PyVector(vector_persisted))
|
||||
pub fn vector(&self) -> Option<&PyVectorInternal> {
|
||||
self.0.vector.as_ref().map(PyVectorInternal::from_ref)
|
||||
}
|
||||
|
||||
#[getter]
|
||||
@@ -41,7 +44,6 @@ impl PyRecord {
|
||||
|
||||
#[derive(IntoPyObject)]
|
||||
pub enum PyOrderValue {
|
||||
// Put Int first so ints don't get parsed as floats (since f64 can extract from ints).
|
||||
Int(i64),
|
||||
Float(f64),
|
||||
}
|
||||
@@ -49,8 +51,8 @@ pub enum PyOrderValue {
|
||||
impl From<OrderValue> for PyOrderValue {
|
||||
fn from(value: OrderValue) -> Self {
|
||||
match value {
|
||||
OrderValue::Int(int) => PyOrderValue::Int(int),
|
||||
OrderValue::Float(float) => PyOrderValue::Float(float),
|
||||
OrderValue::Int(int) => Self::Int(int),
|
||||
OrderValue::Float(float) => Self::Float(float),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,87 +1,85 @@
|
||||
use std::collections::HashMap;
|
||||
use std::mem;
|
||||
|
||||
use derive_more::Into;
|
||||
use pyo3::{pyclass, pymethods};
|
||||
use segment::data_types::vectors::DenseVector;
|
||||
use pyo3::IntoPyObjectExt as _;
|
||||
use pyo3::prelude::*;
|
||||
use segment::data_types::vectors::{DenseVector, VectorStructInternal};
|
||||
use segment::types::VectorNameBuf;
|
||||
use shard::operations::point_ops::{VectorPersisted, VectorStructPersisted};
|
||||
use sparse::common::sparse_vector::SparseVector;
|
||||
use sparse::common::types::{DimId, DimWeight};
|
||||
|
||||
#[pyclass(name = "Vector")]
|
||||
#[derive(Clone, Debug, Into)]
|
||||
pub struct PyVector(pub VectorStructPersisted);
|
||||
pub struct PyVector(VectorStructPersisted);
|
||||
|
||||
#[pymethods]
|
||||
impl PyVector {
|
||||
#[staticmethod]
|
||||
pub fn single(vector: DenseVector) -> Self {
|
||||
Self(VectorStructPersisted::Single(vector))
|
||||
}
|
||||
|
||||
#[staticmethod]
|
||||
pub fn multi_dense(vectors: Vec<DenseVector>) -> Self {
|
||||
Self(VectorStructPersisted::MultiDense(vectors))
|
||||
}
|
||||
|
||||
#[staticmethod]
|
||||
pub fn named(vectors: HashMap<VectorNameBuf, PyNamedVector>) -> Self {
|
||||
// TODO: Transmute!?
|
||||
let vectors = vectors
|
||||
.into_iter()
|
||||
.map(|(name, vector)| (name, vector.into()))
|
||||
.collect();
|
||||
|
||||
Self(VectorStructPersisted::Named(vectors))
|
||||
}
|
||||
}
|
||||
|
||||
impl PyVector {
|
||||
fn _variants(vector: VectorStructPersisted) {
|
||||
match vector {
|
||||
VectorStructPersisted::Single(_) => (),
|
||||
VectorStructPersisted::MultiDense(_) => (),
|
||||
VectorStructPersisted::Named(_) => (),
|
||||
impl<'py> FromPyObject<'py> for PyVector {
|
||||
fn extract_bound(vector: &Bound<'py, PyAny>) -> PyResult<Self> {
|
||||
#[derive(FromPyObject)]
|
||||
enum Helper {
|
||||
Single(DenseVector),
|
||||
Multi(Vec<DenseVector>),
|
||||
Named(HashMap<VectorNameBuf, PyNamedVector>),
|
||||
}
|
||||
|
||||
let vector = match vector.extract()? {
|
||||
Helper::Single(single) => VectorStructPersisted::Single(single),
|
||||
Helper::Multi(multi) => VectorStructPersisted::MultiDense(multi),
|
||||
Helper::Named(named) => {
|
||||
let named = PyNamedVector::into_rust_map(named);
|
||||
VectorStructPersisted::Named(named)
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Self(vector))
|
||||
}
|
||||
}
|
||||
|
||||
#[pyclass(name = "NamedVector")]
|
||||
#[derive(Clone, Debug, Into)]
|
||||
pub struct PyNamedVector(VectorPersisted);
|
||||
|
||||
#[pymethods]
|
||||
impl PyNamedVector {
|
||||
#[staticmethod]
|
||||
pub fn dense(vector: DenseVector) -> Self {
|
||||
Self(VectorPersisted::Dense(vector))
|
||||
}
|
||||
|
||||
#[staticmethod]
|
||||
pub fn multi_dense(vectors: Vec<DenseVector>) -> Self {
|
||||
Self(VectorPersisted::MultiDense(vectors))
|
||||
}
|
||||
|
||||
#[staticmethod]
|
||||
pub fn sparse(vector: PySparseVector) -> Self {
|
||||
Self(VectorPersisted::Sparse(vector.into()))
|
||||
}
|
||||
}
|
||||
#[repr(transparent)]
|
||||
struct PyNamedVector(VectorPersisted);
|
||||
|
||||
impl PyNamedVector {
|
||||
fn _variants(vector: VectorPersisted) {
|
||||
match vector {
|
||||
VectorPersisted::Dense(_) => (),
|
||||
VectorPersisted::MultiDense(_) => (),
|
||||
VectorPersisted::Sparse(_) => (),
|
||||
pub fn into_rust_map(
|
||||
vectors: HashMap<VectorNameBuf, Self>,
|
||||
) -> HashMap<VectorNameBuf, VectorPersisted> {
|
||||
unsafe { mem::transmute(vectors) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'py> FromPyObject<'py> for PyNamedVector {
|
||||
fn extract_bound(vector: &Bound<'py, PyAny>) -> PyResult<Self> {
|
||||
#[derive(FromPyObject)]
|
||||
enum Helper {
|
||||
Dense(DenseVector),
|
||||
MultiDense(Vec<DenseVector>),
|
||||
Sparse(PySparseVector),
|
||||
}
|
||||
|
||||
let vector = match vector.extract()? {
|
||||
Helper::Dense(dense) => VectorPersisted::Dense(dense),
|
||||
Helper::MultiDense(multi) => VectorPersisted::MultiDense(multi),
|
||||
Helper::Sparse(sparse) => {
|
||||
let sparse = PySparseVector::into_rust(sparse);
|
||||
VectorPersisted::Sparse(sparse)
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Self(vector))
|
||||
}
|
||||
}
|
||||
|
||||
#[pyclass(name = "SparseVector")]
|
||||
#[derive(Clone, Debug, Into)]
|
||||
#[repr(transparent)]
|
||||
pub struct PySparseVector(SparseVector);
|
||||
|
||||
impl PySparseVector {
|
||||
pub fn into_rust(self) -> SparseVector {
|
||||
unsafe { mem::transmute(self) }
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PySparseVector {
|
||||
#[new]
|
||||
@@ -90,12 +88,46 @@ impl PySparseVector {
|
||||
}
|
||||
|
||||
#[getter]
|
||||
pub fn indices(&self) -> Vec<DimId> {
|
||||
self.0.indices.clone()
|
||||
pub fn indices(&self) -> &[DimId] {
|
||||
&self.0.indices
|
||||
}
|
||||
|
||||
#[getter]
|
||||
pub fn values(&self) -> Vec<DimWeight> {
|
||||
self.0.values.clone()
|
||||
pub fn values(&self) -> &[DimWeight] {
|
||||
&self.0.values
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Into)]
|
||||
#[repr(transparent)]
|
||||
pub struct PyVectorInternal(pub VectorStructInternal);
|
||||
|
||||
impl PyVectorInternal {
|
||||
pub fn from_ref(vector: &VectorStructInternal) -> &Self {
|
||||
unsafe { mem::transmute(vector) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'py> IntoPyObject<'py> for PyVectorInternal {
|
||||
type Target = PyAny;
|
||||
type Output = Bound<'py, Self::Target>;
|
||||
type Error = PyErr; // Infallible
|
||||
|
||||
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
|
||||
IntoPyObject::into_pyobject(&self, py)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'py> IntoPyObject<'py> for &PyVectorInternal {
|
||||
type Target = PyAny;
|
||||
type Output = Bound<'py, Self::Target>;
|
||||
type Error = PyErr; // Infallible
|
||||
|
||||
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
|
||||
match &self.0 {
|
||||
VectorStructInternal::Single(single) => single.into_bound_py_any(py),
|
||||
VectorStructInternal::MultiDense(_multi) => todo!(),
|
||||
VectorStructInternal::Named(_named) => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ impl PyPoint {
|
||||
#[new]
|
||||
pub fn new(id: PyPointId, vector: PyVector, payload: Option<PyPayload>) -> Result<Self, PyErr> {
|
||||
let point = PointStructPersisted {
|
||||
id: PointIdType::try_from(id)?,
|
||||
id: PointIdType::from(id),
|
||||
vector: VectorStructPersisted::from(vector),
|
||||
payload: payload.map(Payload::from),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user