Keyboard shortcuts

Press ← or → to navigate between chapters

Press ? to show this help

Press Esc to hide this help

함수의 온톨로지 편집과 적용 시점

온톨로지 편집(Ontology edit)은 객체 생성, 수정과 삭제 및 객체 사이 링크 변경입니다. 함수는 편집할 내용을 정의하고 함수 기반 액션이 권한과 제출 조건에 따라 이를 적용합니다.

언어별 편집 수집

방식편집 선언과 수집
TSv1OntologyEditFunction과 Edits 데코레이터를 사용합니다. 객체 변경을 런타임이 수집하므로 코드의 반환은 void 또는 Promise<void>입니다.
TSv2 편집 배치Edits 타입으로 대상을 선언하고 createEditBatch로 모은 편집 목록을 반환합니다.
Python 편집 컨테이너function의 edits 인수로 대상을 선언하고 FoundryClient의 편집 컨테이너에서 list[OntologyEdit]를 반환합니다.
TSv2 적용 대기 쓰기와 Python 적용 대기 쓰기Beta WriteableClient로 편집을 임시 저장하며 같은 실행의 다음 조회에 반영합니다. 편집 목록 대신 다른 지원 값을 반환할 수 있습니다.

선언한 편집 대상 정보는 액션의 권한과 변경 추적에 사용합니다. TSv2와 Python에서는 다대다 링크 자체를, 일대일과 일대다 링크는 외래 키를 가진 객체 유형을 선언합니다. TSv1의 다대다 링크 편집에는 양끝 객체 유형을 선언합니다.

다대다 링크의 예

가상의 Employee와 Ticket에 다대다 assignedTickets 링크가 있고, Python 속성 이름은 assigned_tickets라고 가정합니다. 한 직원에게 티켓 하나를 연결하고 다른 티켓의 연결을 끊는 예제입니다.

TSv1은 양끝 객체 유형을 선언합니다.

import { Edits, OntologyEditFunction } from "@foundry/functions-api";
import { Employee, Ticket } from "@foundry/ontology-api";

export class TicketLinkFunctions {
    @Edits(Employee, Ticket)
    @OntologyEditFunction()
    public reassignTicket(employee: Employee, ticketToAssign: Ticket, ticketToUnassign: Ticket): void {
        employee.assignedTickets.add(ticketToAssign);
        employee.assignedTickets.remove(ticketToUnassign);
    }
}

TSv2의 reassignTicket.ts는 출발 유형과 링크 API 이름으로 Edits.Link를 선언합니다.

import { Employee, Ticket } from "@ontology/sdk";
import { Client, Osdk } from "@osdk/client";
import { createEditBatch, Edits } from "@osdk/functions";

type OntologyEdit = Edits.Link<Employee, "assignedTickets">;

export default function reassignTicket(
    client: Client,
    employee: Osdk.Instance<Employee>,
    ticketToAssign: Osdk.Instance<Ticket>,
    ticketToUnassign: Osdk.Instance<Ticket>
): OntologyEdit[] {
    const batch = createEditBatch<OntologyEdit>(client);
    batch.link(employee, "assignedTickets", ticketToAssign);
    batch.unlink(employee, "assignedTickets", ticketToUnassign);
    return batch.getEdits();
}

Python은 OntologyLinkType에 링크의 온톨로지 API 이름을 전달합니다. 편집 객체의 Python 속성 이름과 같지 않을 수 있습니다.

from functions.api import OntologyEdit, OntologyLinkType, function
from ontology_sdk import FoundryClient
from ontology_sdk.ontology.objects import Employee, Ticket


@function(edits=[OntologyLinkType(Employee, "assignedTickets")])
def reassign_ticket(employee: Employee, ticket_to_assign: Ticket, ticket_to_unassign: Ticket) -> list[OntologyEdit]:
    edits = FoundryClient().ontology.edits()
    editable = edits.objects.Employee.edit(employee)
    editable.assigned_tickets.add(ticket_to_assign)
    editable.assigned_tickets.remove(ticket_to_unassign)
    return edits.get_edits()

편집이 실제로 적용되는 때

Authoring의 함수 도우미에서는 편집 결과를 확인하되 실제 객체에는 적용하지 않습니다. 온톨로지를 변경하려면 함수 기반 액션으로 연결하여 실행해야 합니다.

