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

org.apache.tinkerpop.gremlin.arcadedb.structure.ArcadeCypher 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 org.apache.tinkerpop.gremlin.arcadedb.structure;

import com.arcadedb.GlobalConfiguration;
import com.arcadedb.gremlin.query.CypherQueryEngine;
import com.arcadedb.query.sql.executor.InternalResultSet;
import com.arcadedb.query.sql.executor.Result;
import com.arcadedb.query.sql.executor.ResultSet;
import org.opencypher.gremlin.translation.CypherAst;
import org.opencypher.gremlin.translation.TranslationFacade;
import org.opencypher.gremlin.translation.groovy.GroovyPredicate;
import org.opencypher.gremlin.translation.translator.Translator;

import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;

/**
 * Cypher Expression builder. Transform a cypher expression into Gremlin.
 *
 * @author Luca Garulli ([email protected])
 */

public class ArcadeCypher extends ArcadeGremlin {
  private static final HashMap STATEMENT_CACHE       = new HashMap<>();
  private static final int                              CACHE_SIZE            = GlobalConfiguration.CYPHER_STATEMENT_CACHE.getValueAsInteger();
  private static final AtomicInteger                    totalCachedStatements = new AtomicInteger(0);

  private static class CachedStatement {
    public final String cypher;
    public final String gremlin;
    public       int    used = 0;

    private CachedStatement(final String cypher, final String gremlin) {
      this.cypher = cypher;
      this.gremlin = gremlin;
    }
  }

  protected ArcadeCypher(final ArcadeGraph graph, final String cypherQuery, final Map parameters) {
    super(graph, compileToGremlin(graph, cypherQuery, parameters));
  }

  @Override
  public ResultSet execute() throws ExecutionException, InterruptedException {
    final ResultSet resultSet = super.execute();

    final InternalResultSet result = new InternalResultSet();

    while (resultSet.hasNext()) {
      final Result next = resultSet.next();
      if (next.isElement())
        result.add(next);
      else {
        // unpack single result projections
        Map map = next.toMap();
        Object nextValue = map.values().iterator().next();
        if (map.size() == 1 && nextValue instanceof Map) {
          result.addAll(CypherQueryEngine.transformMap((Map) nextValue));
        } else {
          result.addAll(CypherQueryEngine.transformMap(map));
        }
      }
    }
    return result;
  }

  public static String compileToGremlin(final ArcadeGraph graph, final String cypher, final Map parameters) {
    if (CACHE_SIZE == 0)
      // NO CACHE
      return new TranslationFacade().toGremlinGroovy(cypher);

    synchronized (STATEMENT_CACHE) {
      final String db = graph.getDatabase().getDatabasePath();

      final String mapKey = db + ":" + cypher;

      CachedStatement cached = STATEMENT_CACHE.get(mapKey);
      // FOUND
      if (cached != null) {
        ++cached.used;
        return cached.gremlin;
      }

      // TRANSLATE TO GREMLIN AND CACHE THE STATEMENT FOR FURTHER USAGE
      final CypherAst ast = parameters == null ? CypherAst.parse(cypher) : CypherAst.parse(cypher, parameters);
      Translator translator = Translator.builder().gremlinGroovy().enableCypherExtensions().build();
      final String gremlin = ast.buildTranslation(translator);

      while (totalCachedStatements.get() >= CACHE_SIZE) {
        int leastUsedValue = 0;
        String leastUsedKey = null;

        for (Map.Entry entry : STATEMENT_CACHE.entrySet()) {
          if (leastUsedKey == null || entry.getValue().used < leastUsedValue) {
            leastUsedKey = entry.getKey();
            leastUsedValue = entry.getValue().used;
          }
        }

        STATEMENT_CACHE.remove(leastUsedKey);
        STATEMENT_CACHE.put(cypher, new CachedStatement(cypher, gremlin));
      }

      return gremlin;
    }
  }

  public static void closeDatabase(final ArcadeGraph graph) {
    synchronized (STATEMENT_CACHE) {
      final String mapKey = graph.getDatabase().getDatabasePath() + ":";

      // REMOVE ALL THE ENTRIES RELATIVE TO THE CLOSED DATABASE
      STATEMENT_CACHE.entrySet().removeIf(stringCachedStatementEntry -> stringCachedStatementEntry.getKey().startsWith(mapKey));
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy