Qdrant Edge Python bindings improvements (#7561)

* Use anonymous lifetime in `FromPyObject` implementations

* Use `PyResult` in `IntoPyObject` implementations

* Cleanup imports and derives

* Cleanup `filter` conversions

* Add `PointVectors` getters

* Move `config` module into sub-directory

* Split `config` into sub-modules

* Simplify enum bindings

* Add zero-cost conversions for `PyVectorDataConfig` and `PySparseVectorDataConfig`

* Add getters to config structures

* fixup! Add getters to config structures

More zero-cost conversions for `PyVector*DataConfig`

* Implement `PyHnswIndexConfig`

* Implement `PyQuantizationConfig`

* fixup! Simplify enum bindings

* fixup! Implement `PyHnswIndexConfig`

* fixup! Implement `PyHnswIndexConfig`

* fixup! Implement `PyHnswIndexConfig`

* Implement `PySparseVectorDataConfig`

* fixup! Implement `PySparseVectorDataConfig`

* fixup! Implement `PySparseVectorDataConfig`
This commit is contained in:
Roman Titov
2025-11-26 18:47:17 +01:00
committed by timvisee
parent d25d877b3b
commit 1de85b956e
30 changed files with 1091 additions and 402 deletions

View File

@@ -20,16 +20,16 @@ def load_new_shard():
vector_data={
"": VectorDataConfig(
size=4,
distance=Distance.DOT,
storage_type=VectorStorageType.CHUNKED_MMAP,
index=Indexes.PLAIN,
distance=Distance.Dot,
storage_type=VectorStorageType.ChunkedMmap,
index=PlainIndexConfig(),
quantization_config=None,
multivector_config=None,
datatype=None,
),
},
sparse_vector_data={},
payload_storage_type=PayloadStorageType.IN_RAM_MMAP,
payload_storage_type=PayloadStorageType.InRamMmap,
)
return Shard(DATA_DIRECTORY, config)
@@ -48,4 +48,3 @@ def fill_dummy_data(shard: Shard):
Point(9, [0.45, 0.55, 0.65, 0.75], {"color": "green", "city": ["Berlin"], "score": 0.92}),
Point(10, [0.01, 0.02, 0.03, 0.04], {"color": "yellow", "city": None, "featured": False}),
]))

View File

@@ -1,244 +0,0 @@
use std::collections::HashMap;
use bytemuck::TransparentWrapper;
use derive_more::Into;
use pyo3::prelude::*;
use segment::types::*;
#[pyclass(name = "SegmentConfig")]
#[derive(Clone, Debug, Into, TransparentWrapper)]
#[repr(transparent)]
pub struct PySegmentConfig(SegmentConfig);
#[pymethods]
impl PySegmentConfig {
#[new]
pub fn new(
vector_data: HashMap<String, PyVectorDataConfig>,
sparse_vector_data: HashMap<String, PySparseVectorDataConfig>,
payload_storage_type: PyPayloadStorageType,
) -> Self {
// TODO: Transmute!?
let vector_data = vector_data
.into_iter()
.map(|(vector, config)| (vector, VectorDataConfig::from(config)))
.collect();
// TODO: Transmute!?
let sparse_vector_data = sparse_vector_data
.into_iter()
.map(|(vector, config)| (vector, SparseVectorDataConfig::from(config)))
.collect();
Self(SegmentConfig {
vector_data,
sparse_vector_data,
payload_storage_type: PayloadStorageType::from(payload_storage_type),
})
}
}
#[pyclass(name = "VectorDataConfig")]
#[derive(Clone, Debug, Into, TransparentWrapper)]
#[repr(transparent)]
pub struct PyVectorDataConfig(VectorDataConfig);
#[pymethods]
impl PyVectorDataConfig {
#[new]
pub fn new(
size: usize,
distance: PyDistance,
storage_type: PyVectorStorageType,
index: PyIndexes,
quantization_config: Option<PyQuantizationConfig>,
multivector_config: Option<PyMultiVectorConfig>,
datatype: Option<PyVectorStorageDatatype>,
) -> Self {
Self(VectorDataConfig {
size,
distance: Distance::from(distance),
storage_type: VectorStorageType::from(storage_type),
index: Indexes::from(index),
quantization_config: quantization_config.map(QuantizationConfig::from),
multivector_config: multivector_config.map(MultiVectorConfig::from),
datatype: datatype.map(VectorStorageDatatype::from),
})
}
}
#[pyclass(name = "Distance")]
#[derive(Copy, Clone, Debug, Into)]
pub struct PyDistance(Distance);
#[pymethods]
impl PyDistance {
#[classattr]
pub const COSINE: Self = Self(Distance::Cosine);
#[classattr]
pub const EUCLID: Self = Self(Distance::Euclid);
#[classattr]
pub const DOT: Self = Self(Distance::Dot);
#[classattr]
pub const MANHATTAN: Self = Self(Distance::Manhattan);
}
impl PyDistance {
fn _variants(distance: Distance) {
match distance {
Distance::Cosine => (),
Distance::Euclid => (),
Distance::Dot => (),
Distance::Manhattan => (),
}
}
}
#[pyclass(name = "VectorStorageType")]
#[derive(Copy, Clone, Debug, Into)]
pub struct PyVectorStorageType(VectorStorageType);
#[pymethods]
impl PyVectorStorageType {
#[classattr]
pub const MEMORY: Self = Self(VectorStorageType::Memory);
#[classattr]
pub const MMAP: Self = Self(VectorStorageType::Mmap);
#[classattr]
pub const CHUNKED_MMAP: Self = Self(VectorStorageType::ChunkedMmap);
#[classattr]
pub const IN_RAM_CHUNKED_MMAP: Self = Self(VectorStorageType::InRamChunkedMmap);
}
impl PyVectorStorageType {
fn _variants(storage_type: VectorStorageType) {
match storage_type {
VectorStorageType::Memory => (),
VectorStorageType::Mmap => (),
VectorStorageType::ChunkedMmap => (),
VectorStorageType::InRamChunkedMmap => (),
}
}
}
#[pyclass(name = "Indexes")]
#[derive(Clone, Debug, Into)]
pub struct PyIndexes(Indexes);
#[pymethods]
impl PyIndexes {
#[classattr]
pub const PLAIN: Self = Self(Indexes::Plain {});
// TODO: HNSW!?
}
impl PyIndexes {
fn _variants(indexes: Indexes) {
match indexes {
Indexes::Plain {} => (),
Indexes::Hnsw(_) => (), // TODO!?
}
}
}
#[pyclass(name = "QuantizationConfig")]
#[derive(Clone, Debug, Into)]
pub struct PyQuantizationConfig(QuantizationConfig); // TODO!?
#[pyclass(name = "MultiVectorConfig")]
#[derive(Copy, Clone, Debug, Into)]
pub struct PyMultiVectorConfig(MultiVectorConfig);
#[pymethods]
impl PyMultiVectorConfig {
#[new]
pub fn new(comparator: PyMultiVectorComparator) -> Self {
Self(MultiVectorConfig {
comparator: MultiVectorComparator::from(comparator),
})
}
}
#[pyclass(name = "MultiVectorComparator")]
#[derive(Copy, Clone, Debug, Into)]
pub struct PyMultiVectorComparator(MultiVectorComparator);
#[pymethods]
impl PyMultiVectorComparator {
#[classattr]
pub const MAX_SIM: Self = Self(MultiVectorComparator::MaxSim);
}
impl PyMultiVectorComparator {
fn _variants(comparator: MultiVectorComparator) {
match comparator {
MultiVectorComparator::MaxSim => (),
}
}
}
#[pyclass(name = "VectorStorageDatatype")]
#[derive(Copy, Clone, Debug, Into)]
pub struct PyVectorStorageDatatype(VectorStorageDatatype);
#[pymethods]
impl PyVectorStorageDatatype {
#[classattr]
pub const FLOAT_32: Self = Self(VectorStorageDatatype::Float32);
#[classattr]
pub const FLOAT_16: Self = Self(VectorStorageDatatype::Float16);
#[classattr]
pub const UINT_8: Self = Self(VectorStorageDatatype::Uint8);
}
impl PyVectorStorageDatatype {
fn _variants(storage_datatype: VectorStorageDatatype) {
match storage_datatype {
VectorStorageDatatype::Float32 => (),
VectorStorageDatatype::Float16 => (),
VectorStorageDatatype::Uint8 => (),
}
}
}
#[pyclass(name = "SparseVectorDataConfig")]
#[derive(Copy, Clone, Debug, Into)]
pub struct PySparseVectorDataConfig(SparseVectorDataConfig);
#[pymethods]
impl PySparseVectorDataConfig {
// TODO!?
}
#[pyclass(name = "PayloadStorageType")]
#[derive(Copy, Clone, Debug, Into)]
pub struct PyPayloadStorageType(PayloadStorageType);
#[pymethods]
impl PyPayloadStorageType {
#[classattr]
pub const MMAP: Self = Self(PayloadStorageType::Mmap);
#[classattr]
pub const IN_RAM_MMAP: Self = Self(PayloadStorageType::InRamMmap);
}
impl PyPayloadStorageType {
fn _variants(storage_type: PayloadStorageType) {
#[allow(unreachable_patterns)]
match storage_type {
PayloadStorageType::Mmap => (),
PayloadStorageType::InRamMmap => (),
_ => todo!(), // TODO: Ignore RocksDB storage types
}
}
}

