Keyboard shortcuts

Press ← or → to navigate between chapters

Press ? to show this help

Press Esc to hide this help

TypeScript v1에서 v2로 이전

TSv2는 Node.js 런타임과 온톨로지 SDK(OSDK)를 사용합니다. 함수 선언뿐 아니라 입력 타입, 객체 식별, 검색과 집계 결과 및 편집 수집 방식이 달라집니다. 먼저 언어별 지원 범위를 확인합니다.

함수 선언과 패키지

TSv1은 index.ts에서 내보낸 클래스의 @Function 메서드를 게시합니다. TSv2는 src/functions 아래 파일마다 default export 함수 하나를 게시합니다. 관련 함수는 하위 폴더로 묶을 수 있습니다.

src/functions/reverseStringArray.ts의 예제는 입력 배열 자체의 순서를 뒤집습니다. 원래 순서를 보존해야 하는 업무에서는 복사 여부를 별도로 결정해야 합니다.

export default function reverseStringArray(arr: string[]): string[] {
    return arr.reverse();
}

Integer와 Double 등의 import는 @foundry/functions-api에서 @osdk/functions로 바꿉니다. 예를 들어 비음수 Integer 두 개의 최대공약수를 계산하는 gcd.ts는 다음과 같습니다.

import { Integer, UserFacingError } from "@osdk/functions";

export default function gcd(a: Integer, b: Integer): Integer {
    if (a < 0 || b < 0) throw new UserFacingError("비음수 정수를 입력해야 합니다.");
    while (b !== 0) {
        const remainder = a % b;
        a = b;
        b = remainder;
    }
    return a;
}

TSv1 클래스 안의 메서드를 재귀 호출하는 경우에는 클래스 밖의 gcd가 아니라 this.gcd를 호출해야 합니다. 위 예제는 반복문으로 같은 계산을 수행합니다.

LocalDate와 Timestamp는 DateISOString 및 TimestampISOString으로 바뀌며 ISO 8601 문자열을 사용합니다. 필요한 날짜 산술은 dayjs, date-fns, luxon 등 런타임과 호환되는 npm 라이브러리로 수행할 수 있습니다. 자료형 참조에서 Long 문자열과 집계 구조 등 다른 변경도 확인합니다.

SDK 생성과 설치

Resource imports에서 온톨로지 유형과 링크를 가져오면 초기 SDK 생성 안내가 나타납니다.

Create를 선택하고 SDK 이름을 정한 뒤 Create new version을 선택합니다. 첫 버전을 생성한 이후 이름은 변경할 수 없습니다.

생성이 끝나면 Install을 선택하여 package.json 의존성에 추가합니다. 선택만 하고 설치하지 않으면 코드에서 생성 유형을 사용할 수 없습니다.

사이드바의 Documentation에서 실제 온톨로지에 맞는 예제를 확인합니다. 로컬 SDK 생성을 사용하는 개발 경로도 있습니다.

클라이언트와 객체 수 조회

TSv1은 Objects.search().aircraft().count()를 사용하고, TSv2는 첫 매개변수에 런타임이 주입하는 Client를 받습니다.

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

export default async function countAircraft(client: Client): Promise<Double> {
    const result = await client(Aircraft).aggregate({ $select: { $count: "unordered" } });
    return result.$count;
}

TSv1의 count 응답에 undefined 처리가 필요하면 먼저 await한 뒤 count ?? 0을 적용합니다. Promise 자체에 기본값을 적용하는 것과 다릅니다.

Searchable을 먼저 확인

필터, 정렬과 집계에 사용하는 속성에는 두 버전 모두 Ontology Manager의 Searchable 설정이 필요합니다. TSv1은 해당 속성의 메서드를 생성하지 않아 컴파일 단계에서 알 수 있지만 TSv2는 where, $orderBy와 aggregate에 속성을 넘기는 코드가 컴파일될 수 있습니다.

Searchable이 없는 속성에 $containsAnyTerm 같은 전문 검색을 적용하면 런타임 오류 대신 빈 페이지가 반환될 수 있습니다. 이전 전에 실제 속성 설정을 확인해야 합니다.

필터 연산자

TSv1 filter의 함수형 조건은 TSv2 where의 조건 객체로 바뀝니다.

TSv1TSv2대상
exactMatch(value)$eq 또는 값 직접 지정Boolean, 날짜/시각, 숫자, String
exactMatch(...values)$in: [...]같은 범위
Filters.not(exactMatch(...))$ne같은 범위
hasProperty()$isNull: false배열, Boolean, 날짜/시각, 지리공간, 숫자, String
Filters.not(hasProperty())$isNull: true같은 범위
range().lt, lte, gt, gte$lt, $lte, $gt, $gte날짜/시각과 숫자
isTrue, isFalse$eq: true, $eq: falseBoolean
matchAnyToken$containsAnyTermString
matchAllTokens$containsAllTermsString
phrase$containsAllTermsInOrderString
phrasePrefix, prefixOnLastToken$interval에 $match와 $prefixOnLastTerm: trueString
contains$contains 안의 조건 하나배열
withinDistanceOf, withinPolygon, withinBoundingBox$withinGeopoint, GeoShape
intersectsPolygon, intersectsBoundingBox$intersectsGeoShape
doesNotIntersectPolygon, doesNotIntersectBoundingBox$not으로 $intersects 감싸기GeoShape
isPresent직접 대응 where 키 없음링크

TSv2 지리공간 속성은 $within, $intersects와 $isNull만, 배열 속성은 $contains와 $isNull만 받습니다. 배열 원소 조건은 $contains 안에 하나를 넣습니다.

범위와 여러 값

속성 하나에는 연산자 키 하나를 사용합니다. 아래는 시작 포함과 끝 제외의 시각 구간을 $and로 나누는 예입니다. 가상의 Event.startTime은 Timestamp이며 두 입력은 ISO 시각 문자열입니다.

import { ObjectSet } from "@osdk/client";
import { TimestampISOString } from "@osdk/functions";
import { Event } from "@ontology/sdk";

export default function eventsInRange(
    events: ObjectSet<Event>, start: TimestampISOString, end: TimestampISOString
): ObjectSet<Event> {
    return events.where({ $and: [
        { startTime: { $gte: start } },
        { startTime: { $lt: end } },
    ] });
}

TSv1 contains의 여러 값은 하나라도 맞으면 통과합니다. TSv2에서는 각 값의 $contains 조건을 $or로 묶어 그 의미를 보존합니다. 날짜 및 시각 비교 값도 ISO 8601 문자열을 사용합니다.

링크 탐색

TSv1TSv2
단일 링크 get/getAsync$link.<linkApiName>.fetchOne() 또는 fetchOneWithErrors()
다중 링크 all/allAsync$link.<linkApiName> 자체가 ObjectSet이며 asyncIter로 반복
집합의 searchAround 메서드pivotTo("<linkApiName>")

TSv1 단일 링크는 없으면 undefined를 반환합니다. TSv2 fetchOne은 예외를 던지고 fetchOneWithErrors는 { value } 또는 { error }를 반환합니다. error가 링크 부재인지 다른 실패인지 이 포장만으로 구분할 수 없습니다.

searchAround 메서드 이름에서 링크 API 이름을 추정하면 안 됩니다. 생성 SDK의 $link 키가 실제 API 이름이며 pivotTo에 그대로 전달합니다. 예를 들어 API 이름이 toOtherObjectType이면 pivotTo("toOtherObjectType")입니다.

TSv1은 단일 객체의 MultiLink를 객체 집합으로 바로 바꿀 수 없어 먼저 객체를 집합에 넣고 탐색합니다. TSv2는 다중 $link가 집합이므로 그 자리에서 where, aggregate 또는 pivotTo를 연결할 수 있습니다.

그룹화 방식

TSv1 groupBy와 segmentBy는 TSv2 aggregate의 $groupBy 객체로 바뀝니다.

TSv1TSv2 $groupBy 값
exactValues()"exact"
exactValues({ maxBuckets: n }){ $exactWithLimit: n }
topValues()"exact"
byFixedWidth(50){ $fixedWidth: 50 }
byRanges({ min: 0, max: 50 }, { min: 50, max: 100 }){ $ranges: [[0, 50], [50, 100]] }
byYear(){ $duration: [1, "years"] }
byQuarter(){ $duration: [1, "quarters"] }
byMonth(){ $duration: [1, "months"] }
byWeek(){ $duration: [1, "weeks"] }
byDays(n){ $duration: [n, "days"] }
byHours(n), byMinutes(n), bySeconds(n){ $duration: [n, "hours"] } 등 해당 단위
segmentBy같은 $groupBy의 두 번째 속성 키

