OrientDB以异步方式执行脚本并以惰性方式获取记录

时间:2017-08-06 12:48:22

标签: asynchronous lazy-loading orientdb orientdb2.2

目前,我们在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

也不解决我们的问题。

搜索不同的博客显示到目前为止有两条建议:

  1. 尝试通过在employee和company上引入父类并在其上定义上述两个索引来继承来定义复合索引
  2. https://github.com/orientechnologies/orientdb/issues/5069

    1. 将这两个查询捆绑在一个批处理脚本中:
    2. 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脚本并以懒惰的方式检索结果,从而阻止在我们的应用程序端生成大型列表?

0 个答案:

没有答案