对象上的函数

第 2 课 · 共 6 课 约 7 分钟

以对象和对象集为输入;只读查询函数;语义化版本;用对象集、聚合和 Ontology SQL 读取。

本课目标

读完这一课,你将能够

  • 说出函数怎样接收对象和对象集,为什么对象集最省
  • 说出查询函数是什么,以及它的名字和版本规则
  • 用对象集、聚合和一条只读 SQL 写出「有延期风险的订单」的读取部分

把对象交给函数

对象类型建好以后,函数可以直接把它们当类型来用:参数可以是一个对象、一组对象,或者一个对象集;函数里可以按需搜索、沿关系走,也可以修改。人们常把这类函数叫作「对象上的函数」,但它并不是另一种东西,只是读了对象的函数。

OBJECT SET

对象集(最省)

什么都没加载,用到才取;只要个数或合计时,由后端直接算好。

ARRAY

对象数组

先把这些对象全部加载进函数,适合确实要逐个处理的场合。

ONE OBJECT

单个对象(最费)

每次调用都要加载一次;只有类型里本来就只有一个对象,或业务确实逐个处理时才用。

读者会踩的坑,是在循环里一个一个去取对象:每一轮都是一次往返,最慢也最贵。正确的做法是一次取回,或者干脆让后端聚合。并行发起请求只会更快,不会更省。

300 秒函数运行超时的上限
60 秒函数默认的运行时限

还有一条权限规则:函数读对象时,用的是调用者本人的权限。两个人调用同一个函数,可能看到不同的对象,得到不同的结果。这是有意为之。

查询函数:只读,可以被调用

查询函数是函数里只读的那一部分,可以选择通过 API 暴露出去。它不能有副作用,要改数据必须用 Action。它的名字(API name)要用小写驼峰,少于 100 个字符,不以数字开头,也不能和同一个仓库导入的别的查询重名。「有延期风险的订单」写成查询函数,大致是这个形状:

// Pseudocode: illustrates inputs and outputs, not publishable source.
query atRiskOrders(orders: ObjectSet<salesOrder>, byDate: date): ObjectSet<salesOrder> {
  // keep the orders whose open work orders cannot finish before byDate
}

查询函数按语义化版本发布。Automate 的 function 效果指定 version,也可以设 autoUpgrade。不要把 API 名字理解成永远执行最新打标签的版本。

函数的另一个常见用途是自定义聚合:图表按用户当下选中的对象集,即时算出一个值,例如按产线看「准时交付率」。分组和口径由界面里的选择决定,事先写不成一条固定的聚合,就交给函数。

在 AIDC 里:读的部分今天就能做

这个问题可以拆成两半:先圈出候选,再判断。圈候选是读,AIDC 今天就能做。对象集把「未完成的工单」写成一句话,聚合按订单汇总还差多少:

const client = semantic.ontology();
const open = client.objects("workOrder").where({ status: { $in: ["released", "running", "paused"] } });
const left = await open.aggregate({
  $select: { "plannedQty:sum": "unordered", "goodQty:sum": "unordered" },
  $groupBy: { orderNo: "exact" },
});

如果只是想在结果里多一列,例如每张订单的工单数,也不必写函数:withProperties 给对象集临时加一个沿关系计数的派生属性,读的时候算。派生属性不能用来过滤,也不能排序。

const orders = client.objects("salesOrder").withProperties({
  workOrderCount: (b) => b.pivotTo("workOrders").aggregate("$count"),
});

想把订单和工单连起来一起看,可以用 Ontology SQL 的一条只读 SELECT:表名就是对象类型的 API name,一次最多 10,000 行,20 秒。把它存成文件:

SELECT o."orderNo", o."promisedDate",
       sum(w."plannedQty" - w."goodQty") AS remaining
FROM "salesOrder" o
JOIN "workOrder" w ON w."orderNo" = o."orderNo"
WHERE o.status = 'inProduction' AND w.status <> 'done'
GROUP BY 1, 2
ORDER BY 2
LIMIT 50
aidc semantic sql --file at-risk.sql

剩下的是判断:余量除以产线每班产能,来不来得及在交期前干完?这是逐行计算,工作流的计算步骤能做。要不要升级处理、话怎么说,才是需要判断的事,交给智能体或 AI 分析步骤。两类活不要混在一起:数字由表达式算,结果才可复核。

要点

  • 函数可以接收对象、对象数组或对象集;对象集最省,因为用到才加载,合计由后端算。
  • 别在循环里一个一个取对象;一次取回,或让后端聚合。
  • 查询函数只读,没有副作用。它按语义化版本发布;Automate 指定版本,并可设 autoUpgrade。
  • AIDC 里读取用对象集、聚合和一条只读 SQL;判断留给工作流步骤、智能体。

练一练

把「有延期风险的订单」的读取部分做出来

用示例工厂的 salesOrder、workOrder 和 productionLine。

用 aidc semantic aggregate workOrder 按 orderNo 分组,求 plannedQty 与 goodQty 的合计;--where 只留下 released、running、paused 的工单。

小测

选一个答案,马上看解析。

Q1函数的入参用哪一种最省?

Q2关于查询函数,哪句话对?

Q3在 AIDC 里,把未完成的工单按订单汇总还差多少,最直接的做法是?

延伸阅读