2021-09-09 12:20:08 +02:00
|
|
|
use std::iter;
|
|
|
|
|
2021-09-01 15:14:23 +02:00
|
|
|
use roaring::RoaringBitmap;
|
|
|
|
use rstar::RTree;
|
|
|
|
|
|
|
|
use super::{Criterion, CriterionParameters, CriterionResult};
|
|
|
|
use crate::search::criteria::{resolve_query_tree, CriteriaBuilder};
|
2021-12-14 12:21:24 +01:00
|
|
|
use crate::{lat_lng_to_xyz, GeoPoint, Index, Result};
|
2021-09-01 15:14:23 +02:00
|
|
|
|
|
|
|
pub struct Geo<'t> {
|
|
|
|
index: &'t Index,
|
|
|
|
rtxn: &'t heed::RoTxn<'t>,
|
2021-09-13 14:27:14 +02:00
|
|
|
ascending: bool,
|
2021-09-01 15:14:23 +02:00
|
|
|
parent: Box<dyn Criterion + 't>,
|
|
|
|
candidates: Box<dyn Iterator<Item = RoaringBitmap>>,
|
|
|
|
allowed_candidates: RoaringBitmap,
|
|
|
|
bucket_candidates: RoaringBitmap,
|
|
|
|
rtree: Option<RTree<GeoPoint>>,
|
|
|
|
point: [f64; 2],
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'t> Geo<'t> {
|
2021-09-13 14:27:14 +02:00
|
|
|
pub fn asc(
|
2021-09-01 15:14:23 +02:00
|
|
|
index: &'t Index,
|
|
|
|
rtxn: &'t heed::RoTxn<'t>,
|
|
|
|
parent: Box<dyn Criterion + 't>,
|
|
|
|
point: [f64; 2],
|
2021-09-13 14:27:14 +02:00
|
|
|
) -> Result<Self> {
|
|
|
|
Self::new(index, rtxn, parent, point, true)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn desc(
|
|
|
|
index: &'t Index,
|
|
|
|
rtxn: &'t heed::RoTxn<'t>,
|
|
|
|
parent: Box<dyn Criterion + 't>,
|
|
|
|
point: [f64; 2],
|
|
|
|
) -> Result<Self> {
|
|
|
|
Self::new(index, rtxn, parent, point, false)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn new(
|
|
|
|
index: &'t Index,
|
|
|
|
rtxn: &'t heed::RoTxn<'t>,
|
|
|
|
parent: Box<dyn Criterion + 't>,
|
|
|
|
point: [f64; 2],
|
|
|
|
ascending: bool,
|
2021-09-01 15:14:23 +02:00
|
|
|
) -> Result<Self> {
|
2021-09-09 12:20:08 +02:00
|
|
|
let candidates = Box::new(iter::empty());
|
2021-09-01 15:14:23 +02:00
|
|
|
let allowed_candidates = index.geo_faceted_documents_ids(rtxn)?;
|
|
|
|
let bucket_candidates = RoaringBitmap::new();
|
|
|
|
let rtree = index.geo_rtree(rtxn)?;
|
|
|
|
|
2021-09-01 17:02:51 +02:00
|
|
|
Ok(Self {
|
|
|
|
index,
|
|
|
|
rtxn,
|
2021-09-13 14:27:14 +02:00
|
|
|
ascending,
|
2021-09-01 17:02:51 +02:00
|
|
|
parent,
|
|
|
|
candidates,
|
|
|
|
allowed_candidates,
|
|
|
|
bucket_candidates,
|
|
|
|
rtree,
|
|
|
|
point,
|
|
|
|
})
|
2021-09-01 15:14:23 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-09 12:20:08 +02:00
|
|
|
impl Criterion for Geo<'_> {
|
2021-09-01 15:14:23 +02:00
|
|
|
fn next(&mut self, params: &mut CriterionParameters) -> Result<Option<CriterionResult>> {
|
2021-09-09 15:19:47 +02:00
|
|
|
let rtree = self.rtree.as_ref();
|
2021-09-01 15:14:23 +02:00
|
|
|
|
|
|
|
loop {
|
|
|
|
match self.candidates.next() {
|
|
|
|
Some(mut candidates) => {
|
|
|
|
candidates -= params.excluded_candidates;
|
|
|
|
self.allowed_candidates -= &candidates;
|
|
|
|
return Ok(Some(CriterionResult {
|
|
|
|
query_tree: None,
|
|
|
|
candidates: Some(candidates),
|
|
|
|
filtered_candidates: None,
|
|
|
|
bucket_candidates: Some(self.bucket_candidates.clone()),
|
|
|
|
}));
|
|
|
|
}
|
2021-09-01 17:02:51 +02:00
|
|
|
None => match self.parent.next(params)? {
|
|
|
|
Some(CriterionResult {
|
|
|
|
query_tree,
|
|
|
|
candidates,
|
|
|
|
filtered_candidates,
|
|
|
|
bucket_candidates,
|
|
|
|
}) => {
|
|
|
|
let mut candidates = match (&query_tree, candidates) {
|
|
|
|
(_, Some(candidates)) => candidates,
|
|
|
|
(Some(qt), None) => {
|
|
|
|
let context = CriteriaBuilder::new(&self.rtxn, &self.index)?;
|
|
|
|
resolve_query_tree(&context, qt, params.wdcache)?
|
2021-09-01 15:14:23 +02:00
|
|
|
}
|
2021-09-01 17:02:51 +02:00
|
|
|
(None, None) => self.index.documents_ids(self.rtxn)?,
|
|
|
|
};
|
2021-09-01 15:14:23 +02:00
|
|
|
|
2021-09-01 17:02:51 +02:00
|
|
|
if let Some(filtered_candidates) = filtered_candidates {
|
|
|
|
candidates &= filtered_candidates;
|
|
|
|
}
|
2021-09-01 15:14:23 +02:00
|
|
|
|
2021-09-01 17:02:51 +02:00
|
|
|
match bucket_candidates {
|
|
|
|
Some(bucket_candidates) => self.bucket_candidates |= bucket_candidates,
|
|
|
|
None => self.bucket_candidates |= &candidates,
|
|
|
|
}
|
2021-09-01 15:14:23 +02:00
|
|
|
|
2021-09-01 17:02:51 +02:00
|
|
|
if candidates.is_empty() {
|
|
|
|
continue;
|
2021-09-01 15:14:23 +02:00
|
|
|
}
|
2021-09-01 17:02:51 +02:00
|
|
|
self.allowed_candidates = &candidates - params.excluded_candidates;
|
2021-09-09 15:19:47 +02:00
|
|
|
self.candidates = match rtree {
|
2021-09-13 14:27:14 +02:00
|
|
|
Some(rtree) => geo_point(
|
|
|
|
rtree,
|
|
|
|
self.allowed_candidates.clone(),
|
|
|
|
self.point,
|
|
|
|
self.ascending,
|
|
|
|
),
|
2021-09-09 15:19:47 +02:00
|
|
|
None => Box::new(std::iter::empty()),
|
|
|
|
};
|
2021-09-01 15:14:23 +02:00
|
|
|
}
|
2021-09-01 17:02:51 +02:00
|
|
|
None => return Ok(None),
|
|
|
|
},
|
2021-09-01 15:14:23 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-01 17:02:51 +02:00
|
|
|
fn geo_point(
|
|
|
|
rtree: &RTree<GeoPoint>,
|
2021-09-13 12:42:06 +02:00
|
|
|
mut candidates: RoaringBitmap,
|
2021-09-01 15:14:23 +02:00
|
|
|
point: [f64; 2],
|
2021-09-13 14:27:14 +02:00
|
|
|
ascending: bool,
|
2021-09-01 17:02:51 +02:00
|
|
|
) -> Box<dyn Iterator<Item = RoaringBitmap>> {
|
2021-12-14 12:21:24 +01:00
|
|
|
let point = lat_lng_to_xyz(&point);
|
|
|
|
|
2021-09-13 12:42:06 +02:00
|
|
|
let mut results = Vec::new();
|
|
|
|
for point in rtree.nearest_neighbor_iter(&point) {
|
2021-12-14 12:21:24 +01:00
|
|
|
if candidates.remove(point.data.0) {
|
|
|
|
results.push(std::iter::once(point.data.0).collect());
|
2021-09-13 12:42:06 +02:00
|
|
|
if candidates.is_empty() {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-09-01 17:02:51 +02:00
|
|
|
|
2021-09-13 14:27:14 +02:00
|
|
|
if ascending {
|
|
|
|
Box::new(results.into_iter())
|
|
|
|
} else {
|
|
|
|
Box::new(results.into_iter().rev())
|
|
|
|
}
|
2021-09-01 15:14:23 +02:00
|
|
|
}
|