View File

@@ -0,0 +1,77 @@
pub mod quantization;
pub mod sparse_vector_data;
pub mod vector_data;
use std::collections::HashMap;
use bytemuck::TransparentWrapper;
use derive_more::Into;
use pyo3::prelude::*;
use segment::types::*;
pub use self::quantization::*;
pub use self::sparse_vector_data::*;
pub use self::vector_data::*;
#[pyclass(name = "SegmentConfig")]
#[derive(Clone, Debug, Into, TransparentWrapper)]
#[repr(transparent)]
pub struct PySegmentConfig(SegmentConfig);
#[pymethods]
impl PySegmentConfig {
#[new]
pub fn new(
vector_data: HashMap<String, PyVectorDataConfig>,
sparse_vector_data: HashMap<String, PySparseVectorDataConfig>,
payload_storage_type: PyPayloadStorageType,
) -> Self {
Self(SegmentConfig {
vector_data: PyVectorDataConfig::peel_map(vector_data),
sparse_vector_data: PySparseVectorDataConfig::peel_map(sparse_vector_data),
payload_storage_type: PayloadStorageType::from(payload_storage_type),
})
}
#[getter]
pub fn vector_data(&self) -> &HashMap<String, PyVectorDataConfig> {
PyVectorDataConfig::wrap_map_ref(&self.0.vector_data)
}
#[getter]
pub fn sparse_vector_data(&self) -> &HashMap<String, PySparseVectorDataConfig> {
PySparseVectorDataConfig::wrap_map_ref(&self.0.sparse_vector_data)
}
#[getter]
pub fn payload_storage_type(&self) -> PyPayloadStorageType {
PyPayloadStorageType::from(self.0.payload_storage_type)
}
}
#[pyclass(name = "PayloadStorageType")]
#[derive(Copy, Clone, Debug)]
pub enum PyPayloadStorageType {
Mmap,
InRamMmap,
}
impl From<PayloadStorageType> for PyPayloadStorageType {
fn from(storage_type: PayloadStorageType) -> Self {
#[allow(unreachable_patterns)]
match storage_type {
PayloadStorageType::Mmap => PyPayloadStorageType::Mmap,
PayloadStorageType::InRamMmap => PyPayloadStorageType::InRamMmap,
_ => unimplemented!("RocksDB-backed storage types are not supported by Qdrant Edge"),
}
}
}
impl From<PyPayloadStorageType> for PayloadStorageType {
fn from(storage_type: PyPayloadStorageType) -> Self {
match storage_type {
PyPayloadStorageType::Mmap => PayloadStorageType::Mmap,
PyPayloadStorageType::InRamMmap => PayloadStorageType::InRamMmap,
}
}
}

View File

