<commit_before>package com.tinkerpop.blueprints.impls.orient;

import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.orientechnologies.common.collection.OLazyIterator;
import com.orientechnologies.common.collection.OMultiCollectionIterator;
import com.orientechnologies.common.log.OLogManager;
import com.orientechnologies.common.util.OPair;
import com.orientechnologies.orient.core.command.traverse.OTraverse;
import com.orientechnologies.orient.core.db.record.OIdentifiable;
import com.orientechnologies.orient.core.db.record.ORecordLazyMultiValue;
import com.orientechnologies.orient.core.record.impl.ODocument;
import com.orientechnologies.orient.core.type.tree.OMVRBTreeRIDSet;
import com.tinkerpop.blueprints.Direction;
import com.tinkerpop.blueprints.Edge;
import com.tinkerpop.blueprints.Element;
import com.tinkerpop.blueprints.Index;
import com.tinkerpop.blueprints.Vertex;
import com.tinkerpop.blueprints.util.StringFactory;

/**
 * @author <NAME> (http://www.orientechnologies.com)
 */
@SuppressWarnings("unchecked")
public class OrientVertex extends OrientElement implements Vertex {
  private static final long  serialVersionUID      = 1L;
  public static final String CLASS_NAME            = "V";
  public static final String CONNECTION_OUT_PREFIX = OrientBaseGraph.CONNECTION_OUT + "_";
  public static final String CONNECTION_IN_PREFIX  = OrientBaseGraph.CONNECTION_IN + "_";

  protected OrientVertex(final OrientBaseGraph graph, String className, final Object... fields) {
    super(graph, null);
    if (className != null)
      className = checkForClassInSchema(OrientBaseGraph.encodeClassName(className));

    rawElement = new ODocument(className == null ? CLASS_NAME : className);
    setProperties(fields);
  }

  protected OrientVertex(final OrientBaseGraph graph, final OIdentifiable record) {
    super(graph, record);
  }

  @Override
  public Set<String> getPropertyKeys() {
    final ODocument doc = getRecord();

    final Set<String> result = new HashSet<String>();
    for (String field : doc.fieldNames())
      if (graph.isUseVertexFieldsForEdgeLabels()) {
        if (!field.startsWith(CONNECTION_OUT_PREFIX) && !field.startsWith(CONNECTION_IN_PREFIX))
          result.add(field);
      } else if (!field.equals(OrientBaseGraph.CONNECTION_OUT) && !field.equals(OrientBaseGraph.CONNECTION_IN))
        result.add(field);

    return result;
  }

  /**
   * Returns a lazy iterable instance against vertices.
   */
  @Override
  public Iterable<Vertex> getVertices(final Direction iDirection, final String... iLabels) {
    final ODocument doc = getRecord();

    final OMultiCollectionIterator<Vertex> iterable = new OMultiCollectionIterator<Vertex>();
    for (String fieldName : doc.fieldNames()) {
      final OPair<Direction, String> connection = getConnection(iDirection, fieldName, iLabels);
      if (connection == null)
        // SKIP THIS FIELD
        continue;

      final Object fieldValue = doc.field(fieldName);
      if (fieldValue != null)
        if (fieldValue instanceof OIdentifiable) {
          addSingleVertex(doc, iterable, fieldName, connection, fieldValue, iLabels);

        } else if (fieldValue instanceof Collection<?>) {
          Collection<?> coll = (Collection<?>) fieldValue;

          if (coll.size() == 1) {
            // SINGLE ITEM: AVOID CALLING ITERATOR
            if (coll instanceof ORecordLazyMultiValue)
              addSingleVertex(doc, iterable, fieldName, connection, ((ORecordLazyMultiValue) coll).rawIterator().next(), iLabels);
            else if (coll instanceof List<?>)
              addSingleVertex(doc, iterable, fieldName, connection, ((List<?>) coll).get(0), iLabels);
            else
              addSingleVertex(doc, iterable, fieldName, connection, coll.iterator().next(), iLabels);
          } else {
            final Iterator<?> it = coll instanceof ORecordLazyMultiValue ? ((ORecordLazyMultiValue) coll).rawIterator() : coll
                .iterator();
            // CREATE LAZY Iterable AGAINST COLLECTION FIELD
            iterable.add(new OrientVertexIterator(this, it, connection, iLabels));
          }
        }
    }

    return iterable;
  }

