All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.arcadedb.query.select.Select Maven / Gradle / Ivy

There is a newer version: 24.11.1
Show newest version
package com.arcadedb.query.select;/*
 * Copyright © 2021-present Arcade Data Ltd ([email protected])
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import com.arcadedb.database.DatabaseInternal;
import com.arcadedb.database.Document;
import com.arcadedb.engine.Bucket;
import com.arcadedb.graph.Edge;
import com.arcadedb.graph.Vertex;
import com.arcadedb.schema.DocumentType;
import com.arcadedb.serializer.json.JSONArray;
import com.arcadedb.serializer.json.JSONObject;
import com.arcadedb.utility.Pair;

import java.util.*;
import java.util.concurrent.*;
import java.util.stream.*;

/**
 * Native Query engine is a simple query engine that covers most of the classic use cases, such as the retrieval of records
 * with a where condition. It could be much faster than the same SQL query because it does not use any parser and it is very
 * JIT friendly. Future versions could translate the query into bytecode to have an even faster execution.
 *
 * @author Luca Garulli ([email protected])
 */
public class Select {
  final DatabaseInternal database;

  enum STATE {DEFAULT, WHERE, COMPILED}

  Map              parameters;
  SelectTreeNode                   rootTreeElement;
  DocumentType                     fromType;
  List                     fromBuckets;
  SelectOperator                   operator;
  SelectRuntimeValue               property;
  Object                           propertyValue;
  boolean                          polymorphic = true;
  int                              limit       = -1;
  int                              skip        = 0;
  long                             timeoutInMs = 0;
  boolean                          exceptionOnTimeout;
  ArrayList> orderBy;
  boolean                          parallel    = false;

  STATE state = STATE.DEFAULT;
  private SelectTreeNode lastTreeElement;

  public Select(final DatabaseInternal database) {
    this.database = database;
  }

  public Select fromType(final String fromType) {
    checkNotCompiled();
    if (this.fromType != null)
      throw new IllegalArgumentException("From type has already been set");
    if (this.fromBuckets != null)
      throw new IllegalArgumentException("From bucket(s) has already been set");

    this.fromType = database.getSchema().getType(fromType);
    return this;
  }

  public Select fromBuckets(final String... fromBucketNames) {
    checkNotCompiled();
    if (this.fromType != null)
      throw new IllegalArgumentException("From type has already been set");
    if (this.fromBuckets != null)
      throw new IllegalArgumentException("From bucket(s) has already been set");

    this.fromBuckets = Arrays.stream(fromBucketNames).map(b -> database.getSchema().getBucketByName(b))
        .collect(Collectors.toList());
    return this;
  }

  public Select fromBuckets(final Integer... fromBucketIds) {
    checkNotCompiled();
    if (this.fromType != null)
      throw new IllegalArgumentException("From type has already been set");
    if (this.fromBuckets != null)
      throw new IllegalArgumentException("From bucket(s) has already been set");

    this.fromBuckets = Arrays.stream(fromBucketIds).map(b -> database.getSchema().getBucketById(b)).collect(Collectors.toList());
    return this;
  }

  public Select property(final String name) {
    checkNotCompiled();
    if (property != null)
      throw new IllegalArgumentException("Property has already been set");
    if (state != STATE.WHERE)
      throw new IllegalArgumentException("No context was provided for the parameter");
    this.property = new SelectPropertyValue(name);
    return this;
  }

  public Select value(final Object value) {
    checkNotCompiled();
    if (property == null)
      throw new IllegalArgumentException("Property has not been set");

    switch (state) {
    case WHERE:
      if (operator == null)
        throw new IllegalArgumentException("No operator has been set");
      if (propertyValue != null)
        throw new IllegalArgumentException("Property value has already been set");
      this.propertyValue = value;
      break;
    }

    return this;
  }

  public SelectWhereLeftBlock where() {
    checkNotCompiled();
    if (rootTreeElement != null)
      throw new IllegalArgumentException("Where has already been set");
    state = STATE.WHERE;
    return new SelectWhereLeftBlock(this);
  }

  public Select parameter(final String parameterName) {
    checkNotCompiled();
    this.propertyValue = new SelectParameterValue(this, parameterName);
    return this;
  }

  public Select limit(final int limit) {
    checkNotCompiled();
    this.limit = limit;
    return this;
  }

  public Select skip(final int skip) {
    checkNotCompiled();
    this.skip = skip;
    return this;
  }

  public Select timeout(final long timeoutValue, final TimeUnit timeoutUnit, final boolean exceptionOnTimeout) {
    checkNotCompiled();
    this.timeoutInMs = timeoutUnit.toMillis(timeoutValue);
    this.exceptionOnTimeout = exceptionOnTimeout;
    return this;
  }

  public Select polymorphic(final boolean polymorphic) {
    checkNotCompiled();
    if (fromType == null)
      throw new IllegalArgumentException("FromType was not set");
    this.polymorphic = polymorphic;
    return this;
  }

