ArangoDB – 快速指南

ArangoDB – 快速指南


ArangoDB – 多模型优先数据库

ArangoDB 被开发者誉为原生的多模型数据库。这与其他 NoSQL 数据库不同。在这个数据库中,数据可以存储为文档、键/值对或图形。使用单一的声明式查询语言,您可以访问任何或所有数据。此外,可以在单个查询中组合不同的模型。而且,由于其多模型风格,人们可以制作精益应用程序,可以使用三种数据模型中的任何一种或全部进行水平扩展。

分层与原生多模型数据库

在本节中,我们将重点介绍原生和分层多模型数据库之间的重要区别。

许多数据库供应商称他们的产品为“多模型”,但向键/值或文档存储添加图形层并不符合本机多模型的要求。

使用 ArangoDB,具有相同查询语言的相同核心,可以在单个查询中将不同的数据模型和功能组合在一起,正如我们在前一节中所述。在 ArangoDB 中,没有数据模型之间的“切换”,也没有数据从 A 转移到 B 来执行查询。与“分层”方法相比,它为 ArangoDB 带来了性能优势。

对多模式数据库的需求

解释 [Fowler 的] 基本思想使我们意识到对持久层的不同部分使用各种适当的数据模型的好处,该层是更大的软件架构的一部分。

据此,例如,可以使用关系数据库来持久化结构化的表格数据;用于非结构化、类对象数据的文档存储;哈希表的键/值存储;以及用于高度链接的参考数据的图形数据库。

然而,这种方法的传统实现将导致在同一个项目中使用多个数据库。它可能会导致一些操作摩擦(更复杂的部署、更频繁的升级)以及数据一致性和重复问题。

统一三种数据模型的数据后的下一个挑战是设计和实现一种通用查询语言,允许数据管理员表达各种查询,例如文档查询、键/值查找、图形查询和任意组合这些。

通过图形查询,我们指的是涉及图形理论考虑的查询。特别是,这些可能涉及来自边缘的特定连接特征。例如,ShortestPath、GraphTraversalNeighbors

图非常适合作为关系的数据模型。在许多现实世界的案例中,例如社交网络、推荐系统等,一个非常自然的数据模型是图。它捕获关系并可以保存每个边和每个顶点的标签信息。此外,JSON 文档非常适合存储这种类型的顶点和边数据。

ArangoDB ─ 特点

ArangoDB 有各种显着的特性。我们将突出以下突出特点 –

  • 多模型范式
  • 酸特性
  • HTTP API

ArangoDB 支持所有流行的数据库模型。以下是 ArangoDB 支持的一些模型 –

  • 文档模型
  • 键/值模型
  • 图模型

单一查询语言足以从数据库中检索数据

四个属性原子性、一致性、隔离性持久性(ACID) 描述了数据库事务的保证。ArangoDB 支持符合 ACID 的事务。

ArangoDB 允许客户端(例如浏览器)使用 HTTP API 与数据库交互,该 API 是面向资源的,并且可以使用 JavaScript 进行扩展。

ArangoDB – 优势

以下是使用 ArangoDB 的优势 –

合并

作为原生的多模型数据库,ArangoDB 无需部署多个数据库,从而减少组件数量和维护。因此,它降低了应用程序的技术堆栈复杂性。除了整合您的整体技术需求之外,这种简化还可以降低总拥有成本并提高灵活性。

简化的性能扩展

随着应用程序的不断增长,ArangoDB 可以通过使用不同的数据模型独立扩展来满足不断增长的性能和存储需求。由于 ArangoDB 可以垂直和水平扩展,因此如果您的性能需要降低(故意的、期望的减速),您的后端系统可以轻松缩小以节省硬件和运营成本。

降低操作复杂性

Polyglot Persistence 的宗旨是为您从事的每项工作使用最好的工具。某些任务需要文档数据库,而其他任务可能需要图形数据库。由于使用单模型数据库,可能会导致多种操作挑战。集成单模型数据库本身就是一项艰巨的工作。但最大的挑战是在独立的、不相关的数据库系统之间构建具有数据一致性和容错性的大型内聚结构。这可能被证明几乎是不可能的。

多语言持久性可以使用本机多模型数据库处理,因为它允许轻松拥有多语言数据,但同时在容错系统上具有数据一致性。使用 ArangoDB,我们可以为复杂的工作使用正确的数据模型。

强大的数据一致性

如果使用多个单模型数据库,数据一致性就会成为一个问题。这些数据库不是为了相互通信而设计的,因此需要实现某种形式的事务功能来保持不同模型之间的数据一致。

ArangoDB 支持 ACID 事务,通过单一后端管理您的不同数据模型,提供单实例强一致性,以及在集群模式下运行时的原子操作。

容错

构建具有许多不相关组件的容错系统是一项挑战。使用集群时,这一挑战变得更加复杂。使用不同的技术和/或技术堆栈来部署和维护此类系统需要专业知识。此外,集成多个旨在独立运行的子系统会造成巨大的工程和运营成本。

作为一个整合的技术栈,多模型数据库提供了一个优雅的解决方案。ArangoDB 旨在支持具有不同数据模型的现代模块化架构,也适用于集群使用。

降低总拥有成本

每种数据库技术都需要持续维护、错误修复补丁和供应商提供的其他代码更改。采用多模型数据库只需在设计应用程序时消除数据库技术的数量,就可以显着降低相关的维护成本。

交易

在多台机器上提供事务保证是一个真正的挑战,很少有 NoSQL 数据库提供这些保证。作为原生多模型,ArangoDB 强加事务以保证数据一致性。

基本概念和术语

在本章中,我们将讨论 ArangoDB 的基本概念和术语。了解与我们正在处理的技术主题相关的基本术语非常重要。

下面列出了 ArangoDB 的术语 –

  • 文档
  • 收藏
  • 集合标识符
  • 馆藏名称
  • 数据库
  • 数据库名称
  • 数据库组织

从数据模型的角度来看,ArangoDB 可以被认为是一个面向文档的数据库,因为文档的概念是后者的数学思想。面向文档的数据库是 NoSQL 数据库的主要类别之一。

层次结构是这样的:文档被分组到集合中,而集合存在于数据库中

很明显,Identifier 和 Name 是集合和数据库的两个属性。

通常,存储在文档集合中的两个文档(顶点)由存储在边集合中的文档(边)链接。这是 ArangoDB 的图数据模型。它遵循有向标记图的数学概念,不同之处在于边不仅有标签,而且是完整的文档。