  @Override
  public OrientVertexQuery query() {
    return new OrientVertexQuery(this);
  }

  /**
   * Returns a OTraverse object to start traversing from the current vertex.
   * 
   */
  public OTraverse traverse() {
    return new OTraverse().target(getRecord());
  }

  @Override
  public void remove() {
    final ODocument doc = getRecord();
    if (doc == null)
      return;

    graph.autoStartTransaction();

    final Iterator<OrientIndex<? extends OrientElement>> it = graph.getManualIndices().iterator();

    if (it.hasNext()) {
      final Set<Edge> allEdges = new HashSet<Edge>();
      for (Edge e : getEdges(Direction.BOTH))
        allEdges.add(e);

      while (it.hasNext()) {
        final Index<? extends Element> index = it.next();

        if (Vertex.class.isAssignableFrom(index.getIndexClass())) {
          OrientIndex<OrientVertex> idx = (OrientIndex<OrientVertex>) index;
          idx.removeElement(this);
        }

        if (Edge.class.isAssignableFrom(index.getIndexClass())) {
          OrientIndex<OrientEdge> idx = (OrientIndex<OrientEdge>) index;
          for (Edge e : allEdges)
            idx.removeElement((OrientEdge) e);
        }
      }
    }

    for (String fieldName : doc.fieldNames()) {
      final OPair<Direction, String> connection = getConnection(Direction.BOTH, fieldName);
      if (connection == null)
        // SKIP THIS FIELD
        continue;

      removeEdges(doc, fieldName, null, true, graph.isUseVertexFieldsForEdgeLabels());
    }

    super.remove();
  }

  @Override
  public Edge addEdge(final String label, final Vertex inVertex) {
    return addEdge(label, (OrientVertex) inVertex, null, null, (Object[]) null);
  }

  public OrientEdge addEdge(final String label, final OrientVertex inVertex, final String iClassName) {
    return addEdge(label, inVertex, iClassName, null, (Object[]) null);
  }

  public OrientEdge addEdge(String label, final OrientVertex inVertex, final String iClassName, final String iClusterName,
      final Object... fields) {
    if (inVertex == null)
      throw new IllegalArgumentException("destination vertex is null");

    graph.autoStartTransaction();

    final ODocument outDocument = getRecord();
    final ODocument inDocument = ((OrientVertex) inVertex).getRecord();

    final OrientEdge edge;
    OIdentifiable to;
    OIdentifiable from;

    label = OrientBaseGraph.encodeClassName(label);
    if (label == null && iClassName != null)
      // RETRO-COMPATIBILITY WITH THE SYNTAX CLASS:<CLASS-NAME>
      label = OrientBaseGraph.encodeClassName(iClassName);

    final boolean useVertexFieldsForEdgeLabels = graph.isUseVertexFieldsForEdgeLabels();
    final String outFieldName = getConnectionFieldName(Direction.OUT, label, useVertexFieldsForEdgeLabels);
    final String inFieldName = getConnectionFieldName(Direction.IN, label, useVertexFieldsForEdgeLabels);

    if (canCreateDynamicEdge(outDocument, inDocument, outFieldName, inFieldName, fields, label)) {
      // CREATE A LIGHTWEIGHT DYNAMIC EDGE
      from = rawElement;
      to = inDocument;
      edge = new OrientEdge(graph, from, to, label);
    } else {
      // CREATE THE EDGE DOCUMENT TO STORE FIELDS TOO
      edge = new OrientEdge(graph, label, fields);

      if (graph.isKeepInMemoryReferences())
        edge.getRecord().fields(OrientBaseGraph.CONNECTION_OUT, rawElement.getIdentity(), OrientBaseGraph.CONNECTION_IN,
            inDocument.getIdentity());
      else
        edge.getRecord().fields(OrientBaseGraph.CONNECTION_OUT, rawElement, OrientBaseGraph.CONNECTION_IN, inDocument);

      from = (OIdentifiable) edge.getRecord();
      to = (OIdentifiable) edge.getRecord();
    }

    if (graph.isKeepInMemoryReferences()) {
      // USES REFERENCES INSTEAD OF DOCUMENTS
      from = from.getIdentity();
      to = to.getIdentity();
    }

    // OUT-VERTEX ---> IN-VERTEX/EDGE
    createLink(outDocument, to, outFieldName);

    // IN-VERTEX ---> OUT-VERTEX/EDGE
    createLink(inDocument, from, inFieldName);

    edge.save(iClusterName);
    inDocument.save();
    outDocument.save();

    return edge;
  }

