欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

ArangoDB 图形系列 - 02 基本图形技能

最编程 2024-07-15 12:42:49
...

本文内容来源于官网2019版的课程,使用美国机场航班真实数据,来构建图并写一些有趣的查询。

ArangoDB

官方对ArangoDB的介绍中,它是一个原生多模型数据库

  • 多模型:支持3种主要的NoSQL数据模型,即key-value, documents, graph
  • 原生:使用相同的底层核心和一套查询语言(即AQL)来支持以上数据模型

ArangoDB的存储层级如下图所示

  1. 可以建立多个Databases,其中可包含任意数量的collections,安装好之后,有一个默认的数据库 _system

  2. Collections可以包含任意数量的documents。ArangoDB中有两类型的collection: document collection和edge collection

  3. Documents中存储的都是JSON对象,这些JSON对象中会含有一些系统自带的属性,如 _key, _id, _rev, _from, _to

ArangoDB架构

图基础

在ArangoDB中,每个边都有单一明确的方向,如果两个点之间是双向的关系,则需要两个方向的边来表示。不过在图遍历时,你可以使用 ANY来忽略边的方向(另外两种方向是 INBOUNDOUTBOUND)

ArangoDB允许你存储各种不同形状和体量的图,比如DAG有向无环图/有环/自环/多图(即两个顶点之间有多个边)。

顶点和边都是JSON document,所以说你可以存储任意想存的信息。

截屏2022-04-12 下午3.38.24.png

数据集

案例中使用的数据集来自于美国超过3000个机场,30万个航班在2008年1月1日至1月15日期间的数据

其中airport节点的结构如下,可看到每个机场包含全称,机场缩写,所在城市,州,经纬度,是否有VIP休息室等属性。

截屏2022-04-12 下午3.49.01.png

flights的结构如下,其中包含航班号,尾号,承运人编号,起飞降落时间地点等信息。

截屏2022-04-12 下午3.53.14.png

以下是几个样例数据,会分别存储在airports和flights各自的collection中。

截屏2022-04-12 下午4.06.06.png

如果要形成图,可以把airports当成顶点, flights当成边。airport中的 _id属性可以作为 flights中 _from和_to的值,从而表示某航班是从哪个机场飞到哪

截屏2022-04-12 下午4.52.34.png

最后对ArangoDB中的两类collection做个总结:

Document collections Edge collections
包含document,每个document都是JSON对象 包含含有特殊边属性的document,即 _from和_to
内置索引,每个document可以通过_key被快速检索到 每个edge collection都内置边索引
document可用作图中的顶点,换句话说,如果不建立图,也可以当做普通document使用 相当于RDBMS中存储多对多关系的cross table

下载和安装ArangoDB

之前的文章中已经介绍过了下载安装,一旦server启动后,可以通过在浏览器中访问http://localhost:8529 来打开web界面(ArangoDB社区把该UI界面称为 Aardvark)

本文仍然使用默认的 _system数据库为例

数据导入

arangodb_demodata下载示例数据集,并解压得到两个.csv文件,分别是airports.csv和flights.csv

导入 Airports

使用导入工具arangoimport,在命令行中键入命令来导入数据

arangoimport --file airports.csv的解压路径 --collection airports --create-collection true --type csv

可以使用--help来查看arangoimport帮助:

arangoimport --help

如果使用的不是默认root用户,可以使用 --server.username name来指定,如果没设置密码或者取消了登录认证,则看到提示要输入密码时直接回车。

解压后的airports.csv片段如下:

截屏2022-04-12 下午5.54.23.png

在命令行中敲入导入命令行,会看到类似如下的反馈:

截屏2022-04-12 下午5.56.11.png

arangoimport在背后做了什么呢?

  • 创建了一个新document collection,名叫 airports, 使用_key作为索引
  • CSV文件中的每一行都建立一个document,除了首行和最后一行(为空行)
  • csv的首行定义了属性名称

