Skip to content

Commit

Permalink
Speed up new area creation
Browse files Browse the repository at this point in the history
  • Loading branch information
bubelov committed Nov 12, 2024
1 parent 23bc473 commit 732e9cd
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 62 deletions.
74 changes: 24 additions & 50 deletions src/area/service.rs
Original file line number Diff line number Diff line change
@@ -1,51 +1,26 @@
use super::Area;
use crate::{
area_element::model::AreaElement,
area_element::{self, model::AreaElement},
element::{self, Element},
element_comment::ElementComment,
event::Event,
Error, Result,
};
use geojson::GeoJson;
use rusqlite::Connection;
use serde::Serialize;
use serde_json::{Map, Value};
use std::collections::{HashMap, HashSet};
use time::OffsetDateTime;

pub fn insert(tags: Map<String, Value>, conn: &Connection) -> Result<Area> {
if !tags.contains_key("geo_json") {
return Err(Error::InvalidInput("geo_json tag is missing".into()));
}
let cloned_tags = tags.clone();
let url_alias = cloned_tags
.get("url_alias")
.ok_or(Error::InvalidInput(
"Mandatory tag is missing: url_alias".into(),
))?
.as_str()
.ok_or(Error::InvalidInput(
"This tag should be a string: url_alias".into(),
))?;
let geo_json = tags
.get("geo_json")
.ok_or(Error::InvalidInput(
"Mandatory tag is missing: geo_json".into(),
))?
.as_object()
.ok_or(Error::InvalidInput(
"This tag should be an object: geo_json".into(),
))?;
let geo_json: Result<GeoJson, _> = serde_json::to_string(geo_json).unwrap().parse();
if geo_json.is_err() {
Err(Error::InvalidInput("Invalid geo_json".into()))?
}
if Area::select_by_alias(url_alias, conn)?.is_some() {
Err(Error::Conflict("This url_alias is already in use".into()))?
}
pub fn insert(tags: Map<String, Value>, conn: &mut Connection) -> Result<Area> {
let area = Area::insert(tags, conn)?.ok_or("failed to insert area")?;
let area_elements = element::service::find_in_area(&area, conn)?;
element::service::generate_areas_mapping_old(&area_elements, conn)?;
let area_elements =
area_element::service::get_elements_within_geometries(area.geo_json_geometries()?, &conn)?;
AreaElement::insert_bulk(
area.id,
area_elements.into_iter().map(|it| it.id).collect(),
conn,
)?;
Ok(area)
}

Expand Down Expand Up @@ -200,41 +175,40 @@ pub fn get_comments(area: &Area, conn: &Connection) -> Result<Vec<ElementComment
#[cfg(test)]
mod test {
use crate::area::Area;
use crate::area_element::model::AreaElement;
use crate::element::Element;
use crate::osm::overpass::OverpassElement;
use crate::test::{mock_conn, mock_tags, phuket_geo_json};
use crate::test::{mock_conn, phuket_geo_json};
use crate::Result;
use serde_json::{json, Map};

#[test]
fn insert() -> Result<()> {
let mut conn = mock_conn();
let mut tags = mock_tags();
let url_alias = json!("test");
tags.insert("url_alias".into(), url_alias.clone());
tags.insert("geo_json".into(), phuket_geo_json());
let area = super::insert(tags, &mut conn)?;
let db_area = Area::select_by_id(area.id, &conn)?.unwrap();
assert_eq!(area, db_area);
let area = super::insert(Area::mock_tags(), &mut conn)?;
assert_eq!(Some(area), Area::select_by_id(1, &conn)?);
Ok(())
}

#[test]
fn insert_should_update_areas_tags() -> Result<()> {
fn insert_should_create_area_mappings() -> Result<()> {
let mut conn = mock_conn();
let area_element = OverpassElement {
let element_1 = OverpassElement {
lat: Some(7.979623499157051),
lon: Some(98.33448362485439),
..OverpassElement::mock(1)
};
let area_element = Element::insert(&area_element, &conn)?;
let mut tags = mock_tags();
let url_alias = json!("test");
tags.insert("url_alias".into(), url_alias.clone());
Element::insert(&element_1, &conn)?;
let element_2 = OverpassElement {
lat: Some(50.0),
lon: Some(1.0),
..OverpassElement::mock(2)
};
Element::insert(&element_2, &conn)?;
let mut tags = Area::mock_tags();
tags.insert("geo_json".into(), phuket_geo_json());
super::insert(tags, &mut conn)?;
let db_area_element = Element::select_by_id(area_element.id, &conn)?.unwrap();
assert_eq!(1, db_area_element.tag("areas").as_array().unwrap().len());
assert_eq!(1, AreaElement::select_by_area_id(1, &conn)?.len());
Ok(())
}

Expand Down
9 changes: 9 additions & 0 deletions src/area_element/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,15 @@ const COL_UPDATED_AT: &str = "updated_at";
const COL_DELETED_AT: &str = "deleted_at ";

impl AreaElement {
pub fn insert_bulk(area_id: i64, element_ids: Vec<i64>, conn: &mut Connection) -> Result<()> {
let sp = conn.savepoint()?;
for element in element_ids {
AreaElement::insert(area_id, element, &sp)?;
}
sp.commit()?;
Ok(())
}

pub fn insert(area_id: i64, element_id: i64, conn: &Connection) -> Result<AreaElement> {
let query = format!(
r#"
Expand Down
39 changes: 39 additions & 0 deletions src/area_element/service.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use super::model::AreaElement;
use crate::{area::Area, element::Element};
use crate::{element, Result};
use geo::{Contains, LineString, MultiPolygon, Polygon};
use geojson::Geometry;
use rusqlite::Connection;
use time::OffsetDateTime;

Expand Down Expand Up @@ -35,3 +37,40 @@ pub fn generate_areas_mapping(
}
Ok(Res { has_changes })
}

pub fn get_elements_within_geometries(
geometries: Vec<Geometry>,
conn: &Connection,
) -> Result<Vec<Element>> {
let mut area_elements: Vec<Element> = vec![];
for element in Element::select_all(None, &conn)? {
for geometry in &geometries {
match &geometry.value {
geojson::Value::MultiPolygon(_) => {
let multi_poly: MultiPolygon = (&geometry.value).try_into().unwrap();

if multi_poly.contains(&element.overpass_data.coord()) {
area_elements.push(element.clone());
}
}
geojson::Value::Polygon(_) => {
let poly: Polygon = (&geometry.value).try_into().unwrap();

if poly.contains(&element.overpass_data.coord()) {
area_elements.push(element.clone());
}
}
geojson::Value::LineString(_) => {
let line_string: LineString = (&geometry.value).try_into().unwrap();

if line_string.contains(&element.overpass_data.coord()) {
area_elements.push(element.clone());
}
}
_ => continue,
}
}
}

Ok(area_elements)
}
13 changes: 1 addition & 12 deletions src/test.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::db;
use deadpool_sqlite::{Config, Pool, Runtime};
use rusqlite::Connection;
use serde_json::{json, Map, Value};
use serde_json::{json, Value};
use std::{
collections::HashMap,
sync::atomic::{AtomicUsize, Ordering},
Expand Down Expand Up @@ -46,17 +46,6 @@ fn _mock_db() -> (Connection, Pool) {
)
}

pub fn mock_tags() -> Map<String, Value> {
let mut tags = Map::new();
tags.insert("null".into(), Value::Null);
tags.insert("bool".into(), Value::Bool(true));
tags.insert("number".into(), Value::Number(1.into()));
tags.insert("string".into(), Value::String("test".into()));
tags.insert("array".into(), Value::Array(vec![]));
tags.insert("object".into(), Value::Object(Map::new()));
tags
}

pub fn mock_osm_tags(kv_pairs: &[&str]) -> HashMap<String, String> {
let mut res = HashMap::new();
for chunk in kv_pairs.chunks(2) {
Expand Down

0 comments on commit 732e9cd

Please sign in to comment.