@@ -0,0 +1,277 @@
use derive_more::Into;
use pyo3::IntoPyObjectExt as _;
use pyo3::prelude::*;
use segment::types::*;
#[derive(Clone, Debug, Into)]
pub struct PyQuantizationConfig(pub QuantizationConfig);
impl FromPyObject<'_, '_> for PyQuantizationConfig {
type Error = PyErr;
fn extract(conf: Borrowed<'_, '_, PyAny>) -> PyResult<Self> {
#[derive(FromPyObject)]
enum Helper {
Scalar(PyScalarQuantizationConfig),
Product(PyProductQuantizationConfig),
Binary(PyBinaryQuantizationConfig),
}
let conf = match conf.extract()? {
Helper::Scalar(scalar) => QuantizationConfig::Scalar(ScalarQuantization {
scalar: ScalarQuantizationConfig::from(scalar),
}),
Helper::Product(product) => QuantizationConfig::Product(ProductQuantization {
product: ProductQuantizationConfig::from(product),
}),
Helper::Binary(binary) => QuantizationConfig::Binary(BinaryQuantization {
binary: BinaryQuantizationConfig::from(binary),
}),
};
Ok(Self(conf))
}
}
impl<'py> IntoPyObject<'py> for PyQuantizationConfig {
type Target = PyAny;
type Output = Bound<'py, Self::Target>;
type Error = PyErr;
fn into_pyobject(self, py: Python<'py>) -> PyResult<Self::Output> {
match self.0 {
QuantizationConfig::Scalar(ScalarQuantization { scalar }) => {
PyScalarQuantizationConfig(scalar).into_bound_py_any(py)
}
QuantizationConfig::Product(ProductQuantization { product }) => {
PyProductQuantizationConfig(product).into_bound_py_any(py)
}
QuantizationConfig::Binary(BinaryQuantization { binary }) => {
PyBinaryQuantizationConfig(binary).into_bound_py_any(py)
}
}
}
}
#[pyclass(name = "ScalarQuantizationConfig")]
#[derive(Clone, Debug, Into)]
pub struct PyScalarQuantizationConfig(ScalarQuantizationConfig);
#[pymethods]
impl PyScalarQuantizationConfig {
#[new]
#[pyo3(signature = (r#type, quantile = None, always_ram = None))]
pub fn new(r#type: PyScalarType, quantile: Option<f32>, always_ram: Option<bool>) -> Self {
Self(ScalarQuantizationConfig {
r#type: ScalarType::from(r#type),
quantile,
always_ram,
})
}
#[getter]
pub fn r#type(&self) -> PyScalarType {
PyScalarType::from(self.0.r#type)
}
#[getter]
pub fn quantile(&self) -> Option<f32> {
self.0.quantile
}
#[getter]
pub fn always_ram(&self) -> Option<bool> {
self.0.always_ram
}
}
#[pyclass(name = "ScalarType")]
#[derive(Copy, Clone, Debug)]
pub enum PyScalarType {
Int8,
}
impl From<ScalarType> for PyScalarType {
fn from(scalar_type: ScalarType) -> Self {
match scalar_type {
ScalarType::Int8 => PyScalarType::Int8,
}
}
}
impl From<PyScalarType> for ScalarType {
fn from(scalar_type: PyScalarType) -> Self {
match scalar_type {
PyScalarType::Int8 => ScalarType::Int8,
}
}
}
#[pyclass(name = "ProductQuantizationConfig")]
#[derive(Clone, Debug, Into)]
pub struct PyProductQuantizationConfig(ProductQuantizationConfig);
#[pymethods]
impl PyProductQuantizationConfig {
#[new]
#[pyo3(signature = (compression, always_ram = None))]
pub fn new(compression: PyCompressionRatio, always_ram: Option<bool>) -> Self {
Self(ProductQuantizationConfig {
compression: CompressionRatio::from(compression),
always_ram,
})
}
#[getter]
pub fn compression(&self) -> PyCompressionRatio {
PyCompressionRatio::from(self.0.compression)
}
#[getter]
pub fn always_ram(&self) -> Option<bool> {
self.0.always_ram
}
}
#[pyclass(name = "CompressionRatio")]
#[derive(Copy, Clone, Debug)]
pub enum PyCompressionRatio {
X4,
X8,
X16,
X32,
X64,
}
impl From<CompressionRatio> for PyCompressionRatio {
fn from(compression: CompressionRatio) -> Self {
match compression {
CompressionRatio::X4 => PyCompressionRatio::X4,
CompressionRatio::X8 => PyCompressionRatio::X8,
CompressionRatio::X16 => PyCompressionRatio::X16,
CompressionRatio::X32 => PyCompressionRatio::X32,
CompressionRatio::X64 => PyCompressionRatio::X64,
}
}
}
impl From<PyCompressionRatio> for CompressionRatio {
fn from(compression: PyCompressionRatio) -> Self {
match compression {
PyCompressionRatio::X4 => CompressionRatio::X4,
PyCompressionRatio::X8 => CompressionRatio::X8,
PyCompressionRatio::X16 => CompressionRatio::X16,
PyCompressionRatio::X32 => CompressionRatio::X32,
PyCompressionRatio::X64 => CompressionRatio::X64,
}
}
}
#[pyclass(name = "BinaryQuantizationConfig")]
#[derive(Clone, Debug, Into)]
pub struct PyBinaryQuantizationConfig(BinaryQuantizationConfig);
#[pymethods]
impl PyBinaryQuantizationConfig {
#[new]
#[pyo3(signature = (always_ram = None, encoding = None, query_encoding = None))]
pub fn new(
always_ram: Option<bool>,
encoding: Option<PyBinaryQuantizationEncoding>,
query_encoding: Option<PyBinaryQuantizationQueryEncoding>,
) -> Self {
Self(BinaryQuantizationConfig {
always_ram,
encoding: encoding.map(BinaryQuantizationEncoding::from),
query_encoding: query_encoding.map(BinaryQuantizationQueryEncoding::from),
})
}
#[getter]
pub fn always_ram(&self) -> Option<bool> {
self.0.always_ram
}
#[getter]
pub fn encoding(&self) -> Option<PyBinaryQuantizationEncoding> {
self.0.encoding.map(PyBinaryQuantizationEncoding::from)
}
#[getter]
pub fn query_encoding(&self) -> Option<PyBinaryQuantizationQueryEncoding> {
self.0
.query_encoding
.map(PyBinaryQuantizationQueryEncoding::from)
}
}
#[pyclass(name = "BinaryQuantizationEncoding")]
#[derive(Copy, Clone, Debug)]
pub enum PyBinaryQuantizationEncoding {
OneBit,
TwoBits,
OneAndHalfBits,
}
impl From<BinaryQuantizationEncoding> for PyBinaryQuantizationEncoding {
fn from(encoding: BinaryQuantizationEncoding) -> Self {
match encoding {
BinaryQuantizationEncoding::OneBit => PyBinaryQuantizationEncoding::OneBit,
BinaryQuantizationEncoding::TwoBits => PyBinaryQuantizationEncoding::TwoBits,
BinaryQuantizationEncoding::OneAndHalfBits => {
PyBinaryQuantizationEncoding::OneAndHalfBits
}
}
}
}
impl From<PyBinaryQuantizationEncoding> for BinaryQuantizationEncoding {
fn from(encoding: PyBinaryQuantizationEncoding) -> Self {
match encoding {
PyBinaryQuantizationEncoding::OneBit => BinaryQuantizationEncoding::OneBit,
PyBinaryQuantizationEncoding::TwoBits => BinaryQuantizationEncoding::TwoBits,
PyBinaryQuantizationEncoding::OneAndHalfBits => {
BinaryQuantizationEncoding::OneAndHalfBits
}
}
}
}
#[pyclass(name = "BinaryQuantizationQueryEncoding")]
#[derive(Copy, Clone, Debug)]
pub enum PyBinaryQuantizationQueryEncoding {
Default,
Binary,
Scalar4Bits,
Scalar8Bits,
}
impl From<BinaryQuantizationQueryEncoding> for PyBinaryQuantizationQueryEncoding {
fn from(encoding: BinaryQuantizationQueryEncoding) -> Self {
match encoding {
BinaryQuantizationQueryEncoding::Default => PyBinaryQuantizationQueryEncoding::Default,
BinaryQuantizationQueryEncoding::Binary => PyBinaryQuantizationQueryEncoding::Binary,
BinaryQuantizationQueryEncoding::Scalar4Bits => {
PyBinaryQuantizationQueryEncoding::Scalar4Bits
}
BinaryQuantizationQueryEncoding::Scalar8Bits => {
PyBinaryQuantizationQueryEncoding::Scalar8Bits
}
}
}
}
impl From<PyBinaryQuantizationQueryEncoding> for BinaryQuantizationQueryEncoding {
fn from(encoding: PyBinaryQuantizationQueryEncoding) -> Self {
match encoding {
PyBinaryQuantizationQueryEncoding::Default => BinaryQuantizationQueryEncoding::Default,
PyBinaryQuantizationQueryEncoding::Binary => BinaryQuantizationQueryEncoding::Binary,
PyBinaryQuantizationQueryEncoding::Scalar4Bits => {
BinaryQuantizationQueryEncoding::Scalar4Bits
}
PyBinaryQuantizationQueryEncoding::Scalar8Bits => {
BinaryQuantizationQueryEncoding::Scalar8Bits
}
}
}
}

View File

@@ -0,0 +1,186 @@
use std::collections::HashMap;
use std::mem;
use bytemuck::TransparentWrapper;
use derive_more::Into;
use pyo3::prelude::*;
use segment::data_types::modifier::Modifier;
use segment::index::sparse_index::sparse_index_config::{SparseIndexConfig, SparseIndexType};
use segment::types::*;
use super::vector_data::*;
#[pyclass(name = "SparseVectorDataConfig")]
#[derive(Copy, Clone, Debug, Into, TransparentWrapper)]
#[repr(transparent)]
pub struct PySparseVectorDataConfig(pub SparseVectorDataConfig);
impl PySparseVectorDataConfig {
pub fn peel_map(map: HashMap<String, Self>) -> HashMap<String, SparseVectorDataConfig>
where
Self: TransparentWrapper<SparseVectorDataConfig>,
{
unsafe { mem::transmute(map) }
}
pub fn wrap_map_ref(map: &HashMap<String, SparseVectorDataConfig>) -> &HashMap<String, Self>
where
Self: TransparentWrapper<SparseVectorDataConfig>,
{
unsafe { mem::transmute(map) }
}
}
#[pymethods]
impl PySparseVectorDataConfig {
#[new]
pub fn new(
index: PySparseIndexConfig,
storage_type: PySparseVectorStorageType,
modifier: Option<PyModifier>,
) -> Self {
Self(SparseVectorDataConfig {
index: SparseIndexConfig::from(index),
storage_type: SparseVectorStorageType::from(storage_type),
modifier: modifier.map(Modifier::from),
})
}
#[getter]
pub fn index(&self) -> PySparseIndexConfig {
PySparseIndexConfig(self.0.index)
}
#[getter]
pub fn storage_type(&self) -> PySparseVectorStorageType {
PySparseVectorStorageType::from(self.0.storage_type)
}
#[getter]
pub fn modifier(&self) -> Option<PyModifier> {
self.0.modifier.map(PyModifier::from)
}
}
impl<'py> IntoPyObject<'py> for &PySparseVectorDataConfig {
type Target = PySparseVectorDataConfig;
type Output = Bound<'py, Self::Target>;
type Error = PyErr; // Infallible
fn into_pyobject(self, py: Python<'py>) -> PyResult<Self::Output> {
IntoPyObject::into_pyobject(*self, py)
}
}
#[pyclass(name = "SparseIndexConfig")]
#[derive(Copy, Clone, Debug, Into)]
pub struct PySparseIndexConfig(SparseIndexConfig);
#[pymethods]
impl PySparseIndexConfig {
#[new]
pub fn new(
full_scan_threshold: Option<usize>,
index_type: PySparseIndexType,
datatype: Option<PyVectorStorageDatatype>,
) -> Self {
Self(SparseIndexConfig {
full_scan_threshold,
index_type: SparseIndexType::from(index_type),
datatype: datatype.map(VectorStorageDatatype::from),
})
}
#[getter]
pub fn full_scan_threshold(&self) -> Option<usize> {
self.0.full_scan_threshold
}
#[getter]
pub fn index_type(&self) -> PySparseIndexType {
PySparseIndexType::from(self.0.index_type)
}
#[getter]
pub fn datatype(&self) -> Option<PyVectorStorageDatatype> {
self.0.datatype.map(PyVectorStorageDatatype::from)
}
}
#[pyclass(name = "SparseIndexType")]
#[derive(Copy, Clone, Debug)]
pub enum PySparseIndexType {
MutableRam,
ImmutableRam,
Mmap,
}
impl From<SparseIndexType> for PySparseIndexType {
fn from(index_type: SparseIndexType) -> Self {
match index_type {
SparseIndexType::MutableRam => PySparseIndexType::MutableRam,
SparseIndexType::ImmutableRam => PySparseIndexType::ImmutableRam,
SparseIndexType::Mmap => PySparseIndexType::Mmap,
}
}
}
impl From<PySparseIndexType> for SparseIndexType {
fn from(index_type: PySparseIndexType) -> Self {
match index_type {
PySparseIndexType::MutableRam => SparseIndexType::MutableRam,
PySparseIndexType::ImmutableRam => SparseIndexType::ImmutableRam,
PySparseIndexType::Mmap => SparseIndexType::Mmap,
}
}
}
#[pyclass(name = "SparseVectorStorageType")]
#[derive(Copy, Clone, Debug)]
pub enum PySparseVectorStorageType {
Mmap,
}
impl From<SparseVectorStorageType> for PySparseVectorStorageType {
fn from(storage_type: SparseVectorStorageType) -> Self {
#[allow(unreachable_patterns)]
#[allow(clippy::match_wildcard_for_single_variants)]
match storage_type {
SparseVectorStorageType::Mmap => PySparseVectorStorageType::Mmap,
_ => unimplemented!("RocksDB-backed storage types are not supported by Qdrant Edge"),
}
}
}
impl From<PySparseVectorStorageType> for SparseVectorStorageType {
fn from(storage_type: PySparseVectorStorageType) -> Self {
match storage_type {
PySparseVectorStorageType::Mmap => SparseVectorStorageType::Mmap,
}
}
}
#[pyclass(name = "Modifier")]
#[derive(Copy, Clone, Debug)]
pub enum PyModifier {
None,
Idf,
}
impl From<Modifier> for PyModifier {
fn from(modifier: Modifier) -> Self {
match modifier {
Modifier::None => PyModifier::None,
Modifier::Idf => PyModifier::Idf,
}
}
}
impl From<PyModifier> for Modifier {
fn from(modifier: PyModifier) -> Self {
match modifier {
PyModifier::None => Modifier::None,
PyModifier::Idf => Modifier::Idf,
}
}
}

