目前,我们在OrientDB 2.2版中使用了Document API。让我们假设我们有一个班级公司和一个班级员工。假设我们对所有公司感兴趣,至少有一名员工的名字来自任意列表。员工在我们的公司架构中定义为LINKEDLIST。 我们的查询看起来像这样:
select from Company where employees in (select from Employee where name in ["John", "Paul"])
目前我们已经定义了以下两个索引:
Company.employees(员工链接上的索引(他们的@rid)) - >字典哈希索引和 Employee.name - > notunique index
当使用explain执行上述查询时,我们看到只使用了第二个索引Employee.name,因为我们没有将上面的索引定义为复合索引。据我所知,东方2.x不支持不同类别的复合索引,如我们的情况。 像这样的查询:
select from Company let $e = select from employees where name in ["John", "Paul"] where employees in $e
也不解决我们的问题。
搜索不同的博客显示到目前为止有两条建议:
https://github.com/orientechnologies/orientdb/issues/5069
https://github.com/orientechnologies/orientdb/issues/6684
String cmd =" begin \ n&#34 ;; cmd + ="让a =从员工中选择名称" +查询+" \ n&#34 ;; cmd + ="让b =从员工中选择$ a \ n&#34 ;; cmd + =" COMMIT \ n&#34 ;; cmd + ="返回$ b&#34 ;;
建议1对我们不起作用。
建议2.工作。两个索引都已在每个单独的查询中使用,但后来我们遇到了Orient的下一个限制。批处理脚本似乎只执行同步,这意味着我们只能一次性将结果作为列表获取,而不是以懒惰的方式逐个获取,在我们的例子中,由于内存开销。
我们尝试过的一个天真的解决方法如下:
public class OCommandAsyncScript extends OCommandScript implements OCommandRequestAsynch{
public OCommandAsyncScript(String sql, String cmd) {
super(sql, cmd);
}
@Override
public boolean isAsynchronous() {
return true;
}
private void containsAtLeastOne(final @Nonnull ODatabaseDocumentTx documentTx,
final @Nonnull Consumer<Company> matchConsumer,
final @Nonnull String queryText
) throws TimeoutException {
String cmd = "begin\n";
cmd += "let a = select from Employee where name " + queryText + "\n";
cmd += "let b = select from Company where employees in $a\n";
cmd += "COMMIT\n";
cmd += "return $b";
final OCommandHandler resultListener = new OCommandHandler(documentTx, (document -> {
final Company companies = document2model(document);
matchConsumer.accept(company);
}));
OCommandAsyncScript request = new OCommandAsyncScript("sql", cmd);
request.setResultListener(resultListener);
documentTx.command(request).execute();
...
}
}
public class OCommandHandler implements OCommandResultListener {
private final ODatabaseDocumentTx database;
private final Consumer<ODocument> matchConsumer;
public OCommandHandler(
final @Nonnull ODatabaseDocumentTx database,
final @Nonnull Consumer<ODocument> matchConsumer
) {
this.database = database;
this.matchConsumer = matchConsumer;
}
@Override
public boolean result(Object iRecord) {
if (iRecord != null) {
final ODocument document = (ODocument) iRecord;
/*
Result handler might be asynchronous, if document is loaded in a lazy mode,
database will be queries to fetch various fields. Need to activate it on the current thread.
*/
database.activateOnCurrentThread();
matchConsumer.accept(document);
}
return true;
}
...
}
不幸的是,定义自定义OCommandAsyncScript的方法不起作用。在调试Orient的OStorageRemote类时,似乎没有读取部分结果,这里是源代码的相应摘录:
public Object command(final OCommandRequestText iCommand) {
....
try {
OStorageRemote.this.beginResponse(network, session);
List<ORecord> temporaryResults = new ArrayList();
boolean addNextRecord = true;
byte status;
if(asynch) {
while((status = network.readByte()) > 0) {
ORecord record = (ORecord)OChannelBinaryProtocol.readIdentifiable(network);
if(record != null) {
switch(status) {
case 1:
if(addNextRecord) {
addNextRecord = iCommand.getResultListener().result(record);
database.getLocalCache().updateRecord(record);
}
break;
case 2:
if(record.getIdentity().getClusterId() == -2) {
temporaryResults.add(record);
}
database.getLocalCache().updateRecord(record);
}
}
}
}
}
Network.readbyte()始终为null,因此根本不能获取任何记录。
还有其他解决方法我们如何在异步模式下执行sql脚本并以懒惰的方式检索结果,从而阻止在我们的应用程序端生成大型列表?