이 비적용 범위는 온톨로지 편집에 관한 것입니다. 함수의 외부 API나 웹훅은 미리보기에서도 실제 요청을 할 수 있으므로 외부 효과까지 없는 실행이라고 판단해서는 안 됩니다.

Automate의 Function 효과는 반환된 온톨로지 편집을 적용하지 않습니다. 자동화에서 편집하려면 함수 기반 액션의 액션 효과를 사용합니다.

편집 배치와 객체 검색

일반 편집 배치에서 바꾼 속성이나 링크는 함수 실행 중 객체 집합의 필터, Search Around와 집계에 바로 반영되지 않습니다. 생성과 삭제도 검색 조건에 즉시 반영된다고 가정하면 안 됩니다.

TSv1에서 이미 수정한 객체의 getter는 새 값을 보여 줍니다. 편집하지 않은 기본 키로 같은 객체를 다시 materialize하는 경우에도 대기 편집을 덧씌웁니다. 그러나 수정한 값으로 인덱스를 검색하는 것과는 다릅니다.

아래 예제의 Employee에는 숫자 기본 키 id와 문자열 name이 있고 두 속성은 검색 가능하다고 가정합니다. 대상의 이름을 Bob으로 바꾼 뒤 Bob인 객체의 개수를 조회합니다. 이 개수는 새 편집을 포함한 결과가 아닙니다. 기존 Bob의 수에 따라 결과가 달라지므로 고정 숫자를 예상하면 안 됩니다.

TSv1의 검색 전 상태

import { Edits, Integer, OntologyEditFunction, UserFacingError } from "@foundry/functions-api";
import { Employee, Objects } from "@foundry/ontology-api";

export class CaveatEditFunctions {
    @Edits(Employee)
    @OntologyEditFunction()
    public async editAndSearch(employeeId: Integer): Promise<void> {
        const employees = await Objects.search().employee()
            .filter(employee => employee.id.exactMatch(employeeId)).allAsync();
        const employee = employees[0];
        if (employee === undefined) throw new UserFacingError("대상 직원을 조회할 수 없습니다.");
        employee.name = "Bob";
        const count = await Objects.search().employee()
            .filter(item => item.name.exactMatch("Bob")).count();
        console.log(`Count from the pre-edit search state: ${count ?? 0}`);
    }
}

TSv2의 검색 전 상태

editAndSearch.ts에 작성합니다.

import { Client } from "@osdk/client";
import { Employee } from "@ontology/sdk";
import { Edits, Integer, createEditBatch } from "@osdk/functions";

type OntologyEdit = Edits.Object<Employee>;

export default async function editAndSearch(client: Client, employeeId: Integer): Promise<OntologyEdit[]> {
    const batch = createEditBatch<OntologyEdit>(client);
    const employee = await client(Employee).fetchOne(employeeId);
    batch.update(employee, { name: "Bob" });
    const { $count } = await client(Employee).where({ name: { $eq: "Bob" } })
        .aggregate({ $select: { $count: "unordered" } });
    console.log(`Count from the pre-edit search state: ${$count}`);
    return batch.getEdits();
}

Python의 검색 전 상태

from functions.api import Integer, OntologyEdit, UserFacingError, function
from ontology_sdk import FoundryClient
from ontology_sdk.ontology.objects import Employee


@function(edits=[Employee])
def edit_and_search(employee_id: Integer) -> list[OntologyEdit]:
    client = FoundryClient()
    employee = client.ontology.objects.Employee.get(employee_id)
    if employee is None:
        raise UserFacingError("대상 직원을 조회할 수 없습니다.")
    edits = client.ontology.edits()
    edits.objects.Employee.edit(employee).name = "Bob"
    count = client.ontology.objects.Employee.where(Employee.object_type.name == "Bob").count().compute()
    print(f"Count from the pre-edit search state: {count}")
    return edits.get_edits()

쓰기를 수행한 뒤 같은 실행에서 그 결과를 조회해야 하면 적용 대기 쓰기 실행 모델을 사용합니다. 그 모델의 함수 및 중첩 호출 안에서는 이전 편집을 조회와 집계에 반영합니다.

선택 배열의 호출 차이

TSv1 OntologyEditFunction을 Code Repositories에서 실행할 때 생략한 선택 배열은 undefined입니다. 같은 함수를 액션으로 실행하면 빈 배열로 전달됩니다. 입력 생략과 명시적인 빈 배열을 구분해야 하는 로직은 이 호출 차이를 고려해야 합니다.