查询策略
查询计划 用在两个不同的场景:
- 使用二进制协议的连接可以在客户端预加载记录。遍历关联记录时,不需要再远程调用服务器,因为这些记录已经存储在客户端的缓存中。
- 使用HTTP/JSON协议可以扩展结果JSON,把例如嵌入的关联记录一并返回。
查询计划的格式
对于两种场景,查询计划的语法相同。查询计划用字符串表示,可以查询和加载记录的时候使用。语法如下:
[[levels
]]fieldPath
:depthLevel
- Levels 可选,控制查询图的层次。从
0
开始,在2.1版本, 级别的语法如下:- Level 查询使用的特定级别,例如
[0]
将只查询第一层。 - Range 范围级别。例如
[0-2]
表示从第一层到第三层。也可以使用部分的语法,例如[-3]
表示第一层到第四层,[4-]
表示从第五层到无限 - Any 这个通配符表示在所有层次执行,例如
[*]
。
- Level 查询使用的特定级别,例如
- 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
, city
和orders
。如果在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
,所有的关联都不会被加载。