熟悉了该数据库的核心术语后,我们开始了解 ArangoDB 的图形数据模型。在这个模型中,存在两种类型的集合:文档集合和边缘集合。边缘集合存储文档,还包括两个特殊属性:第一个是_from属性,第二个是_to属性。这些属性用于在图形数据库必不可少的文档之间创建边(关系)。文档集合在图的上下文中也称为顶点集合(参见任何图论书籍)。

现在让我们看看数据库有多重要。它们很重要,因为集合存在于数据库中。在 ArangoDB 的一个实例中,可以有一个或多个数据库。不同的数据库通常用于多租户设置,因为其中的不同数据集(集合、文档等)彼此隔离。默认数据库_system是特殊的,因为它不能被删除。用户在此数据库中进行管理,其凭据对服务器实例的所有数据库均有效。

ArangoDB – 系统要求

在本章中,我们将讨论 ArangoDB 的系统要求。

ArangoDB 的系统要求如下 –

  • 安装了 Ubuntu 的 VPS 服务器
  • 内存:1 GB;中央处理器:2.2 GHz

对于本教程中的所有命令,我们使用了一个 Ubuntu 16.04 (xenial) 的实例,内存为 1GB,一个 CPU 的处理能力为 2.2 GHz。本教程中的所有 arangosh 命令都针对 ArangoDB 版本 3.1.27 进行了测试。

如何安装 ArangoDB?

在本节中,我们将看到如何安装 ArangoDB。ArangoDB 为许多操作系统和发行版预先构建。更多细节请参考 ArangoDB 文档。如前所述,对于本教程,我们将使用 Ubuntu 16.04×64。

第一步是下载其存储库的公钥 –

# wget https://www.arangodb.com/repositories/arangodb31/
xUbuntu_16.04/Release.key

输出

--2017-09-03 12:13:24-- https://www.arangodb.com/repositories/arangodb31/xUbuntu_16.04/Release.key 
Resolving https://www.arangodb.com/
(www.arangodb.com)... 104.25.1 64.21, 104.25.165.21,
2400:cb00:2048:1::6819:a415, ... 
Connecting to https://www.arangodb.com/
(www.arangodb.com)|104.25. 164.21|:443... connected. 
HTTP request sent, awaiting response... 200 OK 
Length: 3924 (3.8K) [application/pgpkeys]
Saving to: ‘Release.key’
Release.key 100%[===================>] 3.83K - .-KB/s in 0.001s
2017-09-03 12:13:25 (2.61 MB/s) - ‘Release.key’ saved [39 24/3924]

重要的一点是您应该看到在输出结束时保存Release.key

让我们使用以下代码行安装保存的密钥 –

# sudo apt-key add Release.key

输出

OK

运行以下命令添加 apt 存储库并更新索引 –

# sudo apt-add-repository 'deb
https://www.arangodb.com/repositories/arangodb31/xUbuntu_16.04/ /'
# sudo apt-get update

作为最后一步,我们可以安装 ArangoDB –

# sudo apt-get install arangodb3

输出

Reading package lists... Done
Building dependency tree
Reading state information... Done
The following package was automatically installed and is no longer required:
grub-pc-bin
Use 'sudo apt autoremove' to remove it.
The following NEW packages will be installed:
arangodb3
0 upgraded, 1 newly installed, 0 to remove and 17 not upgraded.
Need to get 55.6 MB of archives.
After this operation, 343 MB of additional disk space will be used.

Enter现在安装 ArangoDB 的过程将开始 –

Get:1 https://www.arangodb.com/repositories/arangodb31/xUbuntu_16.04
arangodb3 3.1.27 [55.6 MB]
Fetched 55.6 MB in 59s (942 kB/s)
Preconfiguring packages ...
Selecting previously unselected package arangodb3.
(Reading database ... 54209 files and directories currently installed.)
Preparing to unpack .../arangodb3_3.1.27_amd64.deb ...

Unpacking arangodb3 (3.1.27) ...
Processing triggers for systemd (229-4ubuntu19) ...
Processing triggers for ureadahead (0.100.0-19) ...
Processing triggers for man-db (2.7.5-1) ...
Setting up arangodb3 (3.1.27) ...
Database files are up-to-date.

当 ArangoDB 的安装即将完成时,会出现以下屏幕 –

ArangoDB 的安装

在这里,您将被要求为 ArangoDB用户提供密码仔细记下来。

出现以下对话框时选择选项 –

配置对话框

当您在上述对话框中单击是时,将出现以下对话框。在此处单击

配置对话框 2

您还可以使用以下命令检查 ArangoDB 的状态 –

# sudo systemctl status arangodb3

输出

arangodb3.service - LSB: arangodb
Loaded: loaded (/etc/init.d/arangodb3; bad; vendor pre set: enabled)
Active: active (running) since Mon 2017-09-04 05:42:35 UTC;
4min 46s ago
Docs: man:systemd-sysv-generator(8)
Process: 2642 ExecStart=/etc/init.d/arangodb3 start (code = exited,
status = 0/SUC
Tasks: 22
Memory: 158.6M
CPU: 3.117s
CGroup: /system.slice/arangodb3.service
├─2689 /usr/sbin/arangod --uid arangodb
--gid arangodb --pid-file /va
└─2690 /usr/sbin/arangod --uid arangodb
--gid arangodb --pid-file /va
Sep   04 05:42:33   ubuntu-512  systemd[1]:        Starting LSB:  arangodb...
Sep   04 05:42:33   ubuntu-512  arangodb3[2642]:  * Starting arango database server a
Sep   04 05:42:35   ubuntu-512  arangodb3[2642]:   {startup} starting up in daemon mode
Sep   04 05:42:35   ubuntu-512  arangodb3[2642]:   changed working directory for child
Sep   04 05:42:35   ubuntu-512  arangodb3[2642]:   ...done. 
Sep   04 05:42:35   ubuntu-512  systemd[1]:        StartedLSB: arang odb.
Sep   04 05:46:59   ubuntu-512  systemd[1]:        Started LSB: arangodb. lines 1-19/19 (END)

ArangoDB 现在可以使用了。

要调用 arangosh 终端,请在终端中键入以下命令 –

# arangosh

输出

Please specify a password:

提供安装时创建root密码 –

_
__ _ _ __ __ _ _ __ __ _ ___ | |
/ | '__/ _ | ’ \ / ` |/ _ / | ’
| (| | | | (| | | | | (| | () _ \ | | |
_,|| _,|| ||_, |_/|/| ||
|__/

arangosh (ArangoDB 3.1.27 [linux] 64bit, using VPack 0.1.30, ICU 54.1, V8
5.0.71.39, OpenSSL 1.0.2g 1 Mar 2016)

Copyright (c) ArangoDB GmbH

Pretty printing values.
Connected to ArangoDB 'http+tcp://127.0.0.1:8529' version: 3.1.27 [server],
database: '_system', username: 'root'

Please note that a new minor version '3.2.2' is available
Type 'tutorial' for a tutorial or 'help' to see common examples
127.0.0.1:8529@_system> exit

输出窗口根密码

要从 ArangoDB 注销,请键入以下命令 –

127.0.0.1:8529@_system> exit

输出

Uf wiederluege! Na shledanou! Auf Wiedersehen! Bye Bye! Adiau! ¡Hasta luego!
Εις το επανιδείν!

להתראות ! Arrivederci! Tot ziens! Adjö! Au revoir! さようなら До свидания! Até
Breve! !خداحافظ

ArangoDB – 命令行

在本章中,我们将讨论 Arangosh 如何作为 ArangoDB 的命令行工作。我们将从学习如何添加数据库用户开始。

注意– 请记住,数字小键盘可能不适用于 Arangosh。

让我们假设用户是“harry”,密码是“hpwdb”。

127.0.0.1:8529@_system> require("org/arangodb/users").save("harry", "hpwdb");

输出

{
   "user" : "harry",
   "active" : true,
   "extra" : {},
   "changePassword" : false,
   "code" : 201
}

ArangoDB – 网页界面

在本章中,我们将学习如何启用/禁用身份验证,以及如何将 ArangoDB 绑定到公共网络接口。

# arangosh --server.endpoint tcp://127.0.0.1:8529 --server.database "_system"

它会提示您输入之前保存的密码 –

Please specify a password:

在配置中使用您为 root 创建的密码。

您还可以使用 curl 来检查您是否确实获得了需要身份验证的请求的 HTTP 401(未经授权)服务器响应 –

# curl --dump - http://127.0.0.1:8529/_api/version

输出

HTTP/1.1 401 Unauthorized
X-Content-Type-Options: nosniff
Www-Authenticate: Bearer token_type = "JWT", realm = "ArangoDB"
Server: ArangoDB
Connection: Keep-Alive
Content-Type: text/plain; charset = utf-8
Content-Length: 0

为了避免在学习过程中每次都输入密码,我们将禁用身份验证。为此,打开配置文件 –

# vim /etc/arangodb3/arangod.conf

如果代码不可见,您应该更改配色方案。

:colorscheme desert

将身份验证设置为 false,如下面的屏幕截图所示。

输出窗口根密码

重新启动服务 –

# service arangodb3 restart

在使身份验证为 false 时,您将能够登录(在这种情况下使用 root 或创建的用户(如Harry))而无需输入任何密码,请指定密码

让我们在身份验证关闭时检查api版本 –

# curl --dump - http://127.0.0.1:8529/_api/version

输出

HTTP/1.1 200 OK
X-Content-Type-Options: nosniff
Server: ArangoDB
Connection: Keep-Alive
Content-Type: application/json; charset=utf-8
Content-Length: 60
{"server":"arango","version":"3.1.27","license":"community"}

ArangoDB – 示例案例场景

在本章中,我们将考虑两个示例场景。这些示例更容易理解,将帮助我们理解 ArangoDB 功能的工作方式。

为了演示 API,ArangoDB 预装了一组易于理解的图形。有两种方法可以在您的 ArangoDB 中创建这些图的实例 –

  • 在 Web 界面的创建图形窗口中添加示例选项卡,
  • 或者在Arangosh 中加载模块@arangodb/graph-examples/example-graph

首先,让我们在 Web 界面的帮助下加载一个图形。为此,启动 Web 界面并单击图形选项卡。

图形网页界面

创建图表对话框出现。向导包含两个选项卡 –示例图表图表标签是默认打开的; 假设我们要创建一个新图,它会询问该图的名称和其他定义。

图 创建图

现在,我们将上传已经创建的图表。为此,我们将选择“示例”选项卡。

上传创建的图表

我们可以看到三个示例图。选择Knows_Graph并单击绿色按钮 Create。

创建它们后,您可以在 Web 界面中检查它们 – 用于创建下面的图片。

图 创建图片

Knows_Graph

现在让我们看看Knows_Graph如何工作的。选择 Knows_Graph,它将获取图形数据。

该Knows_Graph由一个顶点集合的通过一个边缘收集连接知道它将包含五个人 Alice、Bob、Charlie、Dave 和 Eve 作为顶点。我们将有以下定向关系

Alice knows Bob
Bob knows Charlie
Bob knows Dave
Eve knows Alice
Eve knows Bob

Knows_Graph

如果单击节点(顶点),例如“bob”,它将显示 ID(人员/bob)属性名称。

Knows_Graph 顶点

单击任何边缘时,它将显示 ID (knows/4590) 属性。

单击任何边缘显示 ID

这就是我们创建它的方式,检查它的顶点和边。

让我们添加另一个图形,这次使用 Arangosh。为此,我们需要在 ArangoDB 配置文件中包含另一个端点。

如何添加多个端点

打开配置文件 –

# vim /etc/arangodb3/arangod.conf

添加另一个端点,如下面的终端屏幕截图所示。

端点终端截图

重新启动 ArangoDB –

# service arangodb3 restart

启动 Arangosh –

# arangosh
Please specify a password:
_
__ _ _ __ __ _ _ __ __ _ ___ ___| |__
/ _` | '__/ _` | '_ \ / _` |/ _ \/ __| '_ \
| (_| | | | (_| | | | | (_| | (_) \__ \ | | |
\__,_|_| \__,_|_| |_|\__, |\___/|___/_| |_|
|___/
arangosh (ArangoDB 3.1.27 [linux] 64bit, using VPack 0.1.30, ICU 54.1, V8
5.0.71.39, OpenSSL 1.0.2g 1 Mar 2016)
Copyright (c) ArangoDB GmbH
Pretty printing values.
Connected to ArangoDB 'http+tcp://127.0.0.1:8529' version: 3.1.27
[server], database: '_system', username: 'root'
Please note that a new minor version '3.2.2' is available
Type 'tutorial' for a tutorial or 'help' to see common examples
127.0.0.1:8529@_system>

Social_Graph

现在让我们了解 Social_Graph 是什么以及它是如何工作的。该图显示了一组人和他们的关系 –

此示例将女性和男性作为两个顶点集合(女性和男性)中的顶点。边是它们在关系边集合中的连接。我们已经描述了如何使用 Arangosh 创建此图。读者可以解决它并探索它的属性,就像我们对 Knows_Graph 所做的那样。

ArangoDB – 数据模型和建模

在本章中,我们将重点关注以下主题 –

  • 数据库交互
  • 数据模型
  • 数据检索

ArangoDB 支持基于文档的数据模型以及基于图的数据模型。让我们首先描述基于文档的数据模型。

ArangoDB 的文档非常类似于 JSON 格式。文档中包含零个或多个属性,每个属性都附加一个值。值要么是原子类型,例如数字、布尔值或空值、文字字符串,要么是复合数据类型,例如嵌入的文档/对象或数组。数组或子对象可能由这些数据类型组成,这意味着单个文档可以表示非平凡的数据结构。

进一步在层次结构中,文档被排列成集合,其中可能不包含文档(理论上)或多个文档。人们可以将文档与行进行比较,将集合与表进行比较(这里的表和行是指关系数据库管理系统 – RDBMS 的表和行)。

但是,在 RDBMS 中,定义列是将记录存储到表中并调用这些定义模式的先决条件。然而,作为一个新特性,ArangoDB 是无模式的——没有先验的理由来指定文档将具有哪些属性。

与 RDBMS 不同的是,每个文档都可以以与另一个文档完全不同的方式构建。这些文档可以一起保存在一个集合中。实际上,集合中的文档之间可能存在共同特征,但是数据库系统,即 ArangoDB 本身,不会将您绑定到特定的数据结构。

现在我们将尝试理解 ArangoDB 的[图数据模型],它需要两种集合——第一种是文档集合(在群论语言中称为顶点集合),第二种是边集合。这两种类型之间存在细微差别。Edge 集合也存储文档,但它们的特点是包含两个独特的属性,_from_to,用于在文档之间创建关系。实际上,一个文档(读取边)链接两个文档(读取顶点),它们都存储在各自的集合中。该架构源自带标签的有向图的图论概念,不包括不仅可以具有标签而且本身可以是类似 JSON 的完整文档的边。

为了计算新数据、删除文档或操作它们,使用查询,根据给定的标准选择或过滤文档。无论是简单的“示例查询”还是复杂的“连接”,查询都使用 AQL – ArangoDB 查询语言进行编码。

ArangoDB – 数据库方法

在本章中,我们将讨论 ArangoDB 中不同的数据库方法。

首先,让我们获取数据库的属性 –

  • 名称
  • ID
  • 小路

首先,我们调用 Arangosh。一旦调用了 Arangosh,我们将列出我们迄今为止创建的数据库 –

我们将使用以下代码行来调用 Arangosh –

127.0.0.1:8529@_system> db._databases()

输出

[
   "_system",
   "song_collection"
]

我们看到两个数据库,一个_SYSTEM默认创建的,而第二song_collection我们已经创建。

现在让我们使用以下代码行转移到 song_collection 数据库 –

127.0.0.1:8529@_system> db._useDatabase("song_collection")

输出

true
127.0.0.1:8529@song_collection>

我们将探索我们的 song_collection 数据库的属性。

查找名称

我们将使用以下代码行来查找名称。

127.0.0.1:8529@song_collection> db._name()

输出

song_collection

找到 id –

我们将使用以下代码行来查找 id。

song_collection

输出

4838

找到路径 –

我们将使用以下代码行来查找路径。

127.0.0.1:8529@song_collection> db._path()

输出

/var/lib/arangodb3/databases/database-4838

现在让我们使用以下代码行检查我们是否在系统数据库中 –

127.0.0.1:8529@song_collection&t db._isSystem()

输出

false

这意味着我们不在系统数据库中(因为我们已经创建并转移到了 song_collection)。以下屏幕截图将帮助您理解这一点。

创建移位歌曲输出屏幕截图

要获得特定收藏,请说歌曲 –

我们将使用以下代码行获取特定集合。

127.0.0.1:8529@song_collection> db._collection("songs")

输出

[ArangoCollection 4890, "songs" (type document, status loaded)]

这行代码返回一个集合。

让我们在后续章节中转到数据库操作的基本知识。

ArangoDB – Crud 操作

在本章中,我们将学习 Arangosh 的不同操作。

以下是 Arangosh 可能的操作 –

  • 创建文档集合
  • 创建文档
  • 阅读文件
  • 更新文件

让我们从创建一个新数据库开始。我们将使用以下代码行创建一个新数据库 –

127.0.0.1:8529@_system> db._createDatabase("song_collection")
true

以下代码行将帮助您转移到新数据库 –

127.0.0.1:8529@_system> db._useDatabase("song_collection")
true

提示将转移到“@@song_collection”

127.0.0.1:8529@song_collection>

Promt Shift 歌曲集

从这里我们将学习 CRUD 操作。让我们在新数据库中创建一个集合 –

127.0.0.1:8529@song_collection> db._createDocumentCollection('songs')

输出

[ArangoCollection 4890, "songs" (type document, status loaded)]
127.0.0.1:8529@song_collection>

让我们将一些文档(JSON 对象)添加到我们的“歌曲”集合中。

我们按以下方式添加第一个文档 –

127.0.0.1:8529@song_collection> db.songs.save({title: "A Man's Best Friend",
lyricist: "Johnny Mercer", composer: "Johnny Mercer", Year: 1950, _key:
"A_Man"})

输出

{
   "_id" : "songs/A_Man",
   "_key" : "A_Man",
   "_rev" : "_VjVClbW---"
}

让我们将其他文档添加到数据库中。这将有助于我们学习查询数据的过程。您可以复制这些代码并将其粘贴到 Arangosh 中以模拟该过程 –

127.0.0.1:8529@song_collection> db.songs.save(
   {
      title: "Accentchuate The Politics", 
      lyricist: "Johnny Mercer", 
      composer: "Harold Arlen", Year: 1944,
      _key: "Accentchuate_The"
   }
)

{
   "_id" : "songs/Accentchuate_The",
   "_key" : "Accentchuate_The",
   "_rev" : "_VjVDnzO---"
}

127.0.0.1:8529@song_collection> db.songs.save(
   {
      title: "Affable Balding Me", 
      lyricist: "Johnny Mercer", 
      composer: "Robert Emmett Dolan", 
      Year: 1950,
      _key: "Affable_Balding"
   }
)
{
   "_id" : "songs/Affable_Balding",
   "_key" : "Affable_Balding",
   "_rev" : "_VjVEFMm---"
}

如何阅读文件

_key或文件句柄可用于检索文档。如果不需要遍历集合本身,请使用文档句柄。如果你有一个集合,文档功能很容易使用 –

127.0.0.1:8529@song_collection> db.songs.document("A_Man");
{
   "_key" : "A_Man",
   "_id" : "songs/A_Man",
   "_rev" : "_VjVClbW---",
   "title" : "A Man's Best Friend",
   "lyricist" : "Johnny Mercer",
   "composer" : "Johnny Mercer",
   "Year" : 1950
}

如何更新文档

有两个选项可用于更新保存的数据 – replaceupdate

更新功能修补文档,将其与给定的属性合并。另一方面,替换功能将用新文档替换之前的文档。即使提供了完全不同的属性,替换仍然会发生。我们将首先观察非破坏性更新,更新歌曲中的属性 Production` –

127.0.0.1:8529@song_collection> db.songs.update("songs/A_Man",{production:
"Top Banana"});

输出

{
   "_id" : "songs/A_Man",
   "_key" : "A_Man",
   "_rev" : "_VjVOcqe---",
   "_oldRev" : "_VjVClbW---"
}

现在让我们阅读更新后的歌曲的属性 –

127.0.0.1:8529@song_collection> db.songs.document('A_Man');

输出

{
   "_key" : "A_Man",
   "_id" : "songs/A_Man",
   "_rev" : "_VjVOcqe---",
   "title" : "A Man's Best Friend",
   "lyricist" : "Johnny Mercer",
   "composer" : "Johnny Mercer",
   "Year" : 1950,
   "production" : "Top Banana"
}

使用更新功能可以轻松更新大型文档,尤其是在属性很少的情况下。

相反,替换功能将取消您在同一文档中使用它的数据。

127.0.0.1:8529@song_collection> db.songs.replace("songs/A_Man",{production:
"Top Banana"});

现在让我们使用以下代码行检查我们刚刚更新的歌曲 –

127.0.0.1:8529@song_collection> db.songs.document('A_Man');

输出

{
   "_key" : "A_Man",
   "_id" : "songs/A_Man",
   "_rev" : "_VjVRhOq---",
   "production" : "Top Banana"
}

现在,您可以观察到文档不再具有原始数据。

如何删除文档

remove 函数与文档句柄结合使用以从集合中删除文档 –

127.0.0.1:8529@song_collection> db.songs.remove('A_Man');

现在让我们使用以下代码行检查我们刚刚删除的歌曲的属性 –

127.0.0.1:8529@song_collection> db.songs.document('A_Man');

我们将收到如下异常错误作为输出 –

JavaScript exception in file
'/usr/share/arangodb3/js/client/modules/@arangodb/arangosh.js' at 97,7:
ArangoError 1202: document not found
! throw error;
! ^
stacktrace: ArangoError: document not found

at Object.exports.checkRequestResult
(/usr/share/arangodb3/js/client/modules/@arangodb/arangosh.js:95:21)

at ArangoCollection.document
(/usr/share/arangodb3/js/client/modules/@arangodb/arango-collection.js:667:12)
at <shell command>:1:10

异常错误输出画面

使用 Web 界面进行 Crud 操作

在上一章中,我们学习了如何使用命令行 Arangosh 对文档执行各种操作。我们现在将学习如何使用 Web 界面执行相同的操作。首先,在浏览器的地址栏中输入以下地址 – http://your_server_ip:8529/_db/song_collection/_admin/aardvark/index.html#login。您将被定向到以下登录页面。

登录页面

现在,输入用户名和密码。

登录 用户名 密码

如果成功,将出现以下屏幕。我们需要选择要使用的数据库,_system数据库是默认数据库。让我们选择song_collection数据库,然后单击绿色选项卡 –

歌曲集

创建集合

在本节中,我们将学习如何创建集合。按顶部导航栏中的“收藏”选项卡。

我们的命令行添加的歌曲集是可见的。单击该按钮将显示条目。我们现在将使用 Web 界面添加艺术家的收藏。我们用 Arangosh 创作的收藏歌曲已经在那里了。在 Name 字段中,在出现的New Collection对话框中编写艺术家可以安全地忽略高级选项,默认集合类型,即文档,就可以了。

创建集合

单击“保存”按钮将最终创建集合,现在这两个集合将在此页面上可见。

用文档填充新创建的集合

单击艺术家收藏时,您将看到一个空收藏 –

用文档填充集合

要添加文档,您需要单击右上角的 + 号。当系统提示您输入_key 时,输入Affable_Balding作为密钥。

现在,将出现一个表单来添加和编辑文档的属性。添加属性有两种方式:GraphicalTree图形方式直观但速度较慢,因此,我们将切换到代码视图,使用树下拉菜单选择它 –

树下拉菜单

为了简化流程,我们创建了一个 JSON 格式的示例数据,您可以将其复制并粘贴到查询编辑器区域 –

{“艺术家”:“约翰尼·默瑟”,“标题”:“和蔼可亲的秃顶的我”,“作曲家”:“罗伯特·埃米特·多兰”,“年份”:1950}

(注意:只应使用一对花括号;见下面的截图)

以 JSON 格式创建示例数据

您可以观察到我们在代码查看模式下引用了键和值。现在,点击保存成功完成后,页面上会立即出现绿色闪光。

如何阅读文件

要阅读文档,请返回“收藏”页面。

单击艺术家收藏时,会出现一个新条目。

如何更新文档

编辑文档中的条目很简单;您只需在文档概览中单击要编辑的行。此处将再次显示与创建新文档时相同的查询编辑器。

删除文档

您可以通过按“-”图标来删除文档。每个文档行的末尾都有这个标志。它会提示您确认以避免不安全的删除。

此外,对于特定集合,“集合概览”页面上还存在其他操作,例如过滤文档、管理索引和导入数据

在接下来的章节中,我们将讨论 Web 界面的一个重要特性,即 AQL 查询编辑器。

使用 AQL 查询数据

在本章中,我们将讨论如何使用 AQL 查询数据。我们在之前的章节中已经讨论过,ArangoDB 已经开发了自己的查询语言,并且它的名称是 AQL。

现在让我们开始与 AQL 交互。如下图所示,在 Web 界面中,按下位于导航栏顶部AQL 编辑器选项卡。将出现一个空白的查询编辑器。

需要时,您可以通过单击右上角的查询或结果选项卡从结果视图切换到编辑器,反之亦然,如下图所示 –

从结果视图切换到编辑器

除其他外,编辑器具有语法突出显示、撤消/重做功能和查询保存。详细参考可以看官方文档。我们将重点介绍 AQL 查询编辑器的一些基本和常用功能。

AQL基础

在 AQL 中,查询表示要实现的最终结果,而不是实现最终结果的过程。此功能通常称为语言的声明性属性。此外,AQL 可以查询也可以修改数据,因此可以通过结合这两个过程来创建复杂的查询。

请注意,AQL 完全符合 ACID。阅读或修改查询要么全部结束,要么根本不结束。即使读取文档的数据也会以一致的数据单元结束。

我们将两歌曲添加到我们已经创建的歌曲集中。您可以复制以下查询,而不是键入,并将其粘贴到 AQL 编辑器中 –

FOR song IN [
   {
      title: "Air-Minded Executive", lyricist: "Johnny Mercer",
      composer: "Bernie Hanighen", Year: 1940, _key: "Air-Minded"
   },
   
   {
      title: "All Mucked Up", lyricist: "Johnny Mercer", composer:
      "Andre Previn", Year: 1974, _key: "All_Mucked"
   }
]
INSERT song IN songs

按左下角的执行按钮。

它将在歌曲集中写入两个新文档

此查询描述了 FOR 循环在 AQL 中的工作方式;它遍历 JSON 编码的文档列表,对集合中的每个文档执行编码操作。不同的操作可以是创建新结构、过滤、选择文档、修改或将文档插入数据库(请参阅即时示例)。本质上,AQL 可以高效地执行 CRUD 操作。

为了找到我们数据库中的所有歌曲,让我们再次运行以下查询,相当于一个SQL类型数据库SELECT * FROM歌曲(因为编辑器记住了上次查询,按*New*按钮清理编辑器) –

FOR song IN songs
RETURN song

结果集将显示到目前为止保存在歌曲集中歌曲列表,如下面的屏幕截图所示。

歌曲列表

可以将FILTER、SORTLIMIT等操作添加到For 循环体以缩小和排序结果。

FOR song IN songs
FILTER song.Year > 1940
RETURN song

上述查询将在“结果”选项卡中提供 1940 年之后创建的歌曲(见下图)。

查询Year_1940后创作的歌曲

本示例中使用了文档键,但任何其他属性也可以用作过滤的等效项。由于保证文档键是唯一的,因此不会有多个文档与此过滤器匹配。对于其他属性,情况可能并非如此。要返回按名称升序排序的活动用户子集(由名为 status 的属性确定),我们使用以下语法 –

FOR song IN songs
FILTER song.Year > 1940
SORT song.composer
RETURN song
LIMIT 2

我们特意包含了这个例子。在这里,我们观察到 AQL 以红色突出显示的查询语法错误消息。此语法突出显示错误,有助于调试您的查询,如下面的屏幕截图所示。

语法高亮错误

现在让我们运行正确的查询(注意更正) –

FOR song IN songs
FILTER song.Year > 1940
SORT song.composer
LIMIT 2
RETURN song

运行正确的查询

AQL 中的复杂查询

AQL 为所有支持的数据类型配备了多种功能。查询中的变量分配允许构建非常复杂的嵌套结构。通过这种方式,数据密集型操作更接近后端的数据,而不是客户端(例如浏览器)。为了理解这一点,让我们首先将任意持续时间(长度)添加到歌曲中。

让我们从第一个函数开始,即更新函数 –

UPDATE { _key: "All_Mucked" }
WITH { length: 180 }
IN songs

AQL 中的复杂查询

我们可以看到已经编写了一个文档,如上面的屏幕截图所示。

现在让我们也更新其他文件(歌曲)。

UPDATE { _key: "Affable_Balding" }
WITH { length: 200 }
IN songs

我们现在可以检查我们所有的歌曲是否都有一个新的属性长度

FOR song IN songs
RETURN song

输出

[
   {
      "_key": "Air-Minded",
      "_id": "songs/Air-Minded",
      "_rev": "_VkC5lbS---",
      "title": "Air-Minded Executive",
      "lyricist": "Johnny Mercer",
      "composer": "Bernie Hanighen",
      "Year": 1940,
      "length": 210
   },
   
   {
      "_key": "Affable_Balding",
      "_id": "songs/Affable_Balding",
      "_rev": "_VkC4eM2---",
      "title": "Affable Balding Me",
      "lyricist": "Johnny Mercer",
      "composer": "Robert Emmett Dolan",
      "Year": 1950,
      "length": 200
   },
   
   {
      "_key": "All_Mucked",
      "_id": "songs/All_Mucked",
      "_rev": "_Vjah9Pu---",
      "title": "All Mucked Up",
      "lyricist": "Johnny Mercer",
      "composer": "Andre Previn",
      "Year": 1974,
      "length": 180
   },
   
   {
      "_key": "Accentchuate_The",
      "_id": "songs/Accentchuate_The",
      "_rev": "_VkC3WzW---",
      "title": "Accentchuate The Politics",
      "lyricist": "Johnny Mercer",
      "composer": "Harold Arlen",
      "Year": 1944,
      "length": 190
   }
]

为了说明 AQL 的其他关键字(如 LET、FILTER、SORT 等)的使用,我们现在将歌曲的持续时间格式化mm:ss格式。

询问

FOR song IN songs
FILTER song.length > 150
LET seconds = song.length % 60
LET minutes = FLOOR(song.length / 60)
SORT song.composer
RETURN
{
   Title: song.title, 
   Composer: song.composer, 
   Duration: CONCAT_SEPARATOR(':',minutes, seconds) 
}

AQL 2 中的复杂查询

这次我们将返回歌曲名称和时长。返回功能,您可以创建一个新的JSON对象返回为每个输入文件。

我们现在将讨论 AQL 数据库的“连接”功能。

让我们从创建一个集合composer_dob开始此外,我们将通过在查询框中运行以下查询来创建具有假设的作曲家出生日期的四个文档 –

FOR dob IN [
   {composer: "Bernie Hanighen", Year: 1909}
   ,
   {composer: "Robert Emmett Dolan", Year: 1922}
   ,
   {composer: "Andre Previn", Year: 1943}
   ,
   {composer: "Harold Arlen", Year: 1910}
]
INSERT dob in composer_dob

作曲家 DOB

为了突出与 SQL 的相似性,我们在 AQL 中提出了一个嵌套的 FOR 循环查询,导致 REPLACE 操作,首先在内循环中迭代所有作曲家的 dob,然后在所有相关歌曲上,创建一个包含属性song_with_composer_key而不是歌曲属性。

这是查询 –

FOR s IN songs
FOR c IN composer_dob
FILTER s.composer == c.composer

LET song_with_composer_key = MERGE(
   UNSET(s, 'composer'),
   {composer_key:c._key}
)
REPLACE s with song_with_composer_key IN songs

带有作曲键的歌曲

现在让我们再次运行查询FOR song IN Song RETURN song以查看歌曲集是如何变化的。

输出

[
   {
      "_key": "Air-Minded",
      "_id": "songs/Air-Minded",
      "_rev": "_Vk8kFoK---",
      "Year": 1940,
      "composer_key": "5501",
      "length": 210,
      "lyricist": "Johnny Mercer",
      "title": "Air-Minded Executive"
   },
   
   {
      "_key": "Affable_Balding",
      "_id": "songs/Affable_Balding",
      "_rev": "_Vk8kFoK--_",
      "Year": 1950,
      "composer_key": "5505",
      "length": 200,
      "lyricist": "Johnny Mercer",
      "title": "Affable Balding Me"
   },
   
   {
      "_key": "All_Mucked",
      "_id": "songs/All_Mucked",
      "_rev": "_Vk8kFoK--A",
      "Year": 1974,
      "composer_key": "5507",
      "length": 180,
      "lyricist": "Johnny Mercer",
      "title": "All Mucked Up"
   },
   
   {
      "_key": "Accentchuate_The",
      "_id": "songs/Accentchuate_The",
      "_rev": "_Vk8kFoK--B",
      "Year": 1944,
      "composer_key": "5509",
      "length": 190,
      "lyricist": "Johnny Mercer",
      "title": "Accentchuate The Politics"
   }
]

以上查询完成了数据迁移过程,为每首歌曲添加了composer_key

现在下一个查询再次是一个嵌套的 FOR 循环查询,但这一次导致了 Join 操作,将相关作曲家的名字(在 `composer_key` 的帮助下挑选)添加到每首歌曲 –

FOR s IN songs
FOR c IN composer_dob
FILTER c._key == s.composer_key
RETURN MERGE(s,
{ composer: c.composer }
)

输出

[
   {
      "Year": 1940,
      "_id": "songs/Air-Minded",
      "_key": "Air-Minded",
      "_rev": "_Vk8kFoK---",
      "composer_key": "5501",
      "length": 210,
      "lyricist": "Johnny Mercer",
      "title": "Air-Minded Executive",
      "composer": "Bernie Hanighen"
   },
   
   {
      "Year": 1950,
      "_id": "songs/Affable_Balding",
      "_key": "Affable_Balding",
      "_rev": "_Vk8kFoK--_",
      "composer_key": "5505",
      "length": 200,
      "lyricist": "Johnny Mercer",
      "title": "Affable Balding Me",
      "composer": "Robert Emmett Dolan"
   },

   {
      "Year": 1974,
      "_id": "songs/All_Mucked",
      "_key": "All_Mucked",
      "_rev": "_Vk8kFoK--A",
      "composer_key": "5507",
      "length": 180,
      "lyricist": "Johnny Mercer",
      "title": "All Mucked Up",
      "composer": "Andre Previn"
   },

   {
      "Year": 1944,
      "_id": "songs/Accentchuate_The",
      "_key": "Accentchuate_The",
      "_rev": "_Vk8kFoK--B",
      "composer_key": "5509",
      "length": 190,
      "lyricist": "Johnny Mercer",
      "title": "Accentchuate The Politics",
      "composer": "Harold Arlen"
   }
]

为每首歌曲添加作曲键

ArangoDB – AQL 示例查询

在本章中,我们将考虑演员和电影数据库上的一些 AQL 示例查询这些查询基于图形。

问题

给定一组演员和一组电影,以及一个 actIn 边缘集合(具有年份属性)以连接顶点,如下所示 –

[演员] <- 出演-> [电影]

我们如何获得 –

  • 所有在“电影1”或“电影2”中演出的演员?
  • 所有在“电影1”和“电影2”中都出演过的演员?
  • “actor1”和“actor2”之间的所有常见电影?
  • 所有出演过 3 部或更多电影的演员?
  • 全部由 6 位演员出演的电影?
  • 电影的演员人数?
  • 演员的电影数量?
  • 2005 年到 2010 年由演员出演的电影数量是多少?

解决方案

在解决和获得上述查询的答案的过程中,我们将使用 Arangosh 创建数据集并对其运行查询。所有 AQL 查询都是字符串,可以简单地复制到您最喜欢的驱动程序而不是 Arangosh。

让我们从在 Arangosh 中创建一个测试数据集开始。首先,下载此文件

# wget -O dataset.js
https://drive.google.com/file/d/0B4WLtBDZu_QWMWZYZ3pYMEdqajA/view?usp=sharing

输出

...
HTTP request sent, awaiting response... 200 OK
Length: unspecified [text/html]
Saving to: ‘dataset.js’
dataset.js [ <=> ] 115.14K --.-KB/s in 0.01s
2017-09-17 14:19:12 (11.1 MB/s) - ‘dataset.js’ saved [117907]

您可以在上面的输出中看到我们已经下载了一个 JavaScript 文件dataset.js。该文件包含用于在数据库中创建数据集的 Arangosh 命令。我们将使用Arangosh–javascript.execute选项以非交互方式执行多个命令,而不是一个一个地复制和粘贴命令将其视为救生命令!

现在在 shell 上执行以下命令 –

$ arangosh --javascript.execute dataset.js

外壳命令

如上图所示,在提示时提供密码。现在我们已经保存了数据,因此我们将构建 AQL 查询来回答本章开头提出的具体问题。

第一个问题

让我们来回答第一个问题:所有在“电影1”或“电影2”中演出的演员假设,我们想要找到在“TheMatrix”或“TheDevilsAdvocate”中扮演的所有演员的名字 –

我们将一次从一部电影开始以获取演员的名字 –

127.0.0.1:8529@_system> db._query("FOR x IN ANY 'movies/TheMatrix' actsIn
OPTIONS {bfs: true, uniqueVertices: 'global'} RETURN x._id").toArray();

输出

我们将收到以下输出 –

[
   "actors/Hugo",
   "actors/Emil",
   "actors/Carrie",
   "actors/Keanu",
   "actors/Laurence"
]

第一个问题

现在我们继续形成两个 NEIGHBORS 查询的 UNION_DISTINCT,这将是解决方案 –

127.0.0.1:8529@_system> db._query("FOR x IN UNION_DISTINCT ((FOR y IN ANY
'movies/TheMatrix' actsIn OPTIONS {bfs: true, uniqueVertices: 'global'} RETURN
y._id), (FOR y IN ANY 'movies/TheDevilsAdvocate' actsIn OPTIONS {bfs: true,
uniqueVertices: 'global'} RETURN y._id)) RETURN x").toArray();

输出

[
   "actors/Charlize",
   "actors/Al",
   "actors/Laurence",
   "actors/Keanu",
   "actors/Carrie",
   "actors/Emil",
   "actors/Hugo"
]

第一个问题2

第二个问题

现在让我们考虑第二个问题:所有在 “movie1” 和 “movie2” 中演出的演员这几乎与上面的问题相同。但这一次我们对 UNION 不感兴趣,而是对 INTERSECTION 感兴趣 –

127.0.0.1:8529@_system> db._query("FOR x IN INTERSECTION ((FOR y IN ANY
'movies/TheMatrix' actsIn OPTIONS {bfs: true, uniqueVertices: 'global'} RETURN
y._id), (FOR y IN ANY 'movies/TheDevilsAdvocate' actsIn OPTIONS {bfs: true,
uniqueVertices: 'global'} RETURN y._id)) RETURN x").toArray();

输出

我们将收到以下输出 –

[
   "actors/Keanu"
]

第二个问题

第三个问题

现在让我们考虑第三个问题:“actor1”和“actor2”之间的所有常见电影这其实和关于movie1和movie2中共同演员的问题是一样的。我们只需要改变起始顶点。例如,让我们找出雨果·维文(“雨果”)和基努·里维斯共同主演的所有电影 –

127.0.0.1:8529@_system> db._query(
   "FOR x IN INTERSECTION (
      (
         FOR y IN ANY 'actors/Hugo' actsIn OPTIONS 
         {bfs: true, uniqueVertices: 'global'}
          RETURN y._id
      ),
      
      (
         FOR y IN ANY 'actors/Keanu' actsIn OPTIONS 
         {bfs: true, uniqueVertices:'global'} RETURN y._id
      )
   ) 
   RETURN x").toArray();

输出

我们将收到以下输出 –

[
   "movies/TheMatrixReloaded",
   "movies/TheMatrixRevolutions",
   "movies/TheMatrix"
]

第三个问题

第四题

现在让我们考虑第四个问题。所有出演过 3 部或更多电影的演员这个问题不同;我们不能在这里使用邻居功能。相反,我们将使用 AQL 的边缘索引和 COLLECT 语句进行分组。基本思想是按startVertex对所有边进行分组(在此数据集中,它始终是参与者)。然后我们从结果中删除所有少于 3 部电影的演员,因为这里我们包括了演员出演的电影数量 –

127.0.0.1:8529@_system> db._query("FOR x IN actsIn COLLECT actor = x._from WITH
COUNT INTO counter FILTER counter >= 3 RETURN {actor: actor, movies:
counter}"). toArray()

输出

[
   {
      "actor" : "actors/Carrie",
      "movies" : 3
   },
   
   {
      "actor" : "actors/CubaG",
      "movies" : 4
   },

   {
      "actor" : "actors/Hugo",
      "movies" : 3
   },

   {
      "actor" : "actors/Keanu",
      "movies" : 4
   },

   {
      "actor" : "actors/Laurence",
      "movies" : 3
   },

   {
      "actor" : "actors/MegR",
      "movies" : 5
   },

   {
      "actor" : "actors/TomC",
      "movies" : 3
   },
   
   {
      "actor" : "actors/TomH",
      "movies" : 3
   }
]

第四题

对于剩下的问题,我们将讨论查询形式,并仅提供查询。读者应该自己在 Arangosh 终端上运行查询。

第五题

现在让我们考虑第五个问题:恰好 6 位演员出演的所有电影与之前查询中的想法相同,但使用相等过滤器。但是,现在我们需要电影而不是演员,所以我们返回_to 属性

db._query("FOR x IN actsIn COLLECT movie = x._to WITH COUNT INTO counter FILTER
counter == 6 RETURN movie").toArray()

电影的演员人数?

我们记得在我们的数据集中_to边缘对应于电影,因此我们计算相同_to出现的频率这是演员的数量。该查询与之前的查询几乎相同,但在 COLLECT 之后没有 FILTER

db._query("FOR x IN actsIn COLLECT movie = x._to WITH COUNT INTO counter RETURN
{movie: movie, actors: counter}").toArray()

第六题

现在让我们考虑第六个问题:演员的电影数量

我们为上述查询找到解决方案的方式也将帮助您找到此查询的解决方案。

db._query("FOR x IN actsIn COLLECT actor = x._from WITH COUNT INTO counter
RETURN {actor: actor, movies: counter}").toArray()

ArangoDB – 如何部署

在本章中,我们将描述部署 ArangoDB 的各种可能性。

部署:单实例

我们已经在前面的一章中学习了如何部署 Linux (Ubuntu) 的单个实例。现在让我们看看如何使用 Docker 进行部署。

部署:Docker

对于使用 docker 的部署,我们将在我们的机器上安装 Docker。有关 Docker 的更多信息,请参阅我们的Docker教程

安装 Docker 后,您可以使用以下命令 –

docker run -e ARANGO_RANDOM_ROOT_PASSWORD = 1 -d --name agdb-foo -d
arangodb/arangodb

它将创建并启动 ArangoDB 的 Docker 实例,其标识名称为agdbfoo作为 Docker 后台进程。

终端也会打印进程标识符。

默认情况下,端口 8529 是为 ArangoDB 保留来侦听请求的。此外,此端口可自动用于您可能已链接的所有 Docker 应用程序容器。

觉得文章有用?

点个广告表达一下你的爱意吧 !😁