$ranges는 두 원소 튜플을 사용하며 시작을 포함하고 끝을 제외합니다. $duration의 seconds, minutes, hours와 days만 임의 단위 수를 받으며 weeks, months, quarters와 years는 1만 받습니다.

정확값 그룹의 한도

TSv1 topValues의 기본 버킷 수는 1,000, TSv2 exact의 기본은 10,000입니다. 고유 값이 한도 안에 있어야 정확하며, 넘으면 일부 객체를 그룹 결과에서 제외하여 근사 집계가 됩니다.

플랫폼 응답에는 정확/근사 여부가 있지만 TypeScript OSDK가 이 필드를 노출하지 않으므로 반환 모양만으로 구분할 수 없습니다. 고카디널리티 속성은 $exactWithLimit로 지원 한도를 조정하거나 where로 범위를 좁혀야 합니다. Boolean은 고유 값이 최대 둘이므로 exact의 개수 한도에 걸리지 않습니다.

집계 지표

$count 외의 선택 키는 "<속성 API 이름>:<지표>" 형식이며 값은 unordered, asc 또는 desc입니다.

TSv1TSv2 $select
count()$count: "unordered"
average(e => e.p)"p:avg": "unordered"
max(e => e.p)"p:max": "unordered"
min(e => e.p)"p:min": "unordered"
sum(e => e.p)"p:sum": "unordered"
cardinality(e => e.p)"p:approximateDistinct": "unordered"
직접 대응 없음"p:exactDistinct": "unordered"

숫자는 모든 지표 접미사를 사용합니다. 날짜 및 Timestamp는 min, max, approximateDistinct와 exactDistinct만 사용하며 그 밖의 타입은 두 고유 값 개수 지표만 사용합니다.

aggregate의 요청은 필수 $select와 선택 $groupBy로 구성합니다. where 필드는 없으므로 먼저 객체 집합에 where를 연결합니다. 결과 키는 속성과 지표에서 정하며 임의 지표 별칭을 지원하지 않습니다.

직접 대응하지 않는 기능

기능이전 시 조건
링크 존재 필터 isPresent직접 where 키가 없습니다. 연결 개수 파생 속성으로 조건을 구성할 수 있지만 동일 기능으로 간주하지 않고 자신의 데이터에서 의미를 확인해야 합니다.
Fuzziness 편집 거리$containsAnyTerm와 $containsAllTerms는 fuzzySearch Boolean만 지원합니다. $containsAllTermsInOrder에는 fuzzy 옵션이 없습니다. $interval의 $fuzzy/$fuzziness를 사용하는 대안도 실제 조건을 확인해야 합니다.
열린 그룹 범위$ranges 튜플은 양끝이 모두 필요합니다. TSv1의 min 또는 max 생략과 다릅니다.
Long 그룹화TSv2의 $groupBy 타입은 Long 속성에 대한 그룹화 값을 제공하지 않습니다.
배열 속성 그룹화TSv2 타입이 허용하더라도 지원 결과를 보장하지 않습니다. 스칼라 속성으로 그룹화합니다.
segmentBy의 반환 모양TSv2는 여러 $groupBy 키와 각 행의 $group을 가진 평평한 배열을 반환합니다. ThreeDimensionalAggregation이 필요하면 groups 중첩을 조립합니다.
날짜와 시각의 평균TSv1 average만 지원합니다. TSv2 날짜/시각에 avg와 sum은 없습니다.
take 또는 limitTSv2에는 없으며 fetchPage 또는 asyncIter 옵션을 사용합니다.
all/allAsyncTSv2는 fetchPage 또는 asyncIter를 사용합니다.
전문 검색 관련도 정렬TSv2의 relevance 정렬은 nearestNeighbors에만 적용하며 토큰 where에는 효과가 없습니다.

파생 속성과 집계 타입에서 각 데이터 구조를 확인할 수 있습니다.

정렬, 페이지와 KNN

TSv2는 fetchPage 또는 asyncIter 옵션의 $orderBy로 정렬합니다. 페이지 크기를 직접 지정할 때는 fetchPage의 $pageSize를 사용하며, 이 값은 한 페이지의 크기이고 전체 결과 수의 상한은 아닙니다. 상위 N개만 처리해야 하면 한 페이지 또는 반복 종료 조건으로 전체 범위를 제한합니다.

KNN의 이웃 수는 TSv1의 1~100에서 TSv2의 1~500으로 넓어집니다. TSv2 nearestNeighbors의 numNeighbors가 결과 수를 제한하고 각 객체에 $score가 붙습니다. $orderBy: "relevance"를 이 검색에 사용할 수 있습니다.

