blob: 36e74935ab715152ec2793f8a120fbb0d81a03f9 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.tinkerpop.gremlin.process.traversal.step;
import org.apache.tinkerpop.gremlin.process.traversal.Path;
import org.apache.tinkerpop.gremlin.process.traversal.Pop;
import org.apache.tinkerpop.gremlin.process.traversal.Step;
import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
import java.util.Map;
import java.util.Set;
/**
* This interface is implemented by {@link Step} implementations that access labeled path steps, side-effects or
* {@code Map} values by key, such as {@code select('a')} step. Note that a step like {@code project()} is non-scoping
* because while it creates a {@code Map} it does not introspect them.
* <p/>
* There are four types of scopes:
* <ol>
* <li>Current scope</li> — the current data referenced by the traverser (“path head”).
* <li>Path scope</li> — a particular piece of data in the path of the traverser (“path history”).
* <li>Side-effect scope</li> — a particular piece of data in the global traversal blackboard.
* <li>Map scope</li> — a particular piece of data in the current scope map (“map value by key”).
* </ol>
*
* The current scope refers to the current object referenced by the traverser. That is, the {@code traverser.get()}
* object. Another way to think about the current scope is to think in terms of the path of the traverser where the
* current scope is the head of the path. With the math()-step, the variable {@code _} refers to the current scope.
*
* <pre>
* {@code
* gremlin> g.V().values("age").math("sin _")
* ==>-0.6636338842129675
* ==>0.956375928404503
* ==>0.5514266812416906
* ==>-0.428182669496151
* }
* </pre>
*
* The path scope refers to data previously seen by the traverser. That is, data in the traverser’s path history.
* Paths can be accessed by {@code path()}, however, individual parts of the path can be labeled using {@code as()}
* and accessed later via the path label name. Thus, in the traversal below, “a” and “b” refer to objects previously
* traversed by the traverser.
*
* <pre>
* {@code
* gremlin> g.V().as("a").out("knows").as("b”).
* math("a / b").by("age")
* ==>1.0740740740740742
* ==>0.90625
* }
* </pre>
*
* The side-effect scope refers objects in the global side-effects of the traversal. Side-effects are not local to the
* traverser, but instead, global to the traversal. In the traversal below you can see how “x” is being referenced in
* the math()-step and thus, the side-effect data is being used.
*
* <pre>
* {@code
* gremlin> g.withSideEffect("x",100).V().values("age").math("_ / x")
* ==>0.29
* ==>0.27
* ==>0.32
* ==>0.35
* }
* </pre>
*
* Map scope refers to objects within the current map object. Thus, its like current scope, but a bit “deeper.” In the
* traversal below the {@code project()}-step generates a map with keys “a” and “b”. The subsequent {@code math()}-step
* is then able to access the “a” and “b” values in the respective map and use them for the division operation.
*
* <pre>
* {@code gremlin>
* g.V().hasLabel("person”).
* project("a","b”).
* by("age”).
* by(bothE().count()).
* math("a / b")
* ==>9.666666666666666
* ==>27.0
* ==>10.666666666666666
* ==>35.0
* }
* </pre>
*
* Scoping is all about variable data access and forms the fundamental interface for access to the memory structures
* of Gremlin.
*
* @author Marko A. Rodriguez (http://markorodriguez.com)
* @author Stephen Mallette (http://stephen.genoprime.com)
*/
public interface Scoping {
public enum Variable {START, END}
public default <S> S getScopeValue(final Pop pop, final String key, final Traverser.Admin<?> traverser) throws IllegalArgumentException {
final S o = getNullableScopeValue(pop, key, traverser);
if (null == o)
throw new IllegalArgumentException("Neither the sideEffects, map, nor path has a " + key + "-key: " + this);
return o;
}
public default <S> S getNullableScopeValue(final Pop pop, final String key, final Traverser.Admin<?> traverser) {
final Object object = traverser.get();
if (object instanceof Map && ((Map<String, S>) object).containsKey(key))
return ((Map<String, S>) object).get(key);
if (traverser.getSideEffects().exists(key))
return traverser.getSideEffects().get(key);
final Path path = traverser.path();
if (path.hasLabel(key))
return path.get(pop, key);
return null;
}
/**
* Get the labels that this scoping step will access during the traversal
*
* @return the accessed labels of the scoping step
*/
public Set<String> getScopeKeys();
}