Please ensure Javascript is enabled for purposes of website accessibility
Powered by Zoomin Software. For more details please contactZoomin

CONNECT EAP

Query best practices

  • Last UpdatedMay 13, 2026
  • 7 minute read

Follow these best practices to ensure efficient and maintainable GraphQL queries. If you identify specific use cases or issues, share them with the team to ensure they are addressed in future testing and optimization efforts.

General advice

  • Minimize nested queries: Avoid deeply nested queries to reduce complexity.

  • Limit query depth: Prevent overly complex queries to protect server resources.

  • Request only required fields: Keep selection sets small to reduce payload and resolver work.

  • Prefer paginated reads for UI scenarios: Use limit + continuationToken instead of large single-shot reads.

  • Use deterministic ordering when paginating: Keep where and orderBy stable across page requests.

Relationship depth

Since each field of the Relationship type can be queried recursively, it is important to limit the depth. You can query up to a maximum depth of three for fields with the Relationship type. Here is an example of a query at the maximum allowed depth:

{

entities {

items {

id

relationships {

id

entity {

id

relationships {

id

entity {

id

relationships {

id

}

}

}

}

}

}

}

}

And here is a query that exceeds the maximum allowed depth:

{

entities {

items {

id

relationships {

id

entity {

id

relationships {

id

entity {

id

relationships {

id

entity {

id

relationships {

id

}

}

}

}

}

}

}

}

}

}

With the following error response:

{

"errors": [

{

"message": "Max relationship depth reached.",

"extensions": {

"status": 400,

"title": "Bad Request",

"detail": "Your request could not be processed.",

"type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",

"traceId": "3422a5692f9b48af5944c12ced38efd8",

"resolution": "Verify that your request is correct. Try submitting your request again after correcting any issues.",

"instance": null

}

}

]

}

Filter operations

Filters pointing to basic fields (for example, id: String) indicate that only the equality operation is allowed. Filters pointing at another *Filter input indicate that it supports multiple operations beyond just equality.

Filters that are an array, for example properties: [PropertyFilter], should be used to ask for entities that meet ALL the specified conditions, implying an AND between each condition.

Example: invalid filter operation

A simple colon followed by the value cannot be used for a *Filter input. This will not be accepted:

stringValue: "HEFA-A002"

Recommendation: use eq operation

*Filter requires the eq operation to be explicitly specified:

stringValue: { eq: "HEFA-A002" }

Numeric property filters

The Graph Store, which underpins entity data storage, enforces a specific constraint: only one numeric value filter can be applied per property within a single query. Attempting to use multiple numeric filters on the same property will result in a query error.

When filtering data, it may be tempting to apply several conditions to a single property (for example, specifying both a minimum and maximum value). However, the Graph Store does not permit combining multiple filters for the same property in one query. Queries must be designed to comply with this restriction.

When constructing queries, ensure that each property is associated with only one filter. For more complex logic, consider handling additional conditions within your application or restructuring your queries to align with this limitation.

Example: invalid numeric filters

Filtering the same property twice using separate numeric conditions (gt and lt) will create two filters, which is not supported.

input: {

filter: {

where: {

name: { startsWith: "HEPA-C" }

properties: [

{ id: "class", numberValue: { gt: "0" } }

{ id: "class", numberValue: { lt: "20" } }

]

}

}

}

Recommendation: use combined numeric filters

  • Combine the numeric conditions into a single filter object.

  • The Knowledge Graph interprets this as a range filter, which is permitted.

    input: {

    filter: {

    where: {

    name: { startsWith: "HEPA-C" }

    properties: [

    { id: "class", numberValue: { gt: "0", lt: "20" } }

    ]

    }

    }

    }

Avoid unnecessary requests

Excessive calls to dependent services, such as SDS, inbound relationships, and components, can significantly impact query performance.

Example: query with unnecessary SDS requests

Including relationships -> stream in GraphQL queries adds an extra call to the SDS dependency, increasing response time. For example, adding stream { id } caused a three-second delay compared to the same query without the stream property.

{

entities(

input: {

filter: {

where: {

relationships: [

{

targetBase: STREAM

values: [

"<StreamID>"

]

}

]

}

}

}

) {

items {

id

relationships {

targetId # <StreamID>

# Adds a call to SDS

stream {

id # Also <StreamID>

}

}

}

}

}

Recommendation: avoid unnecessary SDS requests

Use targetId with targetBase: STREAM instead of relationships -> stream -> id if you only need the stream ID.

