循序渐进 · 教学 · 函数(十五)

查询函数:通过 API 网关对外提供只读能力

Query 是函数的只读子集,可以通过 API 网关对外暴露,且不允许有副作用。这篇讲装饰器、API 名校验、版本管理与调用方式。

全部目录 ← 上一篇 查询函数:通过 API 网关对外提供只读能力 下一篇 →
本文来源 · Source 内容整理自 Palantir Foundry 官方文档:
https://www.palantir.com/docs/foundry/functions/query-functions/
原始标题:Language-agnostic features > Publish and call query functions through API gateway

先记住这几条

① Query 必须是只读的
不能有副作用,这是它与普通函数的核心区别。
② 通过 API 网关暴露
外部系统可以直接调用,是函数对外的主要出口。
③ API 名有校验规则
命名不规范会发布失败。
④ 版本化更新
改 query 要走版本更新流程。
0

写在前面

查询(Queries)是函数的只读子集,可以选择性通过 API 网关暴露。它们不能有任何副作用,例如修改 Ontology 或改动外部系统。如果你需要通过 API 网关获得这些额外的编辑能力,应使用 Action

1

Query 装饰器

Query 装饰器

要点:用装饰器把一个函数标记为 query,并可指定 API 名。

使用以下语法定义一个查询函数。

import { Query } from "@foundry/functions-api";

@Query({ apiName: "myTypeScriptV1Function" })
// 在包含该函数的文件中导出一个带有 apiName 参数的配置对象
export const config = {
    apiName: "myTypeScriptV2Function"
};
from functions.api import function

@function(api_name="myPythonFunction")

对于 Python 和 TypeScript v1 函数,装饰器接受一个 string 类型的 API 名称参数,该参数用于定义 API 名称,是必需的。使用 TypeScript v1 时,如果未定义 apiName 参数,该查询的行为将与既有的 @Function 装饰器类似。对应的 Python 语法是 api_name

Example: API-named query

下面的示例演示如何通过 API 网关暴露一个查询:

import { Query, Double } from "@foundry/functions-api";
import { Objects, Aircraft } from "@foundry/ontology-api";

export class PublishedQueries {
    @Query({ apiName: "getReschedulableAircraftCount" })
    public async countAircraftTakingOffAfter(minimumTimeInMinutes: Double): Promise<Double> {
        const aircraftCount = await Objects.search().aircraft()
                 .filter(aircraft => aircraft.timeUntilNextFlight.range().gt(minimumTimeInMinutes))
                 .count();

        return aircraftCount!;
    }
}
import { Client } from "@osdk/client";
import { Double } from "@osdk/functions";
import { Aircraft } from "@ontology/sdk";

export const config = {
    apiName: "getReschedulableAircraftCount"
};

async function countAircraftTakingOffAfter(client: Client, minimumTimeInMinutes: Double): Promise<Double> {
    const { $count } = await client(Aircraft).where({
        timeUntilNextFlight: {
            $gt: minimumTimeInMinutes
        }
    }).aggregate({ $select: { $count: "unordered" } })

    return $count;
}

export default countAircraftTakingOffAfter;
from functions.api import Double, function
from ontology_sdk import FoundryClient
from ontology_sdk.ontology.objects import Aircraft

@function(api_name="getReschedulableAircraftCount")
def count_aircraft_taking_off_after(minimum_time_in_minutes: Double) -> Double:
    client = FoundryClient()
    aircraft_count = client.ontology.objects.Aircraft.where(
        Aircraft.object_type.time_until_next_flight > minimum_time_in_minutes
    ).count().compute()

    return aircraft_count
2

API 名校验

API 名校验

要点:命名规则与校验,避免发布时才发现不合规。

查询的 apiName 必须是一个满足以下要求的字符串:

  • 采用 lowerCamelCase(小驼峰)形式。
  • 长度小于 100 个字符。
  • 不能以数字开头。
  • 在导入到仓库的所有 Ontology 之间保持唯一。
  • 如果 apiName 不唯一,打标签过程会失败,需要你更改名称。

此外,包含 API 命名查询的仓库必须从至少一个 Ontology 导入实体。

3

版本与更新

版本与更新

