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

com.arcadedb.query.sql.executor.CreateEdgesStep Maven / Gradle / Ivy

There is a newer version: 24.11.1
Show newest version
/*
 * 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.
 *
 * SPDX-FileCopyrightText: 2021-present Arcade Data Ltd ([email protected])
 * SPDX-License-Identifier: Apache-2.0
 */
package com.arcadedb.query.sql.executor;

import com.arcadedb.database.DatabaseInternal;
import com.arcadedb.database.Document;
import com.arcadedb.database.Identifiable;
import com.arcadedb.database.RID;
import com.arcadedb.exception.CommandExecutionException;
import com.arcadedb.exception.TimeoutException;
import com.arcadedb.graph.Edge;
import com.arcadedb.graph.MutableEdge;
import com.arcadedb.graph.Vertex;
import com.arcadedb.graph.VertexInternal;
import com.arcadedb.index.Index;
import com.arcadedb.index.IndexCursor;
import com.arcadedb.query.sql.parser.Identifier;

import java.util.*;

/**
 * Created by luigidellaquila on 28/11/16.
 */
public class CreateEdgesStep extends AbstractExecutionStep {

  private final Identifier  targetClass;
  private final Identifier  targetBucket;
  private final String      uniqueIndexName;
  private final Identifier  fromAlias;
  private final Identifier  toAlias;
  private final boolean     ifNotExists;
  private final boolean     unidirectional;
  // operation stuff
  private       Iterator    fromIter;
  private       Iterator    toIterator;
  private       Vertex      currentFrom;
  private       Vertex      currentTo;
  private       MutableEdge edgeToUpdate; // for upsert
  private       boolean     finished = false;
  private final List        toList   = new ArrayList<>();
  private       Index       uniqueIndex;

  private boolean initiated = false;

  public CreateEdgesStep(final Identifier targetClass, final Identifier targetBucketName, final String uniqueIndex,
      final Identifier fromAlias, final Identifier toAlias, final boolean unidirectional, final boolean ifNotExists,
      final CommandContext context, final boolean profilingEnabled) {
    super(context, profilingEnabled);
    this.targetClass = targetClass;
    this.targetBucket = targetBucketName;
    this.uniqueIndexName = uniqueIndex;
    this.fromAlias = fromAlias;
    this.toAlias = toAlias;
    this.unidirectional = unidirectional;
    this.ifNotExists = ifNotExists;
  }

  @Override
  public ResultSet syncPull(final CommandContext context, final int nRecords) throws TimeoutException {
    pullPrevious(context, nRecords);

    init();
    return new CreateRecordResultSet() {
      private int currentBatch = 0;

      @Override
      public boolean hasNext() {
        return (currentBatch < nRecords && (toIterator.hasNext() || (!toList.isEmpty() && fromIter.hasNext())));
      }

      @Override
      public Result next() {
        return next(null);
      }

      @Override
      public Result next(final Object[] properties) {
        if (currentTo == null)
          loadNextFromTo();

        final long begin = profilingEnabled ? System.nanoTime() : 0;
        try {

          if (finished || currentBatch >= nRecords)
            throw new NoSuchElementException();

          if (currentTo == null)
            throw new CommandExecutionException("Invalid TO vertex for edge");

          if (ifNotExists)
            if (context.getDatabase().getGraphEngine()
                .isVertexConnectedTo((VertexInternal) currentFrom, currentTo, Vertex.DIRECTION.OUT, targetClass.getStringValue()))
              // SKIP CREATING EDGE
              return null;

          final String target = targetBucket != null ? "bucket:" + targetBucket.getStringValue() : targetClass.getStringValue();

          final MutableEdge edge =
              edgeToUpdate != null ? edgeToUpdate : currentFrom.newEdge(target, currentTo, !unidirectional, properties);

          final UpdatableResult result = new UpdatableResult(edge);
          currentTo = null;
          currentBatch++;
          return result;
        } finally {
          if (profilingEnabled) {
            cost += (System.nanoTime() - begin);
          }
        }
      }
    };
  }