{

entities(

input: {

filter: {

where: {

relationships: [

{

targetBase: STREAM

values: [

"<StreamID>"

]

}

]

}

}

}

) {

items {

id

relationships {

targetId # <StreamID>

}

}

}

}

Expensive field combinations

Some field combinations can multiply downstream calls and increase latency significantly.

Be especially selective when combining:

  • Deep relationship traversal

  • inbound relationship expansion

  • Stream expansion (relationships -> stream -> data)

Recommendation: start from a minimal query, then add fields one at a time while checking latency.

Inbound relationships

Inbound relationships represent implicit connections where an entity is referenced by another entity, even though the target entity does not explicitly reference it in return. You can think of inbound relationships as the "reverse" of outbound relationships: a way to identify entities that point to a specific target. This creates a complete two-way association, making it easier to understand how entities are linked within the graph, even when those links are not directly declared.

Note: Inbound relationships are the reverse of outbound relationships. For instance, if an entity has an outbound PARENT relationship, it also has an inbound CHILD relationship from the perspective of the related entity.

inbound entity relationship

When querying entities, the inbound collection displays these implicit relationships. Keep in mind that fetching inbound relationships may impact performance, as the Knowledge Graph needs to run additional subqueries. Also, results from the inbound collection are not paginated, which can make it more difficult and slower to work with large datasets.

Note: Future updates may restrict access to inbound relationships, possibly showing only a preview of the first few results.

Example: querying the inbound collection

{

entities(

input: {

filter: {

where: { id: { eq: "D-1337|010 - Olefin Splitter" }}

}

}

) {

items {

id

name

inbound {

items {

id

relationshipId

relationshipType

targetBase

typeId

},

hasMore

}

}

}

}

Example response:

{

"data": {

"entities": {

"items": [

{

"id": "D-1337|010 - Olefin Splitter",

"name": "D-1337|010 - Olefin Splitter",

"inbound": {

"items": [

{

"id": "SurgeDrum-143",

"relationshipId": "SplitterRelationship",

"relationshipType": "CHILD",

"targetBase": "ENTITY",

"typeId": ""

}

],

"hasMore": "false"

}

}

]

}

}

}

Recommendation: query inbound relationships efficiently

To improve performance and enable pagination, consider identifying outbound relationships by filtering for entities that reference a specific target entity. By retrieving these outbound relationships and interpreting them from the target entity’s perspective, you can achieve the same insights as querying inbound, but with added precision and flexibility. This approach is particularly beneficial when working with large or complex datasets.

{

entities(

input: {

filter: {

where: {

relationships: [

{ targetBase: ENTITY, values: ["Target-Entity"] }

]

}

}

}

limit: 10

continuationToken: "<tokenFromPreviousResponse>"

) {

continuationToken

items {

id

name

relationships {

id

type

targetId

}

}

}

}

Example response:

{

"data": {

"entities": {

"continuationToken": "eyJpZCI6IjEwIn0=",

"items": [

{

"id": "Related-Entity",

"name": "Related Entity",

"relationships": [

{

"id": "rel-456",

"type": "PARENT",

"targetId": "Target-Entity"

}

]

}

]

}

}

}

The corresponding inferred inbound relationship would be:

{
"id": "~rel-456",
"type": "CHILD",
"targetId": "Related-Entity"
}

Relating types: two ways to connect entities and types

Entities or events can be linked to types in two main ways: through their components or by direct relationships. This gives you flexibility in how you organize and classify things.

graphic showing an entity/event connecting to a component which connects to Type 1 and relationship which connects to Type 2

  • Component Path: The entity or event connects to a component, which is then classified as a specific type.

    Example: A machine (entity) has a motor (component), and that motor is a certain type.

  • Direct Relationship Path: The entity or event links straight to a type, representing a direct classification or grouping (such as a visualization).

    Example: The same machine is also directly labeled as a “Pump” type.

Real-world example

example showing a Flow Pump 101 entity connecting to a Release Value component which connects to a Release Valve type, and a relationship to a Pump Visualization type

  • Component Relationship: Flow Pump 101 includes a Release Valve (component), which is classified as a Release Valve (type).

  • Type Relationship: Flow Pump 101 is also directly linked to a Pump Visualization type, allowing it to be linked to other entities related to a visualization of that type.

Key points

  • You can classify entities by their parts (components and their types) or by linking them directly to a type.

  • Both paths can be used together, depending on how much detail or flexibility you need.

In This Topic