docs: public QueryResult find(Map<String, Object> query,
                              final List<Index> indexes,
                              long skip,
                              long limit,
                              List<String> fields,
                              final List<FieldSort> sortDocument) throws QueryException {
          //
          // Validate inputs
          //
  
          fields = normaliseFields(fields);
  
          // will throw IllegalArgumentException if there are invalid fields
          validateFields(fields);
  
          // normalise and validate query by passing into the executors
  
          query = QueryValidator.normaliseAndValidateQuery(query);
  
          //
          // Execute the query
          //
  
          Boolean[] indexesCoverQuery = new Boolean[]{ false };
          final ChildrenQueryNode root = translateQuery(query, indexes, indexesCoverQuery);
  
  
  
          Future<List<String>> result = queue.submit(new SQLCallable<List<String>>() {
              @Override
              public List<String> call(SQLDatabase database) throws Exception {
                  Set<String> docIdSet = executeQueryTree(root, database);
                  List<String> docIdList;
  
                  // sorting
                  if (sortDocument != null && !sortDocument.isEmpty()) {
                      docIdList = sortIds(docIdSet, sortDocument, indexes, database);
                  } else {
                      docIdList = docIdSet != null ? new ArrayList<String>(docIdSet) : null;
                  }
  
                  return docIdList;
              }
          });
  
          List<String> docIds;
          try {
              docIds = result.get();
          } catch (ExecutionException e) {
              String message = "Execution error encountered";
              logger.log(Level.SEVERE, message, e);
              throw new QueryException(message, e.getCause());
          } catch (InterruptedException e) {
              String message = "Execution interrupted error encountered";
              logger.log(Level.SEVERE, message, e);
              throw new QueryException(message, e.getCause());
          }
  
          if (docIds == null) {
              return null;
          }
  
          UnindexedMatcher matcher = matcherForIndexCoverage(indexesCoverQuery, query);
  
          if (matcher != null) {
              String msg = "query could not be executed using indexes alone; falling back to ";
              msg += "filtering documents themselves. This will be VERY SLOW as each candidate ";
              msg += "document is loaded from the datastore and matched against the query selector.";
              logger.log(Level.WARNING, msg);
          }
  
          return new QueryResult(docIds, database, fields, skip, limit, matcher);
      }