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

com.tinkerpop.blueprints.impls.orient.OrientBaseGraph Maven / Gradle / Ivy

There is a newer version: 3.2.41
Show newest version
/*
 *
 *  *  Copyright 2014 Orient Technologies LTD (info(at)orientechnologies.com)
 *  *
 *  *  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.
 *  *
 *  * For more information: http://www.orientechnologies.com
 *
 */

package com.tinkerpop.blueprints.impls.orient;

import com.orientechnologies.common.concur.ONeedRetryException;
import com.orientechnologies.common.exception.OException;
import com.orientechnologies.common.io.OFileUtils;
import com.orientechnologies.common.log.OLogManager;
import com.orientechnologies.common.util.OCallable;
import com.orientechnologies.orient.client.remote.OStorageRemote;
import com.orientechnologies.orient.core.OOrientListenerAbstract;
import com.orientechnologies.orient.core.Orient;
import com.orientechnologies.orient.core.command.OCommandRequest;
import com.orientechnologies.orient.core.command.traverse.OTraverse;
import com.orientechnologies.orient.core.config.OGlobalConfiguration;
import com.orientechnologies.orient.core.config.OStorageEntryConfiguration;
import com.orientechnologies.orient.core.conflict.ORecordConflictStrategy;
import com.orientechnologies.orient.core.db.ODatabase.ATTRIBUTES;
import com.orientechnologies.orient.core.db.ODatabaseRecordThreadLocal;
import com.orientechnologies.orient.core.db.OPartitionedDatabasePool;
import com.orientechnologies.orient.core.db.document.ODatabaseDocument;
import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx;
import com.orientechnologies.orient.core.db.record.OIdentifiable;
import com.orientechnologies.orient.core.db.record.ridbag.ORidBag;
import com.orientechnologies.orient.core.exception.ODatabaseException;
import com.orientechnologies.orient.core.exception.ORecordNotFoundException;
import com.orientechnologies.orient.core.id.ORID;
import com.orientechnologies.orient.core.id.ORecordId;
import com.orientechnologies.orient.core.index.*;
import com.orientechnologies.orient.core.intent.OIntent;
import com.orientechnologies.orient.core.metadata.schema.*;
import com.orientechnologies.orient.core.record.ORecord;
import com.orientechnologies.orient.core.record.impl.ODocument;
import com.orientechnologies.orient.core.record.impl.ODocumentInternal;
import com.orientechnologies.orient.core.storage.OStorage;
import com.orientechnologies.orient.core.storage.impl.local.OAbstractPaginatedStorage;
import com.orientechnologies.orient.core.storage.impl.local.OStorageRecoverListener;
import com.tinkerpop.blueprints.*;
import com.tinkerpop.blueprints.util.ExceptionFactory;
import com.tinkerpop.blueprints.util.StringFactory;
import com.tinkerpop.blueprints.util.wrappers.partition.PartitionVertex;
import org.apache.commons.configuration.Configuration;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.*;
import java.util.logging.Level;

/**
 * A Blueprints implementation of the graph database OrientDB (http://www.orientechnologies.com)
 *
 * @author Luca Garulli (http://www.orientechnologies.com)
 */
public abstract class OrientBaseGraph extends OrientConfigurableGraph implements OrientExtendedGraph, OStorageRecoverListener {
  public static final String                                  CONNECTION_OUT      = "out";
  public static final String                                  CONNECTION_IN       = "in";
  public static final String                                  CLASS_PREFIX        = "class:";
  public static final String                                  CLUSTER_PREFIX      = "cluster:";
  public static final String                                  ADMIN               = "admin";
  private static volatile ThreadLocal        activeGraph         = new ThreadLocal();
  private static volatile ThreadLocal> initializationStack = new InitializationStackThreadLocal();

  static {
    Orient.instance().registerListener(new OOrientListenerAbstract() {
      @Override public void onStartup() {
        if (activeGraph == null)
          activeGraph = new ThreadLocal();
        if (initializationStack == null)
          initializationStack = new InitializationStackThreadLocal();
      }

      @Override public void onShutdown() {
        activeGraph = null;
        initializationStack = null;
      }
    });
  }

  private final OPartitionedDatabasePool pool;
  protected ODatabaseDocumentTx          database;
  private String                         url;
  private String                         username;
  private String                         password;

  /**
   * Constructs a new object using an existent database instance.
   *
   * @param iDatabase
   *          Underlying database object to attach
   */
  public OrientBaseGraph(final ODatabaseDocumentTx iDatabase, final String iUserName, final String iUserPassword,
      final Settings iConfiguration) {
    this.pool = null;
    this.username = iUserName;
    this.password = iUserPassword;

    database = iDatabase;

    makeActive();
    putInInitializationStack();

    readDatabaseConfiguration();
    configure(iConfiguration);
  }

  public OrientBaseGraph(final OPartitionedDatabasePool pool) {
    this.pool = pool;

    database = pool.acquire();
    makeActive();
    putInInitializationStack();

    this.username = getDatabase().getUser() != null ? getDatabase().getUser().getName() : null;

    readDatabaseConfiguration();
  }

  public OrientBaseGraph(final OPartitionedDatabasePool pool, final Settings iConfiguration) {
    this.pool = pool;

    database = pool.acquire();
    makeActive();
    putInInitializationStack();
    this.username = getDatabase().getUser() != null ? getDatabase().getUser().getName() : null;

    readDatabaseConfiguration();
    configure(iConfiguration);
  }

  public OrientBaseGraph(final String url) {
    this(url, ADMIN, ADMIN);
  }

  public OrientBaseGraph(final String url, final String username, final String password) {
    this.pool = null;
    this.url = OFileUtils.getPath(url);
    this.username = username;
    this.password = password;
    this.openOrCreate();
    readDatabaseConfiguration();
  }