  public Select orderBy(final String property, final boolean ascending) {
    checkNotCompiled();
    if (this.orderBy == null)
      this.orderBy = new ArrayList<>();
    this.orderBy.add(new Pair<>(property, ascending));
    return this;
  }

  public Select json(final JSONObject json) {
    checkNotCompiled();
    if (json.has("fromType")) {
      fromType(json.getString("fromType"));
      if (json.has("polymorphic"))
        polymorphic(json.getBoolean("polymorphic"));
    } else if (json.has("fromBuckets")) {
      final JSONArray buckets = json.getJSONArray("fromBuckets");
      fromBuckets(
          buckets.toList().stream().map(b -> database.getSchema().getBucketByName(b.toString())).collect(Collectors.toList())
              .toArray(new String[buckets.length()]));
    }

    if (json.has("where")) {
      where();
      parseJsonCondition(json.getJSONArray("where"));
    }

    if (json.has("limit"))
      limit(json.getInt("limit"));
    if (json.has("skip"))
      skip(json.getInt("skip"));

    return this;
  }

  private void parseJsonCondition(final JSONArray condition) {
    if (condition.length() != 3)
      throw new IllegalArgumentException("Invalid condition " + condition);

    final Object parsedLeft = condition.get(0);
    if (parsedLeft instanceof JSONArray)
      parseJsonCondition((JSONArray) parsedLeft);
    else if (parsedLeft instanceof String && ((String) parsedLeft).startsWith(":"))
      property(((String) parsedLeft).substring(1));
    else if (parsedLeft instanceof String && ((String) parsedLeft).startsWith("#"))
      parameter(((String) parsedLeft).substring(1));
    else
      throw new IllegalArgumentException("Unsupported value " + parsedLeft);

    final SelectOperator parsedOperator = SelectOperator.byName(condition.getString(1));

    if (parsedOperator.logicOperator)
      setLogic(parsedOperator);
    else
      setOperator(parsedOperator);

    final Object parsedRight = condition.get(2);
    if (parsedRight instanceof JSONArray)
      parseJsonCondition((JSONArray) parsedRight);
    else if (parsedRight instanceof String && ((String) parsedRight).startsWith(":"))
      property(((String) parsedRight).substring(1));
    else if (parsedRight instanceof String && ((String) parsedRight).startsWith("#"))
      parameter(((String) parsedRight).substring(1));
    else
      value(parsedRight);
  }

  public SelectCompiled compile() {
    if (fromType == null && fromBuckets == null)
      throw new IllegalArgumentException("from (type or buckets) has not been set");
    if (state == STATE.WHERE) {
      setLogic(SelectOperator.run);
    }
    state = STATE.COMPILED;
    return new SelectCompiled(this);
  }

  public SelectIterator vertices() {
    return run();
  }

  public SelectIterator edges() {
    return run();
  }

  public SelectIterator documents() {
    return run();
  }

   SelectIterator run() {
    compile();
    return new SelectExecutor(this).execute();
  }

  SelectWhereLeftBlock setLogic(final SelectOperator newLogicOperator) {
    checkNotCompiled();
    if (operator == null)
      throw new IllegalArgumentException("Missing condition");

    final SelectTreeNode newTreeElement = new SelectTreeNode(property, operator, propertyValue);
    if (rootTreeElement == null) {
      // 1ST TIME ONLY
      rootTreeElement = new SelectTreeNode(newTreeElement, newLogicOperator, null);
      newTreeElement.setParent(rootTreeElement);
      lastTreeElement = newTreeElement;
    } else {
      if (newLogicOperator.equals(SelectOperator.run)) {
        // EXECUTION = LAST NODE: APPEND TO THE RIGHT OF THE LATEST
        lastTreeElement.getParent().setRight(newTreeElement);
      } else if (lastTreeElement.getParent().operator.precedence < newLogicOperator.precedence) {
        // AND+ OPERATOR
        final SelectTreeNode newNode = new SelectTreeNode(newTreeElement, newLogicOperator, null);
        lastTreeElement.getParent().setRight(newNode);
        lastTreeElement = newTreeElement;
      } else {
        // OR+ OPERATOR
        final SelectTreeNode currentParent = lastTreeElement.getParent();
        currentParent.setRight(newTreeElement);
        final SelectTreeNode newNode = new SelectTreeNode(currentParent, newLogicOperator, null);
        if (rootTreeElement.equals(currentParent))
          rootTreeElement = newNode;
        else
          newNode.setParent(currentParent.getParent());
        lastTreeElement = currentParent;
      }
    }

    operator = null;
    property = null;
    propertyValue = null;
    return new SelectWhereLeftBlock(this);
  }

  void checkNotCompiled() {
    if (state == STATE.COMPILED)
      throw new IllegalArgumentException("Cannot modify the structure of a select what has been already compiled");
  }

  SelectWhereRightBlock setOperator(final SelectOperator selectOperator) {
    checkNotCompiled();
    if (operator != null)
      throw new IllegalArgumentException("Operator has already been set (" + operator + ")");
    operator = selectOperator;
    return new SelectWhereRightBlock(this);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy