1. Overview
1.概述
In this tutorial, we’ll give an overview of what SirixDB is and its most important design goals.
在本教程中,我们将概述什么是SirixDB以及其最重要的设计目标。
Next, we’ll give a walk through a low level cursor-based transactional API.
接下来,我们将介绍一个低级别的基于游标的事务性API。
2. SirixDB Features
2.SirixDB的特点
SirixDB is a log-structured, temporal NoSQL document store, which stores evolutionary data. It never overwrites any data on-disk. Thus, we’re able to restore and query the full revision history of a resource in the database efficiently. SirixDB ensures, that a minimum of storage-overhead is created for each new revision.
SirixDB是一个具有日志结构的时态NoSQL文件存储,它存储的是进化数据。它从不覆盖磁盘上的任何数据。因此,我们能够有效地恢复和查询数据库中资源的全部修订历史。SirixDB确保为每一个新的修订创造最小的存储开销。
Currently, SirixDB offers two built-in native data models, namely a binary XML store as well as a JSON store.
目前,SirixDB提供两种内置的本地数据模型,即二进制XML存储以及JSON存储。
2.1. Design Goals
2.1.设计目标
Some of the most important core principles and design goals are:
一些最重要的核心原则和设计目标是。
- Concurrency – SirixDB contains very few locks and aims to be as suitable for multithreaded systems as possible
- Asynchronous REST API – operations can happen independently; each transaction is bound to a specific revision and only one read-write transaction on a resource is permitted concurrently to N read-only transactions
- Versioning/Revision history – SirixDB stores a revision history of every resource in the database while keeping storage-overhead to a minimum. Read and write performance is tunable. It depends on the versioning type, which we can specify for creating a resource
- Data integrity – SirixDB, like ZFS, stores full checksums of the pages in the parent pages. That means that almost all data corruption can be detected upon reading in the future, as the SirixDB developers aim to partition and replicate databases in the future
- Copy-on-write semantics – similarly to the file systems Btrfs and ZFS, SirixDB uses CoW semantics, meaning that SirixDB never overwrites data. Instead, database page fragments are copied and written to a new location
- Per revision and per record versioning – SirixDB does not only version on a per-page, but also on a per-record basis. Thus, whenever we change a potentially small fraction
of records in a data page, it does not have to copy the whole page and write it to a new location on a disk or flash drive. Instead, we can specify one of several versioning strategies known from backup systems or a sliding snapshot algorithm during the creation of a database resource. The versioning type we specify is used by SirixDB to version data pages - Guaranteed atomicity (without a WAL) – the system will never enter an inconsistent state (unless there is hardware failure), meaning that unexpected power-off won’t ever damage the system. This is accomplished without the overhead of a write-ahead-log (WAL)
- Log-structured and SSD friendly – SirixDB batches writes and syncs everything sequentially to a flash drive during commits. It never overwrites committed data
We first want to introduce the low-level API exemplified with JSON data before switching our focus to higher levels in future articles. For instance an XQuery-API for querying both XML and JSON databases or an asynchronous, temporal RESTful API. We can basically use the same low-level API with subtle differences to store, traverse and compare XML resources as well.
我们首先要介绍以JSON数据为例的低级API,然后在未来的文章中把重点转向更高级别的。例如,用于查询XML和JSON数据库的XQuery-API或异步的、时间性的RESTful API。我们基本上可以使用相同的低级别的API,但有细微的差别,来存储、遍历和比较XML资源。
In order to use SirixDB, we at least have to use Java 11.
为了使用SirixDB,我们至少要使用Java 11。。
3. Maven Dependency to Embed SirixDB
3.嵌入SirixDB的Maven依赖项
To follow the examples, we first have to include the sirix-core dependency, for instance, via Maven:
为了遵循这些例子,我们首先要包括sirix-core依赖项,例如,通过Maven。
<dependency>
<groupId>io.sirix</groupId>
<artifactId>sirix-core</artifactId>
<version>0.9.3</version>
</dependency>
Or via Gradle:
或者通过Gradle。
dependencies {
compile 'io.sirix:sirix-core:0.9.3'
}
4. Tree-Encoding in SirixDB
4.SirixDB中的树形编码
A node in SirixDB references other nodes by a firstChild/leftSibling/rightSibling/parentNodeKey/nodeKey encoding:
SirixDB中的一个节点通过firstChild/leftSibling/rightSibling/parentNodeKey/nodeKey编码引用其他节点。
The numbers in the figure are auto-generated unique, stable node IDs generated with a simple sequential number generator.
图中的数字是自动生成的唯一的、稳定的节点ID,由一个简单的顺序数字发生器生成。
Every node may have a first child, a left sibling, a right sibling, and a parent node. Furthermore, SirixDB is able to store the number of children, the number of descendants and hashes of each node.
每个节点都可能有一个第一个孩子,一个左手兄弟姐妹,一个右手兄弟姐妹,以及一个父节点。此外,SirixDB能够存储每个节点的子代数、子孙数和哈希值。
In the following sections, we’ll introduce the core low-level JSON API of SirixDB.
在下面的章节中,我们将介绍SirixDB的核心低级别的JSON API。
5. Create a Database With a Single Resource
5.用单一资源创建数据库
First, we want to show how to create a database with a single resource. The resource is going to be imported from a JSON file and stored persistently in the internal, binary format of SirixDB:
首先,我们要展示如何用一个单一的资源创建一个数据库。该资源将从一个JSON文件中导入,并以SirixDB的内部二进制格式持久性地存储。
var pathToJsonFile = Paths.get("jsonFile");
var databaseFile = Paths.get("database");
Databases.createJsonDatabase(new DatabaseConfiguration(databaseFile));
try (var database = Databases.openJsonDatabase(databaseFile)) {
database.createResource(ResourceConfiguration.newBuilder("resource").build());
try (var manager = database.openResourceManager("resource");
var wtx = manager.beginNodeTrx()) {
wtx.insertSubtreeAsFirstChild(JsonShredder.createFileReader(pathToJsonFile));
wtx.commit();
}
}
We first create a database. Then we open the database and create the first resource. Various options for creating a resource exist (see the official documentation).
我们首先创建一个数据库。然后我们打开数据库并创建第一个资源。存在各种创建资源的选项(参见官方文档)。
We then open a single read-write transaction on the resource to import the JSON file. The transaction provides a cursor for navigation through moveToX methods. Furthermore, the transaction provides methods to insert, delete or modify nodes. Note that the XML API even provides methods for moving nodes in a resource and copying nodes from other XML resources.
然后我们在资源上打开一个读写事务来导入JSON文件。该事务提供一个光标,用于通过moveToX方法进行导航。此外,该事务提供了插入、删除或修改节点的方法。请注意,XML API甚至提供了移动资源中的节点和从其他XML资源中复制节点的方法。
To properly close the opened read-write transaction, the resource manager and the database we use Java’s try-with-resources statement.
为了正确关闭已打开的读写事务、资源管理器和数据库,我们使用Java的try-with-resources语句。
We exemplified the creation of a database and resource on JSON data, but creating an XML database and resource is almost identical.
我们举例说明了在JSON数据上创建数据库和资源,但创建XML数据库和资源几乎是相同的。
In the next section, we’ll open a resource in a database and show navigational axes and methods.
在下一节,我们将在数据库中打开一个资源,并展示导航轴和方法。
6. Open a Resource in a Database and Navigate
6.在数据库中打开一个资源并进行导航
6.1. Preorder Navigation in a JSON Resource
6.1.JSON资源中的预排序导航
To navigate through the tree structure, we’re able to reuse the read-write transaction after committing. In the following code we’ll, however, open the resource again and begin a read-only transaction on the most recent revision:
为了浏览树状结构,我们能够在提交后重新使用读写事务。但在下面的代码中,我们将再次打开资源,并在最近的修订版上开始一个只读事务。
try (var database = Databases.openJsonDatabase(databaseFile);
var manager = database.openResourceManager("resource");
var rtx = manager.beginNodeReadOnlyTrx()) {
new DescendantAxis(rtx, IncludeSelf.YES).forEach((unused) -> {
switch (rtx.getKind()) {
case OBJECT:
case ARRAY:
LOG.info(rtx.getDescendantCount());
LOG.info(rtx.getChildCount());
LOG.info(rtx.getHash());
break;
case OBJECT_KEY:
LOG.info(rtx.getName());
break;
case STRING_VALUE:
case BOOLEAN_VALUE:
case NUMBER_VALUE:
case NULL_VALUE:
LOG.info(rtx.getValue());
break;
default:
}
});
}
We use the descendant axis to iterate over all nodes in preorder (depth-first). Hashes of nodes are built bottom-up for all nodes per default depending on the resource configuration.
我们使用子孙轴以预排序(深度优先)的方式遍历所有节点。根据资源配置,默认情况下,所有节点的哈希值是自下而上建立的。
Array nodes and Object nodes have no name and no value. We can use the same axis to iterate through XML resources, only the node types differ.
阵列节点和对象节点没有名字,也没有值。我们可以使用同样的轴来遍历XML资源,只是节点类型不同。
SirixDB offers a bunch of axes as for instance all XPath-axes to navigate through XML and JSON resources. Furthermore, it provides a LevelOrderAxis, a PostOrderAxis, a NestedAxis to chain axis and several ConcurrentAxis variants to fetch nodes concurrently and in parallel.
SirixDB提供了一堆轴,例如所有的XPath-axes来浏览XML和JSON资源。此外,它还提供了一个LevelOrderAxis、一个PostOrderAxis、一个NestedAxis链轴和几个ConcurrentAxis变体来并发和并行地获取节点。
In the next section, we’ll show how to use the VisitorDescendantAxis, which iterates in preorder, guided by return types of a node visitor.
在下一节中,我们将展示如何使用VisitorDescendantAxis,它在节点访问者的返回类型的引导下,以预置顺序进行迭代。
6.2. Visitor Descendant Axis
6.2.访客后裔轴
As it’s very common to define behavior based on the different node-types SirixDB uses the visitor pattern.
由于根据不同的节点类型来定义行为是非常常见的,SirixDB使用访问者模式。
We can specify a visitor as a builder argument for a special axis called VisitorDescendantAxis. For each type of node, there’s an equivalent visit-method. For instance, for object key nodes it is the method VisitResult visit(ImmutableObjectKeyNode node).
我们可以指定一个访问者作为一个特殊轴的构建器参数,称为VisitorDescendantAxis。对于每种类型的节点,都有一个等价的访问方法。例如,对于对象键节点,它的方法是VisitResult visit(ImmutableObjectKeyNode node) 。
Each method returns a value of type VisitResult. The only implementation of the VisitResult interface is the following enum:
每个方法都返回一个类型为VisitResult的值。VisitResult接口的唯一实现是下面这个枚举。
public enum VisitResultType implements VisitResult {
SKIPSIBLINGS,
SKIPSUBTREE,
CONTINUE,
TERMINATE
}
The VisitorDescendantAxis iterates through the tree structure in preorder. It uses the VisitResultTypes to guide the traversal:
VisitorDescendantAxis以预排序方式遍历树状结构。它使用VisitResultTypes来指导遍历。
- SKIPSIBLINGS means that the traversal should continue without visiting the right siblings of the current node the cursor points to
- SKIPSUBTREE means to continue without visiting the descendants of this node
- We use CONTINUE if the traversal should continue in preorder
- We can also use TERMINATE to terminate the traversal immediately
The default implementation of each method in the Visitor interface returns VisitResultType.CONTINUE for each node type. Thus, we only have to implement the methods for the nodes, which we’re interested in. If we’ve implemented a class which implements the Visitor interface called MyVisitor we can use the VisitorDescendantAxis in the following way:
Visitor接口中每个方法的默认实现都会为每个节点类型返回VisitResultType.CONTINUE。因此,我们只需要为我们感兴趣的节点实现这些方法。如果我们实现了一个实现了Visitor接口的类,叫做MyVisitor,我们就可以按以下方式使用VisitorDescendantAxis。
var axis = VisitorDescendantAxis.newBuilder(rtx)
.includeSelf()
.visitor(new MyVisitor())
.build();
while (axis.hasNext()) axis.next();
The methods in MyVisitor are called for each node in the traversal. The parameter rtx is a read-only transaction. The traversal begins with the node the cursor currently points to.
MyVisitor中的方法为遍历中的每个节点被调用。参数rtx是一个只读的事务。遍历从游标当前指向的节点开始。
6.3. Time Travel Axis
6.3.时间旅行轴
One of the most distinctive features of SirixDB is thorough versioning. Thus, SirixDB not only offers all kinds of axes to iterate through the tree structure within one revision. We’re also able to use one of the following axes to navigate in time:
SirixDB最独特的功能之一是彻底的版本管理。因此,SirixDB不仅提供各种轴来迭代一个修订版中的树状结构。我们还能够使用以下轴之一来进行时间导航。
- FirstAxis
- LastAxis
- PreviousAxis
- NextAxis
- AllTimeAxis
- FutureAxis
- PastAxis
The constructors take a resource manager as well as a transactional cursor as parameters. The cursor navigates to the same node in each revision.
构造函数接受一个资源管理器和一个事务性游标作为参数。 游标在每次修订中都会导航到相同的节点。
If another revision in the axis – as well as the node in the respective revision – exists, then the axis returns a new transaction. The return values are read-only transactions opened on the respective revisions, whereas the cursor points to the same node in the different revisions.
如果轴上的另一个修订版–以及各自修订版中的节点–存在,那么轴就会返回一个新的事务。返回值是在各自修订版上打开的只读事务,而光标则指向不同修订版中的同一节点。
We’ll show a simple example for the PastAxis:
我们将展示一个关于PastAxis的简单例子。
var axis = new PastAxis(resourceManager, rtx);
if (axis.hasNext()) {
var trx = axis.next();
// Do something with the transactional cursor.
}
6.4. Filtering
6.4.筛选
SirixDB provides several filters, which we’re able to use in conjunction with a FilterAxis. The following code, for instance, traverses all children of an object node and filters for object key nodes with the key “a” as in {“a”:1, “b”: “foo”}.
SirixDB提供了几个过滤器,我们能够与FilterAxis一起使用。例如,下面的代码遍历了一个对象节点的所有子节点,并过滤了键为 “a “的对象关键节点,如{“a”:1, “b”:”foo”}。
new FilterAxis<JsonNodeReadOnlyTrx>(new ChildAxis(rtx), new JsonNameFilter(rtx, "a"))
The FilterAxis optionally takes more than one filter as its argument. The filter either is a JsonNameFilter, to filter for names in object keys or one of the node type filters: ObjectFilter, ObjectRecordFilter, ArrayFilter, StringValueFilter, NumberValueFilter, BooleanValueFilter and NullValueFilter.
FilterAxis可以选择接受一个以上的过滤器作为其参数。该过滤器要么是JsonNameFilter,以过滤对象键中的名称,要么是节点类型过滤器之一。ObjectFilter, ObjectRecordFilter, ArrayFilter, StringValueFilter, NumberValueFilter, BooleanValueFilter 和NullValueFilter。
The axis can be used as follows for JSON resources to filter by object key names with the name “foobar”:
对于JSON资源,该轴可以如下使用,通过名称为 “foobar “的对象键名来过滤。
var axis = new VisitorDescendantAxis.Builder(rtx).includeSelf().visitor(myVisitor).build();
var filter = new JsonNameFilter(rtx, "foobar");
for (var filterAxis = new FilterAxis<JsonNodeReadOnlyTrx>(axis, filter); filterAxis.hasNext();) {
filterAxis.next();
}
Alternatively, we could simply stream over the axis (without using the FilterAxis at all) and then filter by a predicate.
另外,我们可以简单地在轴上流转(根本不使用FilterAxis),然后通过一个谓词进行过滤。
rtx is of type NodeReadOnlyTrx in the following example:
rtx在下面的例子中属于NodeReadOnlyTrx类型。
var axis = new PostOrderAxis(rtx);
var axisStream = StreamSupport.stream(axis.spliterator(), false);
axisStream.filter((unusedNodeKey) -> new JsonNameFilter(rtx, "a"))
.forEach((unused) -> /* Do something with the transactional cursor */);
7. Modify a Resource in a Database
7.修改数据库中的资源
Obviously, we want to be able to modify a resource. SirixDB stores a new compact snapshot during each commit.
很明显,我们希望能够修改一个资源。SirixDB在每次提交时都会存储一个新的紧凑快照。
After opening a resource we have to start the single read-write transaction as we’ve seen before.
在打开一个资源后,我们必须像我们之前看到的那样,开始单一的读写事务。
7.1. Simple Update Operations
7.1.简单的更新操作
Once we navigated to the node we want to modify, we’re able to update for instance the name or the value, depending on the node type:
一旦我们导航到我们想要修改的节点,我们就能够更新例如名称或值,这取决于节点的类型。
if (wtx.isObjectKey()) wtx.setObjectKeyName("foo");
if (wtx.isStringValue()) wtx.setStringValue("foo");
We can insert new object records via insertObjectRecordAsFirstChild and insertObjectRecordAsRightSibling. Similar methods exist for all node types. Object records are composed of two nodes: An object key node and an object value node.
我们可以通过insertObjectRecordAsFirstChild和insertObjectRecordAsRightSibling插入新对象记录。所有节点类型都有类似的方法。对象记录由两个节点组成。一个对象键节点和一个对象值节点。
SirixDB checks for consistency and as such it throws an unchecked SirixUsageException if a method call is not permitted on a specific node type.
SirixDB检查一致性,因此,如果在一个特定的节点类型上不允许调用方法,它会抛出一个未检查的SirixUsageException。
Object records, that is key/value pairs, for instance, can only be inserted as a first child if the cursor is located on an object node. We insert both an object key node as well as one of the other node types as the value with the insertObjectRecordAsX methods.
例如,对象记录,也就是键/值对,只有当光标位于一个对象节点上时,才能作为第一子节点插入。我们用insertObjectRecordAsX方法既插入一个对象键节点,也插入其他节点类型之一作为值。
We can also chain the update methods – for this example, wtx is located on an object node:
我们也可以将更新方法连锁化–对于这个例子,wtx位于一个对象节点上。
wtx.insertObjectRecordAsFirstChild("foo", new StringValue("bar"))
.moveToParent().trx()
.insertObjectRecordAsRightSibling("baz", new NullValue());
First, we insert an object key node with the name “foo” as the first child of an object node. Then, a StringValueNode is created as the first child of the newly created object record node.
首先,我们插入一个名称为 “foo “的对象键节点,作为一个对象节点的第一个子节点。然后,创建一个StringValueNode作为新创建的对象记录节点的第一个子节点。
The cursor is moved to the value node after the method call. Thus we first have to move the cursor to the object key node, the parent again. Then, we’re able to insert the next object key node and its child, a NullValueNode as a right sibling.
光标在方法调用后被移动到值节点上。因此,我们首先要把光标移到对象关键节点,即父节点。然后,我们能够插入下一个对象关键节点和它的子节点,一个NullValueNode作为右边的兄弟姐妹。
7.2. Bulk Insertions
7.2.批量插入
More sophisticated bulk insertion methods exist, too, as we’ve already seen when we imported JSON data. SirixDB provides a method to insert JSON data as a first child (insertSubtreeAsFirstChild) and as a right sibling (insertSubtreeAsRightSibling).
更复杂的批量插入方法也存在,正如我们在导入JSON数据时已经看到的。SirixDB提供了一种方法,可以将JSON数据作为第一个孩子(insertSubtreeAsFirstChild)和作为右边的兄弟姐妹(insertSubtreeAsRightSibling)插入。
To insert a new subtree based on a String we can use:
要在一个字符串的基础上插入一个新的子树,我们可以使用。
var json = "{\"foo\": \"bar\",\"baz\": [0, \"bla\", true, null]}";
wtx.insertSubtreeAsFirstChild(JsonShredder.createStringReader(json));
The JSON API currently doesn’t offer the possibility to copy subtrees. However, the XML API does. We’re able to copy a subtree from another XML resource in SirixDB:
JSON API目前不提供复制子树的可能性。但是,XML API提供。我们能够从SirixDB的另一个XML资源中复制一个子树。
wtx.copySubtreeAsRightSibling(rtx);
Here, the node the read-only transaction (rtx) currently points to is copied with its subtree as a new right sibling of the node that the read-write transaction (wtx) points to.
这里,只读事务(rtx)当前指向的节点与它的子树一起被复制为读写事务(wtx)指向的节点的一个新的右边兄弟姐妹。
SirixDB always applies changes in-memory and then flushes them to a disk or the flash drive during a transaction commit. The only exception is if the in-memory cache has to evict some entries into a temporary file due to memory constraints.
SirixDB总是在内存中应用变化,然后在事务提交期间将其刷入磁盘或闪存盘。唯一的例外是,由于内存的限制,内存缓存必须将一些条目驱逐到一个临时文件中。
We can either commit() or rollback() the transaction. Note that we can reuse the transaction after one of the two method calls.
我们可以选择commit()或者rollback()事务。注意,我们可以在这两个方法中的一个调用后重新使用该事务。
SirixDB also applies some optimizations under the hood when invoking bulk insertions.
在调用批量插入时,SirixDB还在引擎盖下应用了一些优化措施。
In the next section, we’ll see other possibilities on how to start a read-write transaction.
在下一节,我们将看到关于如何启动读写事务的其他可能性。
7.3. Start a Read-Write Transaction
7.3.开始一个读写事务
As we’ve seen we can begin a read-write transaction and create a new snapshot by calling the commit method. However, we can also start an auto-committing transactional cursor:
正如我们所看到的,我们可以通过调用commit方法开始一个读写事务并创建一个新的快照。然而,我们也可以启动一个自动提交的事务游标。
resourceManager.beginNodeTrx(TimeUnit.SECONDS, 30);
resourceManager.beginNodeTrx(1000);
resourceManager.beginNodeTrx(1000, TimeUnit.SECONDS, 30);
Either we auto-commit every 30 seconds, after every 1000th modification or every 30 seconds and every 1000th modification.
我们要么每隔30秒,在每1000次修改后自动提交,要么每隔30秒和每1000次修改自动提交。
We’re also able to start a read-write transaction and then revert to a former revision, which we can commit as a new revision:
我们也能够启动一个读写事务,然后恢复到以前的修订版,我们可以把它作为一个新的修订版提交。
resourceManager.beginNodeTrx().revertTo(2).commit();
All revisions in between are still available. Once we have committed more than one revision we can open a specific revision either by specifying the exact revision number or by a timestamp:
介于两者之间的所有修订版仍可使用。一旦我们提交了一个以上的修订版,我们可以通过指定确切的修订号或时间戳来打开一个特定的修订版。
var rtxOpenedByRevisionNumber = resourceManager.beginNodeReadOnlyTrx(2);
var dateTime = LocalDateTime.of(2019, Month.JUNE, 15, 13, 39);
var instant = dateTime.atZone(ZoneId.of("Europe/Berlin")).toInstant();
var rtxOpenedByTimestamp = resourceManager.beginNodeReadOnlyTrx(instant);
8. Compare Revisions
8.比较修订
To compute the differences between any two revisions of a resource, once stored in SirixDB, we can invoke a diff-algorithm:
为了计算一个资源的任何两个修订版之间的差异,一旦存储在SirixDB,我们可以调用一个差异算法。
DiffFactory.invokeJsonDiff(
new DiffFactory.Builder(
resourceManager,
2,
1,
DiffOptimized.HASHED,
ImmutableSet.of(observer)));
The first argument to the builder is the resource manager, which we already used several times. The next two parameters are the revisions to compare. The fourth parameter is an enum, which we use to determine if SirixDB should take hashes into account to speed up the diff-computation or not.
构建器的第一个参数是资源管理器,我们已经使用过几次了。接下来的两个参数是要比较的修订。第四个参数是一个枚举,我们用它来决定SirixDB是否应该考虑到哈希值来加快差异计算的速度。
If a node changes due to update operations in SirixDB, all ancestor nodes adapt their hash values, too. If the hashes and the node keys in the two revisions are identical, SirixDB skips the subtree during the traversal of the two revisions, because there are no changes in the subtree when we specify DiffOptimized.HASHED.
如果一个节点由于SirixDB的更新操作而改变,所有的祖先节点也会调整它们的哈希值。如果两个修订版中的哈希值和节点键是相同的,SirixDB在遍历两个修订版时就会跳过子树,因为当我们指定DiffOptimized.HASHED时,子树中没有变化。
An immutable set of observers is the last argument. An observer has to implement the following interface:
一个不可变的观察者集合是最后一个参数。一个观察者必须实现以下接口。
public interface DiffObserver {
void diffListener(DiffType diffType, long newNodeKey, long oldNodeKey, DiffDepth depth);
void diffDone();
}
The diffListener method as the first parameter specifies the type of diff encountered between two nodes in each revision. The next two arguments are the stable unique node identifiers of the compared nodes in the two revisions. The last argument depth specifies the depth of the two nodes, which SirixDB just compared.
作为第一个参数的diffListener 方法指定了每个修订版中两个节点之间遇到的差异类型。接下来的两个参数是两个修订版中被比较节点的稳定的唯一节点标识符。最后一个参数depth指定两个节点的深度,SirixDB刚刚比较了这两个节点。
9. Serialize to JSON
9.序列化为JSON
At some point in time we want to serialize a JSON resource in SirixDBs binary encoding back to JSON:
在某个时间点,我们想把SirixDBs二进制编码的JSON资源序列化为JSON。
var writer = new StringWriter();
var serializer = new JsonSerializer.Builder(resourceManager, writer).build();
serializer.call();
To serialize revision 1 and 2:
对修订版1和2进行序列化。
var serializer = new
JsonSerializer.Builder(resourceManager, writer, 1, 2).build();
serializer.call();
And all stored revisions:
以及所有存储的修订。
var serializer = new
JsonSerializer.Builder(resourceManager, writer, -1).build();
serializer.call();
10. Conclusion
10.结论
We’ve seen how to use the low-level transactional cursor API to manage JSON databases and resources in SirixDB. Higher level-APIs hide some of the complexity.
我们已经看到了如何使用低级的事务性游标API来管理SirixDB中的JSON数据库和资源。更高级别的API隐藏了一些复杂的东西。
The complete source code is available over on GitHub.
完整的源代码可在GitHub上获得。