in gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java [3743:3801]
public default GraphTraversal<S, E> property(final VertexProperty.Cardinality cardinality, final Object key, final Object value, final Object... keyValues) {
if (key instanceof T && null == value)
throw new IllegalArgumentException("Value of T cannot be null");
if (null == cardinality)
this.asAdmin().getGremlinLang().addStep(Symbols.property, key, value, keyValues);
else
this.asAdmin().getGremlinLang().addStep(Symbols.property, cardinality, key, value, keyValues);
// if it can be detected that this call to property() is related to an addV/E() then we can attempt to fold
// the properties into that step to gain an optimization for those graphs that support such capabilities.
Step endStep = this.asAdmin().getEndStep();
// always try to fold the property() into the initial "AddElementStep" as the performance will be better
// and as it so happens with T the value must be set by way of that approach otherwise you get an error.
// it should be safe to execute this loop this way as we'll either hit an "AddElementStep" or an "EmptyStep".
// if empty, it will just use the regular AddPropertyStep being tacked on to the end of the traversal as usual
while (endStep instanceof AddPropertyStep) {
endStep = endStep.getPreviousStep();
}
// edge properties can always be folded as there are no cardinality/metaproperties. of course, if the
// cardinality is specified as something other than single or null it would be confusing to simply allow it to
// execute and not throw an error.
if ((endStep instanceof AddEdgeStep || endStep instanceof AddEdgeStartStep) && (null != cardinality && cardinality != single))
throw new IllegalStateException(String.format(
"Multi-property cardinality of [%s] can only be set for a Vertex but is being used for addE() with key: %s",
cardinality.name(), key));
// for a vertex mutation, it's possible to fold the property() into the Mutating step if there are no
// metaproperties (i.e. keyValues) and if (1) the key is an instance of T OR OR (3) the key is a string and the
// cardinality is not specified. Note that checking for single cardinality of the argument doesn't work well
// because once folded we lose the cardinality argument associated to the key/value pair and then it relies on
// the graph. that means that if you do:
//
// g.addV().property(single, 'k',1).property(single,'k',2)
//
// you could end up with whatever the cardinality is for the key which might seem "wrong" if you were explicit
// about the specification of "single". it also isn't possible to check the Graph Features for cardinality
// as folding seems to have different behavior based on different graphs - we clearly don't have that aspect
// of things tested/enforced well.
//
// of additional note is the folding that occurs if the key is a Traversal. the key here is technically
// unknown until traversal execution as the anonymous traversal result isn't evaluated during traversal
// construction but during iteration. not folding to AddVertexStep creates different (breaking) traversal
// semantics than we've had in previous versions so right/wrong could be argued, but since it's a breaking
// change we'll just arbitrarily account for it to maintain the former behavior.
if ((endStep instanceof AddEdgeStep || endStep instanceof AddEdgeStartStep) ||
((endStep instanceof AddVertexStep || endStep instanceof AddVertexStartStep) &&
keyValues.length == 0 &&
(key instanceof T || (key instanceof String && null == cardinality) || key instanceof Traversal))) {
((Mutating) endStep).configure(key, value);
} else {
final AddPropertyStep<Element> addPropertyStep = new AddPropertyStep<>(this.asAdmin(), cardinality, key, value);
this.asAdmin().addStep(addPropertyStep);
addPropertyStep.configure(keyValues);
}
return this;
}