查询策略

查询计划 用在两个不同的场景:

  1. 使用二进制协议的连接可以在客户端预加载记录。遍历关联记录时,不需要再远程调用服务器,因为这些记录已经存储在客户端的缓存中。
  2. 使用HTTP/JSON协议可以扩展结果JSON,把例如嵌入的关联记录一并返回。

查询计划的格式

对于两种场景,查询计划的语法相同。查询计划用字符串表示,可以查询和加载记录的时候使用。语法如下:

[[levels]]fieldPath:depthLevel
  • Levels 可选,控制查询图的层次。从0开始,在2.1版本, 级别的语法如下:
    • Level 查询使用的特定级别,例如[0]将只查询第一层。
    • Range 范围级别。例如[0-2]表示从第一层到第三层。也可以使用部分的语法,例如[-3]表示第一层到第四层,[4-]表示从第五层到无限
    • Any 这个通配符表示在所有层次执行,例如[*]
  • Field Path 表示字段名称的路径,以.分割。路径从根记录开始,或者使用*表示所有字段。你可以将通配符放在路径的最后,表示从那个字段名开始的所有路径。
  • Depth Level 加载的深度。语法如下:
    • 0 只加载当前记录Indicates to load the current record.
    • 1-N 加载当前记录到nth记录。
    • -1 无限制级别。
    • -2 排除的级别,-2表示排除当前字段。

多条规则用空格分开。

参考一下查询计划的例子:

查询计划 描述
*:-1 遍历整棵树。
*:-1 orders:0 遍历orders类下的所有记录,只加载自身的内容记录,不加载其他类的记录。
*:0 address.city.country:0 获取根类的非文档字段和 address.city.country字段。
[*]in_*:-2 out_*:-2 查询所有属性,除了边。

预加载记录

默认,Orientdb加载关联记录使用懒加载方式。也就是说,直到遍历关联的字段时,才会加载。在有些情况下,例如查询一条记录的整棵树,非常费时。例如,

Invoice
 3:100
   |
   | customer
   +---------> Customer
   |            5:233
   | address            city            country
   +---------> Address---------> City ---------> Country
   |            10:1             11:2             12:3
   |
   | orders
   +--------->* [OrderItem OrderItem OrderItem]
                [  8:12      8:19      8:23   ]

对于类Invoice, 连接customer, cityorders。如果在Invoice执行SELECT查询,不会关联加载关联类。也就是说,你还需要7次网络调用才能获取所有值。

避免这种情况的性能消耗,OrientDB支持查询策略,也就查询计划,允许你自定义如何加载关联记录。查询计划的目的是一次调用,预加载关联的记录。查询计划最好用在通过远程连接,使用JSON序列化产生内置记录的场景。

注意 OrientDB会在查询关联记录的时候处理循环依赖,避免死循环。

远程连接

默认配置,当客户端从远程的数据库执行一个查询或加载单条记录时,它继续会对查询中的每个关联记录发送网络请求(通过OLazyRecordList)。你可以通过查询计划来避免这种情况。

当客户端执行查询,设置查询计划为非0的级别时。服务器会遍历返回的结果集的所有记录,然后一次性返回。OrientDB加载所有关联记录到本地客户端,意味着集合还是懒加载,但是当访问内容的时候,是从本地缓存还不是远程连接获取。

使用Java APIs的例子

执行一个定制查询计划的查询

List<ODocument> resultset = database.query(new OSQLSynchQuery<ODocument>("select * from Profile").setFetchPlan("*:-1"));

导出JSON格式的文档和它的内置文档

导出一个单据和它的客户:

invoice.toJSON("fetchPlan:customer:1");

导出一个单据和它的客户、订单:

invoice.toJSON("fetchPlan:customer:1 orders:2");

导出一个单据和它的所有关联记录到第3级:

invoice.toJSON("fetchPlan:*:3");

SQL的用法:

SELECT @this.toJSON('fetchPlan:out_Friend:4') FROM #10:20

通过使用通配符,只导出出边,过滤入边 (从2.0版本开始):

SELECT @this.toJSON('fetchPlan:in_*:-2') FROM #10:20

注意::

  • 为了避免循环,已经遍历的记录只会导出RIDs (RecordID)格式。
  • "fetchPlan"的设置是大小写敏感的。

自定义查询计划遍历对象

for (Account a : database.browseClass(Account.class).setFetchPlan("*:0 addresses:-1")) {
  System.out.println( a.getName() );
}

注意: 查询到对象意味着它出现在你的领域实体里。如果使用查询计划*:0,所有的关联都不会被加载。