mirror of
https://github.com/quickwit-oss/tantivy.git
synced 2026-08-18 12:08:22 +00:00
plastic surgery
This commit is contained in:
@@ -37,15 +37,20 @@ impl SegmentSerializer {
|
||||
let fieldnorms_write = segment.open_write(SegmentComponent::FieldNorms)?;
|
||||
let fieldnorms_serializer = FieldNormsSerializer::from_write(fieldnorms_write)?;
|
||||
|
||||
let spatial_write = segment.open_write(SegmentComponent::Spatial)?;
|
||||
let spatial_serializer = SpatialSerializer::from_write(spatial_write)?;
|
||||
let spatial_serializer: Option<SpatialSerializer> =
|
||||
if segment.schema().contains_spatial_field() {
|
||||
let spatial_write = segment.open_write(SegmentComponent::Spatial)?;
|
||||
Some(SpatialSerializer::from_write(spatial_write)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let postings_serializer = InvertedIndexSerializer::open(&mut segment)?;
|
||||
Ok(SegmentSerializer {
|
||||
segment,
|
||||
store_writer,
|
||||
fast_field_write,
|
||||
spatial_serializer: Some(spatial_serializer),
|
||||
spatial_serializer,
|
||||
fieldnorms_serializer: Some(fieldnorms_serializer),
|
||||
postings_serializer,
|
||||
})
|
||||
|
||||
+43
-3
@@ -218,9 +218,14 @@ impl SchemaBuilder {
|
||||
/// Finalize the creation of a `Schema`
|
||||
/// This will consume your `SchemaBuilder`
|
||||
pub fn build(self) -> Schema {
|
||||
let contains_spatial_field = self
|
||||
.fields
|
||||
.iter()
|
||||
.any(|field_entry| field_entry.field_type().value_type() == Type::Spatial);
|
||||
Schema(Arc::new(InnerSchema {
|
||||
fields: self.fields,
|
||||
fields_map: self.fields_map,
|
||||
contains_spatial_field,
|
||||
}))
|
||||
}
|
||||
}
|
||||
@@ -228,6 +233,7 @@ impl SchemaBuilder {
|
||||
struct InnerSchema {
|
||||
fields: Vec<FieldEntry>,
|
||||
fields_map: HashMap<String, Field>, // transient
|
||||
contains_spatial_field: bool,
|
||||
}
|
||||
|
||||
impl PartialEq for InnerSchema {
|
||||
@@ -378,6 +384,11 @@ impl Schema {
|
||||
}
|
||||
Some((field, json_path))
|
||||
}
|
||||
|
||||
/// Returns true if the schema contains a spatial field.
|
||||
pub(crate) fn contains_spatial_field(&self) -> bool {
|
||||
self.0.contains_spatial_field
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Schema {
|
||||
@@ -405,16 +416,16 @@ impl<'de> Deserialize<'de> for Schema {
|
||||
|
||||
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
||||
where A: SeqAccess<'de> {
|
||||
let mut schema = SchemaBuilder {
|
||||
let mut schema_builder = SchemaBuilder {
|
||||
fields: Vec::with_capacity(seq.size_hint().unwrap_or(0)),
|
||||
fields_map: HashMap::with_capacity(seq.size_hint().unwrap_or(0)),
|
||||
};
|
||||
|
||||
while let Some(value) = seq.next_element()? {
|
||||
schema.add_field(value);
|
||||
schema_builder.add_field(value);
|
||||
}
|
||||
|
||||
Ok(schema.build())
|
||||
Ok(schema_builder.build())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1030,4 +1041,33 @@ mod tests {
|
||||
Some((default, "foobar"))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_contains_spatial_field() {
|
||||
// No spatial field
|
||||
{
|
||||
let mut schema_builder = Schema::builder();
|
||||
schema_builder.add_text_field("title", TEXT);
|
||||
let schema = schema_builder.build();
|
||||
assert!(!schema.contains_spatial_field());
|
||||
|
||||
// Serialization check
|
||||
let schema_json = serde_json::to_string(&schema).unwrap();
|
||||
let schema_deserialized: Schema = serde_json::from_str(&schema_json).unwrap();
|
||||
assert!(!schema_deserialized.contains_spatial_field());
|
||||
}
|
||||
// With spatial field
|
||||
{
|
||||
let mut schema_builder = Schema::builder();
|
||||
schema_builder.add_text_field("title", TEXT);
|
||||
schema_builder.add_spatial_field("location", SPATIAL);
|
||||
let schema = schema_builder.build();
|
||||
assert!(schema.contains_spatial_field());
|
||||
|
||||
// Serialization check
|
||||
let schema_json = serde_json::to_string(&schema).unwrap();
|
||||
let schema_deserialized: Schema = serde_json::from_str(&schema_json).unwrap();
|
||||
assert!(schema_deserialized.contains_spatial_field());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+33
-16
@@ -6,6 +6,7 @@
|
||||
//! recovery when needed.
|
||||
|
||||
use i_triangle::advanced::delaunay::IntDelaunay;
|
||||
use i_triangle::i_overlay::i_float::int::point::IntPoint;
|
||||
|
||||
use crate::DocId;
|
||||
|
||||
@@ -141,11 +142,10 @@ impl Triangle {
|
||||
}
|
||||
// change orientation if clockwise (CW)
|
||||
if !is_counter_clockwise(
|
||||
Coord { y: ay, x: ax },
|
||||
Coord { y: by, x: bx },
|
||||
Coord { y: cy, x: cx },
|
||||
)
|
||||
{
|
||||
IntPoint { y: ay, x: ax },
|
||||
IntPoint { y: by, x: bx },
|
||||
IntPoint { y: cy, x: cx },
|
||||
) {
|
||||
// To change the orientation, we simply swap B and C.
|
||||
let temp_x = bx;
|
||||
let temp_y = by;
|
||||
@@ -194,6 +194,22 @@ impl Triangle {
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds a degenerated triangle degenerating for a single point.
|
||||
/// All vertices are that point, and all vertices are boundaries.
|
||||
pub fn from_point(doc_id: DocId, point_x: i32, point_y: i32) -> Triangle {
|
||||
Triangle::new(
|
||||
doc_id,
|
||||
[point_y, point_x, point_y, point_x, point_y, point_x],
|
||||
[true, true, true],
|
||||
)
|
||||
}
|
||||
|
||||
/// Builds a degenerated triangle for a segment.
|
||||
/// Line segment AB is represented as the triangle ABA.
|
||||
pub fn from_line_segment(doc_id: DocId, a_x: i32, a_y: i32, b_x: i32, b_y: i32) -> Triangle {
|
||||
Triangle::new(doc_id, [a_y, a_x, b_y, b_x, a_y, a_x], [true, true, true])
|
||||
}
|
||||
|
||||
/// Create a triangle with only the doc_id and the words initialized to zero.
|
||||
///
|
||||
/// The doc_id and words in the field are delta-compressed as a series with the doc_id
|
||||
@@ -333,21 +349,16 @@ pub fn delaunay_to_triangles(doc_id: u32, delaunay: &IntDelaunay, triangles: &mu
|
||||
}
|
||||
}
|
||||
|
||||
struct Coord {
|
||||
x: i32,
|
||||
y: i32,
|
||||
}
|
||||
|
||||
/// Returns true if the path A -> B -> C is Counter-Clockwise (CCW) or collinear.
|
||||
/// Returns false if it is Clockwise (CW).
|
||||
#[inline(always)]
|
||||
fn is_counter_clockwise(a: Coord, b: Coord, c: Coord) -> bool {
|
||||
fn is_counter_clockwise(a: IntPoint, b: IntPoint, c: IntPoint) -> bool {
|
||||
// We calculate the 2D cross product (determinant) of vectors AB and AC.
|
||||
// Formula: (bx - ax)(cy - ay) - (by - ay)(cx - ax)
|
||||
|
||||
// We cast to i64 to prevent overflow, as multiplying two i32s can exceed i32::MAX.
|
||||
let val = (b.x as i64 - a.x as i64) * (c.y as i64 - a.y as i64)
|
||||
- (b.y as i64 - a.y as i64) * (c.x as i64 - a.x as i64);
|
||||
- (b.y as i64 - a.y as i64) * (c.x as i64 - a.x as i64);
|
||||
|
||||
// If the result is positive, the triangle is CCW.
|
||||
// If negative, it is CW.
|
||||
@@ -393,7 +404,7 @@ mod tests {
|
||||
let input_coords = [
|
||||
50, 40, // A (y, x)
|
||||
10, 60, // B
|
||||
20, 10 // C
|
||||
20, 10, // C
|
||||
];
|
||||
|
||||
// 2. Define Boundaries [ab, bc, ca]
|
||||
@@ -415,7 +426,7 @@ mod tests {
|
||||
let expected_coords = [
|
||||
20, 10, // C
|
||||
10, 60, // B
|
||||
50, 40 // A
|
||||
50, 40, // A
|
||||
];
|
||||
|
||||
// 5. Expected Boundaries
|
||||
@@ -428,8 +439,14 @@ mod tests {
|
||||
// Shift left by 1: [true, false, false]
|
||||
let expected_bounds = [true, false, false];
|
||||
|
||||
assert_eq!(decoded_coords, expected_coords, "Coordinates did not decode as expected");
|
||||
assert_eq!(decoded_bounds, expected_bounds, "Boundary flags were incorrect (likely swap bug)");
|
||||
assert_eq!(
|
||||
decoded_coords, expected_coords,
|
||||
"Coordinates did not decode as expected"
|
||||
);
|
||||
assert_eq!(
|
||||
decoded_bounds, expected_bounds,
|
||||
"Boundary flags were incorrect (likely swap bug)"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
+23
-27
@@ -23,27 +23,27 @@ impl SpatialWriter {
|
||||
let triangles = &mut self.triangles_by_field.entry(field).or_default();
|
||||
match geometry {
|
||||
Geometry::Point(point) => {
|
||||
into_point(triangles, doc_id, point);
|
||||
append_point(triangles, doc_id, point);
|
||||
}
|
||||
Geometry::MultiPoint(multi_point) => {
|
||||
for point in multi_point {
|
||||
into_point(triangles, doc_id, point);
|
||||
append_point(triangles, doc_id, point);
|
||||
}
|
||||
}
|
||||
Geometry::LineString(line_string) => {
|
||||
into_line_string(triangles, doc_id, line_string);
|
||||
append_line_string(triangles, doc_id, line_string);
|
||||
}
|
||||
Geometry::MultiLineString(multi_line_string) => {
|
||||
for line_string in multi_line_string {
|
||||
into_line_string(triangles, doc_id, line_string);
|
||||
append_line_string(triangles, doc_id, line_string);
|
||||
}
|
||||
}
|
||||
Geometry::Polygon(polygon) => {
|
||||
into_polygon(triangles, doc_id, polygon);
|
||||
append_polygon(triangles, doc_id, &polygon);
|
||||
}
|
||||
Geometry::MultiPolygon(multi_polygon) => {
|
||||
for polygon in multi_polygon {
|
||||
into_polygon(triangles, doc_id, polygon);
|
||||
append_polygon(triangles, doc_id, &polygon);
|
||||
}
|
||||
}
|
||||
Geometry::GeometryCollection(geometries) => {
|
||||
@@ -62,7 +62,7 @@ impl SpatialWriter {
|
||||
.sum()
|
||||
}
|
||||
|
||||
/// HUSH
|
||||
/// Serializing our field.
|
||||
pub fn serialize(&mut self, mut serializer: SpatialSerializer) -> io::Result<()> {
|
||||
for (field, triangles) in &mut self.triangles_by_field {
|
||||
serializer.serialize_field(*field, triangles)?;
|
||||
@@ -81,7 +81,7 @@ impl Default for SpatialWriter {
|
||||
}
|
||||
}
|
||||
|
||||
/// HUSH
|
||||
/// Convert a point of (longitude, latitude) to a integer point.
|
||||
pub fn as_point_i32(point: (f64, f64)) -> (i32, i32) {
|
||||
(
|
||||
(point.0 / (360.0 / (1i64 << 32) as f64)).floor() as i32,
|
||||
@@ -89,43 +89,39 @@ pub fn as_point_i32(point: (f64, f64)) -> (i32, i32) {
|
||||
)
|
||||
}
|
||||
|
||||
fn into_point(triangles: &mut Vec<Triangle>, doc_id: DocId, point: (f64, f64)) {
|
||||
fn append_point(triangles: &mut Vec<Triangle>, doc_id: DocId, point: (f64, f64)) {
|
||||
let point = as_point_i32(point);
|
||||
triangles.push(Triangle::new(
|
||||
doc_id,
|
||||
[point.1, point.0, point.1, point.0, point.1, point.0],
|
||||
[true, true, true],
|
||||
));
|
||||
triangles.push(Triangle::from_point(doc_id, point.0, point.1));
|
||||
}
|
||||
|
||||
fn into_line_string(triangles: &mut Vec<Triangle>, doc_id: DocId, line_string: Vec<(f64, f64)>) {
|
||||
fn append_line_string(
|
||||
triangles: &mut Vec<Triangle>,
|
||||
doc_id: DocId,
|
||||
line_string: Vec<(f64, f64)>,
|
||||
) {
|
||||
let mut previous = as_point_i32(line_string[0]);
|
||||
for point in line_string.into_iter().skip(1) {
|
||||
let point = as_point_i32(point);
|
||||
triangles.push(Triangle::new(
|
||||
doc_id,
|
||||
[
|
||||
previous.1, previous.0, point.1, point.0, previous.1, previous.0,
|
||||
],
|
||||
[true, true, true],
|
||||
triangles.push(Triangle::from_line_segment(
|
||||
doc_id, previous.0, previous.1, point.0, point.1,
|
||||
));
|
||||
previous = point
|
||||
}
|
||||
}
|
||||
|
||||
fn into_ring(i_polygon: &mut Vec<Vec<IntPoint>>, ring: Vec<(f64, f64)>) {
|
||||
let mut i_ring = Vec::new();
|
||||
for point in ring {
|
||||
fn append_ring(i_polygon: &mut Vec<Vec<IntPoint>>, ring: &[(f64, f64)]) {
|
||||
let mut i_ring = Vec::with_capacity(ring.len() + 1);
|
||||
for &point in ring {
|
||||
let point = as_point_i32(point);
|
||||
i_ring.push(IntPoint::new(point.0, point.1));
|
||||
}
|
||||
i_polygon.push(i_ring);
|
||||
}
|
||||
|
||||
fn into_polygon(triangles: &mut Vec<Triangle>, doc_id: DocId, polygon: Vec<Vec<(f64, f64)>>) {
|
||||
let mut i_polygon = Vec::new();
|
||||
fn append_polygon(triangles: &mut Vec<Triangle>, doc_id: DocId, polygon: &[Vec<(f64, f64)>]) {
|
||||
let mut i_polygon: Vec<Vec<IntPoint>> = Vec::new();
|
||||
for ring in polygon {
|
||||
into_ring(&mut i_polygon, ring);
|
||||
append_ring(&mut i_polygon, ring);
|
||||
}
|
||||
let delaunay = i_polygon.triangulate().into_delaunay();
|
||||
delaunay_to_triangles(doc_id, &delaunay, triangles);
|
||||
|
||||
Reference in New Issue
Block a user