This is an automated email from the ASF dual-hosted git repository. spmallette pushed a commit to branch jsprocess in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit 84586ac66794a57031c16d1d4ae8b6e9e382b5d3 Author: Stephen Mallette <[email protected]> AuthorDate: Tue Jun 30 12:33:26 2026 -0400 Add is() and loops() to gremlin-javascript Tiny Gremlin loops() reports the enclosing repeat()'s iteration count and pairs with is() to bound a loop, e.g. until(__.loops().is(2)). Also restores the dropped Tiny Gremlin changelog entry. Assisted-by: Claude Code:claude-opus-4-8 --- CHANGELOG.asciidoc | 2 + docs/src/dev/provider/tiny-gremlin.asciidoc | 30 +++++-- .../lib/language/executor/ExecuteVisitor.ts | 18 +++++ .../lib/process/local/LocalExecutor.ts | 28 +++++-- .../gremlin-javascript/lib/process/local/passes.ts | 4 +- .../gremlin-javascript/lib/process/local/steps.ts | 67 +++++++++++---- .../gremlin-javascript/lib/process/local/types.ts | 8 ++ .../test/unit/tiny-gremlin-is-loops-test.js | 94 ++++++++++++++++++++++ .../gremlin/test/features/branch/Repeat.feature | 1 + .../gremlin/test/features/filter/Is.feature | 6 ++ 10 files changed, 230 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 963ad54f5c..85472872c1 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -130,6 +130,8 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima * Added the `gql-gremlin` module containing the TinkerGQL engine, a `MATCH`-pattern executor implementing a deliberate minimal subset of ISO GQL, usable by any graph provider. * Added `Graph.countVerticesByLabel(String)`, `Graph.countEdgesByLabel(String)`, and the `Graph.Index` nested interface as performance-hint extension points for the TinkerGQL engine. * Added GQL support to TinkerGraph with `gql-gremlin`, +* Added Tiny Gremlin semantics which provider a defined subset of Gremlin steps (navigation, filtering, value extraction, path tracking, range, ordering, looping, and local mutation) for use as an official lightweight Gremlin implementation. +* Implemented Tiny Gremlin for Typescript. [[release-4-0-0-beta-2]] === TinkerPop 4.0.0-beta.2 (April 1, 2026) diff --git a/docs/src/dev/provider/tiny-gremlin.asciidoc b/docs/src/dev/provider/tiny-gremlin.asciidoc index 1bbb310827..c7a6f94dd5 100644 --- a/docs/src/dev/provider/tiny-gremlin.asciidoc +++ b/docs/src/dev/provider/tiny-gremlin.asciidoc @@ -42,7 +42,7 @@ deliberately excluded. The result is a small, self-contained, predictable execut === Supported Steps -Tiny Gremlin supports exactly the 37 steps listed below, grouped by category. No other step may be used in a Tiny +Tiny Gremlin supports exactly the 39 steps listed below, grouped by category. No other step may be used in a Tiny Gremlin traversal; doing so results in an error before any graph data is accessed. [width="100%",options="header"] @@ -50,12 +50,12 @@ Gremlin traversal; doing so results in an error before any graph data is accesse |Category |Steps |Source |`V()`, `E()` |Navigation |`out()`, `in()`, `both()`, `outE()`, `inE()`, `bothE()`, `outV()`, `inV()`, `otherV()` -|Filter |`has()`, `hasId()`, `hasLabel()`, `hasNot()` +|Filter |`has()`, `hasId()`, `hasLabel()`, `hasNot()`, `is()` |Value Extraction |`id()`, `label()`, `values()`, `valueMap()`, `elementMap()`, `value()`, `key()` |Path |`path()` |Range |`limit()`, `range()`, `skip()`, `tail()` |Ordering |`order()` -|Branch |`repeat()` +|Branch |`repeat()`, `loops()` |Mutation |`addV()`, `addE()`, `property()` |Modulators |`from()`, `to()`, `times()`, `until()`, `emit()` |=== @@ -228,6 +228,14 @@ Maps a vertex to its incoming edges. Maps an edge to its incoming vertex (the head of the arrow). +=== is() + +*Gremlin Semantics reference:* <<is-step,is()>> + +Filters the stream, keeping only traversers whose value equals the given object or satisfies the given predicate, for +example `is(32)` or `is(P.gte(29))`. It pairs naturally with `loops()` to bound a `repeat()`, as in +`until(__.loops().is(2))`. + === key() *Gremlin Semantics reference:* <<key-step,key()>> @@ -250,6 +258,18 @@ Truncates the result stream to at most `n` elements. * The `Scope` form is not supported and will raise an error. +=== loops() + +*Gremlin Semantics reference:* <<loops-step,loops()>> + +Maps each traverser to the loop count of the nearest enclosing `repeat()` — zero on the first body pass, incrementing +each iteration. It is most often used as an exit condition with `is()`, so `repeat(traversal).until(__.loops().is(2))` +is equivalent to `repeat(traversal).times(2)`. Outside any `repeat()` it is zero. + +*Tiny Gremlin limitations:* + +* The `loops("label")` form, which references a labeled loop, is not supported because Tiny Gremlin has no step labels. + === order() *Gremlin Semantics reference:* <<order-step,order()>> @@ -365,8 +385,8 @@ modulator relative to `repeat()` determines its semantics, matching full Gremlin Declared after `repeat()` it emits traversers leaving the body, and declared before it emits them entering the body. A `condition` for `until()` or `emit()` is an anonymous filter traversal, satisfied when it produces at least one -result. The loop body and these conditions may themselves contain any supported step, including `order().by()` or -`path()`. +result. The loop body and these conditions may themselves contain any supported step, including `order().by()`, +`path()`, or `loops()` — so `until(__.loops().is(2))` bounds the loop by iteration count, equivalent to `times(2)`. The loop is driven breadth-first over the whole frontier: at each loop iteration every surviving traverser advances in lockstep through one shared evaluation of the body. A barrier step such as `order().by()` inside the body diff --git a/gremlin-js/gremlin-javascript/lib/language/executor/ExecuteVisitor.ts b/gremlin-js/gremlin-javascript/lib/language/executor/ExecuteVisitor.ts index 84b77dc3e4..e593bef2a7 100644 --- a/gremlin-js/gremlin-javascript/lib/language/executor/ExecuteVisitor.ts +++ b/gremlin-js/gremlin-javascript/lib/language/executor/ExecuteVisitor.ts @@ -516,6 +516,24 @@ export class ExecuteVisitor { this.push('emit', [this.recoverPredicate(ctx.traversalPredicate())]); } + visitTraversalMethod_loops_Empty(_ctx: any): void { + this.push('loops', []); + } + + // loops(String) — the loop-label reference. Tiny Gremlin has no step labels, so the + // label is carried through for the executor to reject with a clear message. + visitTraversalMethod_loops_String(ctx: any): void { + this.push('loops', [this.recoverStringLiteral(ctx.stringLiteral())]); + } + + visitTraversalMethod_is_Object(ctx: any): void { + this.push('is', [this.recoverGenericArg(ctx.genericArgument())]); + } + + visitTraversalMethod_is_P(ctx: any): void { + this.push('is', [this.recoverPredicate(ctx.traversalPredicate())]); + } + // ── Mutation steps ──────────────────────────────────────────────────────── visitTraversalMethod_addV_Empty(ctx: any): void { diff --git a/gremlin-js/gremlin-javascript/lib/process/local/LocalExecutor.ts b/gremlin-js/gremlin-javascript/lib/process/local/LocalExecutor.ts index a37f25ebff..18090527c5 100644 --- a/gremlin-js/gremlin-javascript/lib/process/local/LocalExecutor.ts +++ b/gremlin-js/gremlin-javascript/lib/process/local/LocalExecutor.ts @@ -24,9 +24,9 @@ import { Graph, Vertex, Edge } from '../../structure/graph.js'; import { wrap, getValue, NON_PRODUCTIVE, type StreamItem, stepOut, stepIn, stepBoth, stepOutE, stepInE, stepBothE, stepOutV, stepInV, stepOtherV, - stepHas, stepHasId, stepHasLabel, stepHasNot, + stepHas, stepHasId, stepHasLabel, stepHasNot, stepIs, stepId, stepLabel, stepValue, stepKey, stepValues, stepValueMap, stepElementMap, - stepPath, stepRepeat, + stepPath, stepRepeat, stepLoops, stepLimit, stepRange, stepSkip, stepTail, stepOrder, stepAddV, stepAddEWithModulators, stepProperty, } from './steps.js'; @@ -183,9 +183,19 @@ export class LocalExecutor { /** Builds the ExecutionContext handed to parent steps for running their child pipelines. */ private makeContext(graph: Graph, trackPaths: boolean): ExecutionContext { + return this.buildContext(graph, trackPaths, 0); + } + + /** + * Builds a context at a specific loops() value. withLoops() returns a fresh context rather + * than mutating, so a nested repeat() can set its own loop count without clobbering the + * outer loop's — each level's loops() reads the value baked into its own context object. + */ + private buildContext(graph: Graph, trackPaths: boolean, loops: number): ExecutionContext { const ctx: ExecutionContext = { graph, trackPaths, + loops, runBranch: (child, src) => { let stream: Iterable<StreamItem> = src; for (const step of child) stream = this.applyStep(step, stream, ctx); @@ -193,17 +203,18 @@ export class LocalExecutor { }, runProject: (child, object) => { // child is a single value-extraction step (validated during folding). - const probe: ExecutionContext = { ...ctx, trackPaths: false }; + const probe = this.buildContext(graph, false, loops); for (const out of this.applyStep(child[0], [wrap(object, [], false)], probe)) { return getValue(out, false); } return NON_PRODUCTIVE; // non-productive by(Traversal) filters the path traverser }, runRooted: (child) => { - const probe: ExecutionContext = { ...ctx, trackPaths: false }; + const probe = this.buildContext(graph, false, loops); for (const item of this.buildChain(child, probe)) return item; return null; }, + withLoops: (n) => this.buildContext(graph, trackPaths, n), }; return ctx; } @@ -329,9 +340,10 @@ function requireTraversalCondition(stepName: string, arg: Arg | undefined): Pipe } // ── Parent step registry ──────────────────────────────────────────────────────── -// Steps that run nested child pipelines. They receive the ExecutionContext and use -// its runBranch/runProject/runRooted helpers, so adding a new branching step is a -// registry entry rather than a special case in applyStep. +// Steps that need the ExecutionContext — to run nested child pipelines (path, addE, +// repeat) or to read loop state (loops). They use its runBranch/runProject/runRooted +// helpers and loops field, so adding such a step is a registry entry rather than a +// special case in applyStep. const PARENT_STEPS = new Map<string, ParentStepFn>([ ['path', (source, args, ctx) => stepPath(source, args, ctx.trackPaths, (sub, obj) => ctx.runProject(sub, obj))], @@ -345,6 +357,7 @@ const PARENT_STEPS = new Map<string, ParentStepFn>([ (sub) => ctx.runRooted(sub as unknown as Pipeline), )], ['repeat', (source, args, ctx) => stepRepeat(source, args[0] as unknown as RepeatSpec, ctx)], + ['loops', (source, args, ctx) => stepLoops(source, args, ctx)], ]); // ── Result copying ──────────────────────────────────────────────────────────── @@ -426,6 +439,7 @@ const STEP_REGISTRY = new Map<string, StepFn>([ ['hasId', stepHasId], ['hasLabel', stepHasLabel], ['hasNot', stepHasNot], + ['is', stepIs], ['id', stepId], ['label', stepLabel], ['value', stepValue], diff --git a/gremlin-js/gremlin-javascript/lib/process/local/passes.ts b/gremlin-js/gremlin-javascript/lib/process/local/passes.ts index be4088837b..948d3915b2 100644 --- a/gremlin-js/gremlin-javascript/lib/process/local/passes.ts +++ b/gremlin-js/gremlin-javascript/lib/process/local/passes.ts @@ -26,12 +26,12 @@ export type OptimizationPass = (pipeline: Pipeline) => Pipeline; const SUPPORTED_STEPS = new Set<string>([ 'V', 'E', 'out', 'in', 'both', 'outE', 'inE', 'bothE', 'outV', 'inV', 'otherV', - 'has', 'hasId', 'hasLabel', 'hasNot', + 'has', 'hasId', 'hasLabel', 'hasNot', 'is', 'id', 'label', 'values', 'valueMap', 'elementMap', 'value', 'key', 'path', 'limit', 'range', 'skip', 'tail', 'order', 'by', - 'repeat', 'times', 'until', 'emit', + 'repeat', 'times', 'until', 'emit', 'loops', 'addV', 'addE', 'property', 'from', 'to', ]); diff --git a/gremlin-js/gremlin-javascript/lib/process/local/steps.ts b/gremlin-js/gremlin-javascript/lib/process/local/steps.ts index 164accf753..70a3598e2c 100644 --- a/gremlin-js/gremlin-javascript/lib/process/local/steps.ts +++ b/gremlin-js/gremlin-javascript/lib/process/local/steps.ts @@ -329,6 +329,20 @@ export function* stepHasNot( } } +export function* stepIs( + source: Iterable<StreamItem>, args: Arg[], graph: Graph, trackPaths: boolean, +): Generator<StreamItem> { + const arg = args[0]; + for (const item of source) { + const v = getValue(item, trackPaths); + if (arg instanceof P || arg instanceof TextP) { + if (evaluatePredicate(arg, v)) yield item; + } else if (v === arg) { + yield item; + } + } +} + export function* stepHasKey( source: Iterable<StreamItem>, args: Arg[], graph: Graph, trackPaths: boolean, ): Generator<StreamItem> { @@ -767,6 +781,24 @@ export function* stepProperty( } } +// ── Loops step ──────────────────────────────────────────────────────────────── + +/** + * Replaces each traverser's value with the loop count of the nearest enclosing repeat() + * (ctx.loops), typically tested by a following is(), e.g. until(__.loops().is(2)). Zero + * outside any repeat(). The loops("label") form is rejected — Tiny Gremlin has no step labels. + */ +export function* stepLoops( + source: Iterable<StreamItem>, args: Arg[], ctx: ExecutionContext, +): Generator<StreamItem> { + if (args.length > 0) { + throw new Error('loops("label") is not supported in Tiny Gremlin; step labels are excluded'); + } + for (const item of source) { + yield wrap(ctx.loops, getPath(item, ctx.trackPaths), ctx.trackPaths); + } +} + // ── Repeat step ─────────────────────────────────────────────────────────────── /** @@ -795,47 +827,54 @@ export function* stepRepeat( ctx: ExecutionContext, ): Generator<StreamItem> { // until()/emit() conditions are always filter traversals here; the predicate form - // is rejected during folding (see requireTraversalCondition in LocalExecutor). - const conditionHolds = (cond: Pipeline, item: StreamItem): boolean => { - for (const _out of ctx.runBranch(cond, [item])) return true; // filter traversal: any output => true + // is rejected during folding (see requireTraversalCondition in LocalExecutor). Each is + // run through a context carrying the loop count it should observe via loops(), so + // until(__.loops().is(n)) sees the same counter that times(n) compares against. + const conditionHolds = (cond: Pipeline, item: StreamItem, condCtx: ExecutionContext): boolean => { + for (const _out of condCtx.runBranch(cond, [item])) return true; // filter traversal: any output => true return false; }; - const exitBeforeBody = (item: StreamItem, loops: number): boolean => { + const exitBeforeBody = (item: StreamItem, loops: number, condCtx: ExecutionContext): boolean => { if (spec.times != null && spec.timesFirst && loops >= spec.times) return true; - if (spec.untilFirst && spec.until != null && conditionHolds(spec.until, item)) return true; + if (spec.untilFirst && spec.until != null && conditionHolds(spec.until, item, condCtx)) return true; return false; }; - const exitAfterBody = (item: StreamItem, loops: number): boolean => { + const exitAfterBody = (item: StreamItem, loops: number, condCtx: ExecutionContext): boolean => { if (spec.times != null && !spec.timesFirst && loops >= spec.times) return true; - if (!spec.untilFirst && spec.until != null && conditionHolds(spec.until, item)) return true; + if (!spec.untilFirst && spec.until != null && conditionHolds(spec.until, item, condCtx)) return true; return false; }; - const shouldEmit = (item: StreamItem): boolean => { + const shouldEmit = (item: StreamItem, condCtx: ExecutionContext): boolean => { if (!spec.emitPresent) return false; if (spec.emit == null) return true; // bare emit() — emit every traverser - return conditionHolds(spec.emit, item); + return conditionHolds(spec.emit, item, condCtx); }; let frontier: StreamItem[] = [...source]; let loops = 0; while (frontier.length > 0) { + // The body, the while-phase until(), and an emit-before condition all observe the + // current loop count; the do-while until() and emit-after condition observe the + // post-body count. + const curCtx = ctx.withLoops(loops); // While-phase: traversers that hit a before-body exit leave the loop now; // the rest (optionally emitted ahead of the body) form the body's input. const entering: StreamItem[] = []; for (const item of frontier) { - if (exitBeforeBody(item, loops)) { yield item; continue; } - if (spec.emitFirst && shouldEmit(item)) yield item; + if (exitBeforeBody(item, loops, curCtx)) { yield item; continue; } + if (spec.emitFirst && shouldEmit(item, curCtx)) yield item; entering.push(item); } if (entering.length === 0) break; // Run the body over the entire frontier in one pass so barriers see it all. const nextLoops = loops + 1; + const nextCtx = ctx.withLoops(nextLoops); const nextFrontier: StreamItem[] = []; - for (const out of ctx.runBranch(spec.body, entering)) { - if (exitAfterBody(out, nextLoops)) { yield out; continue; } - if (!spec.emitFirst && shouldEmit(out)) yield out; + for (const out of curCtx.runBranch(spec.body, entering)) { + if (exitAfterBody(out, nextLoops, nextCtx)) { yield out; continue; } + if (!spec.emitFirst && shouldEmit(out, nextCtx)) yield out; nextFrontier.push(out); } frontier = nextFrontier; diff --git a/gremlin-js/gremlin-javascript/lib/process/local/types.ts b/gremlin-js/gremlin-javascript/lib/process/local/types.ts index d1aee1bb83..9e11ff7286 100644 --- a/gremlin-js/gremlin-javascript/lib/process/local/types.ts +++ b/gremlin-js/gremlin-javascript/lib/process/local/types.ts @@ -67,12 +67,20 @@ export type Pipeline = StepDescriptor[]; export interface ExecutionContext { graph: Graph; trackPaths: boolean; + /** + * The loop count of the nearest enclosing repeat(), read by loops(). Zero outside any + * repeat() and on a repeat()'s first body pass. repeat() supplies the value to its body + * and its until()/emit() conditions via withLoops(). + */ + loops: number; /** Apply a child pipeline as mid-traversal steps over an existing stream (flatMap per traverser). */ runBranch(child: Pipeline, source: Iterable<any>): Iterable<any>; /** Run a single value-extraction child against one object, returning its first result or NON_PRODUCTIVE. */ runProject(child: Pipeline, object: any): any; /** Run a child as a complete source-rooted pipeline (e.g. addE from/to), returning the first result. */ runRooted(child: Pipeline): any; + /** Derive a context with a different loops() value, used by repeat() per loop iteration. */ + withLoops(loops: number): ExecutionContext; } /** diff --git a/gremlin-js/gremlin-javascript/test/unit/tiny-gremlin-is-loops-test.js b/gremlin-js/gremlin-javascript/test/unit/tiny-gremlin-is-loops-test.js new file mode 100644 index 0000000000..8a786ab6d5 --- /dev/null +++ b/gremlin-js/gremlin-javascript/test/unit/tiny-gremlin-is-loops-test.js @@ -0,0 +1,94 @@ +/* + * 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. + */ + +import { assert } from 'chai'; +import anon from '../../lib/process/anonymous-traversal.js'; +import { statics as __ } from '../../lib/process/graph-traversal.js'; +import { P } from '../../lib/process/traversal.js'; +import { buildModernGraph } from '../cucumber/local-fixtures.js'; + +/** + * Unit coverage for is() (value/predicate filter) and loops() (the enclosing repeat()'s + * loop counter) in the Tiny Gremlin local executor. loops() is exercised through repeat() + * since that is where it carries meaning; the two steps are paired because loops() is inert + * without a following is() to test the count. + */ +describe('Tiny Gremlin is() and loops()', () => { + let g; + const markoId = 1; + + beforeEach(() => { + g = anon.traversal().with_(buildModernGraph().graph); + }); + + // ── is() ────────────────────────────────────────────────────────────────── + + it('is(value) keeps only equal values', async () => { + const ages = await g.V().values('age').is(32).toList(); + assert.deepEqual(ages, [32]); + }); + + it('is(P) filters by predicate', async () => { + const ages = await g.V().values('age').is(P.lte(30)).toList(); + assert.sameMembers(ages, [27, 29]); + }); + + it('chains is() predicates as a conjunction', async () => { + const ages = await g.V().values('age').is(P.gte(29)).is(P.lt(34)).toList(); + assert.sameMembers(ages, [29, 32]); + }); + + // ── loops() ─────────────────────────────────────────────────────────────── + + it('until(loops().is(n)) before repeat() is while-bounded like times(n)', async () => { + // condition tested before the body: exits once the current loop count reaches 2 + const names = await g.V().until(__.loops().is(2)).repeat(__.out()).values('name').toList(); + assert.sameMembers(names, ['lop', 'ripple']); + }); + + it('until(loops().is(n)) after repeat() is do-while and matches times(n)', async () => { + const viaLoops = await g.V(markoId).repeat(__.out()).until(__.loops().is(2)).values('name').toList(); + const viaTimes = await g.V(markoId).repeat(__.out()).times(2).values('name').toList(); + assert.sameMembers(viaLoops, viaTimes); + assert.sameMembers(viaLoops, ['lop', 'ripple']); + }); + + it('reports the inner loop count inside a nested repeat()', async () => { + // inner repeat exits when ITS own loops() reaches 1 (one out() hop), then the outer + // repeat takes a second hop; loops() resolves to the nearest enclosing repeat(). + const names = await g.V(markoId) + .repeat(__.repeat(__.out()).until(__.loops().is(1))).until(__.loops().is(2)) + .values('name').toList(); + assert.sameMembers(names, ['lop', 'ripple']); + }); + + it('is zero outside any repeat()', async () => { + const loops = await g.V().loops().toList(); + assert.deepEqual(loops, [0, 0, 0, 0, 0, 0]); + }); + + it('rejects the loops("label") reference (no step labels in Tiny Gremlin)', async () => { + try { + await g.V(markoId).repeat(__.out()).until(__.loops('a').is(2)).toList(); + assert.fail('expected a rejection for loops("label")'); + } catch (err) { + assert.match(err.message, /loops\("label"\) is not supported/); + } + }); +}); diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/branch/Repeat.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/branch/Repeat.feature index a5652a312b..6364afefa3 100644 --- a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/branch/Repeat.feature +++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/branch/Repeat.feature @@ -951,6 +951,7 @@ Feature: Step - repeat() | josh | | peter | + @TinyGremlin Scenario: g_V_untilXloops_isX2XX_repeatXout_order_byXnameXX_valuesXnameX Given the modern graph And the traversal of diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/Is.feature b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/Is.feature index 93afd854b9..95e556cc9b 100644 --- a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/Is.feature +++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/Is.feature @@ -18,6 +18,7 @@ @StepClassFilter @StepIs Feature: Step - is() + @TinyGremlin Scenario: g_V_valuesXageX_isX32X Given the modern graph And the traversal of @@ -29,6 +30,7 @@ Feature: Step - is() | result | | d[32].i | + @TinyGremlin Scenario: g_V_valuesXageX_isX32varX Given the modern graph And using the parameter xx1 defined as "d[32].i" @@ -41,6 +43,7 @@ Feature: Step - is() | result | | d[32].i | + @TinyGremlin Scenario: g_V_valuesXageX_isXlte_30X Given the modern graph And the traversal of @@ -53,6 +56,7 @@ Feature: Step - is() | d[27].i | | d[29].i | + @TinyGremlin Scenario: g_V_valuesXageX_isXlte_30varX Given the modern graph And using the parameter xx1 defined as "d[30].i" @@ -66,6 +70,7 @@ Feature: Step - is() | d[27].i | | d[29].i | + @TinyGremlin Scenario: g_V_valuesXageX_isXgte_29X_isXlt_34X Given the modern graph And the traversal of @@ -78,6 +83,7 @@ Feature: Step - is() | d[29].i | | d[32].i | + @TinyGremlin Scenario: g_V_valuesXageX_isXgte_29vaarX_isXlt_34varX Given the modern graph And using the parameter xx1 defined as "d[29].i"
