mirror of
https://github.com/meilisearch/meilisearch.git
synced 2025-01-30 23:13:09 +08:00
store the geopoint in three dimensions
This commit is contained in:
parent
11a056d116
commit
98a365aaae
@ -54,7 +54,11 @@ pub type FieldId = u16;
|
|||||||
pub type Position = u32;
|
pub type Position = u32;
|
||||||
pub type RelativePosition = u16;
|
pub type RelativePosition = u16;
|
||||||
pub type FieldDistribution = BTreeMap<String, u64>;
|
pub type FieldDistribution = BTreeMap<String, u64>;
|
||||||
pub type GeoPoint = rstar::primitives::GeomWithData<[f64; 2], DocumentId>;
|
|
||||||
|
/// A GeoPoint is a point in cartesian plan, called xyz_point in the code. Its metadata
|
||||||
|
/// is a tuple composed of 1. the DocumentId of the associated document and 2. the original point
|
||||||
|
/// expressed in term of latitude and longitude.
|
||||||
|
pub type GeoPoint = rstar::primitives::GeomWithData<[f64; 3], (DocumentId, [f64; 2])>;
|
||||||
|
|
||||||
pub const MAX_POSITION_PER_ATTRIBUTE: u32 = u16::MAX as u32 + 1;
|
pub const MAX_POSITION_PER_ATTRIBUTE: u32 = u16::MAX as u32 + 1;
|
||||||
|
|
||||||
@ -168,6 +172,17 @@ pub fn distance_between_two_points(a: &[f64; 2], b: &[f64; 2]) -> f64 {
|
|||||||
a.haversine_distance_to(&b).meters()
|
a.haversine_distance_to(&b).meters()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert a point expressed in terms of latitude and longitude to a point in the
|
||||||
|
/// cartesian coordinate expressed in terms of x, y and z.
|
||||||
|
pub fn lat_lng_to_xyz(coord: &[f64; 2]) -> [f64; 3] {
|
||||||
|
let [lat, lng] = coord.map(|f| f.to_radians());
|
||||||
|
let x = lat.cos() * lng.cos();
|
||||||
|
let y = lat.cos() * lng.sin();
|
||||||
|
let z = lat.sin();
|
||||||
|
|
||||||
|
[x, y, z]
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
@ -5,7 +5,7 @@ use rstar::RTree;
|
|||||||
|
|
||||||
use super::{Criterion, CriterionParameters, CriterionResult};
|
use super::{Criterion, CriterionParameters, CriterionResult};
|
||||||
use crate::search::criteria::{resolve_query_tree, CriteriaBuilder};
|
use crate::search::criteria::{resolve_query_tree, CriteriaBuilder};
|
||||||
use crate::{GeoPoint, Index, Result};
|
use crate::{lat_lng_to_xyz, GeoPoint, Index, Result};
|
||||||
|
|
||||||
pub struct Geo<'t> {
|
pub struct Geo<'t> {
|
||||||
index: &'t Index,
|
index: &'t Index,
|
||||||
@ -132,10 +132,12 @@ fn geo_point(
|
|||||||
point: [f64; 2],
|
point: [f64; 2],
|
||||||
ascending: bool,
|
ascending: bool,
|
||||||
) -> Box<dyn Iterator<Item = RoaringBitmap>> {
|
) -> Box<dyn Iterator<Item = RoaringBitmap>> {
|
||||||
|
let point = lat_lng_to_xyz(&point);
|
||||||
|
|
||||||
let mut results = Vec::new();
|
let mut results = Vec::new();
|
||||||
for point in rtree.nearest_neighbor_iter(&point) {
|
for point in rtree.nearest_neighbor_iter(&point) {
|
||||||
if candidates.remove(point.data) {
|
if candidates.remove(point.data.0) {
|
||||||
results.push(std::iter::once(point.data).collect());
|
results.push(std::iter::once(point.data.0).collect());
|
||||||
if candidates.is_empty() {
|
if candidates.is_empty() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,9 @@ use crate::error::{Error, UserError};
|
|||||||
use crate::heed_codec::facet::{
|
use crate::heed_codec::facet::{
|
||||||
FacetLevelValueF64Codec, FacetStringLevelZeroCodec, FacetStringLevelZeroValueCodec,
|
FacetLevelValueF64Codec, FacetStringLevelZeroCodec, FacetStringLevelZeroValueCodec,
|
||||||
};
|
};
|
||||||
use crate::{distance_between_two_points, CboRoaringBitmapCodec, FieldId, Index, Result};
|
use crate::{
|
||||||
|
distance_between_two_points, lat_lng_to_xyz, CboRoaringBitmapCodec, FieldId, Index, Result,
|
||||||
|
};
|
||||||
|
|
||||||
/// The maximum number of filters the filter AST can process.
|
/// The maximum number of filters the filter AST can process.
|
||||||
const MAX_FILTER_DEPTH: usize = 2000;
|
const MAX_FILTER_DEPTH: usize = 2000;
|
||||||
@ -402,12 +404,14 @@ impl<'a> Filter<'a> {
|
|||||||
None => return Ok(RoaringBitmap::new()),
|
None => return Ok(RoaringBitmap::new()),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let xyz_base_point = lat_lng_to_xyz(&base_point);
|
||||||
|
|
||||||
let result = rtree
|
let result = rtree
|
||||||
.nearest_neighbor_iter(&base_point)
|
.nearest_neighbor_iter(&xyz_base_point)
|
||||||
.take_while(|point| {
|
.take_while(|point| {
|
||||||
distance_between_two_points(&base_point, point.geom()) < radius
|
distance_between_two_points(&base_point, &point.data.1) < radius
|
||||||
})
|
})
|
||||||
.map(|point| point.data)
|
.map(|point| point.data.0)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
|
@ -395,9 +395,9 @@ impl<'t, 'u, 'i> DeleteDocuments<'t, 'u, 'i> {
|
|||||||
|
|
||||||
let (points_to_remove, docids_to_remove): (Vec<_>, RoaringBitmap) = rtree
|
let (points_to_remove, docids_to_remove): (Vec<_>, RoaringBitmap) = rtree
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|&point| self.documents_ids.contains(point.data))
|
.filter(|&point| self.documents_ids.contains(point.data.0))
|
||||||
.cloned()
|
.cloned()
|
||||||
.map(|point| (point, point.data))
|
.map(|point| (point, point.data.0))
|
||||||
.unzip();
|
.unzip();
|
||||||
points_to_remove.iter().for_each(|point| {
|
points_to_remove.iter().for_each(|point| {
|
||||||
rtree.remove(&point);
|
rtree.remove(&point);
|
||||||
@ -747,7 +747,7 @@ mod tests {
|
|||||||
|
|
||||||
let all_geo_ids = rtree.iter().map(|point| point.data).collect::<Vec<_>>();
|
let all_geo_ids = rtree.iter().map(|point| point.data).collect::<Vec<_>>();
|
||||||
let all_geo_documents = index
|
let all_geo_documents = index
|
||||||
.documents(&rtxn, all_geo_ids.iter().copied())
|
.documents(&rtxn, all_geo_ids.iter().map(|(id, _)| id).copied())
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(id, _)| *id)
|
.map(|(id, _)| *id)
|
||||||
|
@ -12,7 +12,10 @@ use super::helpers::{
|
|||||||
};
|
};
|
||||||
use crate::heed_codec::facet::{decode_prefix_string, encode_prefix_string};
|
use crate::heed_codec::facet::{decode_prefix_string, encode_prefix_string};
|
||||||
use crate::update::index_documents::helpers::into_clonable_grenad;
|
use crate::update::index_documents::helpers::into_clonable_grenad;
|
||||||
use crate::{BoRoaringBitmapCodec, CboRoaringBitmapCodec, DocumentId, GeoPoint, Index, Result};
|
use crate::{
|
||||||
|
lat_lng_to_xyz, BoRoaringBitmapCodec, CboRoaringBitmapCodec, DocumentId, GeoPoint, Index,
|
||||||
|
Result,
|
||||||
|
};
|
||||||
|
|
||||||
pub(crate) enum TypedChunk {
|
pub(crate) enum TypedChunk {
|
||||||
DocidWordPositions(grenad::Reader<CursorClonableMmap>),
|
DocidWordPositions(grenad::Reader<CursorClonableMmap>),
|
||||||
@ -192,7 +195,9 @@ pub(crate) fn write_typed_chunk_into_index(
|
|||||||
let (lat, tail) = helpers::try_split_array_at::<u8, 8>(value).unwrap();
|
let (lat, tail) = helpers::try_split_array_at::<u8, 8>(value).unwrap();
|
||||||
let (lng, _) = helpers::try_split_array_at::<u8, 8>(tail).unwrap();
|
let (lng, _) = helpers::try_split_array_at::<u8, 8>(tail).unwrap();
|
||||||
let point = [f64::from_ne_bytes(lat), f64::from_ne_bytes(lng)];
|
let point = [f64::from_ne_bytes(lat), f64::from_ne_bytes(lng)];
|
||||||
rtree.insert(GeoPoint::new(point, docid));
|
let xyz_point = lat_lng_to_xyz(&point);
|
||||||
|
|
||||||
|
rtree.insert(GeoPoint::new(xyz_point, (docid, point)));
|
||||||
geo_faceted_docids.insert(docid);
|
geo_faceted_docids.insert(docid);
|
||||||
}
|
}
|
||||||
index.put_geo_rtree(wtxn, &rtree)?;
|
index.put_geo_rtree(wtxn, &rtree)?;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user