要点:带 API 名的 query 如何升级版本。

API 命名的查询始终使用已发布查询的最新标签版本,并不遵循与其他 Foundry 函数相同的语义化版本(semantic versioning)范式。

要将 API 名称与查询解除关联、并在 API 网关中使其失效,必须从查询装饰器中移除 API 名称,并从仓库发布一个新的标签。

在装饰器中更改 API 名称并发布新标签,会使调用方失效。仅支持该查询最新发布的版本。

为了让调用方能够在不产生破坏性变更的情况下按需升级,你可以支持同一 API 名称的多个版本。为此,你必须在仓库中复制一份查询代码,并赋予一个不同的 API 名称,例如 getReschedulableAircraftCountV2

4

搜索与查看

搜索与查看

要点:发布后在哪里找到它们。

与其他函数一样,你可以在 Ontology Manager 中搜索和管理你的查询。你可以按查询名称或 API 名称搜索。在上面的示例中,API 名称对应 getReschedulableAircraftCount,查询名称对应 countAircraftTakingOffAfter

在 Ontology Manager 中搜索查询
在 Ontology Manager 中搜索查询

使用 TypeScript v1 函数时,你可能需要更新仓库中的 functions.json 文件,将 enableQueries 属性设为 true 以启用查询:<br><br>

{
  "enableQueries": true
}
5

调用 query 函数

调用 query 函数

要点:外部怎么真正发起调用。

发布你的 TypeScript 或 Python 查询函数后,导航到你想消费该函数的代码仓库,并使用 Resource imports(资源导入) 侧边栏导入它。

你的函数将可以从消费方仓库中被调用。例如:

import { Queries } from "@foundry/ontology-api";

export class MyFunctions {
    @Function()
    public callQueryFunction(): Promise<Double> {
        return Queries.getReschedulableAircraftCount(10);
    }
}
import { Client } from "@osdk/client";
import { Double } from "@osdk/functions";
import { getReschedulableAircraftCount } from "@ontology/sdk";

async function callQueryFunction(client: Client): Promise<Double> {
    return client(getReschedulableAircraftCount).executeFunction({ timeUntilNextFlight: 10 });
}

export default callQueryFunction;
from functions.api import Double, function
from ontology_sdk import FoundryClient

@function
def call_query_function() -> Double:
    return FoundryClient().ontology.queries.get_reschedulable_aircraft_count(
        time_until_next_flight=Double(10)
    )

对于 TypeScript v1 函数,Foundry 必须知道你从已发布函数中调用了哪些查询函数和语言模型方法。我们自动提供静态分析,尝试检测出被调用的查询。但这一静态分析偶尔可能漏掉某些调用,从而导致运行时错误,提示你添加 @Uses 装饰器。该装饰器用于补全自动检测出的查询使用情况。语言模型方法也列在同一个 queries 数组中。

以下示例演示了 @Uses 装饰器的用法:

import { Uses } from "@foundry/functions-api";
import { Queries } from "@foundry/ontology-api";

export class MyFunctions {
    @Uses({ queries: [Queries.getReschedulableAircraftCount] })
    @Function()
    public callQueryFunction(): Promise<Double> {
        return Queries.getReschedulableAircraftCount(10);
    }
}

延伸阅读 · 相关页面

按主题横向跳转,不必顺着目录一篇篇读。

常见问题速答 · FAQ

关于「查询函数:通过 API 网关对外提供只读能力」,读者最常问的几个问题。

Query 装饰器是什么?
用装饰器把一个函数标记为 query,并可指定 API 名。
API 名校验是什么?
命名规则与校验,避免发布时才发现不合规。查询的 apiName 必须是一个满足以下要求的字符串。
版本与更新是什么?
带 API 名的 query 如何升级版本。API 命名的查询始终使用已发布查询的最新标签版本,并不遵循与其他 Foundry 函数相同的语义化版本(semantic versioning)范式。
搜索与查看是什么?
发布后在哪里找到它们。与其他函数一样,你可以在 Ontology Manager 中搜索和管理你的查询。你可以按查询名称或 API 名称搜索。在上面的示例中,API 名称对应 getReschedulableAircraftCount,查询名称对应 count…