Modeling and querying persistent data structures can be tricky when they involve semi-structured data, complex relationships (with deep traversal) and physical information like geographical positions, areas, shapes and so on. Bad news is that almost all your applications will have to manage such kind of data. In this presentation we will see how to use OrientDB v2.2 Document-Graph engine and its new Geospatial Indexes to efficiently manage ad query connected, spatial information. Very few slides, a lot of live coding, I promise!
2. Luigi Dell’Aquila @ldellaquila Milan - Nov 25-26 2016
Luigi Dell’Aquila
Core Developer and Director of Consulting
OrientDB LTD
Twitter: @ldellaquila
http://www.orientdb.com
4. Luigi Dell’Aquila @ldellaquila Milan - Nov 25-26 2016
Summary
•What is OrientDB
•OrientDB GeoSpatial API
•Importing Geo data (Java and/or Node.js)
•Querying Geo data (OrientDB Studio)
•Displaying Geo data (Angular2, Google Maps)
•Adding Relationships - graph data
•Graph + Spatial queries
5. Luigi Dell’Aquila @ldellaquila Milan - Nov 25-26 2016
What is OrientDB
•Multi-Model Database (Document, Graph and more)
•Tables Classes
•Extended SQL
•JOIN Physical Pointers
•Schema, No-Schema, Hybrid
•HTTP + Binary protocols
•Stand-alone or Embedded
•Distributed Multi-Master
•Apache 2 license
6. Luigi Dell’Aquila @ldellaquila Milan - Nov 25-26 2016
Install
•http://www.orientdb.com/download/
•http://central.maven.org/maven2/com/
orientechnologies/orientdb-spatial/VERSION/
orientdb-spatial-VERSION-dist.jar
> cd orientdb-community/bin/
> ./server.sh
7. Luigi Dell’Aquila @ldellaquila Milan - Nov 25-26 2016
OrientDB GeoSpatial Classes
•OPoint
•OLine
•OPolygon
•OMultiPoint
•OMultiline
•OMultiPlygon
9. Luigi Dell’Aquila @ldellaquila Milan - Nov 25-26 2016
OrientDB GeoSpatial Functions
•ST_GeomFromText(text)
•ST_Equals(geom, geom)
•ST_Contains(geom, geom)
•ST_Disjoint(geom, geom)
•ST_Intersects(geom, geom)
•ST_Distance_Sphere(geom, geom)
•and more…
10. Luigi Dell’Aquila @ldellaquila Milan - Nov 25-26 2016
Create the Schema
CREATE CLASS POI EXTENDS V
CREATE PROPERTY POI.location EMBEDDED OPoint
CREATE INDEX POI.location on POI(location) SPATIAL ENGINE LUCENE
CREATE CLASS Natural EXTENDS V
CREATE PROPERTY Natural.location EMBEDDED OPolygon
CREATE INDEX Natural.location on Natural(location) SPATIAL ENGINE LUCENE
CREATE CLASS Person EXTENDS V
CREATE PROPERTY Person.location EMBEDDED OPoint
CREATE CLASS FriendOf EXTENDS E
11. Luigi Dell’Aquila @ldellaquila Milan - Nov 25-26 2016
Our Data Source (WKT)
WKT,osm_id,name,type
"POINT (14.4641804 50.0972109)",24569342,"Invalidovna, Metro B",station
"POINT (14.4739792 50.1036789)",24569358,"Palmovka, Metro B",station
"POINT (14.4921863 50.1062907)",24569412,"Českomoravská, Metro B",station
12. Luigi Dell’Aquila @ldellaquila Milan - Nov 25-26 2016
Now let’s import data!
Let’s do it in Java
you can download some Geo files from
http://www.mapcruzin.com/free-italy-arcgis-maps-shapefiles.htm
and convert them to WKT using QGis (www.qgis.org)
13. Luigi Dell’Aquila @ldellaquila Milan - Nov 25-26 2016
Maven Dependencies
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-csv</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>com.orientechnologies</groupId>
<artifactId>orientdb-graphdb</artifactId>
<version>2.2.13</version>
</dependency>
14. Luigi Dell’Aquila @ldellaquila Milan - Nov 25-26 2016
Read from CSV
Reader reader = new FileReader("/path/to/poi_file.csv");
CSVParser parser = CSVFormat.DEFAULT.parse(reader);
Iterator<CSVRecord> iterator = parser.iterator();
iterator.next(); //discard the header
while(iterator.hasNext()){ //read each row
importRecord(iterator.next());
}
15. Luigi Dell’Aquila @ldellaquila Milan - Nov 25-26 2016
OrientDB connection
Reader reader = new FileReader("/path/to/poi_file.csv");
CSVParser parser = CSVFormat.DEFAULT.parse(reader);
Iterator<CSVRecord> iterator = parser.iterator();
iterator.next(); //discard the header
OrientGraph graph =
new OrientGraph("remote:localhost/testdb", "admin", "admin");
while(iterator.hasNext()){ //read each row
importRecord(iterator.next(), graph);
}
graph.shutdown();
16. Luigi Dell’Aquila @ldellaquila Milan - Nov 25-26 2016
importRecord()
void importRecord(CSVRecord record, OrientGraph graph) {
String wkt = record.get(0);
String name = record.get(2);
String type = record.get(3);
graph.command(new OCommandSQL(
"insert into Natural " +
"set name = ?, type = ?, " +
"location = ST_GeomFromText(?)"))
.execute(name, type, wkt);
}
19. Luigi Dell’Aquila @ldellaquila Milan - Nov 25-26 2016
Clone the scaffolding
> git clone https://github.com/luigidellaquila/geospatial-demo
> cd geospatial-demo
> npm install
(it’s a clone of https://github.com/angular/quickstart)
> npm start
20. Luigi Dell’Aquila @ldellaquila Milan - Nov 25-26 2016
Clone the scaffolding
> git clone https://github.com/luigidellaquila/geospatial-demo
> cd geospatial-demo
> npm install
(it’s a clone of https://github.com/angular/quickstart)
> npm start
> cd <orientdb-home>/www
> ln -s <quickstart-path>
> tsc -w
21. Luigi Dell’Aquila @ldellaquila Milan - Nov 25-26 2016
We need Google Maps
<script src=“https://maps.googleapis.com/maps/api/js?key=API_KEY"
async defer></script>
22. Luigi Dell’Aquila @ldellaquila Milan - Nov 25-26 2016
Let’s display a map (app.html)
<div class=“container">
<div class="row">
<div class="col-md-12" id="map" style=“height:600px"></div>
</div>
</div>
23. Luigi Dell’Aquila @ldellaquila Milan - Nov 25-26 2016
Draw the map
drawMap(){
var controller = this;
let mapProp = {
center: new google.maps.LatLng(40.399640, -3.8375544),
zoom: 16,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
controller.map = new google.maps.Map(document.getElementById("map"), mapProp);
controller.map.addListener("click", function(point: any){
controller.zone.run(()=> {
controller.lat = point.latLng.lat();
controller.lon = point.latLng.lng();
});
});
}
24. Luigi Dell’Aquila @ldellaquila Milan - Nov 25-26 2016
Create a Person
createPerson(): void{
var location = {
// the location object
}
var queryString = ””; // OrientDB statement
this.orient.command(
queryString,
(result) => { /* Success callback */ },
(error) => { /* Error callback */ }
);
}
25. Luigi Dell’Aquila @ldellaquila Milan - Nov 25-26 2016
Create a Person
createPerson(): void{
var location = {
"@class": "OPoint",
coordinates: [this.lon, this.lat]
}
var queryString = ””; // OrientDB statement
this.orient.command(
queryString,
(result) => { /* Success callback */ },
(error) => { /* Error callback */ }
);}
26. Luigi Dell’Aquila @ldellaquila Milan - Nov 25-26 2016
Create a Person
createPerson(): void{
var location = {
"@class": "OPoint",
coordinates: [this.lon, this.lat]
}
var queryString = `insert into Person
set name = '${this.personName}',
location = ${JSON.stringify(location)}`;
this.orient.command(
queryString,
(result) => { /* Success callback */ },
(error) => { /* Error callback */ }
);
}
27. Luigi Dell’Aquila @ldellaquila Milan - Nov 25-26 2016
Create a Person
createPerson(): void{
var location = {
"@class": "OPoint",
coordinates: [this.lon, this.lat]
}
var queryString = `insert into Person
set name = '${this.personName}',
location = ${JSON.stringify(location)}`;
this.orient.command(
queryString,
(res) => {
let body = res.json();
let person = body.result[0];
this.addPersonToMap(person)
},
(e) => { console.log(e) });
}
28. Luigi Dell’Aquila @ldellaquila Milan - Nov 25-26 2016
Add Person Vertex to Orient via REST API
command(statement: string, success: (data: any) => void, error: (err: any) => void): void{
var url = this.url + "sql/-/-1"
var headers = new Headers();
headers.append("Authorization", "Basic " + btoa(this.username+":"+this.password));
this.http.post( // HTTP POST
url, // the URL
JSON.stringify({
"command": statement // the SQL command
}),
{headers: headers} // the authentication data
).toPromise()
.then(success)
.catch(error);
}
29. Luigi Dell’Aquila @ldellaquila Milan - Nov 25-26 2016
Add Person to the Map
addPersonToMap(personData:any){
let location = personData.location;
let coordinates = location.coordinates;
let controller = this;
let marker = new google.maps.Marker({
position: {lat:coordinates[1], lng:coordinates[0]},
map: this.map,
title: personData.name,
rid: personData["@rid"]
});
google.maps.event.addListener(marker, 'click', function() {
controller.onMarkerClick(marker);
});
}
30. Luigi Dell’Aquila @ldellaquila Milan - Nov 25-26 2016
Add an edge between people
(FriendOf)
createEdge(from:any, to:any): void{
this.orient.command(
`create edge FriendOf from ${from.rid} to ${to.rid}`,
(x)=>{console.log(x)},
(x)=>{console.log(x)}
)
this.addEdgeBetweenMarkersToMap(from, to);
}