View File

@@ -0,0 +1,343 @@
use std::collections::HashMap;
use std::mem;
use bytemuck::TransparentWrapper;
use derive_more::Into;
use pyo3::IntoPyObjectExt as _;
use pyo3::prelude::*;
use segment::types::*;
use super::quantization::*;
#[pyclass(name = "VectorDataConfig")]
#[derive(Clone, Debug, Into, TransparentWrapper)]
#[repr(transparent)]
pub struct PyVectorDataConfig(pub VectorDataConfig);
impl PyVectorDataConfig {
pub fn peel_map(map: HashMap<String, Self>) -> HashMap<String, VectorDataConfig>
where
Self: TransparentWrapper<VectorDataConfig>,
{
unsafe { mem::transmute(map) }
}
pub fn wrap_map_ref(map: &HashMap<String, VectorDataConfig>) -> &HashMap<String, Self>
where
Self: TransparentWrapper<VectorDataConfig>,
{
unsafe { mem::transmute(map) }
}
}
#[pymethods]
impl PyVectorDataConfig {
#[new]
pub fn new(
size: usize,
distance: PyDistance,
storage_type: PyVectorStorageType,
index: PyIndexes,
quantization_config: Option<PyQuantizationConfig>,
multivector_config: Option<PyMultiVectorConfig>,
datatype: Option<PyVectorStorageDatatype>,
) -> Self {
Self(VectorDataConfig {
size,
distance: Distance::from(distance),
storage_type: VectorStorageType::from(storage_type),
index: Indexes::from(index),
quantization_config: quantization_config.map(QuantizationConfig::from),
multivector_config: multivector_config.map(MultiVectorConfig::from),
datatype: datatype.map(VectorStorageDatatype::from),
})
}
#[getter]
pub fn size(&self) -> usize {
self.0.size
}
#[getter]
pub fn distance(&self) -> PyDistance {
PyDistance::from(self.0.distance)
}
#[getter]
pub fn storage_type(&self) -> PyVectorStorageType {
PyVectorStorageType::from(self.0.storage_type)
}
#[getter]
pub fn index(&self) -> PyIndexes {
PyIndexes(self.0.index.clone())
}
#[getter]
pub fn quantization_config(&self) -> Option<PyQuantizationConfig> {
self.0.quantization_config.clone().map(PyQuantizationConfig)
}
#[getter]
pub fn multivector_config(&self) -> Option<PyMultiVectorConfig> {
self.0.multivector_config.map(PyMultiVectorConfig)
}
#[getter]
pub fn datatype(&self) -> Option<PyVectorStorageDatatype> {
self.0.datatype.map(PyVectorStorageDatatype::from)
}
}
impl<'py> IntoPyObject<'py> for &PyVectorDataConfig {
type Target = PyVectorDataConfig;
type Output = Bound<'py, Self::Target>;
type Error = PyErr; // Infallible
fn into_pyobject(self, py: Python<'py>) -> PyResult<Self::Output> {
Bound::new(py, self.clone())
}
}
#[pyclass(name = "Distance")]
#[derive(Copy, Clone, Debug)]
pub enum PyDistance {
Cosine,
Euclid,
Dot,
Manhattan,
}
impl From<Distance> for PyDistance {
fn from(distance: Distance) -> Self {
match distance {
Distance::Cosine => PyDistance::Cosine,
Distance::Euclid => PyDistance::Euclid,
Distance::Dot => PyDistance::Dot,
Distance::Manhattan => PyDistance::Manhattan,
}
}
}
impl From<PyDistance> for Distance {
fn from(distance: PyDistance) -> Self {
match distance {
PyDistance::Cosine => Distance::Cosine,
PyDistance::Euclid => Distance::Euclid,
PyDistance::Dot => Distance::Dot,
PyDistance::Manhattan => Distance::Manhattan,
}
}
}
#[pyclass(name = "VectorStorageType")]
#[derive(Copy, Clone, Debug)]
pub enum PyVectorStorageType {
Memory,
Mmap,
ChunkedMmap,
InRamChunkedMmap,
}
impl From<VectorStorageType> for PyVectorStorageType {
fn from(storage_type: VectorStorageType) -> Self {
match storage_type {
VectorStorageType::Memory => PyVectorStorageType::Memory,
VectorStorageType::Mmap => PyVectorStorageType::Mmap,
VectorStorageType::ChunkedMmap => PyVectorStorageType::ChunkedMmap,
VectorStorageType::InRamChunkedMmap => PyVectorStorageType::InRamChunkedMmap,
}
}
}
impl From<PyVectorStorageType> for VectorStorageType {
fn from(storage_type: PyVectorStorageType) -> Self {
match storage_type {
PyVectorStorageType::Memory => VectorStorageType::Memory,
PyVectorStorageType::Mmap => VectorStorageType::Mmap,
PyVectorStorageType::ChunkedMmap => VectorStorageType::ChunkedMmap,
PyVectorStorageType::InRamChunkedMmap => VectorStorageType::InRamChunkedMmap,
}
}
}
#[derive(Clone, Debug, Into)]
pub struct PyIndexes(Indexes);
impl FromPyObject<'_, '_> for PyIndexes {
type Error = PyErr;
fn extract(indexes: Borrowed<'_, '_, PyAny>) -> PyResult<Self> {
#[derive(FromPyObject)]
enum Helper {
Plain(PyPlainIndexConfig),
Hnsw(PyHnswIndexConfig),
}
fn _variants(indexes: Indexes) {
match indexes {
Indexes::Plain {} => (),
Indexes::Hnsw(_) => (),
}
}
let indexes = match indexes.extract()? {
Helper::Plain(_) => Indexes::Plain {},
Helper::Hnsw(hnsw) => Indexes::Hnsw(HnswConfig::from(hnsw)),
};
Ok(Self(indexes))
}
}
impl<'py> IntoPyObject<'py> for PyIndexes {
type Target = PyAny;
type Output = Bound<'py, Self::Target>;
type Error = PyErr;
fn into_pyobject(self, py: Python<'py>) -> PyResult<Self::Output> {
match self.0 {
Indexes::Plain {} => PyPlainIndexConfig.into_bound_py_any(py),
Indexes::Hnsw(hnsw) => PyHnswIndexConfig(hnsw).into_bound_py_any(py),
}
}
}
#[pyclass(name = "PlainIndexConfig")]
#[derive(Copy, Clone, Debug, Default, Into)]
pub struct PyPlainIndexConfig;
#[pymethods]
impl PyPlainIndexConfig {
#[new]
pub fn new() -> Self {
Self
}
}
#[pyclass(name = "HnswIndexConfig")]
#[derive(Copy, Clone, Debug, Into)]
pub struct PyHnswIndexConfig(HnswConfig);
#[pymethods]
impl PyHnswIndexConfig {
#[new]
#[pyo3(signature = (m, ef_construct, full_scan_threshold, on_disk=None, payload_m=None, inline_storage=None))]
pub fn new(
m: usize,
ef_construct: usize,
full_scan_threshold: usize,
on_disk: Option<bool>,
payload_m: Option<usize>,
inline_storage: Option<bool>,
) -> Self {
Self(HnswConfig {
m,
ef_construct,
full_scan_threshold,
max_indexing_threads: 0,
on_disk,
payload_m,
inline_storage,
})
}
#[getter]
pub fn m(&self) -> usize {
self.0.m
}
#[getter]
pub fn ef_construct(&self) -> usize {
self.0.ef_construct
}
#[getter]
pub fn full_scan_threshold(&self) -> usize {
self.0.full_scan_threshold
}
#[getter]
pub fn on_disk(&self) -> Option<bool> {
self.0.on_disk
}
#[getter]
pub fn payload_m(&self) -> Option<usize> {
self.0.payload_m
}
#[getter]
pub fn inline_storage(&self) -> Option<bool> {
self.0.inline_storage
}
}
#[pyclass(name = "MultiVectorConfig")]
#[derive(Copy, Clone, Debug, Into)]
pub struct PyMultiVectorConfig(MultiVectorConfig);
#[pymethods]
impl PyMultiVectorConfig {
#[new]
pub fn new(comparator: PyMultiVectorComparator) -> Self {
Self(MultiVectorConfig {
comparator: MultiVectorComparator::from(comparator),
})
}
#[getter]
pub fn comparator(&self) -> PyMultiVectorComparator {
PyMultiVectorComparator::from(self.0.comparator)
}
}
#[pyclass(name = "MultiVectorComparator")]
#[derive(Copy, Clone, Debug)]
pub enum PyMultiVectorComparator {
MaxSim,
}
impl From<MultiVectorComparator> for PyMultiVectorComparator {
fn from(comparator: MultiVectorComparator) -> Self {
match comparator {
MultiVectorComparator::MaxSim => PyMultiVectorComparator::MaxSim,
}
}
}
impl From<PyMultiVectorComparator> for MultiVectorComparator {
fn from(comparator: PyMultiVectorComparator) -> Self {
match comparator {
PyMultiVectorComparator::MaxSim => MultiVectorComparator::MaxSim,
}
}
}
#[pyclass(name = "VectorStorageDatatype")]
#[derive(Copy, Clone, Debug)]
pub enum PyVectorStorageDatatype {
Float32,
Float16,
Uint8,
}
impl From<VectorStorageDatatype> for PyVectorStorageDatatype {
fn from(datatype: VectorStorageDatatype) -> Self {
match datatype {
VectorStorageDatatype::Float32 => PyVectorStorageDatatype::Float32,
VectorStorageDatatype::Float16 => PyVectorStorageDatatype::Float16,
VectorStorageDatatype::Uint8 => PyVectorStorageDatatype::Uint8,
}
}
}
impl From<PyVectorStorageDatatype> for VectorStorageDatatype {
fn from(datatype: PyVectorStorageDatatype) -> Self {
match datatype {
PyVectorStorageDatatype::Float32 => VectorStorageDatatype::Float32,
PyVectorStorageDatatype::Float16 => VectorStorageDatatype::Float16,
PyVectorStorageDatatype::Uint8 => VectorStorageDatatype::Uint8,
}
}
}