  /**
   * Builds a OrientGraph instance passing a configuration. Supported configuration settings are:
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
   * 
NameDescriptionDefault value
blueprints.orientdb.urlDatabase URL-
blueprints.orientdb.usernameUser nameadmin
blueprints.orientdb.passwordUser passwordadmin
blueprints.orientdb.saveOriginalIdsSaves the original element IDs by using the property origId. This could be useful on import of graph to preserve original * idsfalse
blueprints.orientdb.keepInMemoryReferencesAvoid to keep records in memory but only RIDsfalse
blueprints.orientdb.useCustomClassesForEdgesUse Edge's label as OrientDB class. If doesn't exist create it under the hoodtrue
blueprints.orientdb.useCustomClassesForVertexUse Vertex's label as OrientDB class. If doesn't exist create it under the hoodtrue
blueprints.orientdb.useVertexFieldsForEdgeLabelsStore the edge relationships in vertex by using the Edge's class. This allow to use multiple fields and make faster * traversal by edge's label (class)true
blueprints.orientdb.lightweightEdgesUses lightweight edges. This avoid to create a physical document per edge. Documents are created only when they have * propertiestrue
blueprints.orientdb.autoScaleEdgeTypeSet auto scale of edge type. True means one edge is managed as LINK, 2 or more are managed with a LINKBAGfalse
blueprints.orientdb.edgeContainerEmbedded2TreeThresholdChanges the minimum number of edges for edge containers to transform the underlying structure from embedded to tree. Use -1 * to disable transformation-1
blueprints.orientdb.edgeContainerTree2EmbeddedThresholdChanges the minimum number of edges for edge containers to transform the underlying structure from tree to embedded. Use -1 * to disable transformation-1
* * @param configuration * of graph */ public OrientBaseGraph(final Configuration configuration) { this(configuration.getString("blueprints.orientdb.url", null), configuration.getString("blueprints.orientdb.username", null), configuration.getString("blueprints.orientdb.password", null)); super.init(configuration); } abstract OrientEdge addEdgeInternal(OrientVertex currentVertex, String label, OrientVertex inVertex, String iClassName, String iClusterName, Object... fields); abstract void removeEdgesInternal(final OrientVertex vertex, final ODocument iVertex, final OIdentifiable iVertexToRemove, final boolean iAlsoInverse, final boolean useVertexFieldsForEdgeLabels, final boolean autoScaleEdgeType); abstract void removeEdgeInternal(OrientEdge currentVertex); public static OrientBaseGraph getActiveGraph() { return activeGraph.get(); } /** * Internal use only. */ public static void clearInitStack() { final ThreadLocal> is = initializationStack; if (is != null) is.get().clear(); final ThreadLocal ag = activeGraph; if (ag != null) ag.remove(); } @Override public void onStorageRecover() { final String sqlGraphConsistencyMode = OGlobalConfiguration.SQL_GRAPH_CONSISTENCY_MODE.getValueAsString(); if ("notx_sync_repair".equalsIgnoreCase(sqlGraphConsistencyMode)) { // WAIT FOR REPAIR TO COMPLETE new OGraphRepair().repair(this, OLogManager.instance().getCommandOutputListener(this, Level.INFO)); } else if ("notx_async_repair".equalsIgnoreCase(sqlGraphConsistencyMode)) { // RUNNING REPAIR IN BACKGROUND final OrientBaseGraph g = this; new Thread(new Runnable() { @Override public void run() { new OGraphRepair().repair(g, OLogManager.instance().getCommandOutputListener(this, Level.INFO)); } }).start(); } } /** * (Internal) */ public static void encodeClassNames(final String... iLabels) { if (iLabels != null) // ENCODE LABELS for (int i = 0; i < iLabels.length; ++i) iLabels[i] = encodeClassName(iLabels[i]); } /** * (Internal) Returns the case sensitive edge class names. */ public static void getEdgeClassNames(final OrientBaseGraph graph, final String... iLabels) { if (iLabels != null && graph != null && graph.isUseClassForEdgeLabel()) { for (int i = 0; i < iLabels.length; ++i) { final OrientEdgeType edgeType = graph.getEdgeType(iLabels[i]); if (edgeType != null) // OVERWRITE CLASS NAME BECAUSE ATTRIBUTES ARE CASE SENSITIVE iLabels[i] = edgeType.getName(); } } } /** * (Internal) */ public static String encodeClassName(String iClassName) { if (iClassName == null) return null; if (Character.isDigit(iClassName.charAt(0))) iClassName = "-" + iClassName; try { return URLEncoder.encode(iClassName, "UTF-8"); } catch (UnsupportedEncodingException e) { OLogManager.instance().error(null, "Error on encoding class name using encoding '%s'", e, "UTF-8"); return iClassName; } } /** * (Internal) */ public static String decodeClassName(String iClassName) { if (iClassName == null) return null; if (iClassName.charAt(0) == '-') iClassName = iClassName.substring(1); try { return URLDecoder.decode(iClassName, "UTF-8"); } catch (UnsupportedEncodingException e) { OLogManager.instance().error(null, "Error on decoding class name using encoding '%s'", e, "UTF-8"); return iClassName; } } public void makeActive() { activeGraph.set(this); final ODatabaseDocument tlDb = ODatabaseRecordThreadLocal.INSTANCE.getIfDefined(); if (getDatabase() != null && tlDb != getDatabase()) ODatabaseRecordThreadLocal.INSTANCE.set(getDatabase()); } /** * (Blueprints Extension) Configure the Graph instance. * * @param iSetting * Settings object containing all the settings */ public OrientBaseGraph configure(final Settings iSetting) { makeActive(); if (iSetting != null) { if (settings == null) { settings = iSetting; } else { settings.copyFrom(iSetting); } } return this; } /** * (Blueprints Extension) Drops the database */ public void drop() { makeActive(); getRawGraph().drop(); pollGraphFromStack(true); } @SuppressWarnings({ "unchecked", "rawtypes" }) public Index createIndex(final String indexName, final Class indexClass, final Parameter... indexParameters) { makeActive(); return executeOutsideTx(new OCallable, OrientBaseGraph>() { public Index call(final OrientBaseGraph g) { final OIndexManager indexManager = getDatabase().getMetadata().getIndexManager(); if (indexManager.getIndex(indexName) != null) throw ExceptionFactory.indexAlreadyExists(indexName); final OrientIndex index = new OrientIndex(g, indexName, indexClass, null); // SAVE THE CONFIGURATION INTO THE GLOBAL CONFIG saveIndexConfiguration(); return (Index) index; } }, "create index '", indexName, "'"); } /** * Returns an index by name and class * * @param indexName * Index name * @param indexClass * Class as one or subclass of Vertex.class and Edge.class * @return Index instance */ @SuppressWarnings("unchecked") @Override public Index getIndex(final String indexName, final Class indexClass) { makeActive(); final OIndexManager indexManager = getDatabase().getMetadata().getIndexManager(); final OIndex idx = indexManager.getIndex(indexName); if (idx == null || !hasIndexClass(idx)) return null; final Index index = new OrientIndex(this, idx); if (indexClass.isAssignableFrom(index.getIndexClass())) return (Index) index; else throw ExceptionFactory.indexDoesNotSupportClass(indexName, indexClass); } /** * Returns all the indices. * * @return Iterable of Index instances */ public Iterable> getIndices() { makeActive(); return loadManualIndexes(); } /** * Drops an index by name. * * @param indexName * Index name */ public void dropIndex(final String indexName) { makeActive(); executeOutsideTx(new OCallable() { @Override public Object call(OrientBaseGraph g) { try { final OIndexManager indexManager = getRawGraph().getMetadata().getIndexManager(); final OIndex index = indexManager.getIndex(indexName); ODocument metadata = index.getConfiguration().field("metadata"); String recordMapIndexName = null; if (metadata != null) { recordMapIndexName = metadata.field(OrientIndex.CONFIG_RECORD_MAP_NAME); } indexManager.dropIndex(indexName); if (recordMapIndexName != null) getRawGraph().getMetadata().getIndexManager().dropIndex(recordMapIndexName); saveIndexConfiguration(); return null; } catch (Exception e) { g.rollback(); throw new RuntimeException(e.getMessage(), e); } } }, "drop index '", indexName, "'"); } /** * Creates a new unconnected vertex with no fields in the Graph. * * @param id * Optional, can contains the Vertex's class name by prefixing with "class:" * @return The new OrientVertex created */ @Override public OrientVertex addVertex(final Object id) { makeActive(); return addVertex(id, (Object[]) null); } public ORecordConflictStrategy getConflictStrategy() { makeActive(); return getDatabase().getStorage().getConflictStrategy(); } public OrientBaseGraph setConflictStrategy(final String iStrategyName) { makeActive(); getDatabase().setConflictStrategy(Orient.instance().getRecordConflictStrategy().getStrategy(iStrategyName)); return this; } public OrientBaseGraph setConflictStrategy(final ORecordConflictStrategy iResolver) { makeActive(); getDatabase().setConflictStrategy(iResolver); return this; } /** * (Blueprints Extension) Creates a new unconnected vertex in the Graph setting the initial field values. * * @param id * Optional, can contains the Vertex's class name by prefixing with "class:" * @param prop * Fields must be a odd pairs of key/value or a single object as Map containing entries as key/value pairs * @return The new OrientVertex created */ public OrientVertex addVertex(Object id, final Object... prop) { makeActive(); String className = null; String clusterName = null; Object[] fields = null; if (id != null) { if (id instanceof String) { // PARSE ARGUMENTS final String[] args = ((String) id).split(","); for (String s : args) { if (s.startsWith(CLASS_PREFIX)) // GET THE CLASS NAME className = s.substring(CLASS_PREFIX.length()); else if (s.startsWith(CLUSTER_PREFIX)) // GET THE CLASS NAME clusterName = s.substring(CLUSTER_PREFIX.length()); else id = s; } } if (isSaveOriginalIds()) // SAVE THE ID TOO fields = new Object[] { OrientElement.DEF_ORIGINAL_ID_FIELDNAME, id }; } setCurrentGraphInThreadLocal(); autoStartTransaction(); final OrientVertex vertex = new OrientVertex(this, className, fields); vertex.setPropertiesInternal(prop); // SAVE IT if (clusterName != null) vertex.save(clusterName); else vertex.save(); return vertex; } /** * (Blueprints Extension) Creates a new unconnected vertex with no fields of specific class in a cluster in the Graph. * * @param iClassName * Vertex class name * @param iClusterName * Vertex cluster name * @return New vertex created */ public OrientVertex addVertex(final String iClassName, final String iClusterName) { makeActive(); setCurrentGraphInThreadLocal(); autoStartTransaction(); final OrientVertex vertex = new OrientVertex(this, iClassName); // SAVE IT if (iClusterName != null) vertex.save(iClusterName); else vertex.save(); return vertex; } /** * (Blueprints Extension) Creates a temporary vertex setting the initial field values. The vertex is not saved and the transaction * is not started. * * @param iClassName * Vertex's class name * @param prop * Fields must be a odd pairs of key/value or a single object as Map containing entries as key/value pairs * @return added vertex */ public OrientVertex addTemporaryVertex(final String iClassName, final Object... prop) { makeActive(); setCurrentGraphInThreadLocal(); autoStartTransaction(); final OrientVertex vertex = new OrientVertex(this, iClassName); vertex.setPropertiesInternal(prop); return vertex; } /** * Creates an edge between a source Vertex and a destination Vertex setting label as Edge's label. * * @param id * Optional, can contains the Edge's class name by prefixing with "class:" * @param outVertex * Source vertex * @param inVertex * Destination vertex * @param label * Edge's label * @return */ @Override public OrientEdge addEdge(final Object id, Vertex outVertex, Vertex inVertex, final String label) { makeActive(); String className = null; String clusterName = null; if (id != null) { if (id instanceof String) { // PARSE ARGUMENTS final String[] args = ((String) id).split(","); for (String s : args) { if (s.startsWith(CLASS_PREFIX)) // GET THE CLASS NAME className = s.substring(CLASS_PREFIX.length()); else if (s.startsWith(CLUSTER_PREFIX)) // GET THE CLASS NAME clusterName = s.substring(CLUSTER_PREFIX.length()); } } } // SAVE THE ID TOO? final Object[] fields = isSaveOriginalIds() && id != null ? new Object[] { OrientElement.DEF_ORIGINAL_ID_FIELDNAME, id } : null; if (outVertex instanceof PartitionVertex) // WRAPPED: GET THE BASE VERTEX outVertex = ((PartitionVertex) outVertex).getBaseVertex(); if (inVertex instanceof PartitionVertex) // WRAPPED: GET THE BASE VERTEX inVertex = ((PartitionVertex) inVertex).getBaseVertex(); return ((OrientVertex) outVertex).addEdge(label, (OrientVertex) inVertex, className, clusterName, fields); } /** * Returns a vertex by an ID. * * @param id * Can by a String, ODocument or an OIdentifiable object. */ public OrientVertex getVertex(final Object id) { makeActive(); if (null == id) throw ExceptionFactory.vertexIdCanNotBeNull(); if (id instanceof OrientVertex) return (OrientVertex) id; else if (id instanceof ODocument) return new OrientVertex(this, (OIdentifiable) id); setCurrentGraphInThreadLocal(); ORID rid; if (id instanceof OIdentifiable) rid = ((OIdentifiable) id).getIdentity(); else { try { rid = new ORecordId(id.toString()); } catch (IllegalArgumentException iae) { // orientdb throws IllegalArgumentException: Argument 'xxxx' is // not a RecordId in form of string. Format must be: // : return null; } } if (!rid.isValid()) return null; final ORecord rec = rid.getRecord(); if (rec == null || !(rec instanceof ODocument)) return null; final OClass cls = ((ODocument) rec).getSchemaClass(); if (cls != null && cls.isEdgeType()) throw new IllegalArgumentException("Cannot retrieve a vertex with the RID " + rid + " because it is an edge"); return new OrientVertex(this, rec); } @Override public void declareIntent(final OIntent iIntent) { makeActive(); getRawGraph().declareIntent(iIntent); } /** * Removes a vertex from the Graph. All the edges connected to the Vertex are automatically removed. * * @param vertex * Vertex to remove */ public void removeVertex(final Vertex vertex) { makeActive(); vertex.remove(); } /** * Get all the Vertices in Graph. * * @return Vertices as Iterable */ public Iterable getVertices() { makeActive(); return getVerticesOfClass(OrientVertexType.CLASS_NAME, true); } /** * Get all the Vertices in Graph specifying if consider or not sub-classes of V. * * @param iPolymorphic * If true then get all the vertices of any sub-class * @return Vertices as Iterable */ public Iterable getVertices(final boolean iPolymorphic) { makeActive(); return getVerticesOfClass(OrientVertexType.CLASS_NAME, iPolymorphic); } /** * Get all the Vertices in Graph of a specific vertex class and all sub-classes. * * @param iClassName * Vertex class name to filter * @return Vertices as Iterable */ public Iterable getVerticesOfClass(final String iClassName) { makeActive(); return getVerticesOfClass(iClassName, true); } /** * Get all the Vertices in Graph of a specific vertex class and all sub-classes only if iPolymorphic is true. * * @param iClassName * Vertex class name to filter * @param iPolymorphic * If true consider also Vertex iClassName sub-classes * @return Vertices as Iterable */ public Iterable getVerticesOfClass(final String iClassName, final boolean iPolymorphic) { makeActive(); final OClass cls = getRawGraph().getMetadata().getSchema().getClass(iClassName); if (cls == null) throw new IllegalArgumentException("Cannot find class '" + iClassName + "' in database schema"); if (!cls.isSubClassOf(OrientVertexType.CLASS_NAME)) throw new IllegalArgumentException("Class '" + iClassName + "' is not a vertex class"); return new OrientElementScanIterable(this, iClassName, iPolymorphic); } /** * Get all the Vertices in Graph filtering by field name and value. Example: * Iterable resultset = getVertices("name", "Jay"); * * * @param iKey * Field name * @param iValue * Field value * @return Vertices as Iterable */ public Iterable getVertices(final String iKey, Object iValue) { makeActive(); if (iKey.equals("@class")) return getVerticesOfClass(iValue.toString()); int pos = iKey.indexOf('.'); final String className = pos > -1 ? iKey.substring(0, pos):OrientVertexType.CLASS_NAME; final String key = pos > -1 ? iKey.substring(pos + 1) : iKey; OClass clazz = getDatabase().getMetadata().getImmutableSchemaSnapshot().getClass(className); if(clazz == null){ throw new IllegalArgumentException("OClass not found in the schema: " + className); } OIndex idx = null; final Collection> indexes = clazz.getIndexes(); for (OIndex index : indexes) { OIndexDefinition indexDef = index.getDefinition(); List indexedFields = indexDef.getFields(); if (indexedFields != null && indexedFields.size() > 0 && indexedFields.get(0).equals(key)) { idx = index; break; } } if(idx == null){ idx = getDatabase().getMetadata().getIndexManager().getIndex(iKey); } if (idx != null) { iValue = convertKey(idx, iValue); Object indexValue = idx.get(iValue); if (indexValue != null && !(indexValue instanceof Iterable)) indexValue = Arrays.asList(indexValue); return new OrientElementIterable(this, (Iterable) indexValue); } else { // NO INDEX: EXECUTE A QUERY OrientGraphQuery query = (OrientGraphQuery) query(); query.labels(clazz.getName()); return query.has(key, iValue).vertices(); } } /** * Lookup for a vertex by id using an index.
* This API relies on Unique index (SBTREE/HASH) but is deprecated.
* Example: * Vertex v = getVertexByKey("V.name", "name", "Jay"); * * * @param iKey * Name of the indexed property * @param iValue * Field value * @return Vertex instance if found, otherwise null * @see #getVertices(String, Object) */ @Deprecated public Vertex getVertexByKey(final String iKey, Object iValue) { makeActive(); String indexName; if (iKey.indexOf('.') > -1) indexName = iKey; else indexName = OrientVertexType.CLASS_NAME + "." + iKey; final OIndex idx = getDatabase().getMetadata().getIndexManager().getIndex(indexName); if (idx != null) { iValue = convertKey(idx, iValue); Object v = idx.get(iValue); if (v != null) return getVertex(v); return null; } else throw new IllegalArgumentException("Index '" + indexName + "' not found"); } /** * Get all the Vertices in Graph filtering by field name and value. Example: * Iterable resultset = getVertices("Person",new String[] {"name","surname"},new Object[] { "Sherlock" ,"Holmes"}); * * * @param iKey * Fields name * @param iValue * Fields value * @return Vertices as Iterable */ public Iterable getVertices(final String label, final String[] iKey, Object[] iValue) { makeActive(); final OClass clazz = getDatabase().getMetadata().getImmutableSchemaSnapshot().getClass(label); Set> indexes = clazz.getInvolvedIndexes(Arrays.asList(iKey)); if (indexes.iterator().hasNext()) { final OIndex idx = indexes.iterator().next(); if (idx != null) { List keys = Arrays.asList(convertKeys(idx, iValue)); Object key; if (keys.size() == 1) { key = keys.get(0); } else { key = new OCompositeKey(keys); } Object indexValue = idx.get(key); if (indexValue != null && !(indexValue instanceof Iterable)) indexValue = Arrays.asList(indexValue); return new OrientElementIterable(this, (Iterable) indexValue); } } // NO INDEX: EXECUTE A QUERY OrientGraphQuery query = (OrientGraphQuery) query(); query.labels(label); for (int i = 0; i < iKey.length; i++) { query.has(iKey[i], iValue[i]); } return query.vertices(); } /** * Returns all the edges in Graph. * * @return Edges as Iterable */ public Iterable getEdges() { makeActive(); return getEdgesOfClass(OrientEdgeType.CLASS_NAME, true); } /** * Get all the Edges in Graph specifying if consider or not sub-classes of E. * * @param iPolymorphic * If true then get all the edge of any sub-class * @return Edges as Iterable */ public Iterable getEdges(final boolean iPolymorphic) { makeActive(); return getEdgesOfClass(OrientEdgeType.CLASS_NAME, iPolymorphic); } /** * Get all the Edges in Graph of a specific edge class and all sub-classes. * * @param iClassName * Edge class name to filter * @return Edges as Iterable */ public Iterable getEdgesOfClass(final String iClassName) { makeActive(); return getEdgesOfClass(iClassName, true); } /** * Get all the Edges in Graph of a specific edges class and all sub-classes only if iPolymorphic is true. * * @param iClassName * Edge class name to filter * @param iPolymorphic * If true consider also iClassName Edge sub-classes * @return Edges as Iterable */ public Iterable getEdgesOfClass(final String iClassName, final boolean iPolymorphic) { makeActive(); final OClass cls = getRawGraph().getMetadata().getSchema().getClass(iClassName); if (cls == null) throw new IllegalArgumentException("Cannot find class '" + iClassName + "' in database schema"); if (!cls.isSubClassOf(OrientEdgeType.CLASS_NAME)) throw new IllegalArgumentException("Class '" + iClassName + "' is not an edge class"); return new OrientElementScanIterable(this, iClassName, iPolymorphic); } /** * Get all the Edges in Graph filtering by field name and value. Example: * Iterable resultset = getEdges("name", "Jay"); * * * @param iKey * Field name * @param iValue * Field value * @return Edges as Iterable */ public Iterable getEdges(final String iKey, Object iValue) { makeActive(); if (iKey.equals("@class")) return getEdgesOfClass(iValue.toString()); final String indexName; final String key; int pos = iKey.indexOf('.'); if (pos > -1) { indexName = iKey; key = iKey.substring(iKey.indexOf('.') + 1); } else { indexName = OrientEdgeType.CLASS_NAME + "." + iKey; key = iKey; } final OIndex idx = getDatabase().getMetadata().getIndexManager().getIndex(indexName); if (idx != null) { iValue = convertKey(idx, iValue); Object indexValue = idx.get(iValue); if (indexValue != null && !(indexValue instanceof Iterable)) indexValue = Arrays.asList(indexValue); return new OrientElementIterable(this, (Iterable) indexValue); } // NO INDEX: EXECUTE A QUERY return query().has(key, iValue).edges(); } /** * Returns a edge by an ID. * * @param id * Can by a String, ODocument or an OIdentifiable object. */ public OrientEdge getEdge(final Object id) { makeActive(); if (null == id) throw ExceptionFactory.edgeIdCanNotBeNull(); if (id instanceof OrientEdge) return (OrientEdge) id; else if (id instanceof ODocument) return new OrientEdge(this, (OIdentifiable) id); final OIdentifiable rec; if (id instanceof OIdentifiable) rec = (OIdentifiable) id; else { final String str = id.toString(); int pos = str.indexOf("->"); if (pos > -1) { // DUMMY EDGE: CREATE IT IN MEMORY final String from = str.substring(0, pos); final String to = str.substring(pos + 2); return new OrientEdge(this, new ORecordId(from), new ORecordId(to)); } try { rec = new ORecordId(str); } catch (IllegalArgumentException iae) { // orientdb throws IllegalArgumentException: Argument 'xxxx' is // not a RecordId in form of string. Format must be: // [#]: return null; } } final ODocument doc = rec.getRecord(); if (doc == null) return null; final OClass cls = doc.getSchemaClass(); if (cls != null) { if (cls.isVertexType()) throw new IllegalArgumentException("Cannot retrieve an edge with the RID " + id + " because it is a vertex"); if (!cls.isEdgeType()) throw new IllegalArgumentException("Class '" + doc.getClassName() + "' is not an edge class"); } return new OrientEdge(this, rec); } /** * Removes an edge from the Graph. * * @param edge * Edge to remove */ public void removeEdge(final Edge edge) { makeActive(); edge.remove(); } /** * Reuses the underlying database avoiding to create and open it every time. * * @param iDatabase * Underlying database object */ public OrientBaseGraph reuse(final ODatabaseDocumentTx iDatabase) { ODatabaseRecordThreadLocal.INSTANCE.set(iDatabase); this.url = iDatabase.getURL(); database = iDatabase; makeActive(); return this; } /** * Checks if the Graph has been closed. * * @return True if it is closed, otherwise false */ public boolean isClosed() { return database == null || database.isClosed(); } /** * Closes the Graph. After closing the Graph cannot be used. */ public void shutdown() { shutdown(true); } /** * Closes the Graph. After closing the Graph cannot be used. */ public void shutdown(boolean closeDb) { shutdown(closeDb, true); } /** * Closes the Graph. After closing the Graph cannot be used. */ public void shutdown(boolean closeDb, boolean commitTx) { makeActive(); try { if (!isClosed() && commitTx) { final OStorage storage = getDatabase().getStorage().getUnderlying(); if (storage instanceof OAbstractPaginatedStorage) { if (((OAbstractPaginatedStorage) storage).getWALInstance() != null) getDatabase().commit(); } } } catch (RuntimeException e) { OLogManager.instance().error(this, "Error during context close for db " + url, e); throw e; } catch (Exception e) { OLogManager.instance().error(this, "Error during context close for db " + url, e); throw OException.wrapException(new ODatabaseException("Error during context close for db " + url), e); } finally { try { if (closeDb) { getDatabase().close(); if (getDatabase().isPooled()) { database = null; } } } catch (Exception e) { OLogManager.instance().error(this, "Error during context close for db " + url, e); } } url = null; username = null; password = null; pollGraphFromStack(closeDb); if (!closeDb) getDatabase().activateOnCurrentThread(); } /** * Returns the Graph URL. */ public String toString() { return StringFactory.graphString(this, getRawGraph().getURL()); } /** * Returns the underlying Database instance as ODatabaseDocumentTx instance. */ public ODatabaseDocumentTx getRawGraph() { return getDatabase(); } /** * begins current transaction (if the graph is transactional) */ public void begin() { makeActive(); } /** * Commits the current active transaction. */ public void commit() { makeActive(); } /** * Rollbacks the current active transaction. All the pending changes are rollbacked. */ public void rollback() { makeActive(); } /** * Returns the V persistent class as OrientVertexType instance. */ public OrientVertexType getVertexBaseType() { makeActive(); return new OrientVertexType(this, getRawGraph().getMetadata().getSchema().getClass(OrientVertexType.CLASS_NAME)); } /** * Returns the persistent class for type iTypeName as OrientVertexType instance. * * @param iTypeName * Vertex class name */ public OrientVertexType getVertexType(final String iTypeName) { makeActive(); final OClass cls = getRawGraph().getMetadata().getSchema().getClass(iTypeName); if (cls == null) return null; OrientVertexType.checkType(cls); return new OrientVertexType(this, cls); } /** * Creates a new Vertex persistent class. * * @param iClassName * Vertex class name * @return OrientVertexType instance representing the persistent class */ public OrientVertexType createVertexType(final String iClassName) { makeActive(); return createVertexType(iClassName, (String) null); } /** * Creates a new Vertex persistent class. * * @param iClassName * Vertex class name * @param clusters * The number of clusters to create for the new class. By default the MINIMUMCLUSTERS database setting is used. In v2.2 * and later, the number of clusters are proportioned to the amount of cores found on the machine * @return OrientVertexType instance representing the persistent class */ public OrientVertexType createVertexType(final String iClassName, final int clusters) { makeActive(); return createVertexType(iClassName, (String) null, clusters); } /** * Creates a new Vertex persistent class specifying the super class. * * @param iClassName * Vertex class name * @param iSuperClassName * Vertex class name to extend * @return OrientVertexType instance representing the persistent class */ public OrientVertexType createVertexType(final String iClassName, final String iSuperClassName) { makeActive(); return createVertexType(iClassName, iSuperClassName == null ? getVertexBaseType() : getVertexType(iSuperClassName)); } /** * Creates a new Vertex persistent class specifying the super class. * * @param iClassName * Vertex class name * @param iSuperClassName * Vertex class name to extend * @param clusters * The number of clusters to create for the new class. By default the MINIMUMCLUSTERS database setting is used. In v2.2 * and later, the number of clusters are proportioned to the amount of cores found on the machine * @return OrientVertexType instance representing the persistent class */ public OrientVertexType createVertexType(final String iClassName, final String iSuperClassName, final int clusters) { makeActive(); return createVertexType(iClassName, iSuperClassName == null ? getVertexBaseType() : getVertexType(iSuperClassName), clusters); } /** * Creates a new Vertex persistent class specifying the super class. * * @param iClassName * Vertex class name * @param iSuperClass * OClass Vertex to extend * @return OrientVertexType instance representing the persistent class */ public OrientVertexType createVertexType(final String iClassName, final OClass iSuperClass) { makeActive(); OrientVertexType.checkType(iSuperClass); return executeOutsideTx(new OCallable() { @Override public OrientVertexType call(final OrientBaseGraph g) { return new OrientVertexType(g, getRawGraph().getMetadata().getSchema().createClass(iClassName, iSuperClass)); } }, "create vertex type '", iClassName, "' as subclass of '", iSuperClass.getName(), "'"); } /** * Creates a new Vertex persistent class specifying the super class. * * @param iClassName * Vertex class name * @param iSuperClass * OClass Vertex to extend * @return OrientVertexType instance representing the persistent class */ public OrientVertexType createVertexType(final String iClassName, final OClass iSuperClass, final int clusters) { makeActive(); OrientVertexType.checkType(iSuperClass); return executeOutsideTx(new OCallable() { @Override public OrientVertexType call(final OrientBaseGraph g) { return new OrientVertexType(g, getRawGraph().getMetadata().getSchema().createClass(iClassName, clusters, iSuperClass)); } }, "create vertex type '", iClassName, "' as subclass of '", iSuperClass.getName(), "' (clusters=" + clusters + ")"); } /** * Drop a vertex class. * * @param iTypeName * Vertex class name */ public void dropVertexType(final String iTypeName) { makeActive(); executeOutsideTx(new OCallable() { @Override public OClass call(final OrientBaseGraph g) { getRawGraph().getMetadata().getSchema().dropClass(iTypeName); return null; } }, "drop vertex type '", iTypeName, "'"); } /** * Returns the E persistent class as OrientEdgeType instance. */ public OrientEdgeType getEdgeBaseType() { makeActive(); return new OrientEdgeType(this); } /** * Returns the persistent class for type iTypeName as OrientEdgeType instance. * * @param iTypeName * Edge class name */ public OrientEdgeType getEdgeType(final String iTypeName) { makeActive(); final OClass cls = getRawGraph().getMetadata().getSchema().getClass(iTypeName); if (cls == null) return null; OrientEdgeType.checkType(cls); return new OrientEdgeType(this, cls); } /** * Creates a new Edge persistent class. * * @param iClassName * Edge class name * @return OrientEdgeType instance representing the persistent class */ public OrientEdgeType createEdgeType(final String iClassName) { makeActive(); return createEdgeType(iClassName, (String) null); } /** * Creates a new Edge persistent class. * * @param iClassName * Edge class name * @param clusters * The number of clusters to create for the new class. By default the MINIMUMCLUSTERS database setting is used. In v2.2 * and later, the number of clusters are proportioned to the amount of cores found on the machine * @return OrientEdgeType instance representing the persistent class */ public OrientEdgeType createEdgeType(final String iClassName, final int clusters) { makeActive(); return createEdgeType(iClassName, (String) null, clusters); } /** * Creates a new Edge persistent class specifying the super class. * * @param iClassName * Edge class name * @param iSuperClassName * Edge class name to extend * @return OrientEdgeType instance representing the persistent class */ public OrientEdgeType createEdgeType(final String iClassName, final String iSuperClassName) { makeActive(); return createEdgeType(iClassName, iSuperClassName == null ? getEdgeBaseType() : getEdgeType(iSuperClassName)); } /** * Creates a new Edge persistent class specifying the super class. * * @param iClassName * Edge class name * @param iSuperClassName * Edge class name to extend * @param clusters * The number of clusters to create for the new class. By default the MINIMUMCLUSTERS database setting is used. In v2.2 * and later, the number of clusters are proportioned to the amount of cores found on the machine * @return OrientEdgeType instance representing the persistent class */ public OrientEdgeType createEdgeType(final String iClassName, final String iSuperClassName, final int clusters) { makeActive(); return createEdgeType(iClassName, iSuperClassName == null ? getEdgeBaseType() : getEdgeType(iSuperClassName), clusters); } /** * Creates a new Edge persistent class specifying the super class. * * @param iClassName * Edge class name * @param iSuperClass * OClass Edge to extend * @param clusters * The number of clusters to create for the new class. By default the MINIMUMCLUSTERS database setting is used. In v2.2 * and later, the number of clusters are proportioned to the amount of cores found on the machine * @return OrientEdgeType instance representing the persistent class */ public OrientEdgeType createEdgeType(final String iClassName, final OClass iSuperClass, final int clusters) { makeActive(); OrientEdgeType.checkType(iSuperClass); return executeOutsideTx(new OCallable() { @Override public OrientEdgeType call(final OrientBaseGraph g) { return new OrientEdgeType(g, getRawGraph().getMetadata().getSchema().createClass(iClassName, clusters, iSuperClass)); } }, "create edge type '", iClassName, "' as subclass of '", iSuperClass.getName(), "' (clusters=" + clusters + ")"); } /** * Creates a new Edge persistent class specifying the super class. * * @param iClassName * Edge class name * @param iSuperClass * OClass Edge to extend * @return OrientEdgeType instance representing the persistent class */ public OrientEdgeType createEdgeType(final String iClassName, final OClass iSuperClass) { makeActive(); OrientEdgeType.checkType(iSuperClass); return executeOutsideTx(new OCallable() { @Override public OrientEdgeType call(final OrientBaseGraph g) { return new OrientEdgeType(g, getRawGraph().getMetadata().getSchema().createClass(iClassName, iSuperClass)); } }, "create edge type '", iClassName, "' as subclass of '", iSuperClass.getName(), "'"); } /** * Drops an edge class. * * @param iTypeName * Edge class name */ public void dropEdgeType(final String iTypeName) { makeActive(); executeOutsideTx(new OCallable() { @Override public OClass call(final OrientBaseGraph g) { getRawGraph().getMetadata().getSchema().dropClass(iTypeName); return null; } }, "drop edge type '", iTypeName, "'"); } /** * Detaches a Graph Element to be used offline. All the changes will be committed on further @attach call. * * @param iElement * Graph element to detach * @return The detached element * @see #attach(OrientElement) */ public OrientElement detach(final OrientElement iElement) { makeActive(); iElement.detach(); return iElement; } /** * Attaches a previously detached Graph Element to the current Graph. All the pending changes will be committed. * * @param iElement * Graph element to attach * @return The attached element * @see #detach(OrientElement) */ public OrientElement attach(final OrientElement iElement) { makeActive(); return iElement.attach(this); } /** * Returns a graph element, vertex or edge, starting from an ID. * * @param id * Can by a String, ODocument or an OIdentifiable object. * @return OrientElement subclass such as OrientVertex or OrientEdge */ public OrientElement getElement(final Object id) { makeActive(); if (null == id) throw new IllegalArgumentException("id cannot be null"); if (id instanceof OrientElement) return (OrientElement) id; OIdentifiable rec; if (id instanceof OIdentifiable) rec = (OIdentifiable) id; else try { rec = new ORecordId(id.toString()); } catch (IllegalArgumentException iae) { // orientdb throws IllegalArgumentException: Argument 'xxxx' is // not a RecordId in form of string. Format must be: // : return null; } final ODocument doc = rec.getRecord(); if (doc != null) { final OImmutableClass schemaClass = ODocumentInternal.getImmutableSchemaClass(doc); if (schemaClass != null && schemaClass.isEdgeType()) return new OrientEdge(this, doc); else return new OrientVertex(this, doc); // else // throw new IllegalArgumentException("Type error. The class " + schemaClass + " does not extend class neither '" // + OrientVertexType.CLASS_NAME + "' nor '" + OrientEdgeType.CLASS_NAME + "'"); } return null; } /** * Drops the index against a field name. * * @param key * Field name * @param elementClass * Element class as instances of Vertex and Edge */ public void dropKeyIndex(final String key, final Class elementClass) { makeActive(); if (elementClass == null) throw ExceptionFactory.classForElementCannotBeNull(); executeOutsideTx(new OCallable() { @Override public OClass call(final OrientBaseGraph g) { final String className = getClassName(elementClass); getRawGraph().getMetadata().getIndexManager().dropIndex(className + "." + key); return null; } }, "drop key index '", elementClass.getSimpleName(), ".", key, "'"); } /** * Creates an automatic indexing structure for indexing provided key for element class. * * @param key * the key to create the index for * @param elementClass * the element class that the index is for * @param indexParameters * a collection of parameters for the underlying index implementation: *
    *
  • "type" is the index type between the supported types (UNIQUE, NOTUNIQUE, FULLTEXT). The default type is NOT_UNIQUE *
  • "class" is the class to index when it's a custom type derived by Vertex (V) or Edge (E) *
  • "keytype" to use a key type different by OType.STRING,
  • *
* @param * the element class specification */ @SuppressWarnings({ "rawtypes" }) @Override public void createKeyIndex(final String key, final Class elementClass, final Parameter... indexParameters) { makeActive(); if (elementClass == null) throw ExceptionFactory.classForElementCannotBeNull(); executeOutsideTx(new OCallable() { @Override public OClass call(final OrientBaseGraph g) { String indexType = OClass.INDEX_TYPE.NOTUNIQUE.name(); OType keyType = OType.STRING; String className = null; String collate = null; ODocument metadata = null; final String ancestorClassName = getClassName(elementClass); // READ PARAMETERS for (Parameter p : indexParameters) { if (p.getKey().equals("type")) indexType = p.getValue().toString().toUpperCase(); else if (p.getKey().equals("keytype")) keyType = OType.valueOf(p.getValue().toString().toUpperCase()); else if (p.getKey().equals("class")) className = p.getValue().toString(); else if (p.getKey().equals("collate")) collate = p.getValue().toString(); else if (p.getKey().toString().startsWith("metadata.")) { if (metadata == null) metadata = new ODocument(); metadata.field(p.getKey().toString().substring("metadata.".length()), p.getValue()); } } if (className == null) className = ancestorClassName; final ODatabaseDocumentTx db = getRawGraph(); final OSchema schema = db.getMetadata().getSchema(); final OClass cls = schema.getOrCreateClass(className, schema.getClass(ancestorClassName)); final OProperty property = cls.getProperty(key); if (property != null) keyType = property.getType(); OPropertyIndexDefinition indexDefinition = new OPropertyIndexDefinition(className, key, keyType); if (collate != null) indexDefinition.setCollate(collate); db.getMetadata().getIndexManager().createIndex(className + "." + key, indexType, indexDefinition, cls.getPolymorphicClusterIds(), null, metadata); return null; } }, "create key index on '", elementClass.getSimpleName(), ".", key, "'"); } /** * Returns the indexed properties. * * @param elementClass * the element class that the index is for * @return Set of String containing the indexed properties */ @Override public Set getIndexedKeys(final Class elementClass) { makeActive(); return getIndexedKeys(elementClass, false); } /** * Returns the indexed properties. * * @param elementClass * the element class that the index is for * @param includeClassNames * If true includes also the class name as prefix of fields * @return Set of String containing the indexed properties */ public Set getIndexedKeys(final Class elementClass, final boolean includeClassNames) { makeActive(); if (elementClass == null) throw ExceptionFactory.classForElementCannotBeNull(); final OSchema schema = getRawGraph().getMetadata().getImmutableSchemaSnapshot(); final String elementOClassName = getClassName(elementClass); Set result = new HashSet(); final Collection> indexes = getRawGraph().getMetadata().getIndexManager().getIndexes(); for (OIndex index : indexes) { String indexName = index.getName(); int point = indexName.indexOf("."); if (point > 0) { String oClassName = indexName.substring(0, point); OClass oClass = schema.getClass(oClassName); if (oClass.isSubClassOf(elementOClassName)) { if (includeClassNames) result.add(index.getName()); else result.add(index.getDefinition().getFields().get(0)); } } } return result; } /** * Returns a GraphQuery object to execute queries against the Graph. * * @return new GraphQuery instance */ @Override public GraphQuery query() { makeActive(); return new OrientGraphQuery(this); } /** * Returns a OTraverse object to start traversing the graph. */ public OTraverse traverse() { makeActive(); return new OTraverse(); } /** * Executes commands against the graph. Commands are executed outside transaction. * * @param iCommand * Command request between SQL, GREMLIN and SCRIPT commands */ public OCommandRequest command(final OCommandRequest iCommand) { makeActive(); return new OrientGraphCommand(this, getRawGraph().command(iCommand)); } /** * Counts the vertices in graph. * * @return Long as number of total vertices */ public long countVertices() { return countVertices(OrientVertexType.CLASS_NAME); } /** * Counts the vertices in graph of a particular class. * * @return Long as number of total vertices */ public long countVertices(final String iClassName) { makeActive(); return getRawGraph().countClass(iClassName); } /** * Counts the edges in graph. Edge counting works only if useLightweightEdges is false. * * @return Long as number of total edges */ public long countEdges() { return countEdges(OrientEdgeType.CLASS_NAME); } /** * Counts the edges in graph of a particular class. Edge counting works only if useLightweightEdges is false. * * @return Long as number of total edges */ public long countEdges(final String iClassName) { makeActive(); if (isUseLightweightEdges()) throw new UnsupportedOperationException("Graph set to use Lightweight Edges, count against edges is not supported"); return getRawGraph().countClass(iClassName); } public RET executeOutsideTx(final OCallable iCallable, final String... iOperationStrings) throws RuntimeException { makeActive(); final int committed; final ODatabaseDocumentTx raw = getRawGraph(); if (raw.getTransaction().isActive()) { if (isWarnOnForceClosingTx() && OLogManager.instance().isWarnEnabled() && iOperationStrings.length > 0) { // COMPOSE THE MESSAGE final StringBuilder msg = new StringBuilder(256); for (String s : iOperationStrings) msg.append(s); // ASSURE PENDING TX IF ANY IS COMMITTED OLogManager.instance().warn(this, "Requested command '%s' must be executed outside active transaction: the transaction will be committed and reopen right after it. To avoid this behavior execute it outside a transaction", msg.toString()); } committed = raw.getTransaction().amountOfNestedTxs(); raw.commit(true); } else committed = 0; try { return iCallable.call(this); } finally { // RESTART TRANSACTION for (int i = 0; i < committed; ++i) ((OrientTransactionalGraph) this).begin(); } } protected void autoStartTransaction() { } protected void saveIndexConfiguration() { getRawGraph().getMetadata().getIndexManager().getConfiguration().save(); } protected String getClassName(final Class elementClass) { if (elementClass.isAssignableFrom(Vertex.class)) return OrientVertexType.CLASS_NAME; else if (elementClass.isAssignableFrom(Edge.class)) return OrientEdgeType.CLASS_NAME; throw new IllegalArgumentException("Class '" + elementClass + "' is neither a Vertex, nor an Edge"); } protected Object convertKey(final OIndex idx, Object iValue) { if (iValue != null) { final OType[] types = idx.getKeyTypes(); if (types.length == 0) iValue = iValue.toString(); else iValue = OType.convert(iValue, types[0].getDefaultJavaType()); } return iValue; } protected Object[] convertKeys(final OIndex idx, Object[] iValue) { if (iValue != null) { final OType[] types = idx.getKeyTypes(); if (types.length == iValue.length) { Object[] newValue = new Object[types.length]; for (int i = 0; i < types.length; i++) { newValue[i] = OType.convert(iValue[i], types[i].getDefaultJavaType()); } iValue = newValue; } } return iValue; } void throwRecordNotFoundException(final ORID identity, final String message) { if (settings.isStandardExceptions()) throw new IllegalStateException(message); else throw new ORecordNotFoundException(identity, message); } protected void setCurrentGraphInThreadLocal() { if (getThreadMode() == THREAD_MODE.MANUAL) return; final ODatabaseDocument tlDb = ODatabaseRecordThreadLocal.INSTANCE.getIfDefined(); if (getThreadMode() == THREAD_MODE.ALWAYS_AUTOSET || tlDb == null) { if (getDatabase() != null && tlDb != getDatabase()) // SET IT ODatabaseRecordThreadLocal.INSTANCE.set(getDatabase()); } } private void putInInitializationStack() { Deque stack = initializationStack.get(); stack.push(this); } private void pollGraphFromStack(boolean updateDb) { final Deque stack = initializationStack.get(); stack.remove(this); final OrientBaseGraph prevGraph = stack.peek(); if (prevGraph != null) { activeGraph.set(prevGraph); prevGraph.makeActive(); } else { activeGraph.set(null); if (updateDb) ODatabaseRecordThreadLocal.INSTANCE.set(null); } } @SuppressWarnings("unchecked") private void readDatabaseConfiguration() { final ODatabaseDocumentTx databaseDocumentTx = getRawGraph(); final List custom = (List) databaseDocumentTx.get(ATTRIBUTES.CUSTOM); for (OStorageEntryConfiguration c : custom) { if (c.name.equals("useLightweightEdges")) setUseLightweightEdges(Boolean.parseBoolean(c.value)); else if (c.name.equals("txRequiredForSQLGraphOperations")) // Since v2.2.0 setTxRequiredForSQLGraphOperations(Boolean.parseBoolean(c.value)); else if (c.name.equals("maxRetries")) // Since v2.2.0 setMaxRetries(Integer.parseInt(c.value)); else if (c.name.equals("useClassForEdgeLabel")) setUseClassForEdgeLabel(Boolean.parseBoolean(c.value)); else if (c.name.equals("useClassForVertexLabel")) setUseClassForVertexLabel(Boolean.parseBoolean(c.value)); else if (c.name.equals("useVertexFieldsForEdgeLabels")) setUseVertexFieldsForEdgeLabels(Boolean.parseBoolean(c.value)); else if (c.name.equals("standardElementConstraints")) setStandardElementConstraints(Boolean.parseBoolean(c.value)); } } private void openOrCreate() { if (url == null) throw new IllegalStateException("Database is closed"); if (pool == null) { database = new ODatabaseDocumentTx(url); final String connMode = settings.getConnectionStrategy(); getDatabase().setProperty(OStorageRemote.PARAM_CONNECTION_STRATEGY, connMode); if (getDatabase().getStorage().getUnderlying() instanceof OAbstractPaginatedStorage) ((OAbstractPaginatedStorage) getDatabase().getStorage().getUnderlying()).registerRecoverListener(this); if (url.startsWith("remote:") || getDatabase().exists()) { if (getDatabase().isClosed()) getDatabase().open(username, password); } else getDatabase().create(); } else { database = pool.acquire(); if (getDatabase().getStorage().getUnderlying() instanceof OAbstractPaginatedStorage) ((OAbstractPaginatedStorage) getDatabase().getStorage().getUnderlying()).registerRecoverListener(this); } makeActive(); putInInitializationStack(); } private List> loadManualIndexes() { final List> result = new ArrayList>(); for (OIndex idx : getDatabase().getMetadata().getIndexManager().getIndexes()) { if (hasIndexClass(idx)) // LOAD THE INDEXES result.add(new OrientIndex(this, idx)); } return result; } private boolean hasIndexClass(OIndex idx) { final ODocument metadata = idx.getMetadata(); return (metadata != null && metadata.field(OrientIndex.CONFIG_CLASSNAME) != null) // compatibility with versions earlier 1.6.3 || idx.getConfiguration().field(OrientIndex.CONFIG_CLASSNAME) != null; } protected ODatabaseDocumentTx getDatabase() { if (database == null) { throw new ODatabaseException("Database is closed"); } return database; } private static class InitializationStackThreadLocal extends ThreadLocal> { @Override protected Deque initialValue() { return new LinkedList(); } } /** * (Internal only) */ protected static void removeEdges(final OrientBaseGraph graph, final ODocument iVertex, final String iFieldName, final OIdentifiable iVertexToRemove, final boolean iAlsoInverse, final boolean useVertexFieldsForEdgeLabels, final boolean autoScaleEdgeType, final boolean forceReload) { if (iVertex == null) return; final Object fieldValue = iVertexToRemove != null ? iVertex.field(iFieldName) : iVertex.removeField(iFieldName); if (fieldValue == null) return; if (fieldValue instanceof OIdentifiable) { // SINGLE RECORD if (iVertexToRemove != null) { if (!fieldValue.equals(iVertexToRemove)) // NOT FOUND return; iVertex.removeField(iFieldName); deleteEdgeIfAny(iVertexToRemove, forceReload); } if (iAlsoInverse) removeInverseEdge(graph, iVertex, iFieldName, iVertexToRemove, (OIdentifiable) fieldValue, useVertexFieldsForEdgeLabels, autoScaleEdgeType, forceReload); } else if (fieldValue instanceof ORidBag) { // COLLECTION OF RECORDS: REMOVE THE ENTRY final ORidBag bag = (ORidBag) fieldValue; if (iVertexToRemove != null) { // SEARCH SEQUENTIALLY (SLOWER) for (Iterator it = bag.rawIterator(); it.hasNext();) { final ODocument curr = getDocument(it.next(), forceReload); if (curr == null) // ALREADY DELETED (BYPASSING GRAPH API?), JUST REMOVE THE REFERENCE FROM BAG it.remove(); else if (iVertexToRemove.equals(curr)) { // FOUND AS VERTEX it.remove(); if (iAlsoInverse) removeInverseEdge(graph, iVertex, iFieldName, iVertexToRemove, curr, useVertexFieldsForEdgeLabels, autoScaleEdgeType, forceReload); break; } else if (ODocumentInternal.getImmutableSchemaClass(curr).isEdgeType()) { final Direction direction = OrientVertex.getConnectionDirection(iFieldName, useVertexFieldsForEdgeLabels); // EDGE, REMOVE THE EDGE if (iVertexToRemove.equals(OrientEdge.getConnection(curr, direction.opposite()))) { it.remove(); if (iAlsoInverse) removeInverseEdge(graph, iVertex, iFieldName, iVertexToRemove, curr, useVertexFieldsForEdgeLabels, autoScaleEdgeType, forceReload); break; } } } deleteEdgeIfAny(iVertexToRemove, forceReload); } else { // DELETE ALL THE EDGES for (Iterator it = bag.rawIterator(); it.hasNext();) { OIdentifiable edge = it.next(); if (iAlsoInverse) removeInverseEdge(graph, iVertex, iFieldName, null, edge, useVertexFieldsForEdgeLabels, autoScaleEdgeType, forceReload); deleteEdgeIfAny(edge, forceReload); } } if (autoScaleEdgeType && bag.isEmpty()) // FORCE REMOVAL OF ENTIRE FIELD iVertex.removeField(iFieldName); } else if (fieldValue instanceof Collection) { final Collection col = (Collection) fieldValue; if (iVertexToRemove != null) { // SEARCH SEQUENTIALLY (SLOWER) for (Iterator it = col.iterator(); it.hasNext();) { final ODocument curr = getDocument(it.next(), forceReload); if (iVertexToRemove.equals(curr)) { // FOUND AS VERTEX it.remove(); if (iAlsoInverse) removeInverseEdge(graph, iVertex, iFieldName, iVertexToRemove, curr, useVertexFieldsForEdgeLabels, autoScaleEdgeType, forceReload); break; } else if (ODocumentInternal.getImmutableSchemaClass(curr).isVertexType()) { final Direction direction = OrientVertex.getConnectionDirection(iFieldName, useVertexFieldsForEdgeLabels); // EDGE, REMOVE THE EDGE if (iVertexToRemove.equals(OrientEdge.getConnection(curr, direction.opposite()))) { it.remove(); if (iAlsoInverse) removeInverseEdge(graph, iVertex, iFieldName, iVertexToRemove, curr, useVertexFieldsForEdgeLabels, autoScaleEdgeType, forceReload); break; } } } deleteEdgeIfAny(iVertexToRemove, forceReload); } else { // DELETE ALL THE EDGES for (OIdentifiable edge : (Iterable) col) { if (iAlsoInverse) removeInverseEdge(graph, iVertex, iFieldName, null, edge, useVertexFieldsForEdgeLabels, autoScaleEdgeType, forceReload); deleteEdgeIfAny(edge, forceReload); } } if (autoScaleEdgeType && col.isEmpty()) // FORCE REMOVAL OF ENTIRE FIELD iVertex.removeField(iFieldName); } } /** * (Internal only) */ private static void removeInverseEdge(final OrientBaseGraph graph, final ODocument iVertex, final String iFieldName, final OIdentifiable iVertexToRemove, final OIdentifiable currentRecord, final boolean useVertexFieldsForEdgeLabels, final boolean autoScaleEdgeType, boolean forceReload) { final ODocument r = getDocument(currentRecord, forceReload); if (r == null) return; final String inverseFieldName = OrientVertex.getInverseConnectionFieldName(iFieldName, useVertexFieldsForEdgeLabels); OImmutableClass immutableClass = ODocumentInternal.getImmutableSchemaClass(r); if (immutableClass.isVertexType()) { // DIRECT VERTEX removeEdges(graph, r, inverseFieldName, iVertex, false, useVertexFieldsForEdgeLabels, autoScaleEdgeType, forceReload); r.save(); } else if (immutableClass.isEdgeType()) { // EDGE, REMOVE THE EDGE final OIdentifiable otherVertex = OrientEdge.getConnection(r, OrientVertex.getConnectionDirection(inverseFieldName, useVertexFieldsForEdgeLabels)); if (otherVertex != null) { if (iVertexToRemove == null || otherVertex.equals(iVertexToRemove)) { final int maxRetries = graph.getMaxRetries(); for (int retry = 0; retry < maxRetries; ++retry) { try { final ODocument otherVertexRecord = getDocument(otherVertex, forceReload); // BIDIRECTIONAL EDGE removeEdges(graph, otherVertexRecord, inverseFieldName, (OIdentifiable) currentRecord, false, useVertexFieldsForEdgeLabels, autoScaleEdgeType, forceReload); if (otherVertexRecord != null) otherVertexRecord.save(); break; } catch (ONeedRetryException e) { // RETRY } } } } } } private static ODocument getDocument(final OIdentifiable id, final boolean forceReload) { final ODocument doc = id.getRecord(); if (doc != null && forceReload) { try { doc.reload(); } catch (ORecordNotFoundException e) { // IGNORE IT AND RETURN NULL } } return doc; } /** * (Internal only) */ private static void deleteEdgeIfAny(final OIdentifiable iRecord, boolean forceReload) { if (iRecord != null) { final ODocument doc = getDocument(iRecord, forceReload); if (doc != null) { final OImmutableClass clazz = ODocumentInternal.getImmutableSchemaClass(doc); if (clazz != null && clazz.isEdgeType()) // DELETE THE EDGE RECORD TOO doc.delete(); } } } }