打开web界面,左侧菜单栏选择COLLECTIONS,可以看到airports,打开后即可浏览刚才导入的数据,如下图:

截屏2022-04-12 下午6.10.42.png

导入 Flights

刚才导入的机场数据是图中的顶点,现在我们导入航班数据作为边来将顶点连接。

原始flights.csv数据如下, 该csv文件有超过28万行数据,一会儿使用arangoimport导入时,会比airports.csv耗时:

截屏2022-04-13 下午2.12.24.png

由于是边数据,导入命令稍有不同,多了一个参数 --create-collection-type edge

arangoimport --file ~/Downloads/GraphCourse_DemoData_ArangoDB-2/flights.csv --collection flights --create-collection true --type csv --create-collection-type edge

键入导入命令后,会看到类似如下的显示:

截屏2022-04-13 下午2.21.38.png

这次,arangoimport在背后做了什么呢?

  • 创建了一个新edge collection,名叫flights, 使用_key作为索引, _from和_to作为边索引。 由于csv文件中没有_key字段,所以ArangoDB会自动生成
  • CSV文件中的每一行都建立一个edge document,除了首行和最后一行(为空行),其中_from和_to分别引用airports中的 _ids

可以想象,导入两批数据后,形成的图大概长这样:

截屏2022-04-13 下午2.29.17.png

打开web界面,依然是从左侧菜单栏选择COLLECTIONS,此时会发现刚导入的flights,而且它的图标隐含了这是一个 edge collection.

截屏2022-04-13 下午2.30.51.png

AQL查询

数据就绪后,我们可以使用ArangoDB的查询语言AQL来写一些查询了。

截屏2022-04-13 下午2.35.50.png

如上图,从web界面中选择 QUERIES, 进入查询编辑页面来编写、执行和分析查询。这里支持语法高亮显示,还允许用户保存和管理查询。

截屏2022-04-13 下午2.46.49.png

图:各区域的功能

动手实践

# 练习1,使用DOCUMENT()函数通过 _id 来找到John F. Kennedy机场

RETURN DOCUMENT("airports/JFK")


###########################################
# 练习2,使用FOR循环遍历airports, 使用_key过滤出 Kennedy机场,该模式会自动启用索引优化

FOR airport IN airports
    FILTER airport._key == "JFK"
    RETURN airport

###########################################
# 练习3,使用FILTER编写更复杂的过滤条件

FOR airport IN airports
    FILTER airport.city == "New York"
       AND airport.state == "NY"
    RETURN airport
    

###########################################
练习4, 使用SORT和LIMIT语句排序并限制结果数量

FOR a IN airports
    FILTER a.vip
    SORT a.state, a.name DESC
    LIMIT 5
    RETURN a
    

###########################################
练习5, 不返回整个document,仅返回部分所需字段

FOR a IN airports
    FILTER a._key IN ["JFK", "LAX"] 
    RETURN { fullName: a.name }


###########################################
练习6, 使用500个机场的经纬度通过GEO_POINT()来构建 GeoJSON对象

FOR a IN airports
    LIMIT 500
    RETURN GEO_POINT(a.long, a.lat)
    
    
###########################################
练习7, 返回collection中所有的文档数量

ArangoDB GraphCourse_Beginners Update 2

RETURN COUNT(airports)


###########################################
练习8, 返回所有提供VIP候机室的机场数量, 此例使用COLLECT实现

FOR airport IN airports
    FILTER airport.vip
    COLLECT WITH COUNT INTO count
    RETURN count

图遍历 Graph Traversal

图数据库的好处是可以很高效地执行图遍历,即沿着某些边在图上进行游走,经过的边数量称为遍历深度,遍历起点S的深度为0;与S相邻的节点,他们的遍历深度为1, 如下图的A,B,C,同理,D,E,F的遍历深度为2

截屏2022-04-13 下午4.10.19.png

在正式执行图遍历前,我们先来熟悉一下相关的查询语句语法:

截屏2022-04-13 下午4.28.31.png

注意:语法中的FOR,IN,ANY等都是大写,这只是一个实践惯例,其实大小写无所谓。但变量名,属性,collection名都是要区分大小写的。

FOR之后最多跟三个变量,分别是,

  • vertex (文档对象),当前被遍历的顶点
  • edge (文档对象,可选),当前被遍历的边
  • path (文档对象,可选),用两个成员来代表当前的路径
    • vertices, 一个顶点数组,代表该路径上的所有顶点
    • edges, 一个边数组,代表该路径上的所有边

IN min..max:定义遍历的最小最大深度,min默认为1,max默认为min

startVertex: 遍历的起始点

OUTBOUND|INBOUND|ANY,这三个参数指定遍历时边的方向

  • OUTBOUND,沿着出方向

  • INBOUND,沿着入方向

  • ANY,沿着任意方向

edgeCollection, edge collection的名字,其中存有遍历时的边

动手实践

# 返回从洛杉矶机场LAX可直达(深度为1)的所有机场

FOR airport IN 1..1 OUTBOUND
    "airports/LAX" flights
    RETURN DISTINCT airport.name
    
    
    
    
# 找出从洛杉矶机场起飞的任意10个目的地,返回格式为 {"airport": {机场对象}, "flight": {航班对象}}

FOR airport, flight IN OUTBOUND 
'airports/LAX' flights
    LIMIT 10
    RETURN {airport, flight}



# 返回10个飞往俾斯麦机场BIS的航班号

FOR airport, flight IN INBOUND
"airports/BIS" flights
    LIMIT 10
    RETURN flight.FlightNum
    
    
# 查找15日至17日之间,从俾斯麦机场起飞或者降落的航班,返回对方机场所在城市跟达到时间

FOR airport, flight IN ANY 
'airports/BIS' flights
    FILTER flight.Month == 1
       AND flight.Day >= 5
       AND flight.Day <= 7
    RETURN { city: airport.city, 
             time: flight.ArrTimeUTC}

之前我们学习过基础的AQL查询,如果跟遍历结合使用呢? 一起看几个例子:

# 仅使用基础的FOR循环查询,即看做是对collection中文档的查询

FOR flight IN flights
    FILTER flight.TailNum == "N238JB" 
    RETURN flight
    

# 找出所有15日,FlightNum为860的航班,仅返回_from和_to属性
FOR flight IN flights
    FILTER flight.FlightNum == 860
    AND    flight.Month == 1
    AND    flight.Day == 5
    RETURN KEEP(flight, "_from", "_to") 
    # 等价于 RETURN {_from: flight._from, _to:flight._to}, 很显然KEEP写法更简洁
    
    
# 找出所有FlightNum为859860,在JFK机场起飞或降落的航班
# 方法1:直接用FOR循环文档过滤
FOR flight IN flights
    FILTER flight.FlightNum IN [859, 860]
     AND   (flight._from == "airports/JFK" OR flight._to == "airports/JFK")
    RETURN KEEP(flight, "_from", "_to", "FlightNum")
    
# 方法2: 用图遍历,优点是不仅可以访问flight信息,还可以访问airport信息
FOR a, f IN ANY "airports/JFK" flights
    FILTER f.FlightNum IN [859,860] 
    RETURN { airport: a.name, flight: f.FlightNum }

    
    
# 结合FOR循环跟图遍历, 从JFK和PBI两个机场同时作为起始点遍历,找出跟这两机场有关的编号为859860的航班,以及对手机场的名字和城市

FOR orig IN airports
    FILTER orig._key IN ["JFK", "PBI"]
    FOR dest, flight IN ANY orig flights
    FILTER flight.FlightNum in [859, 860]
    RETURN {A_airport: orig.name, B_airport: dest.name, B_city:dest.city, unique_flightnum:CONCAT(flight.UniqueCarrier, flight.FlightNum), day: flight.Day}

参考

[1]Graph Course for Freshers

推荐阅读