View File

@@ -23,12 +23,24 @@ mod qdrant_edge {
#[pymodule_export]
use super::PyShard;
#[pymodule_export]
use super::config::{
PyDistance, PyIndexes, PyMultiVectorComparator, PyMultiVectorConfig, PyPayloadStorageType,
PyQuantizationConfig, PySegmentConfig, PySparseVectorDataConfig, PyVectorDataConfig,
PyVectorStorageDatatype, PyVectorStorageType,
use super::config::quantization::{
PyBinaryQuantizationConfig, PyBinaryQuantizationEncoding,
PyBinaryQuantizationQueryEncoding, PyCompressionRatio, PyProductQuantizationConfig,
PyScalarQuantizationConfig, PyScalarType,
};
#[pymodule_export]
use super::config::sparse_vector_data::{
PyModifier, PySparseIndexConfig, PySparseIndexType, PySparseVectorDataConfig,
PySparseVectorStorageType,
};
#[pymodule_export]
use super::config::vector_data::{
PyDistance, PyHnswIndexConfig, PyMultiVectorComparator, PyMultiVectorConfig,
PyPlainIndexConfig, PyVectorDataConfig, PyVectorStorageDatatype, PyVectorStorageType,
};
#[pymodule_export]
use super::config::{PyPayloadStorageType, PySegmentConfig};
#[pymodule_export]
use super::query::{
PyDirection, PyFusion, PyMmr, PyOrderBy, PyPrefetch, PyQueryRequest, PySample,
};

View File

@@ -76,10 +76,10 @@ impl PyPrefetch {
#[derive(Clone, Debug, Into)]
pub struct PyScoringQuery(ScoringQuery);
impl<'py> FromPyObject<'_, 'py> for PyScoringQuery {
impl FromPyObject<'_, '_> for PyScoringQuery {
type Error = PyErr;
fn extract(query: Borrowed<'_, 'py, PyAny>) -> PyResult<Self> {
fn extract(query: Borrowed<'_, '_, PyAny>) -> PyResult<Self> {
#[derive(FromPyObject)]
enum Helper {
Vector(PyQuery),
@@ -180,10 +180,10 @@ impl From<PyDirection> for Direction {
#[derive(Clone, Debug, Into)]
pub struct PyStartFrom(StartFrom);
impl<'py> FromPyObject<'_, 'py> for PyStartFrom {
impl FromPyObject<'_, '_> for PyStartFrom {
type Error = PyErr;
fn extract(start_from: Borrowed<'_, 'py, PyAny>) -> PyResult<Self> {
fn extract(start_from: Borrowed<'_, '_, PyAny>) -> PyResult<Self> {
#[derive(FromPyObject)]
enum Helper {
Integer(IntPayloadType),
@@ -220,7 +220,7 @@ impl<'py> IntoPyObject<'py> for PyStartFrom {
type Output = Bound<'py, Self::Target>;
type Error = PyErr;
fn into_pyobject(self, py: Python<'py>) -> std::result::Result<Self::Output, Self::Error> {
fn into_pyobject(self, py: Python<'py>) -> PyResult<Self::Output> {
IntoPyObject::into_pyobject(&self, py)
}
}
@@ -230,7 +230,7 @@ impl<'py> IntoPyObject<'py> for &PyStartFrom {
type Output = Bound<'py, Self::Target>;
type Error = PyErr;
fn into_pyobject(self, py: Python<'py>) -> std::result::Result<Self::Output, Self::Error> {
fn into_pyobject(self, py: Python<'py>) -> PyResult<Self::Output> {
match &self.0 {
StartFrom::Integer(int) => int.into_bound_py_any(py),
StartFrom::Float(float) => float.into_bound_py_any(py),

View File

@@ -97,9 +97,10 @@ impl PyAcornSearchParams {
#[derive(Clone, Debug, Into)]
pub struct PyWithVector(WithVector);
impl<'py> FromPyObject<'_, 'py> for PyWithVector {
impl FromPyObject<'_, '_> for PyWithVector {
type Error = PyErr;
fn extract(with_vector: Borrowed<'_, 'py, PyAny>) -> PyResult<Self> {
fn extract(with_vector: Borrowed<'_, '_, PyAny>) -> PyResult<Self> {
#[derive(FromPyObject)]
enum Helper {
Bool(bool),
@@ -127,7 +128,7 @@ impl<'py> IntoPyObject<'py> for PyWithVector {
type Output = Bound<'py, Self::Target>;
type Error = PyErr; // Infallible?
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
fn into_pyobject(self, py: Python<'py>) -> PyResult<Self::Output> {
IntoPyObject::into_pyobject(&self, py)
}
}
@@ -137,7 +138,7 @@ impl<'py> IntoPyObject<'py> for &PyWithVector {
type Output = Bound<'py, Self::Target>;
type Error = PyErr; // Infallible?
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
fn into_pyobject(self, py: Python<'py>) -> PyResult<Self::Output> {
match &self.0 {
WithVector::Bool(bool) => bool.into_bound_py_any(py),
WithVector::Selector(vectors) => vectors.into_bound_py_any(py),
@@ -148,9 +149,10 @@ impl<'py> IntoPyObject<'py> for &PyWithVector {
#[derive(Clone, Debug, Into)]
pub struct PyWithPayload(WithPayloadInterface);
impl<'py> FromPyObject<'_, 'py> for PyWithPayload {
impl FromPyObject<'_, '_> for PyWithPayload {
type Error = PyErr;
fn extract(with_payload: Borrowed<'_, 'py, PyAny>) -> PyResult<Self> {
fn extract(with_payload: Borrowed<'_, '_, PyAny>) -> PyResult<Self> {
#[derive(FromPyObject)]
enum Helper {
Bool(bool),
@@ -183,7 +185,7 @@ impl<'py> IntoPyObject<'py> for PyWithPayload {
type Output = Bound<'py, Self::Target>;
type Error = PyErr; // Infallible?
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
fn into_pyobject(self, py: Python<'py>) -> PyResult<Self::Output> {
IntoPyObject::into_pyobject(&self, py)
}
}
@@ -193,7 +195,7 @@ impl<'py> IntoPyObject<'py> for &PyWithPayload {
type Output = Bound<'py, Self::Target>;
type Error = PyErr; // Infallible?
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
fn into_pyobject(self, py: Python<'py>) -> PyResult<Self::Output> {
match &self.0 {
WithPayloadInterface::Bool(bool) => bool.into_bound_py_any(py),
WithPayloadInterface::Fields(fields) => {
@@ -210,9 +212,10 @@ impl<'py> IntoPyObject<'py> for &PyWithPayload {
#[repr(transparent)]
pub struct PyPayloadSelector(PayloadSelector);
impl<'py> FromPyObject<'_, 'py> for PyPayloadSelector {
impl FromPyObject<'_, '_> for PyPayloadSelector {
type Error = PyErr;
fn extract(selector: Borrowed<'_, 'py, PyAny>) -> PyResult<Self> {
fn extract(selector: Borrowed<'_, '_, PyAny>) -> PyResult<Self> {
let selector = match selector.extract()? {
PyPayloadSelectorInterface::Include(keys) => {
PayloadSelector::Include(PayloadSelectorInclude {
@@ -235,7 +238,7 @@ impl<'py> IntoPyObject<'py> for PyPayloadSelector {
type Output = Bound<'py, Self::Target>;
type Error = PyErr; // Infallible?
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
fn into_pyobject(self, py: Python<'py>) -> PyResult<Self::Output> {
let selector = match self.0 {
PayloadSelector::Include(PayloadSelectorInclude { include }) => {
PyPayloadSelectorInterface::Include(PyJsonPath::wrap_vec(include))

View File

@@ -2,6 +2,7 @@ use bytemuck::TransparentWrapper;
use derive_more::Into;
use pyo3::IntoPyObjectExt as _;
use pyo3::prelude::*;
use segment::json_path::JsonPath;
use segment::types::*;
use segment::utils::maybe_arc::MaybeArc;
@@ -11,9 +12,10 @@ use crate::types::*;
#[repr(transparent)]
pub struct PyCondition(pub Condition);
impl<'py> FromPyObject<'_, 'py> for PyCondition {
impl FromPyObject<'_, '_> for PyCondition {
type Error = PyErr;
fn extract(condition: Borrowed<'_, 'py, PyAny>) -> PyResult<Self> {
fn extract(condition: Borrowed<'_, '_, PyAny>) -> PyResult<Self> {
#[derive(FromPyObject)]
#[expect(clippy::large_enum_variant)]
enum Helper {
@@ -45,7 +47,7 @@ impl<'py> IntoPyObject<'py> for PyCondition {
type Output = Bound<'py, PyAny>;
type Error = PyErr; // Infallible
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
fn into_pyobject(self, py: Python<'py>) -> PyResult<Self::Output> {
match self.0 {
Condition::Field(field) => PyFieldCondition(field).into_bound_py_any(py),
Condition::IsEmpty(is_empty) => PyIsEmptyCondition(is_empty).into_bound_py_any(py),
@@ -72,7 +74,9 @@ impl PyIsEmptyCondition {
#[new]
pub fn new(key: PyJsonPath) -> Result<Self, PyErr> {
Ok(Self(IsEmptyCondition {
is_empty: PayloadField { key: key.into() },
is_empty: PayloadField {
key: JsonPath::from(key),
},
}))
}
}
@@ -86,7 +90,9 @@ impl PyIsNullCondition {
#[new]
pub fn new(key: PyJsonPath) -> Result<Self, PyErr> {
Ok(Self(IsNullCondition {
is_null: PayloadField { key: key.into() },
is_null: PayloadField {
key: JsonPath::from(key),
},
}))
}
}

View File

@@ -1,24 +1,10 @@
use bytemuck::{TransparentWrapper, TransparentWrapperAlloc as _};
use derive_more::Into;
use ordered_float::OrderedFloat;
use pyo3::IntoPyObjectExt as _;
use pyo3::exceptions::PyValueError;
use pyo3::{PyErr, pyclass, pymethods};
use segment::types::{
GeoBoundingBox, GeoLineString, GeoPoint, GeoPolygon, GeoPolygonShadow, GeoRadius,
};
#[pyclass(name = "GeoPoint")]
#[derive(Clone, Debug, Into)]
pub struct PyGeoPoint(pub GeoPoint);
#[pymethods]
impl PyGeoPoint {
#[new]
pub fn new(lon: f64, lat: f64) -> Result<Self, PyErr> {
Ok(Self(GeoPoint::new(lon, lat).map_err(|err| {
PyErr::new::<PyValueError, _>(err.to_string())
})?))
}
}
use pyo3::prelude::*;
use segment::types::*;
#[pyclass(name = "GeoBoundingBox")]
#[derive(Clone, Debug, Into)]
@@ -29,8 +15,8 @@ impl PyGeoBoundingBox {
#[new]
pub fn new(top_left: PyGeoPoint, bottom_right: PyGeoPoint) -> Self {
Self(GeoBoundingBox {
top_left: top_left.0,
bottom_right: bottom_right.0,
top_left: GeoPoint::from(top_left),
bottom_right: GeoPoint::from(bottom_right),
})
}
}
@@ -44,7 +30,7 @@ impl PyGeoRadius {
#[new]
pub fn new(center: PyGeoPoint, radius: f64) -> Self {
Self(GeoRadius {
center: center.0,
center: GeoPoint::from(center),
radius: OrderedFloat(radius),
})
}
@@ -59,30 +45,59 @@ impl PyGeoPolygon {
#[new]
#[pyo3(signature = (exterior, interiors=None))]
pub fn new(
exterior: Vec<PyGeoPoint>,
interiors: Option<Vec<Vec<PyGeoPoint>>>,
exterior: PyGeoLineString,
interiors: Option<Vec<PyGeoLineString>>,
) -> Result<Self, PyErr> {
let exterior = GeoLineString {
points: exterior.into_iter().map(GeoPoint::from).collect(),
};
let interiors = interiors.map(|interiors| {
interiors
.into_iter()
.map(|ring| GeoLineString {
points: ring.into_iter().map(GeoPoint::from).collect(),
})
.collect()
});
let shadow = GeoPolygonShadow {
exterior,
interiors,
exterior: GeoLineString::from(exterior),
interiors: interiors.map(PyGeoLineString::peel_vec),
};
let polygon = GeoPolygon::try_from(shadow)
.map_err(|err| PyErr::new::<PyValueError, _>(err.to_string()))?;
let polygon =
GeoPolygon::try_from(shadow).map_err(|err| PyValueError::new_err(err.to_string()))?;
Ok(Self(polygon))
}
}
#[pyclass(name = "GeoPoint")]
#[derive(Clone, Debug, Into, TransparentWrapper)]
#[repr(transparent)]
pub struct PyGeoPoint(pub GeoPoint);
#[pymethods]
impl PyGeoPoint {
#[new]
pub fn new(lon: f64, lat: f64) -> Result<Self, PyErr> {
let point =
GeoPoint::new(lon, lat).map_err(|err| PyValueError::new_err(err.to_string()))?;
Ok(Self(point))
}
}
#[derive(Clone, Debug, Into, TransparentWrapper)]
#[repr(transparent)]
pub struct PyGeoLineString(GeoLineString);
impl FromPyObject<'_, '_> for PyGeoLineString {
type Error = PyErr;
fn extract(points: Borrowed<'_, '_, PyAny>) -> PyResult<Self> {
let points = points.extract()?;
Ok(Self(GeoLineString {
points: PyGeoPoint::peel_vec(points),
}))
}
}
impl<'py> IntoPyObject<'py> for PyGeoLineString {
type Target = PyAny; // PyList
type Output = Bound<'py, Self::Target>;
type Error = PyErr; // Infallible
fn into_pyobject(self, py: Python<'py>) -> PyResult<Self::Output> {
PyGeoPoint::wrap_vec(self.0.points).into_bound_py_any(py)
}
}

View File

@@ -1,11 +1,8 @@
use derive_more::Into;
use pyo3::{FromPyObject, IntoPyObject, pyclass, pymethods};
use segment::types::{
AnyVariants, IntPayloadType, Match, MatchAny, MatchExcept, MatchPhrase, MatchText,
MatchTextAny, MatchValue, ValueVariants,
};
use pyo3::prelude::*;
use segment::types::*;
#[derive(Clone, Debug, IntoPyObject, FromPyObject)]
#[derive(Clone, Debug, FromPyObject, IntoPyObject)]
pub enum PyMatch {
Value(PyMatchValue),
Text(PyMatchText),
@@ -18,12 +15,12 @@ pub enum PyMatch {
impl From<PyMatch> for Match {
fn from(value: PyMatch) -> Self {
match value {
PyMatch::Value(v) => Match::Value(MatchValue::from(v)),
PyMatch::Text(v) => Match::Text(MatchText::from(v)),
PyMatch::TextAny(v) => Match::TextAny(MatchTextAny::from(v)),
PyMatch::Phrase(v) => Match::Phrase(MatchPhrase::from(v)),
PyMatch::Any(v) => Match::Any(MatchAny::from(v)),
PyMatch::Except(v) => Match::Except(MatchExcept::from(v)),
PyMatch::Value(value) => Match::Value(MatchValue::from(value)),
PyMatch::Text(text) => Match::Text(MatchText::from(text)),
PyMatch::TextAny(text_any) => Match::TextAny(MatchTextAny::from(text_any)),
PyMatch::Phrase(phrase) => Match::Phrase(MatchPhrase::from(phrase)),
PyMatch::Any(any) => Match::Any(MatchAny::from(any)),
PyMatch::Except(except) => Match::Except(MatchExcept::from(except)),
}
}
}
@@ -31,12 +28,12 @@ impl From<PyMatch> for Match {
impl From<Match> for PyMatch {
fn from(value: Match) -> Self {
match value {
Match::Value(v) => PyMatch::Value(PyMatchValue(v)),
Match::Text(v) => PyMatch::Text(PyMatchText(v)),
Match::TextAny(v) => PyMatch::TextAny(PyMatchTextAny(v)),
Match::Phrase(v) => PyMatch::Phrase(PyMatchPhrase(v)),
Match::Any(v) => PyMatch::Any(PyMatchAny(v)),
Match::Except(v) => PyMatch::Except(PyMatchExcept(v)),
Match::Value(value) => PyMatch::Value(PyMatchValue(value)),
Match::Text(text) => PyMatch::Text(PyMatchText(text)),
Match::TextAny(text_any) => PyMatch::TextAny(PyMatchTextAny(text_any)),
Match::Phrase(phrase) => PyMatch::Phrase(PyMatchPhrase(phrase)),
Match::Any(any) => PyMatch::Any(PyMatchAny(any)),
Match::Except(except) => PyMatch::Except(PyMatchExcept(except)),
}
}
}
@@ -55,6 +52,23 @@ impl PyMatchValue {
}
}
#[derive(Clone, Debug, FromPyObject, IntoPyObject)]
pub enum PyValueVariants {
String(String),
Integer(IntPayloadType),
Bool(bool),
}
impl From<PyValueVariants> for ValueVariants {
fn from(value: PyValueVariants) -> Self {
match value {
PyValueVariants::String(str) => ValueVariants::String(str),
PyValueVariants::Integer(int) => ValueVariants::Integer(int),
PyValueVariants::Bool(bool) => ValueVariants::Bool(bool),
}
}
}
#[pyclass(name = "MatchText")]
#[derive(Clone, Debug, Into)]
pub struct PyMatchText(pub MatchText);
@@ -119,34 +133,17 @@ impl PyMatchExcept {
}
}
#[derive(Clone, Debug, IntoPyObject, FromPyObject)]
pub enum PyValueVariants {
String(String),
Integer(IntPayloadType),
Bool(bool),
}
impl From<PyValueVariants> for ValueVariants {
fn from(value: PyValueVariants) -> Self {
match value {
PyValueVariants::String(s) => ValueVariants::String(s),
PyValueVariants::Integer(i) => ValueVariants::Integer(i),
PyValueVariants::Bool(b) => ValueVariants::Bool(b),
}
}
}
#[derive(Clone, Debug, IntoPyObject, FromPyObject)]
#[derive(Clone, Debug, FromPyObject, IntoPyObject)]
pub enum PyAnyVariants {
Strings(Vec<String>),
Integers(Vec<IntPayloadType>),
}
impl From<PyAnyVariants> for AnyVariants {
fn from(value: PyAnyVariants) -> Self {
match value {
PyAnyVariants::Strings(s) => AnyVariants::Strings(s.into_iter().collect()),
PyAnyVariants::Integers(i) => AnyVariants::Integers(i.into_iter().collect()),
fn from(any: PyAnyVariants) -> Self {
match any {
PyAnyVariants::Strings(str) => AnyVariants::Strings(str.into_iter().collect()),
PyAnyVariants::Integers(int) => AnyVariants::Integers(int.into_iter().collect()),
}
}
}

View File

@@ -1,6 +1,7 @@
use bytemuck::TransparentWrapperAlloc as _;
use derive_more::Into;
use pyo3::prelude::*;
use segment::types::{Condition, MinShould};
use segment::types::MinShould;
use crate::types::filter::condition::PyCondition;
@@ -12,9 +13,8 @@ pub struct PyMinShould(pub MinShould);
impl PyMinShould {
#[new]
pub fn new(conditions: Vec<PyCondition>, min_count: usize) -> Self {
let conditions = conditions.into_iter().map(Condition::from).collect();
Self(MinShould {
conditions,
conditions: PyCondition::peel_vec(conditions),
min_count,
})
}

View File

@@ -4,7 +4,7 @@ use pyo3::exceptions::PyValueError;
use pyo3::prelude::*;
use segment::types::*;
#[derive(Copy, Clone, Debug, IntoPyObject, FromPyObject)]
#[derive(Copy, Clone, Debug, FromPyObject, IntoPyObject)]
pub enum PyRange {
Float(PyRangeFloat),
DateTime(PyRangeDateTime),

View File

@@ -1,5 +1,5 @@
use derive_more::Into;
use pyo3::{pyclass, pymethods};
use pyo3::prelude::*;
use segment::types::ValuesCount;
#[pyclass(name = "ValuesCount")]

View File

@@ -35,9 +35,10 @@ impl PyFormula {
#[repr(transparent)]
pub struct PyExpression(ExpressionInternal);
impl<'py> FromPyObject<'_, 'py> for PyExpression {
impl FromPyObject<'_, '_> for PyExpression {
type Error = PyErr;
fn extract(helper: Borrowed<'_, 'py, PyAny>) -> PyResult<Self> {
fn extract(helper: Borrowed<'_, '_, PyAny>) -> PyResult<Self> {
let expr = match helper.extract()? {
PyExpressionInterface::Constant(val) => ExpressionInternal::Constant(val),
PyExpressionInterface::Variable(var) => ExpressionInternal::Variable(var),
@@ -101,7 +102,7 @@ impl<'py> IntoPyObject<'py> for PyExpression {
type Output = Bound<'py, Self::Target>;
type Error = PyErr;
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
fn into_pyobject(self, py: Python<'py>) -> PyResult<Self::Output> {
let helper = match self.0 {
ExpressionInternal::Constant(var) => PyExpressionInterface::Constant(var),
ExpressionInternal::Variable(var) => PyExpressionInterface::Variable(var),

View File

@@ -12,9 +12,10 @@ use segment::json_path::JsonPath;
#[repr(transparent)]
pub struct PyJsonPath(pub JsonPath);
impl<'py> FromPyObject<'_, 'py> for PyJsonPath {
impl FromPyObject<'_, '_> for PyJsonPath {
type Error = PyErr;
fn extract(json_path: Borrowed<'_, 'py, PyAny>) -> PyResult<Self> {
fn extract(json_path: Borrowed<'_, '_, PyAny>) -> PyResult<Self> {
let json_path: String = json_path.extract()?;
let json_path = JsonPath::from_str(&json_path)
.map_err(|_| PyValueError::new_err(format!("invalid JSON path {json_path}")))?;

View File

@@ -9,9 +9,10 @@ use super::value::*;
#[repr(transparent)]
pub struct PyPayload(pub Payload);
impl<'py> FromPyObject<'_, 'py> for PyPayload {
impl FromPyObject<'_, '_> for PyPayload {
type Error = PyErr;
fn extract(payload: Borrowed<'_, 'py, PyAny>) -> PyResult<Self> {
fn extract(payload: Borrowed<'_, '_, PyAny>) -> PyResult<Self> {
let payload = value_map_from_py(&payload)?;
Ok(Self(Payload(payload)))
}
@@ -22,7 +23,7 @@ impl<'py> IntoPyObject<'py> for PyPayload {
type Output = Bound<'py, Self::Target>;
type Error = PyErr;
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
fn into_pyobject(self, py: Python<'py>) -> PyResult<Self::Output> {
IntoPyObject::into_pyobject(&self, py)
}
}
@@ -32,7 +33,7 @@ impl<'py> IntoPyObject<'py> for &PyPayload {
type Output = Bound<'py, Self::Target>;
type Error = PyErr;
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
fn into_pyobject(self, py: Python<'py>) -> PyResult<Self::Output> {
value_map_into_py(&self.0.0, py)
}
}

View File

@@ -21,9 +21,10 @@ impl PyPointId {
}
}
impl<'py> FromPyObject<'_, 'py> for PyPointId {
impl FromPyObject<'_, '_> for PyPointId {
type Error = PyErr;
fn extract(point_id: Borrowed<'_, 'py, PyAny>) -> PyResult<Self> {
fn extract(point_id: Borrowed<'_, '_, PyAny>) -> PyResult<Self> {
#[derive(FromPyObject)]
enum Helper {
NumId(u64),
@@ -59,7 +60,7 @@ impl<'py> IntoPyObject<'py> for PyPointId {
type Output = Bound<'py, PyAny>;
type Error = PyErr; // Infallible
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
fn into_pyobject(self, py: Python<'py>) -> PyResult<Self::Output> {
IntoPyObject::into_pyobject(&self, py)
}
}
@@ -69,7 +70,7 @@ impl<'py> IntoPyObject<'py> for &PyPointId {
type Output = Bound<'py, PyAny>;
type Error = PyErr; // Infallible
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
fn into_pyobject(self, py: Python<'py>) -> PyResult<Self::Output> {
match &self.0 {
PointIdType::NumId(id) => id.into_bound_py_any(py),
PointIdType::Uuid(uuid) => uuid.into_bound_py_any(py),

View File

@@ -19,4 +19,14 @@ impl PyPointVectors {
vector: VectorStructPersisted::from(vector),
})
}
#[getter]
fn id(&self) -> PyPointId {
PyPointId(self.0.id)
}
#[getter]
fn vector(&self) -> PyVector {
PyVector::from(self.0.vector.clone())
}
}

View File

@@ -11,9 +11,10 @@ use crate::types::*;
#[derive(Clone, Debug, Into)]
pub struct PyQuery(QueryEnum);
impl<'py> FromPyObject<'_, 'py> for PyQuery {
impl FromPyObject<'_, '_> for PyQuery {
type Error = PyErr;
fn extract(query: Borrowed<'_, 'py, PyAny>) -> PyResult<Self> {
fn extract(query: Borrowed<'_, '_, PyAny>) -> PyResult<Self> {
let query = match query.extract()? {
PyQueryInterface::Nearest { query, using } => QueryEnum::Nearest(NamedQuery {
query: VectorInternal::try_from(query)?,
@@ -61,7 +62,7 @@ impl<'py> IntoPyObject<'py> for PyQuery {
type Output = Bound<'py, Self::Target>;
type Error = PyErr; // Infallible?
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
fn into_pyobject(self, py: Python<'py>) -> PyResult<Self::Output> {
let query = match self.0 {
QueryEnum::Nearest(NamedQuery { query, using }) => PyQueryInterface::Nearest {
query: PyNamedVector::from(query),

View File

@@ -21,9 +21,10 @@ impl PyValue {
}
}
impl<'py> FromPyObject<'_, 'py> for PyValue {
impl FromPyObject<'_, '_> for PyValue {
type Error = PyErr;
fn extract(value: Borrowed<'_, 'py, PyAny>) -> PyResult<Self> {
fn extract(value: Borrowed<'_, '_, PyAny>) -> PyResult<Self> {
#[derive(FromPyObject)]
enum Helper {
Bool(bool),
@@ -66,7 +67,7 @@ impl<'py> IntoPyObject<'py> for PyValue {
type Output = Bound<'py, Self::Target>;
type Error = PyErr;
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
fn into_pyobject(self, py: Python<'py>) -> PyResult<Self::Output> {
IntoPyObject::into_pyobject(&self, py)
}
}
@@ -76,7 +77,7 @@ impl<'py> IntoPyObject<'py> for &PyValue {
type Output = Bound<'py, Self::Target>;
type Error = PyErr;
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
fn into_pyobject(self, py: Python<'py>) -> PyResult<Self::Output> {
match &self.0 {
serde_json::Value::Null => Ok(py.None().into_bound(py)),
serde_json::Value::Bool(bool) => bool.into_bound_py_any(py),

View File

@@ -9,7 +9,7 @@ use shard::operations::point_ops::{VectorPersisted, VectorStructPersisted};
use sparse::common::sparse_vector::SparseVector;
use sparse::common::types::{DimId, DimWeight};
#[derive(Clone, Debug, IntoPyObject, FromPyObject)]
#[derive(Clone, Debug, FromPyObject, IntoPyObject)]
pub enum PyVector {
// Put Int first so ints don't get parsed as floats (since f64 can extract from ints).
Single(DenseVector),
@@ -85,7 +85,7 @@ impl TryFrom<PyVector> for VectorStructInternal {
}
}
#[derive(Clone, Debug, IntoPyObject, FromPyObject)]
#[derive(Clone, Debug, FromPyObject, IntoPyObject)]
pub enum PyNamedVector {
// Put Int first so ints don't get parsed as floats (since f64 can extract from ints).
Dense(DenseVector),