Query filtering filtering with attritbutes
Updated: Oct 2, 2025
Queries are used to find entities which have a specific set of components or have components that have changed since the last tick. However, you may want to further filter the results of a query to only include entities that match certain criteria. For example, you may want to only include entities whose attributes are equal to some specific values. This is where filters come in.
To write a filter, you need to access the attribute data of an entity. An entity’s attribute data is stored in its components. The Spatial SDK build process generates an attribute data property for each attribute of each component. The name of the attribute data property is always the name of the attribute with the suffix “Data”. For example, the min attribute of Box has the attribute data minData.
Spatial SDK provides a set of filter functions that can be used to filter entities based on their attribute data.
There are three logical operators in a filter: and, or, and not.
- The
and operator is used to “and” the two conditions on either side. - The
or operator is used to “or” the two conditions on either side. - The
not operator is used to negate the condition within the parentheses.
// Filters entities that are local and whose "type" attribute is equal to "eye_gaze" or "head"
val entities = Query.where { changed(AvatarAttachment.id) }
.filter { isLocal() and (by(AvatarAttachment.typeData).isEqualTo("eye_gaze") or by(AvatarAttachment.typeData).isEqualTo("head")) }
.eval()
For example, the code above shows how to use the and and or operators to filter entities based on the type attribute data of the AvatarAttachment component. You can use parentheses to group conditions together.
IntAttribute, FloatAttribute, LongAttribute, TimeAttribute
The code below shows how to use IntAttribute to filter entities based on the data of the attribute intAttr.
// Filters entities where intAttr is equal to 1
val ents0 = Query.where { has(TestComponent.id) }
.filter { by(TestComponent.intAttrData).isEqualTo(1) }
.eval()
// Filters entities where intAttr is greater than 1
val ents1 = Query.where { has(TestComponent.id) }
.filter { by(TestComponent.intAttrData).greaterThan(1) }
.eval()
// Filters entities where intAttr is greater than or equal to 1
val ents2 = Query.where { has(TestComponent.id) }
.filter { by(TestComponent.intAttrData).greaterThanOrEqualTo(1) }
.eval()
// Filters entities where intAttr is less than 1
val ents3 = Query.where { has(TestComponent.id) }
.filter { by(TestComponent.intAttrData).lessThan(1) }
.eval()
// Filters entities where intAttr is less than or equal to 1
val ents4 = Query.where { has(TestComponent.id) }
.filter { by(TestComponent.intAttrData).lessThanOrEqualTo(1) }
.eval()
FloatAttribute, LongAttribute, and TimeAttribute have identical APIs to those of IntAttribute.
The code below shows how to use BooleanAttribute to filter entities based on the data of the attribute boolAttr.
// Filters entities where boolAttr is equal to true
val ents0 = Query.where { has(TestComponent.id) }
.filter { by(TestComponent.boolAttrData).isEqualTo(true) }
.eval()
The code below shows how to use StringAttribute to filter entities based on the data of the attribute stringAttr.
// Filters entities where stringAttr is equal to "hello"
val ents0 = Query.where { has(TestComponent.id) }
.filter { by(TestComponent.stringAttrData).isEqualTo("hello") }
.eval()
// Filters entities where stringAttr is equal to "Hello" case-insensitively
val ents1 = Query.where { has(TestComponent.id) }
.filter { by(TestComponent.stringAttrData).isEqualToCaseInsensitive("Hello") }
.eval()
// Filters entities where stringAttr contains "ell"
val ents2 = Query.where { has(TestComponent.id) }
.filter { by(TestComponent.stringAttrData).contains("ell") }
.eval()
// Filters entities where stringAttr starts with "he"
val ents3 = Query.where { has(TestComponent.id) }
.filter { by(TestComponent.stringAttrData).startsWith("he") }
.eval()
// Filters entities where stringAttr ends with "lo"
val ents4 = Query.where { has(TestComponent.id) }
.filter { by(TestComponent.stringAttrData).endsWith("lo") }
.eval()
// Filters entities where stringAttr is greater than "hello"
val ents5 = Query.where { has(TestComponent.id) }
.filter { by(TestComponent.stringAttrData).greaterThan("hello") }
.eval()
// Filters entities where stringAttr is less than "hello"
val ents6 = Query.where { has(TestComponent.id) }
.filter { by(TestComponent.stringAttrData).lessThan("hello") }
.eval()
// Filters entities where stringAttr is greater than or equal to "hello"
val ents7 = Query.where { has(TestComponent.id) }
.filter { by(TestComponent.stringAttrData).greaterThanOrEqualTo("hello") }
.eval()
Filter entities based on enumAttr
The code below shows how to use EnumAttribute to filter entities based on the data of the attribute enumAttr.
// Filters entities where enumAttr is equal to MyEnum.VALUE1
val filteredEntities = Query.where { has(TestComponent.id) }
.filter { by(TestComponent.enumAttrData).isEqualTo(MyEnum.VALUE1) }
.eval()
Fileter entities based on vector4Attr
The code below shows how to use Vector4Attribute to filter entities based on the data of the attribute vector4Attr.
// Filters entities where vector4Attr is equal to Vector4(1.0f, 2.0f, 3.0f, 4.0f)
val ents0 = Query.where { has(TestComponent.id) }
.filter { by(TestComponent.vector4AttrData).isEqualTo(Vector4(1.0f, 2.0f, 3.0f, 4.0f)) }
.eval()
// Filters entities where vector4Attr's x property is greater than 1.0f
val ents1 = Query.where { has(TestComponent.id) }
.filter { by(TestComponent.vector4AttrData).byX().greaterThan(1.0f) }
.eval()
// Filters entities where vector4Attr's y property is less than 2.0f
val ents2 = Query.where { has(TestComponent.id) }
.filter { by(TestComponent.vector4AttrData).byY().lessThan(2.0f) }
.eval()
// Filters entities where vector4Attr's z property is greater than or equal to 3.0f
val ents3 = Query.where { has(TestComponent.id) }
.filter { by(TestComponent.vector4AttrData).byZ().greaterThanOrEqualTo(3.0f) }
.eval()
// Filters entities where vector4Attr's w property is less than or equal to 4.0f
val ents4 = Query.where { has(TestComponent.id) }
.filter { by(TestComponent.vector4AttrData).byW().lessThanOrEqualTo(4.0f) }
.eval()
Vector2Attribute and Vector3Attribute have similar APIs to those of Vector4Attribute.
Filter entities based on poseAttr
The code below shows how to use PoseAttribute to filter entities based on the data of the attribute poseAttr.
// Filters entities where poseAttr is equal to Pose(Vector3(1.0f, 2.0f, 3.0f), Quaternion(4.0f, 5.0f, 6.0f, 7.0f))
val ents0 = Query.where { has(TestComponent.id) }
.filter { by(TestComponent.poseAttrData).isEqualTo(Pose(Vector3(1.0f, 2.0f, 3.0f), Quaternion(4.0f, 5.0f, 6.0f, 7.0f))) }
.eval()
// Filters entities where poseAttr's position x property is greater than 1.0f
val ents1 = Query.where { has(TestComponent.id) }
.filter { by(TestComponent.poseAttrData).byPositionX().greaterThan(1.0f) }
.eval()
// Filters entities where poseAttr's position y property is less than 2.0f
val ents2 = Query.where { has(TestComponent.id) }
.filter { by(TestComponent.poseAttrData).byPositionY().lessThan(2.0f) }
.eval()
// Filters entities where poseAttr's position z property is greater than or equal to 3.0f
val ents3 = Query.where { has(TestComponent.id) }
.filter { by(TestComponent.poseAttrData).byPositionZ().greaterThanOrEqualTo(3.0f) }
.eval()
// Filters entities where poseAttr's orientation w property is less than or equal to 4.0f
val ents4 = Query.where { has(TestComponent.id) }
.filter { by(TestComponent.poseAttrData).byOrientationW().lessThanOrEqualTo(4.0f) }
.eval()
// Filters entities where poseAttr's orientation x property is greater than 5.0f
val ents5 = Query.where { has(TestComponent.id) }
.filter { by(TestComponent.poseAttrData).byOrientationX().greaterThan(5.0f) }
.eval()
// Filters entities where poseAttr's orientation y property is less than 6.0f
val ents6 = Query.where { has(TestComponent.id) }
.filter { by(TestComponent.poseAttrData).byOrientationY().lessThan(6.0f) }
.eval()
// Filters entities where poseAttr's orientation z property is greater than or equal to 7.0f
val ents7 = Query.where { has(TestComponent.id) }
.filter { by(TestComponent.poseAttrData).byOrientationZ().greaterThanOrEqualTo(7.0f) }
.eval()
Filter entities based on uuidAttr
The code below shows how to use UUIDAttribute to filter entities based on the data of the attribute uuidAttr.
// Filters entities where uuidAttr is equal to UUID.fromString("123e4567-e89b-12d3-a456-426614174000")
val ents0 = Query.where { has(TestComponent.id) }
.filter { by(TestComponent.uuidAttrData).isEqualTo(UUID.fromString("123e4567-e89b-12d3-a456-426614174000")) }
.eval()
Filter entities based on entityAttr
The code below shows how to use EntityAttribute to filter entities based on the data of the attribute entityAttr.
// Filters entities where entityAttr's entity id is equal to that of entity1
val entity1 = Entity.create() // entity1 is an Entity object
val ents0 = Query.where { has(TestComponent.id) }
.filter { by(TestComponent.entityAttrData).isEqualTo(entity1) }
.eval()
Filter entities based on mapAttr
The code below shows how to use ExperimentalMapAttribute to filter entities based on the data of the attribute mapAttr.
// Filters entities where mapAttr contains the key "key1"
val ents0 = Query.where { has(TestComponent.id) }
.filter { by(TestComponent.mapAttrData).containsKey("key1") }
.eval()
The code below shows how to use IsLocal to filter entities based on whether they are local or not.
// Filters entities that are local
val ents0 = Query.where { has(TestComponent.id) }
.filter { isLocal() }
.eval()
Kotlin filters vs. native filters
The two queries below will return the same results, but the first uses a Kotlin filter and the second uses a native filter.
val droneEntities0 =
Query.where { has(DroneComponent.id, Transform.id) }
.eval()
.filter { it.getComponent<DroneComponent>().enabled == true }
val droneEntities1 =
Query.where { has(DroneComponent.id, Transform.id) }
.filter { by(DroneComponent.enabledData).isEqualTo(true) }
.eval()
For a query with thousands of entities, using a native filter can be 100 times more efficient than using a Kotlin filter. This is because it.getComponent<DroneComponent>() is a function that creates a new DroneComponent object for each entity, and object creation in large numbers is slow in Kotlin. Meanwhile, by(DroneComponent.enabledData) is an API that directly accesses the data of the DroneComponent on the C++ side, and is much faster than creating a new DroneComponent object.