  private void init() {
    synchronized (this) {
      if (this.initiated)
        return;

      initiated = true;
    }
    Object fromValues = context.getVariable(fromAlias.getStringValue());
    if (fromValues instanceof Iterable && !(fromValues instanceof Identifiable))
      fromValues = ((Iterable) fromValues).iterator();
    else if (!(fromValues instanceof Iterator))
      fromValues = Collections.singleton(fromValues).iterator();

    if (fromValues instanceof InternalResultSet)
      fromValues = ((InternalResultSet) fromValues).copy();

    Object toValues = context.getVariable(toAlias.getStringValue());
    if (toValues instanceof Iterable && !(toValues instanceof Identifiable))
      toValues = ((Iterable) toValues).iterator();
    else if (!(toValues instanceof Iterator))
      toValues = Collections.singleton(toValues).iterator();

    if (toValues instanceof InternalResultSet)
      toValues = ((InternalResultSet) toValues).copy();

    fromIter = (Iterator) fromValues;
    if (fromIter instanceof ResultSet) {
      try {
        ((ResultSet) fromIter).reset();
      } catch (final Exception ignore) {
      }
    }

    final Iterator toIter = (Iterator) toValues;

    if (toIter instanceof ResultSet) {
      try {
        ((ResultSet) toIter).reset();
      } catch (final Exception ignore) {
      }
    }

    while (toIter != null && toIter.hasNext())
      toList.add(toIter.next());

    toIterator = toList.iterator();

    currentFrom = fromIter != null && fromIter.hasNext() ? asVertex(fromIter.next()) : null;

    if (uniqueIndexName != null) {
      final DatabaseInternal database = context.getDatabase();
      uniqueIndex = database.getSchema().getIndexByName(uniqueIndexName);
      if (uniqueIndex == null)
        throw new CommandExecutionException("Index not found for upsert: " + uniqueIndexName);
    }
  }

  protected void loadNextFromTo() {
    final long begin = profilingEnabled ? System.nanoTime() : 0;
    try {
      edgeToUpdate = null;
      this.currentTo = null;
      if (!toIterator.hasNext()) {
        toIterator = toList.iterator();
        if (!fromIter.hasNext()) {
          finished = true;
          return;
        }
        currentFrom = asVertex(fromIter.next());
      }
      if (toIterator.hasNext() || (toList.size() > 0 && fromIter.hasNext())) {
        if (currentFrom == null) {
          if (!fromIter.hasNext()) {
            finished = true;
            return;
          }
        }

        final Object obj = toIterator.next();

        currentTo = asVertex(obj);
        if (currentTo == null) {
          throw new CommandExecutionException("Invalid TO vertex for edge");
        }

        if (isUpsert()) {
          final Edge existingEdge = getExistingEdge(currentFrom, currentTo);
          if (existingEdge != null) {
            edgeToUpdate = existingEdge.modify();
          }
        }

      } else {
        this.currentTo = null;
      }
    } finally {
      if (profilingEnabled) {
        cost += (System.nanoTime() - begin);
      }
    }
  }

  private Edge getExistingEdge(final Vertex currentFrom, final Vertex currentTo) {
    final RID[] key = new RID[] { currentFrom.getIdentity(), currentTo.getIdentity() };

    final IndexCursor cursor = uniqueIndex.get(key);
    if (cursor.hasNext())
      return cursor.next().asEdge();

    return null;
  }

  private boolean isUpsert() {
    return uniqueIndex != null;
  }

  private Vertex asVertex(Object currentFrom) {
    if (currentFrom instanceof RID)
      currentFrom = ((RID) currentFrom).getRecord();

    if (currentFrom instanceof Result) {
      final Object from = currentFrom;
      currentFrom = ((Result) currentFrom).getVertex()
          .orElseThrow(() -> new CommandExecutionException("Invalid vertex for edge creation: " + from));
    }

    if (currentFrom instanceof Vertex)
      return (Vertex) currentFrom;
    else if (currentFrom instanceof Document)
      return ((Document) currentFrom).asVertex();
    else if (RID.is(currentFrom))
      return new RID(getContext().getDatabase(), currentFrom.toString()).asVertex();

    throw new CommandExecutionException(
        "Invalid vertex for edge creation: " + (currentFrom == null ? "null" : currentFrom.toString()));
  }

  @Override
  public String prettyPrint(final int depth, final int indent) {
    final String spaces = ExecutionStepInternal.getIndent(depth, indent);
    String result = spaces + "+ FOR EACH x in " + fromAlias + "\n";
    result += spaces + "    FOR EACH y in " + toAlias + "\n";
    result +=
        spaces + "       CREATE EDGE " + targetClass + " FROM x TO y " + (unidirectional ? "UNIDIRECTIONAL" : "BIDIRECTIONAL");
    if (profilingEnabled)
      result += " (" + getCostFormatted() + ")";

    if (targetBucket != null)
      result += "\n" + spaces + "       (target cluster " + targetBucket + ")";

    return result;
  }

  @Override
  public boolean canBeCached() {
    return true;
  }

  @Override
  public ExecutionStep copy(final CommandContext context) {
    return new CreateEdgesStep(targetClass == null ? null : targetClass.copy(), targetBucket == null ? null : targetBucket.copy(),
        uniqueIndexName, fromAlias == null ? null : fromAlias.copy(), toAlias == null ? null : toAlias.copy(), unidirectional,
        ifNotExists, context, profilingEnabled);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy