跳转到内容

搜索 Business Object 和节点

RealityConnect API 提供两个作用域不同的搜索路由:business object 搜索在孪生体内部查找 POI、区域、资产及其元数据,而节点搜索在你组织的节点层级结构(站点、文件夹、孪生体和文件)中查找。两个路由都是实验性的,可能会发生变化。


方法路径作用域用途
GET/v1/twin/{contextId}/searchBasic搜索孪生体内部的 business object 与元数据
GET/v1/nodes/searchReadHierarchy搜索组织的节点层级结构

GET /v1/twin/{contextId}/search 在单个孪生体(或由 contextId 标识的其站点/草稿)内部搜索 POI、区域、box asset、测量及其他 business object,匹配依据为名称、保留字段、资产元数据以及对象特定属性。

参数类型说明
fieldFiltersJSON 对象数组保留键过滤器。见下文。
metadataFiltersJSON 对象数组匹配孪生体的资产元数据属性。见下文。
additionalPropertyFiltersJSON 对象数组匹配对象类型特定的属性(例如 POI 的 icon)。见下文。
page整数正整数,默认 1
limit整数150,默认 50
searchNameQuerystring独立于 fieldFilters 中名称的专用名称过滤器。
nameMatchexact | containssearchNameQuery 的匹配模式,默认 contains
createdByIduuid按创建者过滤。
createdBefore / createdAftertimestamp严格早于/晚于。格式为 YYYY-MM-DDTHH:mm:ss(无时区偏移,无毫秒)。
updatedBefore / updatedAftertimestamp格式相同。
sortByname | createdAt | updatedAt排序字段。
sortDirASC | DESC排序方向。

fieldFiltersmetadataFiltersadditionalPropertyFilters 各自以一个 JSON 对象字符串表示一个过滤器,多个过滤器时重复该查询参数:

?fieldFilters={"key":"type","values":["poi"]}&fieldFilters={"key":"type","values":["zone"]}

每个过滤器对象的形状为 { "key": string, "values": string[] }。同一个过滤器 values 数组中的多个值以 OR 组合。

fieldFilters 仅接受以下保留键 —— 其他键会被静默忽略:

匹配对象
typeBusiness object 类型。可以是枚举值(PoiZoneBoxAssetCubePrimitivePlanePrimitiveDistanceMeasureCoordinateMeasureDiameterMeasureSurfaceMeasureOrthogonalMeasureCutModelAsset),也可以是其规范化的 kebab-case 令牌 —— 即将 PascalCase 名称转为带连字符的小写形式,例如 BoxAssetbox-asset
asset-type对象的资产类型(映射到 assetTypeId)。精确匹配。
name仅对名称进行前缀匹配,无模糊性。
id精确匹配对象 id。

fieldFilters 的值始终是纯字符串或枚举令牌 —— 下文的数值/范围语法不适用于它。

metadataFiltersadditionalPropertyFilters 通过键来定位一个由 schema 定义的元数据属性(metadataFilters),或一个不属于通用元数据的对象类型特定属性(additionalPropertyFilters,例如 POI 的 icon)。values 中的每个值支持:

值语法含义
纯字符串精确匹配。values 中的多个纯字符串以 OR 组合。
* 或空字符串被忽略 —— 若这是唯一的值,过滤器仅按键是否存在进行匹配。
=123!=123>123>=123<123<=123数值比较。支持整数、小数和科学计数法(例如 1.5e+35)。
range:[1..10]闭区间数值范围。
range:(1..10)开区间数值范围。
range:[1..10) / range:(1..10]半开区间的混合边界。
true / falseadditionalPropertyFilters —— 按字面量匹配布尔属性。

fieldFiltersmetadataFiltersadditionalPropertyFilters 都以 AND 语义组合:结果必须满足每一个过滤器。在单个过滤器内部,其 values 数组中的多个值以 OR 组合 —— 因此两个共享同一 keyfieldFilters 对象表现为”满足任一值即可”,而作用于不同键(或不同过滤器数组)的过滤器则会进一步收窄结果集。

类型过滤器与数值元数据范围结合(检测压力记录在 80 到 120 之间的 POI):

GET {api_url}/v1/twin/{contextId}/search?fieldFilters=%7B%22key%22%3A%22type%22%2C%22values%22%3A%5B%22poi%22%5D%7D&metadataFilters=%7B%22key%22%3A%22inspection.pressure%22%2C%22values%22%3A%5B%22range%3A%5B80..120%5D%22%5D%7D
Authorization: Bearer {access_token}

资产类型与名称过滤器结合,按最近更新排序:

GET {api_url}/v1/twin/{contextId}/search?fieldFilters=%7B%22key%22%3A%22asset-type%22%2C%22values%22%3A%5B%22pump%22%5D%7D&fieldFilters=%7B%22key%22%3A%22name%22%2C%22values%22%3A%5B%22vibration%22%5D%7D&sortBy=updatedAt&sortDir=DESC
Authorization: Bearer {access_token}

含义为:资产类型为 pump 名称以 “vibration” 开头的对象,按最近更新排序。

同一键上的两个 fieldFilters(OR 组合)与不同键上的 fieldFilters(AND 组合)结合 —— POI 或区域,限定为 pump 资产类型:

GET {api_url}/v1/twin/{contextId}/search?fieldFilters=%7B%22key%22%3A%22type%22%2C%22values%22%3A%5B%22poi%22%2C%22zone%22%5D%7D&fieldFilters=%7B%22key%22%3A%22asset-type%22%2C%22values%22%3A%5B%22pump%22%5D%7D
Authorization: Bearer {access_token}

布尔型 additionalPropertyFilters 字面量与字符串型 metadataFilters 匹配结合 —— 元数据中检测状态记录为 “warning” 的活动 POI:

GET {api_url}/v1/twin/{contextId}/search?fieldFilters=%7B%22key%22%3A%22type%22%2C%22values%22%3A%5B%22poi%22%5D%7D&additionalPropertyFilters=%7B%22key%22%3A%22isActive%22%2C%22values%22%3A%5B%22true%22%5D%7D&metadataFilters=%7B%22key%22%3A%22inspection.status%22%2C%22values%22%3A%5B%22warning%22%5D%7D
Authorization: Bearer {access_token}

同时使用全部三种过滤输入、一个专用名称过滤器、一个日期范围、分页和排序的完整请求 —— 展示完整的请求形状:

GET {api_url}/v1/twin/{contextId}/search?fieldFilters=%7B%22key%22%3A%22type%22%2C%22values%22%3A%5B%22poi%22%2C%22zone%22%5D%7D&metadataFilters=%7B%22key%22%3A%22inspection.pressure%22%2C%22values%22%3A%5B%22range%3A%5B80..120%5D%22%5D%7D&additionalPropertyFilters=%7B%22key%22%3A%22isActive%22%2C%22values%22%3A%5B%22true%22%5D%7D&searchNameQuery=Pump&nameMatch=contains&updatedAfter=2026-01-01T00%3A00%3A00&updatedBefore=2026-12-31T23%3A59%3A59&page=1&limit=25&sortBy=updatedAt&sortDir=DESC
Authorization: Bearer {access_token}
sortBy排序依据
name对象名称
createdAt创建时间戳
updatedAt最后更新时间戳

每个 sortBy 值都可以与任意 sortDir 值组合:

GET {api_url}/v1/twin/{contextId}/search?fieldFilters=%7B%22key%22%3A%22type%22%2C%22values%22%3A%5B%22poi%22%5D%7D&sortBy=name&sortDir=ASC
Authorization: Bearer {access_token}
GET {api_url}/v1/twin/{contextId}/search?fieldFilters=%7B%22key%22%3A%22type%22%2C%22values%22%3A%5B%22poi%22%5D%7D&sortBy=createdAt&sortDir=DESC
Authorization: Bearer {access_token}
GET {api_url}/v1/twin/{contextId}/search?fieldFilters=%7B%22key%22%3A%22type%22%2C%22values%22%3A%5B%22poi%22%5D%7D&sortBy=updatedAt&sortDir=DESC
Authorization: Bearer {access_token}

省略 sortDir 时默认为 ASCsortBy 没有文档化的默认字段 —— 省略它时会按内部相关性得分(降序)排序,并以 id(升序)作为平局判定依据。只有 key: "name"fieldFilters 会影响该得分;其他所有过滤器都只是纯粹的是/否匹配,对排名没有影响,因此在实践中,如果省略 sortBy 且没有 name 过滤器,结果将按 id 顺序返回。这是一项实现细节,而非有保证的约定 —— 当特定的字段顺序对你的集成很重要时,请始终显式设置 sortBy

metadataFiltersadditionalPropertyFilters 的值内部,一个值可以携带比较运算符或范围,而不是进行字面量匹配。此语法适用于 fieldFilters —— 那些始终是字面量/枚举匹配(typeasset-typenameid)。

运算符示例含义
==123等于
!=!=123不等于
>>123大于
>=>=123大于或等于
<<123小于
<=<=123小于或等于
range:[a..b]range:[80..120]两端均为闭区间
range:(a..b)range:(80..120)两端均为开区间
range:[a..b)range:[80..120)下界闭区间,上界开区间
range:(a..b]range:(80..120]下界开区间,上界闭区间

支持整数、小数、负数和科学计数法,例如 >=-40=3.14range:[1.5e+35..2.0e+35]

按名称匹配有两种不同的方式,它们的行为各不相同:

参数匹配方式是否可与其他过滤器组合?
fieldFilters={"key":"name",...}前缀匹配,无模糊性是 —— 始终以 AND 组合
searchNameQuery + nameMatchcontains(默认)或 exact,无模糊性是 —— 始终以 AND 组合

使用 key: "name"fieldFilters 进行仅名称的前缀匹配,它会按与 typeasset-type 相同的 AND/OR 规则组合。当你需要不带模糊容错的精确子字符串或精确名称查找时(例如验证某个名称是否逐字存在),使用带 nameMatchsearchNameQuery

{
"results": [
{
"id": "5e1c0b3a-2d9a-4b7d-8f0b-9a2c8f1b7a10",
"name": "Fire extinguisher 12",
"type": "Poi",
"matchedFields": [
{ "matchKey": "name", "highlight": "Fire <em>extinguisher</em> 12" }
],
"createdAt": "2026-06-01T10:00:00.000Z",
"updatedAt": "2026-06-01T10:00:00.000Z",
"createdById": "8f0b9a2c-8f1b-4a10-9e1c-0b3a2d9a4b7d"
}
],
"count": 1
}

matchedFields 显示哪个字段或元数据属性匹配,匹配到的文本用 <em> 包裹。matchKey 要么是根字段名称(例如 name),要么是匹配到的嵌套元数据/属性值对应的 @<metadata-path>

单个结果可以携带多个 matchedFields 条目 —— 每个匹配的字段或元数据属性对应一条。例如,一个同时匹配对象名称和检测元数据值的 fieldFilters 名称匹配与 metadataFilters 组合请求会返回:

{
"id": "5e1c0b3a-2d9a-4b7d-8f0b-9a2c8f1b7a10",
"name": "Pressure gauge 04",
"type": "Poi",
"matchedFields": [
{ "matchKey": "name", "highlight": "<em>Pressure</em> gauge 04" },
{ "matchKey": "@inspection.status", "highlight": "<em>Warning</em>: recalibration due" }
],
"createdAt": "2026-06-01T10:00:00.000Z",
"updatedAt": "2026-06-01T10:00:00.000Z",
"createdById": "8f0b9a2c-8f1b-4a10-9e1c-0b3a2d9a4b7d"
}

高亮片段在插入 <em> 标记之前会先进行 HTML 转义,因此匹配文本中的任何 <>& 会以 &lt;&gt;&amp; 的形式出现,而不是原始标记 —— 可以直接安全地渲染为 HTML,无需二次转义,但如果你需要纯文本值,请先解码它。

情况行为
没有任何过滤器(或全部为空)返回孪生体内的所有 business object,最多 limit 条,不进行排名(match_all)。
无法识别的 fieldFilters被静默忽略 —— 该过滤器对查询没有任何贡献,不会引发 400
metadataFilters/additionalPropertyFilters 的值为 *""被跳过。如果该过滤器 values 数组中的每个值都以这种方式被跳过,该过滤器仍会要求对象在该键上存在属性 —— 它变成一个不带值约束的键存在性检查。

名称前缀搜索:

GET {api_url}/v1/twin/{contextId}/search?fieldFilters=%7B%22key%22%3A%22name%22%2C%22values%22%3A%5B%22extinguisher%22%5D%7D
Authorization: Bearer {access_token}

按名称排序的名称包含搜索:

GET {api_url}/v1/twin/{contextId}/search?searchNameQuery=Pump&nameMatch=contains&sortBy=name&sortDir=ASC
Authorization: Bearer {access_token}
状态原因
400 Bad RequestfieldFiltersmetadataFiltersadditionalPropertyFilters 的某个值不是合法 JSON,或不符合 { key, values } 的形状。

重复的过滤器参数(多个带有不同 JSON 值的 fieldFiltersmetadataFiltersadditionalPropertyFilters)无法用 requests 基于字典的 params 构建 —— 一个字典每个键只能保存一个值。请改用 urllib.parse.urlencode 和元组列表自行构建查询字符串:

import json
import requests
from urllib.parse import urlencode
API_URL = "https://<your-regional-api-url>"
TOKEN = "<access_token>"
CONTEXT_ID = "<twin-id>"
headers = {"Authorization": f"Bearer {TOKEN}"}
params = [
("fieldFilters", json.dumps({"key": "type", "values": ["poi"]})),
("metadataFilters", json.dumps({"key": "inspection.pressure", "values": ["range:[80..120]"]})),
("sortBy", "name"),
("sortDir", "ASC"),
]
response = requests.get(
f"{API_URL}/v1/twin/{CONTEXT_ID}/search?{urlencode(params)}",
headers=headers,
).json()
for result in response["results"]:
print(result["type"], result["name"], result["id"])

GET /v1/nodes/search 搜索的是你组织的节点层级结构 —— 分部、站点、文件夹、孪生体、asset library 和数据文件 —— 而不是某个孪生体内部的 business object。组织信息从 access token 中解析,而不是作为参数传入。

参数类型说明
nodeTypesDataNodeType 数组按一个或多个节点类型过滤。重复该参数,例如 ?nodeTypes=Site&nodeTypes=Folder。取值:OrganizationSitePointcloudMeshSiteFileFolderProjectDivisionArtifactDataBundleTwinAssetLibrary
excludeNodeTypesDataNodeType 数组排除一个或多个节点类型。重复语法相同。
searchNameQuerystring按节点名称过滤。
nameMatchexact | containssearchNameQuery 的匹配模式,默认 contains
parentIduuid仅该节点的直接子节点。
ancestorIduuid任意深度的后代节点,不含祖先节点本身。用于站点或文件夹下的嵌套内容;如只需直接子节点,使用 parentId
createdByIduuid按创建者过滤。
createdBefore / createdAftertimestamp严格早于/晚于。格式为 YYYY-MM-DDTHH:mm:ss
updatedBefore / updatedAftertimestamp格式相同。
bytesStored整数精确匹配。
minBytesStored / maxBytesStored整数闭区间边界。
childrenCount整数精确匹配。
minChildrenCount / maxChildrenCount整数闭区间边界。
includesThumbnail'true' | 'false'在可用时包含带签名的缩略图 URL。默认 'false'
page整数默认 1
limit整数150,默认 20
sortByname | createdAt | updatedAt | bytesStored | childrenCount排序字段。
sortDirASC | DESC排序方向,默认 ASC
{
"items": [
{
"id": "a6f75b3c-f261-4b27-9a2a-9a6cc1234c53",
"createdAt": "2026-06-01T10:00:00.000Z",
"updatedAt": "2026-06-01T10:00:00.000Z",
"name": "Building A",
"type": "Site",
"createdById": "8f0b9a2c-8f1b-4a10-9e1c-0b3a2d9a4b7d",
"parentId": "d2a1c4b7-3e10-4f2c-9a6b-7c1d8e2f3a45",
"childrenCount": 4,
"bytesStored": 15728640,
"thumbnailSignedUrl": null
}
],
"total": 1
}

某祖先节点下的所有站点:

GET {api_url}/v1/nodes/search?nodeTypes=Site&ancestorId=d2a1c4b7-3e10-4f2c-9a6b-7c1d8e2f3a45
Authorization: Bearer {access_token}

名称以 “Archive” 开头的文件夹,按名称排序:

GET {api_url}/v1/nodes/search?nodeTypes=Folder&searchNameQuery=Archive&nameMatch=contains&sortBy=name&sortDir=ASC
Authorization: Bearer {access_token}
import requests
from urllib.parse import urlencode
API_URL = "https://<your-regional-api-url>"
TOKEN = "<access_token>"
ANCESTOR_ID = "<division-or-site-id>"
headers = {"Authorization": f"Bearer {TOKEN}"}
params = [
("nodeTypes", "Site"),
("ancestorId", ANCESTOR_ID),
("sortBy", "name"),
("sortDir", "ASC"),
]
response = requests.get(
f"{API_URL}/v1/nodes/search?{urlencode(params)}",
headers=headers,
).json()
for node in response["items"]:
print(node["type"], node["name"], node["id"])

要在节点搜索返回的节点下创建资源,请参阅创建 Data Bundle