Hi Andrea,
Example of using these interfaces.
GraphStep implements ElementReader.
This step is almost always overridden by providers, even in TinkerPop.
By implementing ElementReader interface provider can easily understand what
needs to be read from storage database.
VertexStep implements ElementReader, AdjacentProcessor, PropertiesProcessor
implementation of AdjacentProcessor can be like
```
public Pair<AdjacentRequirement, String[]> getAdjacentRequirement() {
return Pair.with(
this.direction == Direction.IN ? AdjacentRequirement.IN :
this.direction == Direction.OUT ?
AdjacentRequirement.OUT : AdjacentRequirement.BOTH,
this.edgeLabels);
}
@Override
public Pair<PropertiesRequirement, String[]> getPropertiesRequirement() {
return Pair.with(PropertiesRequirement.NONE, null);
}
```
PropertiesStep implements PropertiesProcessor, AdjacentProcessor
this step can uses only a subset of the properties and does not use information
about adjacent edges, so implementation can be like
```
@Override
public Pair<AdjacentRequirement, String[]> getAdjacentRequirement() {
return Pair.with(AdjacentRequirement.NONE, null);
}
@Override
public Pair<PropertiesRequirement, String[]> getPropertiesRequirement() {
if (propertyKeys == null) {
return Pair.with(PropertiesRequirement.ALL, null);
} else if (propertyKeys.length == 0) {
return Pair.with(PropertiesRequirement.NONE, null);
} else {
// if there are some keys, then it is CUSTOM
return Pair.with(PropertiesRequirement.CUSTOM, propertyKeys);
}
}
```
Strategy is also needed to spread information about the requirements of the
different steps, from back to start.
There are some peculiarities in working with labels (as('a')...select('a')) and
child traversers.
Example of optimization for traversal g.V().out().values("name")
PropertiesStep will report its require only "name" property and no edge ids.
VertexStep will use that information to read vertices from db with only
required fields/columns/properties.
GraphStep will know to read adjusted edges for direction OUT and skip
properties.
Thank you,
Valentyn
On 2025/07/22 22:39:52 Andrea Child wrote:
> Hi Valentyn,
>
> I can see how your proposal would be beneficial for providers. Could you
> provide an example of step implementation of those interfaces? The example(s)
> would be helpful to communicate how you envision the proposed interfaces to
> be used by providers.
>
> Thanks,
>
> Andrea
>
> From: Valentyn Kahamlyk <[email protected]>
> Date: Tuesday, July 22, 2025 at 1:58 PM
> To: [email protected] <[email protected]>
> Subject: [DISCUSS] Proposal for provider-focused interfaces for efficient
> data retrieval
> Hi all,
>
> I would like to discuss this idea for TinkerPop 4.
>
> # Proposal: Provider-focused interfaces for efficient data retrieval
>
> When building Gremlin providers - whether connecting to remote databases,
> in‐memory stores, or custom backends - there’s a recurring challenge:
> TinkerPop’s core traversal engine asks for full element payloads, while
> many providers only need subsets of data (selected properties or specific
> edge directions) to satisfy a step. This mismatch leads to:
>
> - Unnecessary data transfer or object materialization
> - Wasted CPU cycles filtering out unused fields
> - Hard-to-maintain provider code packed with ad hoc “if this step needs X,
> fetch Y” logic
>
> Introducing three lightweight interfaces - `PropertiesProcessor`,
> `AdjacentProcessor`, and `ElementReader` - offers a clean extension point
> that lives in provider code without bloating the TinkerPop core.
>
> ---
>
> ## Proposed Interfaces
>
> ```java
> public interface PropertiesProcessor {
> enum PropertiesRequirement {
> NONE,
> CUSTOM,
> ALL,
> }
>
> // second value is properties
> Pair<PropertiesRequirement,String> getPropertiesRequirement();
> }
>
> public interface AdjacentProcessor {
> enum AdjacentRequirement {
> NONE,
> IN,
> OUT,
> BOTH
> }
>
> // second value is edge labels. Null if all labels are required.
> Pair<AdjacentRequirement, String> getAdjacentRequirement();
> }
>
> public interface ElementReader {
> void setElementRequirement(Pair<PropertiesRequirement, String>
> propertiesRequirement,
> Pair<AdjacentRequirement, String>
> adjacentRequirement);
> }
> ```
>
> ---
>
> ## Why This Helps Providers
>
> - **Performance Gains**
> By knowing exactly which properties and adjacent edges are required,
> providers can:
> - Send targeted queries to the backend (e.g., read only needed
> columns/properties)
> - Avoid loading full element metadata into memory
> - Reduce network bandwidth for remote stores
>
> - **Clear Separation of Concerns**
> Core TinkerPop remains focused on traversal semantics. Providers
> implement these interfaces to translate greedy element requests into
> optimized backend calls.
>
> - **Consistent Extension Point**
> Instead of scattering `if (step instanceof X)` checks across provider
> code, every step that implements `PropertiesProcessor` or
> `AdjacentProcessor` exposes its requirements via the same API.
>
> - **Easier Maintenance & Evolution**
> Future provider enhancements - like supporting richer filters or
> push-down queries - build upon these interfaces. There’s no need to touch
> core TinkerPop logic, only provider‐side readers.
>
> ---
>
> ## Impact on TinkerPop Core
>
> - No new dependencies in the core module
> - Default behavior: if a step doesn’t implement these interfaces, providers
> fall back to fetching full elements
>
> ---
>
> ## Conclusion
>
> Adding `PropertiesProcessor`, `AdjacentProcessor`, and `ElementReader` as
> provider‐focused interfaces creates a lightweight, uniform way for Gremlin
> providers to express data needs. This yields measurable performance
> improvements, cleaner code, and a more maintainable separation between
> TinkerPop’s traversal engine and its many backend implementations.
>
> Thank you,
> Valentyn
> Warning: The sender of this message could not be validated and may not be the
> actual sender.
>