TSv1의 matchAnyToken, orderByRelevance, take 조합과 같은 전문 검색 상위 N개 패턴은 TSv2에 직접 대응하지 않습니다.

객체 식별

TSv1TSv2조건
rid$rid읽을 때 $includeRid: true를 지정해야 포함
primaryKey$primaryKey항상 존재
typeId$apiName 또는 $objectType둘 다 객체 유형 API 이름이며 $apiName만 리터럴 타입
없음$objectSpecifier유형과 기본 키를 포함하는 문자열
EmployeeOsdk.Instance<Employee>로드한 인스턴스 타입
OntologyObject직접 대응 없음. 가장 가까운 타입은 @osdk/api의 OsdkBase동일 API라고 가정하지 않음
IsOntologyObject직접 대응 없음제거됨
객체 키 FunctionsMapRecord<ObjectSpecifier<T>, V>키에 객체의 $objectSpecifier 사용

TSv1은 typeId와 primaryKey를 함께 비교하지만 TSv2는 다음 helper처럼 $objectSpecifier를 비교합니다. $rid는 기본 읽기에서 없으므로 동일성 기준으로 사용하지 않습니다.

export function isEqual(a: { $objectSpecifier: string }, b: { $objectSpecifier: string }): boolean {
    return a.$objectSpecifier === b.$objectSpecifier;
}

편집 목록 반환

TSv1은 @OntologyEditFunction과 @Edits로 편집을 선언하고 void를 반환합니다. TSv2는 Edits 합집합과 createEditBatch를 사용해 실제 편집 목록을 반환합니다.

가상의 Aircraft.businessCapacity를 3으로, Employee.department를 HR로 바꾸는 예제입니다.

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

type OntologyEdit = Edits.Object<Aircraft> | Edits.Object<Employee>;

export default function myFunction(
    client: Client, aircraft: Osdk.Instance<Aircraft>, employee: Osdk.Instance<Employee>
): OntologyEdit[] {
    const batch = createEditBatch<OntologyEdit>(client);
    batch.update(aircraft, { businessCapacity: 3 });
    batch.update(employee, { department: "HR" });
    return batch.getEdits();
}

선언한 Edits 범위 밖의 객체나 링크를 편집하면 타입 오류가 발생합니다. 일반 TSv2 배치는 Edits.Interface로 인터페이스 속성을 통한 생성, 수정과 삭제도 지원합니다.

함수의 일반 편집 배치는 실행 중 검색에 즉시 적용하지 않습니다. 함수 기반 액션이 성공해야 적용합니다. 쓰기 뒤 조회에 새 값이 필요하면 WriteableClient 적용 대기 쓰기를 사용하며 이 모델은 편집 목록을 직접 반환하지 않아도 됩니다.

새 객체 키와 warm 실행

TSv1은 @foundry/functions-utils의 Uuid.random, TSv2는 Node의 randomUUID를 사용할 수 있습니다. ID 생성 예제를 따릅니다.

UUID 같은 실행별 값은 함수 본문 안에서 생성합니다. TSv2의 warm 호출은 모듈 초기화 코드를 다시 실행하지 않으므로 모듈 최상위에서 만든 UUID가 이후 호출에도 재사용될 수 있습니다.

전체 객체 로드와 순차 처리

TSv1 all/allAsync는 전체 집합을 메모리에 올립니다. TSv2 asyncIter는 페이지를 순차적으로 읽어 처리하므로 전체 객체 배열을 먼저 만들지 않아도 됩니다.

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

export default async function editAircraft(
    client: Client, aircraft: ObjectSet<Aircraft>
): Promise<Edits.Object<Aircraft>[]> {
    const batch = createEditBatch<Edits.Object<Aircraft>>(client);
    for await (const item of aircraft.asyncIter()) batch.update(item, { arrived: true });
    return batch.getEdits();
}

예제는 편집할 집합을 입력으로 받아 해당 객체의 arrived를 true로 바꿉니다. 배치에는 최종 편집 목록이 계속 쌓이므로 객체 조회를 페이지로 나눠도 액션의 편집 수나 메모리 한도가 없어지지는 않습니다.

작은 집합을 한 번에 배열로 만들어야 한다면 await Array.fromAsync(client(Aircraft).asyncIter())를 사용할 수 있습니다. 이 경우 모든 객체를 메모리에 보관하는 비용이 다시 생깁니다.