  private boolean canCreateDynamicEdge(final ODocument iFromVertex, final ODocument iToVertex, final String iOutFieldName,
      final String iInFieldName, final Object[] fields, final String label) {

    if (!graph.isUseVertexFieldsForEdgeLabels() && label != null)
      return false;

    if (graph.isUseLightweightEdges() && (fields == null || fields.length == 0 || fields[0] == null)) {
      Object field = iFromVertex.field(iOutFieldName);
      if (field != null)
        if (field instanceof OIdentifiable) {
          if (field.equals(iToVertex))
            // ALREADY EXISTS, FORCE THE EDGE-DOCUMENT TO AVOID MULTIPLE DYN-EDGES AGAINST THE SAME VERTICES
            return false;
        } else if (field instanceof OMVRBTreeRIDSet)
          if (((OMVRBTreeRIDSet) field).contains(iToVertex))
            // ALREADY EXISTS, FORCE THE EDGE-DOCUMENT TO AVOID MULTIPLE DYN-EDGES AGAINST THE SAME VERTICES
            return false;

      field = iToVertex.field(iInFieldName);
      if (field != null)
        if (field instanceof OIdentifiable) {
          if (field.equals(iFromVertex))
            // ALREADY EXISTS, FORCE THE EDGE-DOCUMENT TO AVOID MULTIPLE DYN-EDGES AGAINST THE SAME VERTICES
            return false;
        } else if (field instanceof OMVRBTreeRIDSet)
          if (((OMVRBTreeRIDSet) field).contains(iFromVertex))
            // ALREADY EXISTS, FORCE THE EDGE-DOCUMENT TO AVOID MULTIPLE DYN-EDGES AGAINST THE SAME VERTICES
            return false;

      // CAN USE DYNAMIC EDGES
      return true;
    }
    return false;
  }

  public long countEdges(final Direction iDirection, final String... iLabels) {
    long counter = 0;
    if (graph.isUseVertexFieldsForEdgeLabels() || iLabels == null || iLabels.length == 0) {
      // VERY FAST
      final ODocument doc = getRecord();
      for (String fieldName : doc.fieldNames()) {
        final OPair<Direction, String> connection = getConnection(iDirection, fieldName, iLabels);
        if (connection == null)
          // SKIP THIS FIELD
          continue;

        final Object fieldValue = doc.field(fieldName);
        if (fieldValue != null)
          if (fieldValue instanceof Collection<?>)
            counter += ((Collection<?>) fieldValue).size();
          else if (fieldValue instanceof Map<?, ?>)
            counter += ((Map<?, ?>) fieldValue).size();
          else
            counter++;
      }
    } else {
      // SLOWER: BROWSE & FILTER
      for (Edge e : getEdges(iDirection, iLabels))
        if (e != null)
          counter++;
    }
    return counter;
  }

