由于网上关于ArangoDB的中文资料较少,系列文章的内容均来自于官网和其他资料
本文带领用户初步熟悉如何在本机启动ArangoDB服务程序,使用web界面与之交互,并简单看几个数据的增删改查例子。
安装
Download ArangoDB database: Enterprise and Community
打开下载链接,如下图所示,我们点击下载社区版:
图1.1:从download页面下载社区版,oasis是官方提供的云服务,与企业版一样,均收费
官方提供了各种操作系统的下载安装方式,请根据自身情况选择。
安装后,默认包含一个数据库,名叫 _system,一个默认用户, 名叫root
设置密码
安装官方说明,如果是在Debian和Windows安装,则安装过程会提示输入密码, Red-Hat会设置一个随机的密码,其他系统上安装,需要自行执行如下命令,会要求为root用户设置一个密码
shell> arango-secure-installation #
本文使用了Mac OS下载dmg的方式安装,未对root用户设置密码,可认为密码为空
登录认证
上一节提到,在安装期间会创建一个默认用户 root,该用户可以访问所有数据库。 在实际开发中,应该为你的应用创建一个单独的数据库,并创建相应有权限的用户(详见,管理用户)
如下是一个示例,创建一个样例数据库叫做 example, 一个用户叫做 root@example
arangosh> db._createDatabase("example");
arangosh> var users = require("@arangodb/users");
arangosh> users.save("root@example", "password");
arangosh> users.grantDatabase("root@example", "example");
随后可以使用用户名root@example来连接到新建的数据库
shell> arangosh --server.username "root@example" --server.database example
Web界面
Server本身 (也叫arangod) 使用 HTTP / REST,也可以使用 Web 界面(叫做Aardvark)或者arangosh命令行的方式来交互。
本节介绍Web界面,当安装完成之后,Server会自动启动,此时可以用http://localhost:8529打开web界面,之前提到默认的用户名是root,如果没设置过密码,则为空
图3.1:web界面,输入用户名密码
随后,会要求选择连接哪个数据库,安装后默认有一个_system,此处选择_system继续。
图3.2 选择数据库
登录后,可以点击左侧的dashboard仪表盘,查看server的相关信息:
图3.3:dashboard界面
Databases, Collections 和 Documents
本节介绍几个术语。使用传统的RDBMS类比,会发现ArangoDB与MongoDB中的术语一样。在Arangdb中,用户无需提前定义documents中有哪些属性
表4.1:RDBMS与Arangodb术语对比
在web界面中,左侧的菜单栏里选择 COLLECTIONS, 随后点击空白处的 Add Collection,添加一个 名为 users 的collection,其他选项不用修改(保持users是一个 document collection)并保存,随后会看到刚建立的名为 users的项弹出。
图4.1:添加第一个Colletion,名为users
此时还没有创建任何的documents,可以点击右上角 白十字绿色圆圈创建第一个document。 会弹出一个窗口,要求填写 _key,如果不填,系统会自动生成一个 _key,这里注意,_key一旦创建就是不可变的,_key总是string类型,比如本例中 "4904", 相应地,document _id为 "users/4904" (你本地生成的_key应该跟本例不一样)
图4.2:创建第一个document,_key由系统自动生成
创建完后,从新点击左侧菜单栏COLLECTIONS,打开名为users的collection,会看到我们刚建立的document。
图4.3:查看新建立的document
AQL
ArangoDB所用的查询语言叫AQL,本节我们使用AQL做一些基本增删改查操作。
从左侧菜单栏中,选择 QUERIES,在右侧的编辑窗口中,输入如下AQL语句(把document _id换成你本地的_id), 然后点击右下角的 Execute执行, 查询结果会直接出现在编辑窗口下方。
RETURN DOCUMENT("users/4904") # 此处使用了DOCUMENT函数,返回_id为users/4904的document
图4.4: 执行AQL语句, 查询新建立的document
刚才的语句执行了查询操作,现在我们执行一条插入语句,执行后的结果为 [] (空的array),因为我们没有使用关键字RETURN来明确指明要返回什么
INSERT { name: "Katie Foster", age: 27 } INTO users # 注意INTO后面的collection名称无需加引号
上述语句在users中新建立了一个document,如果想在执行插入后,确认刚插入的document,可以这样写AQL:
INSERT { name: "James Hendrix", age: 69 } INTO usersRETURN NEW
结果如图4.5所示:
图4.5:插入并返回所插入的数据
至此,我们一共建立了3个users, 如果想查询出全部3个users,可以执行如下语句:
RETURN DOCUMENT( ["users/4904", "users/5637", "users/5805"] ) # DOCUMENT函数中传入一个数组[],
很显然此种方式很不方便,还有更好的查询方式:
FOR user IN users
RETURN user # 此处使用了变量名user,实际上可以任意命名
执行结果有两种显示方式 Table/JSON, 如图4.6所示,可以来回切换:
图4.6: 查询结果显示方式
你可能会发现,返回结果的顺序不一定跟插入的顺序一致。返回结果的顺序确实无任何保证,除非你明确指定了排序,例如:
FOR user IN users
SORT user._key
RETURN user # 注意, user._key是string类型,所以 10074按照字典序排序后,会在9883之前
如果按照年龄排序(年龄是int整形):
FOR user IN users
SORT user.age DESC # 默认不填为ASC升序
RETURN user
还可以加入过滤条件:
FOR user IN users
FILTER user.age > 30 # Katie年龄不符合,最终返回两条结果
SORT user.age
RETURN user
下面是如何修改某个user的属性,此处我们把Katie的年龄也修改为大于30, 使用UPDATE可以部分修改某个document, 本例中,只更新了age属性,其他属性都原封不动。还有一个REPLACE关键字,会删掉除_key和_id的所有属性,只留下用户指定的新属性
UPDATE "5637" WITH { age: 40 } IN users # katie的_key为 5637
RETURN NEW
修改后,我们再次用年龄过滤,这一次只返回满足过滤条件的人名:
FOR user IN users
FILTER user.age > 30
SORT user.age
RETURN user.name
# 返回结果:
[
"John Smith",
"Katie Foster",
"James Hendrix"
]
###########################################
# 还可以修改返回的列名
FOR user IN users
FILTER user.age > 30
SORT user.age
RETURN {userName: user.name, age: user.age}
# 返回结果:
[
{
"userName": "John Smith",
"age": 32
},
{
"userName": "Katie Foster",
"age": 40
},
{
"userName": "James Hendrix",
"age": 69
}
]
# 还可以对不同的返回字段进行拼接
FOR user IN users
FILTER user.age > 30
SORT user.age
RETURN CONCAT(user.name, "'s age is ", user.age) # CONCAT返回string
# 返回结果:
[
"John Smith's age is 32",
"Katie Foster's age is 40",
"James Hendrix's age is 69"
]
再来看一个复杂的例子,我们使用双层FOR循环,将任意两个用户的组合全部找到(3个users,一共是3*3=9种可能,排除掉自己跟自己的组合,还剩6种)
FOR user1 IN users
FOR user2 IN users
FILTER user1 != user2
RETURN {user1name: user1.name, user2name:user2.name}
# 返回结果:
[ { "user1name": "James Hendrix", "user2name": "John Smith" }, { "user1name": "Katie Foster", "user2name": "John Smith" }, { "user1name": "John Smith", "user2name": "James Hendrix" }, { "user1name": "Katie Foster", "user2name": "James Hendrix" }, { "user1name": "John Smith", "user2name": "Katie Foster" }, { "user1name": "James Hendrix", "user2name": "Katie Foster" }]
# RETURN语句不一样,最终返回的格式不一样
FOR user1 IN users
FOR user2 IN users
FILTER user1 != user2
RETURN [user1.name, user2.name]
# 返回结果:
[ [ "James Hendrix", "John Smith" ],
[ "Katie Foster", "John Smith" ],
[ "John Smith", "James Hendrix" ],
[ "Katie Foster", "James Hendrix" ],
[ "John Smith", "Katie Foster" ],
[ "James Hendrix", "Katie Foster" ]
]
还可以在循环中使用LET定义临时变量,以便在FILTER中使用该临时变量:
FOR user1 IN users
FOR user2 IN users
FILTER user1 != user2
LET sumOfAges = user1.age + user2.age # 定义sumOfAges临时变量
FILTER sumOfAges < 100 # 第一次使用sumOfAges
RETURN {
pair: [user1.name, user2.name],
sumOfAges: sumOfAges # 第二次使用sumOfAges
# sumOfAges: sumOfAges 冒号两边都一样时,可以只写一遍 sumOfAges
}
#返回结果
[
{
"pair": [
"Katie Foster",
"John Smith"
],
"sumOfAges": 72
},
{
"pair": [
"John Smith",
"Katie Foster"
],
"sumOfAges": 72
}
]
最后看看删除语句:
REMOVE "4904" IN users
# 使用FOR循环删除掉所有大于等于30岁的用户, 至此users成为空,所有用户都被删除掉了
FOR user IN users
FILTER user.age >= 30
REMOVE user IN users
来自SQL背景
如果你是来自RDMBS的SQL背景,那么AQL主要的区别之一是循环,这使得AQL更像一种编程语言,这很适合schema-less的模型,使AQL更强大同时还容易阅读和编写。
AQL中的RETURN语句,会为每个将返回的document返回一项,可以返回整个document,也可以返回该document的一部分,以下例子中,我们假定 oneDocument 是一个document,不同的RETURN语句,返回结果不同:
RETURN oneDocument
[ { "_id": "myusers/3456789", "_key": "3456789", "_rev": "14253647", "firstName": "John", "lastName": "Doe", "address": { "city": "Gotham", "street": "Road To Nowhere 1" }, "hobbies": [ { "name": "swimming", "howFavorite": 10 }, { "name": "biking", "howFavorite": 6 }, { "name": "programming", "howFavorite": 4 } ]
}
]
# 只返回hobbies
RETURN oneDocument.hobbies
[ [ { "name": "swimming", "howFavorite": 10 }, { "name": "biking", "howFavorite": 6 }, { "name": "programming", "howFavorite": 4 } ]
]
# 只返回第一个hobby的名字
RETURN oneDocument.hobbies[0].name
["swimming"]
#将所有hobbies的名字作为字符串返回
RETURN { hobbies: oneDocument.hobbies[*].name }
[ { "hobbies": ["swimming","biking","programming"]
}
]