Support copy, deepcopy, eq on types (#99)
parent
0032362e97
commit
8b33e00c58
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use pyo3::{
|
use pyo3::{
|
||||||
|
basic::CompareOp,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
types::{
|
types::{
|
||||||
PyAny, PyDateAccess, PyDateTime, PyDict, PyList, PyTimeAccess, PyTuple,
|
PyAny, PyDateAccess, PyDateTime, PyDict, PyList, PyTimeAccess, PyTuple,
|
||||||
|
@ -148,7 +149,7 @@ fn value_to_string(value: &Value) -> String {
|
||||||
/// schema,
|
/// schema,
|
||||||
/// )
|
/// )
|
||||||
#[pyclass]
|
#[pyclass]
|
||||||
#[derive(Default)]
|
#[derive(Clone, Default, PartialEq)]
|
||||||
pub(crate) struct Document {
|
pub(crate) struct Document {
|
||||||
pub(crate) field_values: BTreeMap<String, Vec<tv::schema::Value>>,
|
pub(crate) field_values: BTreeMap<String, Vec<tv::schema::Value>>,
|
||||||
}
|
}
|
||||||
|
@ -552,6 +553,27 @@ impl Document {
|
||||||
fn __repr__(&self) -> PyResult<String> {
|
fn __repr__(&self) -> PyResult<String> {
|
||||||
Ok(format!("{self:?}"))
|
Ok(format!("{self:?}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn __copy__(&self) -> Self {
|
||||||
|
self.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn __deepcopy__(&self, _memo: &PyDict) -> Self {
|
||||||
|
self.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn __richcmp__(
|
||||||
|
&self,
|
||||||
|
other: &Self,
|
||||||
|
op: CompareOp,
|
||||||
|
py: Python<'_>,
|
||||||
|
) -> PyObject {
|
||||||
|
match op {
|
||||||
|
CompareOp::Eq => (self == other).into_py(py),
|
||||||
|
CompareOp::Ne => (self != other).into_py(py),
|
||||||
|
_ => py.NotImplemented(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Document {
|
impl Document {
|
||||||
|
|
19
src/facet.rs
19
src/facet.rs
|
@ -1,4 +1,4 @@
|
||||||
use pyo3::{prelude::*, types::PyType};
|
use pyo3::{basic::CompareOp, prelude::*, types::PyType};
|
||||||
use tantivy::schema;
|
use tantivy::schema;
|
||||||
|
|
||||||
/// A Facet represent a point in a given hierarchy.
|
/// A Facet represent a point in a given hierarchy.
|
||||||
|
@ -10,8 +10,8 @@ use tantivy::schema;
|
||||||
/// implicitely imply that a document belonging to a facet also belongs to the
|
/// implicitely imply that a document belonging to a facet also belongs to the
|
||||||
/// ancestor of its facet. In the example above, /electronics/tv_and_video/
|
/// ancestor of its facet. In the example above, /electronics/tv_and_video/
|
||||||
/// and /electronics.
|
/// and /electronics.
|
||||||
#[pyclass]
|
#[pyclass(frozen)]
|
||||||
#[derive(Clone)]
|
#[derive(Clone, PartialEq)]
|
||||||
pub(crate) struct Facet {
|
pub(crate) struct Facet {
|
||||||
pub(crate) inner: schema::Facet,
|
pub(crate) inner: schema::Facet,
|
||||||
}
|
}
|
||||||
|
@ -67,4 +67,17 @@ impl Facet {
|
||||||
fn __repr__(&self) -> PyResult<String> {
|
fn __repr__(&self) -> PyResult<String> {
|
||||||
Ok(format!("Facet({})", self.to_path_str()))
|
Ok(format!("Facet({})", self.to_path_str()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn __richcmp__(
|
||||||
|
&self,
|
||||||
|
other: &Self,
|
||||||
|
op: CompareOp,
|
||||||
|
py: Python<'_>,
|
||||||
|
) -> PyObject {
|
||||||
|
match op {
|
||||||
|
CompareOp::Eq => (self == other).into_py(py),
|
||||||
|
CompareOp::Ne => (self != other).into_py(py),
|
||||||
|
_ => py.NotImplemented(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ use pyo3::prelude::*;
|
||||||
use tantivy as tv;
|
use tantivy as tv;
|
||||||
|
|
||||||
/// Tantivy's Query
|
/// Tantivy's Query
|
||||||
#[pyclass]
|
#[pyclass(frozen)]
|
||||||
pub(crate) struct Query {
|
pub(crate) struct Query {
|
||||||
pub(crate) inner: Box<dyn tv::query::Query>,
|
pub(crate) inner: Box<dyn tv::query::Query>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,28 @@
|
||||||
use pyo3::prelude::*;
|
use pyo3::{basic::CompareOp, prelude::*};
|
||||||
use tantivy as tv;
|
use tantivy as tv;
|
||||||
|
|
||||||
/// Tantivy schema.
|
/// Tantivy schema.
|
||||||
///
|
///
|
||||||
/// The schema is very strict. To build the schema the `SchemaBuilder` class is
|
/// The schema is very strict. To build the schema the `SchemaBuilder` class is
|
||||||
/// provided.
|
/// provided.
|
||||||
#[pyclass]
|
#[pyclass(frozen)]
|
||||||
|
#[derive(PartialEq)]
|
||||||
pub(crate) struct Schema {
|
pub(crate) struct Schema {
|
||||||
pub(crate) inner: tv::schema::Schema,
|
pub(crate) inner: tv::schema::Schema,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pymethods]
|
#[pymethods]
|
||||||
impl Schema {}
|
impl Schema {
|
||||||
|
fn __richcmp__(
|
||||||
|
&self,
|
||||||
|
other: &Self,
|
||||||
|
op: CompareOp,
|
||||||
|
py: Python<'_>,
|
||||||
|
) -> PyObject {
|
||||||
|
match op {
|
||||||
|
CompareOp::Eq => (self == other).into_py(py),
|
||||||
|
CompareOp::Ne => (self != other).into_py(py),
|
||||||
|
_ => py.NotImplemented(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#![allow(clippy::new_ret_no_self)]
|
#![allow(clippy::new_ret_no_self)]
|
||||||
|
|
||||||
use crate::{document::Document, query::Query, to_pyerr};
|
use crate::{document::Document, query::Query, to_pyerr};
|
||||||
use pyo3::{exceptions::PyValueError, prelude::*};
|
use pyo3::{basic::CompareOp, exceptions::PyValueError, prelude::*};
|
||||||
use tantivy as tv;
|
use tantivy as tv;
|
||||||
use tantivy::collector::{Count, MultiCollector, TopDocs};
|
use tantivy::collector::{Count, MultiCollector, TopDocs};
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ pub(crate) struct Searcher {
|
||||||
pub(crate) inner: tv::Searcher,
|
pub(crate) inner: tv::Searcher,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, PartialEq)]
|
||||||
enum Fruit {
|
enum Fruit {
|
||||||
Score(f32),
|
Score(f32),
|
||||||
Order(u64),
|
Order(u64),
|
||||||
|
@ -37,7 +37,8 @@ impl ToPyObject for Fruit {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyclass]
|
#[pyclass(frozen)]
|
||||||
|
#[derive(Clone, PartialEq)]
|
||||||
/// Object holding a results successful search.
|
/// Object holding a results successful search.
|
||||||
pub(crate) struct SearchResult {
|
pub(crate) struct SearchResult {
|
||||||
hits: Vec<(Fruit, DocAddress)>,
|
hits: Vec<(Fruit, DocAddress)>,
|
||||||
|
@ -60,6 +61,19 @@ impl SearchResult {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn __richcmp__(
|
||||||
|
&self,
|
||||||
|
other: &Self,
|
||||||
|
op: CompareOp,
|
||||||
|
py: Python<'_>,
|
||||||
|
) -> PyObject {
|
||||||
|
match op {
|
||||||
|
CompareOp::Eq => (self == other).into_py(py),
|
||||||
|
CompareOp::Ne => (self != other).into_py(py),
|
||||||
|
_ => py.NotImplemented(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[getter]
|
#[getter]
|
||||||
/// The list of tuples that contains the scores and DocAddress of the
|
/// The list of tuples that contains the scores and DocAddress of the
|
||||||
/// search results.
|
/// search results.
|
||||||
|
@ -200,8 +214,8 @@ impl Searcher {
|
||||||
/// It consists in an id identifying its segment, and its segment-local DocId.
|
/// It consists in an id identifying its segment, and its segment-local DocId.
|
||||||
/// The id used for the segment is actually an ordinal in the list of segment
|
/// The id used for the segment is actually an ordinal in the list of segment
|
||||||
/// hold by a Searcher.
|
/// hold by a Searcher.
|
||||||
#[pyclass]
|
#[pyclass(frozen)]
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub(crate) struct DocAddress {
|
pub(crate) struct DocAddress {
|
||||||
pub(crate) segment_ord: tv::SegmentOrdinal,
|
pub(crate) segment_ord: tv::SegmentOrdinal,
|
||||||
pub(crate) doc: tv::DocId,
|
pub(crate) doc: tv::DocId,
|
||||||
|
@ -221,6 +235,19 @@ impl DocAddress {
|
||||||
fn doc(&self) -> u32 {
|
fn doc(&self) -> u32 {
|
||||||
self.doc
|
self.doc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn __richcmp__(
|
||||||
|
&self,
|
||||||
|
other: &Self,
|
||||||
|
op: CompareOp,
|
||||||
|
py: Python<'_>,
|
||||||
|
) -> PyObject {
|
||||||
|
match op {
|
||||||
|
CompareOp::Eq => (self == other).into_py(py),
|
||||||
|
CompareOp::Ne => (self != other).into_py(py),
|
||||||
|
_ => py.NotImplemented(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&tv::DocAddress> for DocAddress {
|
impl From<&tv::DocAddress> for DocAddress {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
import copy
|
||||||
import tantivy
|
import tantivy
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
@ -457,6 +458,21 @@ class TestClass(object):
|
||||||
schema,
|
schema,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_search_result_eq(self, ram_index, spanish_index):
|
||||||
|
eng_index = ram_index
|
||||||
|
eng_query = eng_index.parse_query("sea whale", ["title", "body"])
|
||||||
|
|
||||||
|
esp_index = spanish_index
|
||||||
|
esp_query = esp_index.parse_query("vieja", ["title", "body"])
|
||||||
|
|
||||||
|
eng_result1 = eng_index.searcher().search(eng_query, 10)
|
||||||
|
eng_result2 = eng_index.searcher().search(eng_query, 10)
|
||||||
|
esp_result = esp_index.searcher().search(esp_query, 10)
|
||||||
|
|
||||||
|
assert eng_result1 == eng_result2
|
||||||
|
assert eng_result1 != esp_result
|
||||||
|
assert eng_result2 != esp_result
|
||||||
|
|
||||||
|
|
||||||
class TestUpdateClass(object):
|
class TestUpdateClass(object):
|
||||||
def test_delete_update(self, ram_index):
|
def test_delete_update(self, ram_index):
|
||||||
|
@ -570,6 +586,24 @@ class TestDocument(object):
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
tantivy.Document(name={})
|
tantivy.Document(name={})
|
||||||
|
|
||||||
|
def test_document_eq(self):
|
||||||
|
doc1 = tantivy.Document(name="Bill", reference=[1, 2])
|
||||||
|
doc2 = tantivy.Document.from_dict({"name": "Bill", "reference": [1, 2]})
|
||||||
|
doc3 = tantivy.Document(name="Bob", reference=[3, 4])
|
||||||
|
|
||||||
|
assert doc1 == doc2
|
||||||
|
assert doc1 != doc3
|
||||||
|
assert doc2 != doc3
|
||||||
|
|
||||||
|
def test_document_copy(self):
|
||||||
|
doc1 = tantivy.Document(name="Bill", reference=[1, 2])
|
||||||
|
doc2 = copy.copy(doc1)
|
||||||
|
doc3 = copy.deepcopy(doc2)
|
||||||
|
|
||||||
|
assert doc1 == doc2
|
||||||
|
assert doc1 == doc3
|
||||||
|
assert doc2 == doc3
|
||||||
|
|
||||||
|
|
||||||
class TestJsonField:
|
class TestJsonField:
|
||||||
def test_query_from_json_field(self):
|
def test_query_from_json_field(self):
|
||||||
|
@ -665,3 +699,23 @@ def test_bytes(bytes_kwarg, bytes_payload):
|
||||||
writer.add_document(doc)
|
writer.add_document(doc)
|
||||||
writer.commit()
|
writer.commit()
|
||||||
index.reload()
|
index.reload()
|
||||||
|
|
||||||
|
|
||||||
|
def test_schema_eq():
|
||||||
|
schema1 = schema()
|
||||||
|
schema2 = schema()
|
||||||
|
schema3 = schema_numeric_fields()
|
||||||
|
|
||||||
|
assert schema1 == schema2
|
||||||
|
assert schema1 != schema3
|
||||||
|
assert schema2 != schema3
|
||||||
|
|
||||||
|
|
||||||
|
def test_facet_eq():
|
||||||
|
facet1 = tantivy.Facet.from_string("/europe/france")
|
||||||
|
facet2 = tantivy.Facet.from_string("/europe/france")
|
||||||
|
facet3 = tantivy.Facet.from_string("/europe/germany")
|
||||||
|
|
||||||
|
assert facet1 == facet2
|
||||||
|
assert facet1 != facet3
|
||||||
|
assert facet2 != facet3
|
||||||
|
|
Loading…
Reference in New Issue