  public Iterable<Edge> getEdges(final Direction iDirection, final String... iLabels) {
    return getEdges(null, iDirection, iLabels);
  }

  public Iterable<Edge> getEdges(final Vertex iDestination, final Direction iDirection, final String... iLabels) {
    final ODocument doc = getRecord();

    final OMultiCollectionIterator<Edge> iterable = new OMultiCollectionIterator<Edge>();
    for (String fieldName : doc.fieldNames()) {
      final OPair<Direction, String> connection = getConnection(iDirection, fieldName, iLabels);
      if (connection == null)
        // SKIP THIS FIELD
        continue;

      final Object fieldValue = doc.field(fieldName);
      if (fieldValue != null) {
        final OIdentifiable destinationVId = iDestination != null ? (OIdentifiable) iDestination.getId() : null;

        if (fieldValue instanceof OIdentifiable) {
          addSingleEdge(doc, iterable, fieldName, connection, fieldValue, destinationVId, iLabels);

        } else if (fieldValue instanceof Collection<?>) {
          Collection<?> coll = (Collection<?>) fieldValue;

          if (coll.size() == 1) {
            // SINGLE ITEM: AVOID CALLING ITERATOR
            if (coll instanceof ORecordLazyMultiValue)
              addSingleEdge(doc, iterable, fieldName, connection, ((ORecordLazyMultiValue) coll).rawIterator().next(),
                  destinationVId, iLabels);
            else if (coll instanceof List<?>)
              addSingleEdge(doc, iterable, fieldName, connection, ((List<?>) coll).get(0), destinationVId, iLabels);
            else<commit_msg>Edge now uses embedded iterator to avoid save() of temporary documents<commit_after>package com.tinkerpop.blueprints.impls.orient;

import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.orientechnologies.common.collection.OLazyIterator;
import com.orientechnologies.common.collection.OMultiCollectionIterator;
import com.orientechnologies.common.log.OLogManager;
import com.orientechnologies.common.util.OPair;
import com.orientechnologies.orient.core.command.traverse.OTraverse;
import com.orientechnologies.orient.core.db.record.OIdentifiable;
import com.orientechnologies.orient.core.db.record.ORecordLazyMultiValue;
import com.orientechnologies.orient.core.record.impl.ODocument;
import com.orientechnologies.orient.core.type.tree.OMVRBTreeRIDSet;
import com.tinkerpop.blueprints.Direction;
import com.tinkerpop.blueprints.Edge;
import com.tinkerpop.blueprints.Element;
import com.tinkerpop.blueprints.Index;
import com.tinkerpop.blueprints.Vertex;
import com.tinkerpop.blueprints.util.StringFactory;

/**
 * @author <NAME> (http://www.orientechnologies.com)
 */
@SuppressWarnings("unchecked")
public class OrientVertex extends OrientElement implements Vertex {
  private static final long  serialVersionUID      = 1L;
  public static final String CLASS_NAME            = "V";
  public static final String CONNECTION_OUT_PREFIX = OrientBaseGraph.CONNECTION_OUT + "_";
  public static final String CONNECTION_IN_PREFIX  = OrientBaseGraph.CONNECTION_IN + "_";

  protected OrientVertex(final OrientBaseGraph graph, String className, final Object... fields) {
    super(graph, null);
    if (className != null)
      className = checkForClassInSchema(OrientBaseGraph.encodeClassName(className));

    rawElement = new ODocument(className == null ? CLASS_NAME : className);
    setProperties(fields);
  }

  protected OrientVertex(final OrientBaseGraph graph, final OIdentifiable record) {
    super(graph, record);
  }

  @Override
  public Set<String> getPropertyKeys() {
    final ODocument doc = getRecord();

    final Set<String> result = new HashSet<String>();
    for (String field : doc.fieldNames())
      if (graph.isUseVertexFieldsForEdgeLabels()) {
        if (!field.startsWith(CONNECTION_OUT_PREFIX) && !field.startsWith(CONNECTION_IN_PREFIX))
          result.add(field);
      } else if (!field.equals(OrientBaseGraph.CONNECTION_OUT) && !field.equals(OrientBaseGraph.CONNECTION_IN))
        result.add(field);

    return result;
  }

  /**
   * Returns a lazy iterable instance against vertices.
   */
  @Override
  public Iterable<Vertex> getVertices(final Direction iDirection, final String... iLabels) {
    final ODocument doc = getRecord();

    final OMultiCollectionIterator<Vertex> iterable = new OMultiCollectionIterator<Vertex>();
    for (String fieldName : doc.fieldNames()) {
      final OPair<Direction, String> connection = getConnection(iDirection, fieldName, iLabels);
      if (connection == null)
        // SKIP THIS FIELD
        continue;

      final Object fieldValue = doc.field(fieldName);
      if (fieldValue != null)
        if (fieldValue instanceof OIdentifiable) {
          addSingleVertex(doc, iterable, fieldName, connection, fieldValue, iLabels);

        } else if (fieldValue instanceof Collection<?>) {
          Collection<?> coll = (Collection<?>) fieldValue;

          if (coll.size() == 1) {
            // SINGLE ITEM: AVOID CALLING ITERATOR
            if (coll instanceof ORecordLazyMultiValue)
              addSingleVertex(doc, iterable, fieldName, connection, ((ORecordLazyMultiValue) coll).rawIterator().next(), iLabels);
            else if (coll instanceof List<?>)
              addSingleVertex(doc, iterable, fieldName, connection, ((List<?>) coll).get(0), iLabels);
            else
              addSingleVertex(doc, iterable, fieldName, connection, coll.iterator().next(), iLabels);
          } else {
            final Iterator<?> it = coll instanceof ORecordLazyMultiValue ? ((ORecordLazyMultiValue) coll).rawIterator() : coll
                .iterator();
            // CREATE LAZY Iterable AGAINST COLLECTION FIELD
            iterable.add(new OrientVertexIterator(this, it, connection, iLabels));
          }
        }
    }

    return iterable;
  }

  @Override
  public OrientVertexQuery query() {
    return new OrientVertexQuery(this);
  }

  /**
   * Returns a OTraverse object to start traversing from the current vertex.
   * 
   */
  public OTraverse traverse() {
    return new OTraverse().target(getRecord());
  }

  @Override
  public void remove() {
    final ODocument doc = getRecord();
    if (doc == null)
      return;

    graph.autoStartTransaction();

    final Iterator<OrientIndex<? extends OrientElement>> it = graph.getManualIndices().iterator();

    if (it.hasNext()) {
      final Set<Edge> allEdges = new HashSet<Edge>();
      for (Edge e : getEdges(Direction.BOTH))
        allEdges.add(e);

      while (it.hasNext()) {
        final Index<? extends Element> index = it.next();

        if (Vertex.class.isAssignableFrom(index.getIndexClass())) {
          OrientIndex<OrientVertex> idx = (OrientIndex<OrientVertex>) index;
          idx.removeElement(this);
        }

        if (Edge.class.isAssignableFrom(index.getIndexClass())) {
          OrientIndex<OrientEdge> idx = (OrientIndex<OrientEdge>) index;
          for (Edge e : allEdges)
            idx.removeElement((OrientEdge) e);
        }
      }
    }

    for (String fieldName : doc.fieldNames()) {
      final OPair<Direction, String> connection = getConnection(Direction.BOTH, fieldName);
      if (connection == null)
        // SKIP THIS FIELD
        continue;

      removeEdges(doc, fieldName, null, true, graph.isUseVertexFieldsForEdgeLabels());
    }

    super.remove();
  }

  @Override
  public Edge addEdge(final String label, final Vertex inVertex) {
    return addEdge(label, (OrientVertex) inVertex, null, null, (Object[]) null);
  }

  public OrientEdge addEdge(final String label, final OrientVertex inVertex, final String iClassName) {
    return addEdge(label, inVertex, iClassName, null, (Object[]) null);
  }

  public OrientEdge addEdge(String label, final OrientVertex inVertex, final String iClassName, final String iClusterName,
      final Object... fields) {
    if (inVertex == null)
      throw new IllegalArgumentException("destination vertex is null");

    graph.autoStartTransaction();

    final ODocument outDocument = getRecord();
    final ODocument inDocument = ((OrientVertex) inVertex).getRecord();

    final OrientEdge edge;
    OIdentifiable to;
    OIdentifiable from;

    label = OrientBaseGraph.encodeClassName(label);
    if (label == null && iClassName != null)
      // RETRO-COMPATIBILITY WITH THE SYNTAX CLASS:<CLASS-NAME>
      label = OrientBaseGraph.encodeClassName(iClassName);

    final boolean useVertexFieldsForEdgeLabels = graph.isUseVertexFieldsForEdgeLabels();
    final String outFieldName = getConnectionFieldName(Direction.OUT, label, useVertexFieldsForEdgeLabels);
    final String inFieldName = getConnectionFieldName(Direction.IN, label, useVertexFieldsForEdgeLabels);

    if (canCreateDynamicEdge(outDocument, inDocument, outFieldName, inFieldName, fields, label)) {
      // CREATE A LIGHTWEIGHT DYNAMIC EDGE
      from = rawElement;
      to = inDocument;
      edge = new OrientEdge(graph, from, to, label);
    } else {
      // CREATE THE EDGE DOCUMENT TO STORE FIELDS TOO
      edge = new OrientEdge(graph, label, fields);

      if (graph.isKeepInMemoryReferences())
        edge.getRecord().fields(OrientBaseGraph.CONNECTION_OUT, rawElement.getIdentity(), OrientBaseGraph.CONNECTION_IN,
            inDocument.getIdentity());
      else
        edge.getRecord().fields(OrientBaseGraph.CONNECTION_OUT, rawElement, OrientBaseGraph.CONNECTION_IN, inDocument);

      from = (OIdentifiable) edge.getRecord();
      to = (OIdentifiable) edge.getRecord();
    }

    if (graph.isKeepInMemoryReferences()) {
      // USES REFERENCES INSTEAD OF DOCUMENTS
      from = from.getIdentity();
      to = to.getIdentity();
    }

    // OUT-VERTEX ---> IN-VERTEX/EDGE
    createLink(outDocument, to, outFieldName);

    // IN-VERTEX ---> OUT-VERTEX/EDGE
    createLink(inDocument, from, inFieldName);

    edge.save(iClusterName);
    inDocument.save();
    outDocument.save();

    return edge;
  }

  private boolean canCreateDynamicEdge(final ODocument iFromVertex, final ODocument iToVertex, final String iOutFieldName,
      final String iInFieldName, final Object[] fields, final String label) {

    if (!graph.isUseVertexFieldsForEdgeLabels() && label != null)
      return false;

    if (graph.isUseLightweightEdges() && (fields == null || fields.length == 0 || fields[0] == null)) {
      Object field = iFromVertex.field(iOutFieldName);
      if (field != null)
        if (field instanceof OIdentifiable) {
          if (field.equals(iToVertex))
            // ALREADY EXISTS, FORCE THE EDGE-DOCUMENT TO AVOID MULTIPLE DYN-EDGES AGAINST THE SAME VERTICES
            return false;
        } else if (field instanceof OMVRBTreeRIDSet)
          if (((OMVRBTreeRIDSet) field).contains(iToVertex))
            // ALREADY EXISTS, FORCE THE EDGE-DOCUMENT TO AVOID MULTIPLE DYN-EDGES AGAINST THE SAME VERTICES
            return false;

      field = iToVertex.field(iInFieldName);
      if (field != null)
        if (field instanceof OIdentifiable) {
          if (field.equals(iFromVertex))
            // ALREADY EXISTS, FORCE THE EDGE-DOCUMENT TO AVOID MULTIPLE DYN-EDGES AGAINST THE SAME VERTICES
            return false;
        } else if (field instanceof OMVRBTreeRIDSet)
          if (((OMVRBTreeRIDSet) field).contains(iFromVertex))
            // ALREADY EXISTS, FORCE THE EDGE-DOCUMENT TO AVOID MULTIPLE DYN-EDGES AGAINST THE SAME VERTICES
            return false;

      // CAN USE DYNAMIC EDGES
      return true;
    }
    return false;
  }

  public long countEdges(final Direction iDirection, final String... iLabels) {
    long counter = 0;
    if (graph.isUseVertexFieldsForEdgeLabels() || iLabels == null || iLabels.length == 0) {
      // VERY FAST
      final ODocument doc = getRecord();
      for (String fieldName : doc.fieldNames()) {
        final OPair<Direction, String> connection = getConnection(iDirection, fieldName, iLabels);
        if (connection == null)
          // SKIP THIS FIELD
          continue;

        final Object fieldValue = doc.field(fieldName);
        if (fieldValue != null)
          if (fieldValue instanceof Collection<?>)
            counter += ((Collection<?>) fieldValue).size();
          else if (fieldValue instanceof Map<?, ?>)
            counter += ((Map<?, ?>) fieldValue).size();
          else
            counter++;
      }
    } else {
      // SLOWER: BROWSE & FILTER
      for (Edge e : getEdges(iDirection, iLabels))
        if (e != null)
          counter++;
    }
    return counter;
  }

  public Iterable<Edge> getEdges(final Direction iDirection, final String... iLabels) {
    return getEdges(null, iDirection, iLabels);
  }

  public Iterable<Edge> getEdges(final Vertex iDestination, final Direction iDirection, final String... iLabels) {
    final ODocument doc = getRecord();

    final OMultiCollectionIterator<Edge> iterable = new OMultiCollectionIterator<Edge>().setEmbedded(true);
    for (String fieldName : doc.fieldNames()) {
      final OPair<Direction, String> connection = getConnection(iDirection, fieldName, iLabels);
      if (connection == null)
        // SKIP THIS FIELD
        continue;

      final Object fieldValue = doc.field(fieldName);
      if (fieldValue != null) {
        final OIdentifiable destinationVId = iDestination != null ? (OIdentifiable) iDestination.getId() : null;

        if (fieldValue instanceof OIdentifiable) {
          addSingleEdge(doc, iterable, fieldName, connection, fieldValue, destinationVId, iLabels);

        } else if (fieldValue instanceof Collection<?>) {
          Collection<?> coll = (Collection<?>) fieldValue;

          if (coll.size() == 1) {
            // SINGLE ITEM: AVOID CALLING ITERATOR
            if (coll instanceof ORecordLazyMultiValue)
              addSingleEdge(doc, iterable, fieldName, connection, ((ORecordLazyMultiValue) coll).rawIterator().next(),
                  destinationVId, iLabels);
            else if (coll instanceof List<?>)
              addSingleEdge(doc, iterable, fieldName, connection, ((List<?>) coll).get(0), destinationVId, iLabels);
            else
{'fraction_non_alphanumeric': 0.10571758246130586, 'fraction_numerical': 0.0007161898698921736, 'mean_word_length': 3.211461126005362, 'pattern_counts': {'":': 0, '<': 83, '<?xml version=': 0, '>': 87, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4895936', 'n_tokens_mistral': 7294, 'n_tokens_neox': 7064, 'n_words': 1815}