blob: 6dea49de1afd58179802ed4853398dbabfe6265e [file]
<!doctype html>
<html lang="en-US" data-theme="light">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<meta name="generator" content="VuePress 2.0.0-rc.26" />
<meta name="theme" content="VuePress Theme Hope 2.0.0-rc.99" />
<style>
:root {
--vp-c-bg: #fff;
}
[data-theme="dark"] {
--vp-c-bg: #1b1b1f;
}
html,
body {
background: var(--vp-c-bg);
}
</style>
<script>
const userMode = localStorage.getItem("vuepress-theme-hope-scheme");
const systemDarkMode =
window.matchMedia &&
window.matchMedia("(prefers-color-scheme: dark)").matches;
if (userMode === "dark" || (userMode !== "light" && systemDarkMode)) {
document.documentElement.setAttribute("data-theme", "dark");
}
</script>
<script type="application/ld+json">{"@context":"https://schema.org","@type":"Article","headline":"UDF","image":[""],"dateModified":"2025-11-13T10:04:03.000Z","author":[]}</script><meta property="og:url" content="https://iotdb.apache.org/UserGuide/latest/User-Manual/User-defined-function_apache.html"><meta property="og:site_name" content="IoTDB Website"><meta property="og:title" content="UDF"><meta property="og:description" content="UDF 1. UDF Introduction UDF (User Defined Function) refers to user-defined functions. IoTDB provides a variety of built-in time series processing functions and also supports ext..."><meta property="og:type" content="article"><meta property="og:locale" content="en-US"><meta property="og:locale:alternate" content="zh-CN"><meta property="og:updated_time" content="2025-11-13T10:04:03.000Z"><meta property="article:modified_time" content="2025-11-13T10:04:03.000Z"><link rel="alternate" hreflang="zh-cn" href="https://iotdb.apache.org/zh/UserGuide/latest/User-Manual/User-defined-function_apache.html"><script async src="https://widget.kapa.ai/kapa-widget.bundle.js" data-website-id="2d37bfdd-8d98-40ba-9223-9d4f81bfb327" data-project-name="Apache IoTDB" data-project-color="#FFFFFF" data-button-z-index="1999" data-button-padding="4px" data-button-border-radius="4px" data-button-image-height="24px" data-button-image-width="20px" data-button-text-color="#9E2878" data-project-logo="https://iotdb.apache.org/img/logo.svg" data-button-position-right="16px" data-button-position-bottom="8px" data-button-height="56px" data-button-width="48px" data-button-text="Ask" data-modal-override-open-selector="#custom-ask-ai-button" data-modal-image-width="150px" data-modal-title="AI Docs" data-modal-title-color="#9E2878" data-deep-thinking-button-active-bg-color="#F6F7F8" data-deep-thinking-button-active-text-color="#9E2878" data-deep-thinking-button-active-hover-text-color="#9E2878" data-modal-disclaimer="This is a custom LLM for Apache IoTDB with access to all [documentation](iotdb.apache.org/docs/), [GitHub Open Issues, PRs and READMEs](github.com/apache/iotdb).&#10;&#10;
If you encounter <Error in verifying browser for feedback submission. Captcha token could not be obtained.> please ensure that you can access Google services." data-user-analytics-fingerprint-enabled="true" data-consent-required="true" data-consent-screen-disclaimer="By clicking <I agree, let's chat>, you consent to the use of the AI assistant in accordance with kapa.ai's [Privacy Policy](https://www.kapa.ai/content/privacy-policy). This service uses reCAPTCHA, which requires your consent to Google's [Privacy Policy](https://policies.google.com/privacy) and [Terms of Service](https://policies.google.com/terms). By proceeding, you explicitly agree to both kapa.ai's and Google's privacy policies."></script><link rel="icon" href="/favicon.ico"><meta name="Description" content="Apache IoTDB: Time Series Database for IoT"><meta name="Keywords" content="TSDB, time series, time series database, IoTDB, IoT database, IoT data management, 时序数据库, 时间序列管理, IoTDB, 物联网数据库, 实时数据库, 物联网数据管理, 物联网数据"><meta name="baidu-site-verification" content="wfKETzB3OT"><meta name="google-site-verification" content="mZWAoRY0yj_HAr-s47zHCGHzx5Ju-RVm5wDbPnwQYFo"><script type="text/javascript">var _paq = window._paq = window._paq || [];
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
_paq.push(["setDoNotTrack", true]);
_paq.push(["disableCookies"]);
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function() {
var u="https://analytics.apache.org/";
_paq.push(['setTrackerUrl', u+'matomo.php']);
_paq.push(['setSiteId', '56']);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
})();
</script><title>UDF | IoTDB Website</title><meta name="description" content="UDF 1. UDF Introduction UDF (User Defined Function) refers to user-defined functions. IoTDB provides a variety of built-in time series processing functions and also supports ext...">
<link rel="preload" href="/assets/style-JraSdRX4.css" as="style"><link rel="stylesheet" href="/assets/style-JraSdRX4.css">
<link rel="modulepreload" href="/assets/app-pmkzp6TS.js"><link rel="modulepreload" href="/assets/User-defined-function_apache.html-C4IPMj3l.js"><link rel="modulepreload" href="/assets/stateWindow-phT5xDMn.js"><link rel="modulepreload" href="/assets/99787878-47b51480-2b5b-11eb-8ed3-84088c5c30f7-CHu25FDG.js">
</head>
<body>
<div id="app"><!--[--><!--[--><!--[--><span tabindex="-1"></span><a href="#main-content" class="vp-skip-link sr-only">Skip to main content</a><!--]--><!--[--><div class="theme-container external-link-icon has-toc" vp-container><!--[--><header id="navbar" class="vp-navbar" vp-navbar><div class="vp-navbar-start"><button type="button" class="vp-toggle-sidebar-button" title="Toggle Sidebar"><span class="icon"></span></button><!--[--><a class="route-link vp-brand" href="/" aria-label="Take me home"><img class="vp-nav-logo" src="/logo.png" alt><!----><!----></a><!--]--></div><div class="vp-navbar-center"><!--[--><!--]--></div><div class="vp-navbar-end"><!--[--><!--[--><div id="docsearch-container" style="display:none;"></div><div class="docsearch-placeholder"><button type="button" aria-label="Search" aria-keyshortcuts="Control+k" class="DocSearch DocSearch-Button"><span class="DocSearch-Button-Container"><svg width="20" height="20" viewBox="0 0 24 24" aria-hidden="true" class="DocSearch-Search-Icon"><circle cx="11" cy="11" r="8" stroke="currentColor" fill="none" stroke-width="1.4"></circle><path d="m21 21-4.3-4.3" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"></path></svg><span class="DocSearch-Button-Placeholder">Search</span></span><span class="DocSearch-Button-Keys"><kbd class="DocSearch-Button-Key DocSearch-Button-Key--ctrl">Ctrl</kbd><kbd class="DocSearch-Button-Key">K</kbd></span></button></div><!--]--><div><button id="custom-ask-ai-button"> ✨ Ask AI </button></div><nav class="vp-nav-links"><div class="vp-nav-item hide-in-mobile"><div class="vp-dropdown-wrapper"><button type="button" class="vp-dropdown-title" aria-label="Documentation"><!--[--><!---->Documentation<!--]--><span class="arrow"></span><ul class="vp-dropdown"><li class="vp-dropdown-item"><a class="route-link auto-link" href="/UserGuide/latest/QuickStart/QuickStart_apache.html" aria-label="v2.0.x"><!---->v2.0.x<!----></a></li><li class="vp-dropdown-item"><a class="route-link auto-link" href="/UserGuide/V1.3.x/QuickStart/QuickStart_apache.html" aria-label="v1.3.x"><!---->v1.3.x<!----></a></li><li class="vp-dropdown-item"><a class="route-link auto-link" href="/UserGuide/V1.2.x/QuickStart/QuickStart.html" aria-label="v1.2.x"><!---->v1.2.x<!----></a></li><li class="vp-dropdown-item"><a class="route-link auto-link" href="/UserGuide/V0.13.x/QuickStart/QuickStart.html" aria-label="v0.13.x"><!---->v0.13.x<!----></a></li></ul></button></div></div><div class="vp-nav-item hide-in-mobile"><a class="auto-link external-link" href="https://cwiki.apache.org/confluence/display/IOTDB/System+Design" aria-label="Design" rel="noopener noreferrer" target="_blank"><!---->Design<!----></a></div><div class="vp-nav-item hide-in-mobile"><a class="route-link auto-link" href="/Download/" aria-label="Download"><!---->Download<!----></a></div><div class="vp-nav-item hide-in-mobile"><div class="vp-dropdown-wrapper"><button type="button" class="vp-dropdown-title" aria-label="Community"><!--[--><!---->Community<!--]--><span class="arrow"></span><ul class="vp-dropdown"><li class="vp-dropdown-item"><a class="route-link auto-link" href="/Community/About-the-Community.html" aria-label="About the Community"><!---->About the Community<!----></a></li><li class="vp-dropdown-item"><a class="route-link auto-link" href="/Community/Development-Guide.html" aria-label="Development Guide"><!---->Development Guide<!----></a></li><li class="vp-dropdown-item"><a class="route-link auto-link" href="/Community/Community-Partners.html" aria-label="Community Partners"><!---->Community Partners<!----></a></li><li class="vp-dropdown-item"><a class="route-link auto-link" href="/Community/Communication-Channels.html" aria-label="Communication Channels"><!---->Communication Channels<!----></a></li><li class="vp-dropdown-item"><a class="route-link auto-link" href="/Community/Events-and-Reports.html" aria-label="Events and Reports"><!---->Events and Reports<!----></a></li><li class="vp-dropdown-item"><a class="route-link auto-link" href="/Community/Committers.html" aria-label="Committers"><!---->Committers<!----></a></li></ul></button></div></div><div class="vp-nav-item hide-in-mobile"><div class="vp-dropdown-wrapper"><button type="button" class="vp-dropdown-title" aria-label="ASF"><!--[--><!---->ASF<!--]--><span class="arrow"></span><ul class="vp-dropdown"><li class="vp-dropdown-item"><a class="auto-link external-link" href="https://www.apache.org/" aria-label="Foundation" rel="noopener noreferrer" target="_blank"><!---->Foundation<!----></a></li><li class="vp-dropdown-item"><a class="auto-link external-link" href="https://www.apache.org/licenses/" aria-label="License" rel="noopener noreferrer" target="_blank"><!---->License<!----></a></li><li class="vp-dropdown-item"><a class="auto-link external-link" href="https://www.apache.org/security/" aria-label="Security" rel="noopener noreferrer" target="_blank"><!---->Security<!----></a></li><li class="vp-dropdown-item"><a class="auto-link external-link" href="https://www.apache.org/foundation/sponsorship.html" aria-label="Sponsorship" rel="noopener noreferrer" target="_blank"><!---->Sponsorship<!----></a></li><li class="vp-dropdown-item"><a class="auto-link external-link" href="https://www.apache.org/foundation/thanks.html" aria-label="Thanks" rel="noopener noreferrer" target="_blank"><!---->Thanks<!----></a></li><li class="vp-dropdown-item"><a class="auto-link external-link" href="https://www.apache.org/events/current-event" aria-label="Current Events" rel="noopener noreferrer" target="_blank"><!---->Current Events<!----></a></li><li class="vp-dropdown-item"><a class="auto-link external-link" href="https://privacy.apache.org/policies/privacy-policy-public.html" aria-label="Privacy" rel="noopener noreferrer" target="_blank"><!---->Privacy<!----></a></li></ul></button></div></div></nav><div class="vp-nav-item"><div class="vp-dropdown-wrapper"><button type="button" class="vp-dropdown-title" aria-label="Select language"><!--[--><svg xmlns="http://www.w3.org/2000/svg" class="icon i18n-icon" viewBox="0 0 1024 1024" fill="currentColor" aria-label="i18n icon" name="i18n" style="width:1rem;height:1rem;vertical-align:middle;"><path d="M379.392 460.8 494.08 575.488l-42.496 102.4L307.2 532.48 138.24 701.44l-71.68-72.704L234.496 460.8l-45.056-45.056c-27.136-27.136-51.2-66.56-66.56-108.544h112.64c7.68 14.336 16.896 27.136 26.112 35.84l45.568 46.08 45.056-45.056C382.976 312.32 409.6 247.808 409.6 204.8H0V102.4h256V0h102.4v102.4h256v102.4H512c0 70.144-37.888 161.28-87.04 210.944L378.88 460.8zM576 870.4 512 1024H409.6l256-614.4H768l256 614.4H921.6l-64-153.6H576zM618.496 768h196.608L716.8 532.48 618.496 768z"></path></svg><!--]--><span class="arrow"></span><ul class="vp-dropdown"><li class="vp-dropdown-item"><a class="route-link route-link-active auto-link" href="/UserGuide/latest/User-Manual/User-defined-function_apache.html" aria-label="English"><!---->English<!----></a></li><li class="vp-dropdown-item"><a class="route-link auto-link" href="/zh/UserGuide/latest/User-Manual/User-defined-function_apache.html" aria-label="简体中文"><!---->简体中文<!----></a></li></ul></button></div></div><div class="vp-nav-item hide-in-mobile"><button type="button" class="vp-color-mode-switch" id="color-mode-switch"><svg xmlns="http://www.w3.org/2000/svg" class="icon auto-icon" viewBox="0 0 1024 1024" fill="currentColor" aria-label="auto icon" name="auto" style="display:none;"><path d="M512 992C246.92 992 32 777.08 32 512S246.92 32 512 32s480 214.92 480 480-214.92 480-480 480zm0-840c-198.78 0-360 161.22-360 360 0 198.84 161.22 360 360 360s360-161.16 360-360c0-198.78-161.22-360-360-360zm0 660V212c165.72 0 300 134.34 300 300 0 165.72-134.28 300-300 300z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" class="icon dark-icon" viewBox="0 0 1024 1024" fill="currentColor" aria-label="dark icon" name="dark" style="display:none;"><path d="M524.8 938.667h-4.267a439.893 439.893 0 0 1-313.173-134.4 446.293 446.293 0 0 1-11.093-597.334A432.213 432.213 0 0 1 366.933 90.027a42.667 42.667 0 0 1 45.227 9.386 42.667 42.667 0 0 1 10.24 42.667 358.4 358.4 0 0 0 82.773 375.893 361.387 361.387 0 0 0 376.747 82.774 42.667 42.667 0 0 1 54.187 55.04 433.493 433.493 0 0 1-99.84 154.88 438.613 438.613 0 0 1-311.467 128z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" class="icon light-icon" viewBox="0 0 1024 1024" fill="currentColor" aria-label="light icon" name="light" style="display:block;"><path d="M952 552h-80a40 40 0 0 1 0-80h80a40 40 0 0 1 0 80zM801.88 280.08a41 41 0 0 1-57.96-57.96l57.96-58a41.04 41.04 0 0 1 58 58l-58 57.96zM512 752a240 240 0 1 1 0-480 240 240 0 0 1 0 480zm0-560a40 40 0 0 1-40-40V72a40 40 0 0 1 80 0v80a40 40 0 0 1-40 40zm-289.88 88.08-58-57.96a41.04 41.04 0 0 1 58-58l57.96 58a41 41 0 0 1-57.96 57.96zM192 512a40 40 0 0 1-40 40H72a40 40 0 0 1 0-80h80a40 40 0 0 1 40 40zm30.12 231.92a41 41 0 0 1 57.96 57.96l-57.96 58a41.04 41.04 0 0 1-58-58l58-57.96zM512 832a40 40 0 0 1 40 40v80a40 40 0 0 1-80 0v-80a40 40 0 0 1 40-40zm289.88-88.08 58 57.96a41.04 41.04 0 0 1-58 58l-57.96-58a41 41 0 0 1 57.96-57.96z"></path></svg></button></div><div class="vp-nav-item vp-action"><a class="vp-action-link" href="https://github.com/apache/iotdb" target="_blank" rel="noopener noreferrer" aria-label="GitHub"><svg xmlns="http://www.w3.org/2000/svg" class="icon github-icon" viewBox="0 0 1024 1024" fill="currentColor" aria-label="github icon" name="github" style="width:1.25rem;height:1.25rem;vertical-align:middle;"><path d="M511.957 21.333C241.024 21.333 21.333 240.981 21.333 512c0 216.832 140.544 400.725 335.574 465.664 24.49 4.395 32.256-10.07 32.256-23.083 0-11.69.256-44.245 0-85.205-136.448 29.61-164.736-64.64-164.736-64.64-22.315-56.704-54.4-71.765-54.4-71.765-44.587-30.464 3.285-29.824 3.285-29.824 49.195 3.413 75.179 50.517 75.179 50.517 43.776 75.008 114.816 53.333 142.762 40.79 4.523-31.66 17.152-53.377 31.19-65.537-108.971-12.458-223.488-54.485-223.488-242.602 0-53.547 19.114-97.323 50.517-131.67-5.035-12.33-21.93-62.293 4.779-129.834 0 0 41.258-13.184 134.912 50.346a469.803 469.803 0 0 1 122.88-16.554c41.642.213 83.626 5.632 122.88 16.554 93.653-63.488 134.784-50.346 134.784-50.346 26.752 67.541 9.898 117.504 4.864 129.834 31.402 34.347 50.474 78.123 50.474 131.67 0 188.586-114.73 230.016-224.042 242.09 17.578 15.232 33.578 44.672 33.578 90.454v135.85c0 13.142 7.936 27.606 32.854 22.87C862.25 912.597 1002.667 728.747 1002.667 512c0-271.019-219.648-490.667-490.71-490.667z"></path></svg></a></div><!--]--><button type="button" class="vp-toggle-navbar-button" aria-label="Toggle Navbar" aria-expanded="false" aria-controls="nav-screen"><span><span class="vp-top"></span><span class="vp-middle"></span><span class="vp-bottom"></span></span></button></div></header><!----><!--]--><!----><div class="toggle-sidebar-wrapper"><span class="arrow start"></span></div><aside id="sidebar" class="vp-sidebar" vp-sidebar><!--[--><p class="vp-sidebar-header iotdb-sidebar-header"><span class="vp-sidebar-title">IoTDB User Guide (V2.0.x)</span></p><div class="sidebar-top-wrapper"><ul class="switch-list"><li class="switch-type switch-active">Tree</li><li class="switch-type">Table</li></ul><div class="help-icon-wrapper"><div class="help-icon"><span>Description</span></div></div></div><!--]--><ul class="vp-sidebar-links"><li><section class="vp-sidebar-group"><p class="vp-sidebar-header"><!----><span class="vp-sidebar-title">IoTDB User Guide (V2.0.x)</span><!----></p><ul class="vp-sidebar-links"></ul></section></li><li><section class="vp-sidebar-group"><button class="vp-sidebar-header clickable" type="button"><!----><span class="vp-sidebar-title">About IoTDB</span><span class="vp-arrow end"></span></button><!----></section></li><li><section class="vp-sidebar-group"><button class="vp-sidebar-header clickable" type="button"><!----><span class="vp-sidebar-title">Background knowledge</span><span class="vp-arrow end"></span></button><!----></section></li><li><a class="route-link auto-link vp-sidebar-link" href="/UserGuide/latest/QuickStart/QuickStart_apache.html" aria-label="Quick Start"><!---->Quick Start<!----></a></li><li><section class="vp-sidebar-group"><button class="vp-sidebar-header clickable" type="button"><!----><span class="vp-sidebar-title">Installation and Deployment</span><span class="vp-arrow end"></span></button><!----></section></li><li><section class="vp-sidebar-group"><button class="vp-sidebar-header clickable" type="button"><!----><span class="vp-sidebar-title">Basic Functions</span><span class="vp-arrow end"></span></button><!----></section></li><li><section class="vp-sidebar-group"><button class="vp-sidebar-header clickable active" type="button"><!----><span class="vp-sidebar-title">Advanced Features</span><span class="vp-arrow down"></span></button><ul class="vp-sidebar-links"><li><a class="route-link auto-link vp-sidebar-link" href="/UserGuide/latest/User-Manual/Data-Sync_apache.html" aria-label="Data Sync"><!---->Data Sync<!----></a></li><li><a class="route-link auto-link vp-sidebar-link" href="/UserGuide/latest/User-Manual/Data-subscription_apache.html" aria-label="Data Subscription"><!---->Data Subscription<!----></a></li><li><section class="vp-sidebar-group"><button class="vp-sidebar-header clickable" type="button"><!----><span class="vp-sidebar-title">Stream Computing</span><span class="vp-arrow end"></span></button><!----></section></li><li><a class="route-link route-link-active auto-link vp-sidebar-link active" href="/UserGuide/latest/User-Manual/User-defined-function_apache.html" aria-label="UDF"><!---->UDF<!----></a></li><li><section class="vp-sidebar-group"><button class="vp-sidebar-header clickable" type="button"><!----><span class="vp-sidebar-title">Security Management</span><span class="vp-arrow end"></span></button><!----></section></li><li><section class="vp-sidebar-group"><button class="vp-sidebar-header clickable" type="button"><!----><span class="vp-sidebar-title">System Maintenance</span><span class="vp-arrow end"></span></button><!----></section></li></ul></section></li><li><section class="vp-sidebar-group"><button class="vp-sidebar-header clickable" type="button"><!----><span class="vp-sidebar-title">AI capability</span><span class="vp-arrow end"></span></button><!----></section></li><li><section class="vp-sidebar-group"><button class="vp-sidebar-header clickable" type="button"><!----><span class="vp-sidebar-title">Tools System</span><span class="vp-arrow end"></span></button><!----></section></li><li><section class="vp-sidebar-group"><button class="vp-sidebar-header clickable" type="button"><!----><span class="vp-sidebar-title">API</span><span class="vp-arrow end"></span></button><!----></section></li><li><section class="vp-sidebar-group"><button class="vp-sidebar-header clickable" type="button"><!----><span class="vp-sidebar-title">Ecosystem Integration</span><span class="vp-arrow end"></span></button><!----></section></li><li><section class="vp-sidebar-group"><button class="vp-sidebar-header clickable" type="button"><!----><span class="vp-sidebar-title">SQL Manual</span><span class="vp-arrow end"></span></button><!----></section></li><li><section class="vp-sidebar-group"><button class="vp-sidebar-header clickable" type="button"><!----><span class="vp-sidebar-title">Technical Insider</span><span class="vp-arrow end"></span></button><!----></section></li><li><section class="vp-sidebar-group"><button class="vp-sidebar-header clickable" type="button"><!----><span class="vp-sidebar-title">Reference</span><span class="vp-arrow end"></span></button><!----></section></li><li><section class="vp-sidebar-group"><button class="vp-sidebar-header clickable" type="button"><!----><span class="vp-sidebar-title">FAQ</span><span class="vp-arrow end"></span></button><!----></section></li></ul><!----></aside><!--[--><main id="main-content" class="vp-page"><!--[--><!----><!----><nav class="vp-breadcrumb disable"></nav><div class="vp-page-title"><h1><!---->UDF</h1><div class="page-info"><!----><!----><span class="page-date-info" aria-label="Writing Date📅" data-balloon-pos="up"><svg xmlns="http://www.w3.org/2000/svg" class="icon calendar-icon" viewBox="0 0 1024 1024" fill="currentColor" aria-label="calendar icon" name="calendar"><path d="M716.4 110.137c0-18.753-14.72-33.473-33.472-33.473-18.753 0-33.473 14.72-33.473 33.473v33.473h66.993v-33.473zm-334.87 0c0-18.753-14.72-33.473-33.473-33.473s-33.52 14.72-33.52 33.473v33.473h66.993v-33.473zm468.81 33.52H716.4v100.465c0 18.753-14.72 33.473-33.472 33.473a33.145 33.145 0 01-33.473-33.473V143.657H381.53v100.465c0 18.753-14.72 33.473-33.473 33.473a33.145 33.145 0 01-33.473-33.473V143.657H180.6A134.314 134.314 0 0046.66 277.595v535.756A134.314 134.314 0 00180.6 947.289h669.74a134.36 134.36 0 00133.94-133.938V277.595a134.314 134.314 0 00-133.94-133.938zm33.473 267.877H147.126a33.145 33.145 0 01-33.473-33.473c0-18.752 14.72-33.473 33.473-33.473h736.687c18.752 0 33.472 14.72 33.472 33.473a33.145 33.145 0 01-33.472 33.473z"></path></svg><span data-allow-mismatch="text">11/8/24</span><meta property="datePublished" content="2024-11-08T07:08:05.000Z"></span><!----><span class="page-reading-time-info" aria-label="Reading Time⌛" data-balloon-pos="up"><svg xmlns="http://www.w3.org/2000/svg" class="icon timer-icon" viewBox="0 0 1024 1024" fill="currentColor" aria-label="timer icon" name="timer"><path d="M799.387 122.15c4.402-2.978 7.38-7.897 7.38-13.463v-1.165c0-8.933-7.38-16.312-16.312-16.312H256.33c-8.933 0-16.311 7.38-16.311 16.312v1.165c0 5.825 2.977 10.874 7.637 13.592 4.143 194.44 97.22 354.963 220.201 392.763-122.204 37.542-214.893 196.511-220.2 389.397-4.661 5.049-7.638 11.651-7.638 19.03v5.825h566.49v-5.825c0-7.379-2.849-13.981-7.509-18.9-5.049-193.016-97.867-351.985-220.2-389.527 123.24-37.67 216.446-198.453 220.588-392.892zM531.16 450.445v352.632c117.674 1.553 211.787 40.778 211.787 88.676H304.097c0-48.286 95.149-87.382 213.728-88.676V450.445c-93.077-3.107-167.901-81.297-167.901-177.093 0-8.803 6.99-15.793 15.793-15.793 8.803 0 15.794 6.99 15.794 15.793 0 80.261 63.69 145.635 142.01 145.635s142.011-65.374 142.011-145.635c0-8.803 6.99-15.793 15.794-15.793s15.793 6.99 15.793 15.793c0 95.019-73.789 172.82-165.96 177.093z"></path></svg><span>About 25 min</span><meta property="timeRequired" content="PT25M"></span><!----><!----></div><hr></div><!----><div class="" vp-content><!----><div id="markdown-content"><h1 id="udf" tabindex="-1"><a class="header-anchor" href="#udf"><span>UDF</span></a></h1><h2 id="_1-udf-introduction" tabindex="-1"><a class="header-anchor" href="#_1-udf-introduction"><span>1. UDF Introduction</span></a></h2><p>UDF (User Defined Function) refers to user-defined functions. IoTDB provides a variety of built-in time series processing functions and also supports extending custom functions to meet more computing needs.</p><p>In IoTDB, you can expand two types of UDF:</p><table style="text-align:left;"><tbody><tr><th>UDF Class</th><th>AccessStrategy</th><th>Description</th></tr><tr><td rowspan="2">UDTF</td><td>MAPPABLE_ROW_BY_ROW</td><td>Custom scalar function, input k columns of time series and 1 row of data, output 1 column of time series and 1 row of data, can be used in any clause and expression that appears in the scalar function, such as select clause, where clause, etc.</td></tr><tr><td>ROW_BY_ROW <br>SLIDING_TIME_WINDOW <br>SLIDING_SIZE_WINDOW <br>SESSION_TIME_WINDOW <br>STATE_WINDOW</td><td>Custom time series generation function, input k columns of time series m rows of data, output 1 column of time series n rows of data, the number of input rows m can be different from the number of output rows n, and can only be used in SELECT clauses.</td></tr><tr><td>UDAF</td><td>-</td><td>Custom aggregation function, input k columns of time series m rows of data, output 1 column of time series 1 row of data, can be used in any clause and expression that appears in the aggregation function, such as select clause, having clause, etc.</td></tr></tbody></table><h3 id="_1-1-udf-usage" tabindex="-1"><a class="header-anchor" href="#_1-1-udf-usage"><span>1.1 UDF usage</span></a></h3><p>The usage of UDF is similar to that of regular built-in functions, and can be directly used in SELECT statements like calling regular functions.</p><h4 id="_1-basic-sql-syntax-support" tabindex="-1"><a class="header-anchor" href="#_1-basic-sql-syntax-support"><span>1.Basic SQL syntax support</span></a></h4><ul><li>Support <code>SLIMIT</code> / <code>SOFFSET</code></li><li>Support <code>LIMIT</code> / <code>OFFSET</code></li><li>Support queries with value filters</li><li>Support queries with time filters</li></ul><h4 id="_2-queries-with-in-select-clauses" tabindex="-1"><a class="header-anchor" href="#_2-queries-with-in-select-clauses"><span>2. Queries with * in SELECT Clauses</span></a></h4><p>Assume that there are 2 time series (<code>root.sg.d1.s1</code> and <code>root.sg.d1.s2</code>) in the system.</p><ul><li><strong><code>SELECT example(*) from root.sg.d1</code></strong></li></ul><p>Then the result set will include the results of <code>example (root.sg.d1.s1)</code> and <code>example (root.sg.d1.s2)</code>.</p><ul><li><strong><code>SELECT example(s1, *) from root.sg.d1</code></strong></li></ul><p>Then the result set will include the results of <code>example(root.sg.d1.s1, root.sg.d1.s1)</code> and <code>example(root.sg.d1.s1, root.sg.d1.s2)</code>.</p><ul><li><strong><code>SELECT example(*, *) from root.sg.d1</code></strong></li></ul><p>Then the result set will include the results of <code>example(root.sg.d1.s1, root.sg.d1.s1)</code>, <code>example(root.sg.d1.s2, root.sg.d1.s1)</code>, <code>example(root.sg.d1.s1, root.sg.d1.s2)</code> and <code>example(root.sg.d1.s2, root.sg.d1.s2)</code>.</p><h4 id="_3-queries-with-key-value-attributes-in-udf-parameters" tabindex="-1"><a class="header-anchor" href="#_3-queries-with-key-value-attributes-in-udf-parameters"><span>3. Queries with Key-value Attributes in UDF Parameters</span></a></h4><p>You can pass any number of key-value pair parameters to the UDF when constructing a UDF query. The key and value in the key-value pair need to be enclosed in single or double quotes. Note that key-value pair parameters can only be passed in after all time series have been passed in. Here is a set of examples:</p><p>Example:</p><div class="language-sql line-numbers-mode" data-highlighter="shiki" data-ext="sql" style="background-color:#282c34;color:#abb2bf;"><pre class="shiki one-dark-pro vp-code"><code class="language-sql"><span class="line"><span style="color:#C678DD;">SELECT</span><span style="color:#ABB2BF;"> example(s1, </span><span style="color:#98C379;">&#39;key1&#39;</span><span style="color:#56B6C2;">=</span><span style="color:#98C379;">&#39;value1&#39;</span><span style="color:#ABB2BF;">, </span><span style="color:#98C379;">&#39;key2&#39;</span><span style="color:#56B6C2;">=</span><span style="color:#98C379;">&#39;value2&#39;</span><span style="color:#ABB2BF;">), example(*, </span><span style="color:#98C379;">&#39;key3&#39;</span><span style="color:#56B6C2;">=</span><span style="color:#98C379;">&#39;value3&#39;</span><span style="color:#ABB2BF;">) </span><span style="color:#C678DD;">FROM</span><span style="color:#D19A66;"> root</span><span style="color:#ABB2BF;">.</span><span style="color:#D19A66;">sg</span><span style="color:#ABB2BF;">.d1;</span></span>
<span class="line"><span style="color:#C678DD;">SELECT</span><span style="color:#ABB2BF;"> example(s1, s2, </span><span style="color:#98C379;">&#39;key1&#39;</span><span style="color:#56B6C2;">=</span><span style="color:#98C379;">&#39;value1&#39;</span><span style="color:#ABB2BF;">, </span><span style="color:#98C379;">&#39;key2&#39;</span><span style="color:#56B6C2;">=</span><span style="color:#98C379;">&#39;value2&#39;</span><span style="color:#ABB2BF;">) </span><span style="color:#C678DD;">FROM</span><span style="color:#D19A66;"> root</span><span style="color:#ABB2BF;">.</span><span style="color:#D19A66;">sg</span><span style="color:#ABB2BF;">.d1;</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div></div></div><h4 id="_4-nested-queries" tabindex="-1"><a class="header-anchor" href="#_4-nested-queries"><span>4. Nested Queries</span></a></h4><p>Example:</p><div class="language-sql line-numbers-mode" data-highlighter="shiki" data-ext="sql" style="background-color:#282c34;color:#abb2bf;"><pre class="shiki one-dark-pro vp-code"><code class="language-sql"><span class="line"><span style="color:#C678DD;">SELECT</span><span style="color:#ABB2BF;"> s1, s2, example(s1, s2) </span><span style="color:#C678DD;">FROM</span><span style="color:#D19A66;"> root</span><span style="color:#ABB2BF;">.</span><span style="color:#D19A66;">sg</span><span style="color:#ABB2BF;">.d1;</span></span>
<span class="line"><span style="color:#C678DD;">SELECT</span><span style="color:#ABB2BF;"> *, example(*) </span><span style="color:#C678DD;">FROM</span><span style="color:#D19A66;"> root</span><span style="color:#ABB2BF;">.</span><span style="color:#D19A66;">sg</span><span style="color:#ABB2BF;">.d1 </span><span style="color:#C678DD;">DISABLE</span><span style="color:#ABB2BF;"> ALIGN;</span></span>
<span class="line"><span style="color:#C678DD;">SELECT</span><span style="color:#ABB2BF;"> s1 * example(* / s1 + s2) </span><span style="color:#C678DD;">FROM</span><span style="color:#D19A66;"> root</span><span style="color:#ABB2BF;">.</span><span style="color:#D19A66;">sg</span><span style="color:#ABB2BF;">.d1;</span></span>
<span class="line"><span style="color:#C678DD;">SELECT</span><span style="color:#ABB2BF;"> s1, s2, s1 + example(s1, s2), s1 - example(s1 + example(s1, s2) / s2) </span><span style="color:#C678DD;">FROM</span><span style="color:#D19A66;"> root</span><span style="color:#ABB2BF;">.</span><span style="color:#D19A66;">sg</span><span style="color:#ABB2BF;">.d1;</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2 id="_2-udf-management" tabindex="-1"><a class="header-anchor" href="#_2-udf-management"><span>2. UDF management</span></a></h2><h3 id="_2-1-udf-registration" tabindex="-1"><a class="header-anchor" href="#_2-1-udf-registration"><span>2.1 UDF Registration</span></a></h3><p>The process of registering a UDF in IoTDB is as follows:</p><ol><li>Implement a complete UDF class, assuming the full class name of this class is <code>org.apache.iotdb.udf.ExampleUDTF</code>.</li><li>Convert the project into a JAR package. If using Maven to manage the project, you can refer to the <a href="https://github.com/apache/iotdb/tree/master/example/udf" target="_blank" rel="noopener noreferrer">Maven project example</a> above.</li><li>Make preparations for registration according to the registration mode. For details, see the following example.</li><li>You can use following SQL to register UDF.</li></ol><div class="language-sql line-numbers-mode" data-highlighter="shiki" data-ext="sql" style="background-color:#282c34;color:#abb2bf;"><pre class="shiki one-dark-pro vp-code"><code class="language-sql"><span class="line"><span style="color:#C678DD;">CREATE</span><span style="color:#C678DD;"> FUNCTION</span><span style="color:#56B6C2;"> &lt;</span><span style="color:#ABB2BF;">UDF-</span><span style="color:#C678DD;">NAME</span><span style="color:#56B6C2;">&gt;</span><span style="color:#C678DD;"> AS</span><span style="color:#56B6C2;"> &lt;</span><span style="color:#ABB2BF;">UDF-CLASS-FULL-PATHNAME</span><span style="color:#56B6C2;">&gt;</span><span style="color:#ABB2BF;"> (</span><span style="color:#C678DD;">USING</span><span style="color:#ABB2BF;"> URI URI-STRING)</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div></div></div><h4 id="example-register-udf-named-example-you-can-choose-either-of-the-following-two-registration-methods" tabindex="-1"><a class="header-anchor" href="#example-register-udf-named-example-you-can-choose-either-of-the-following-two-registration-methods"><span>Example: register UDF named <code>example</code>, you can choose either of the following two registration methods</span></a></h4><h4 id="method-1-manually-place-the-jar-package" tabindex="-1"><a class="header-anchor" href="#method-1-manually-place-the-jar-package"><span>Method 1: Manually place the jar package</span></a></h4><p>Prepare:<br> When registering using this method, it is necessary to place the JAR package in advance in the <code>ext/udf</code> directory of all nodes in the cluster (which can be configured).</p><p>Registration statement:</p><div class="language-sql line-numbers-mode" data-highlighter="shiki" data-ext="sql" style="background-color:#282c34;color:#abb2bf;"><pre class="shiki one-dark-pro vp-code"><code class="language-sql"><span class="line"><span style="color:#C678DD;">CREATE</span><span style="color:#C678DD;"> FUNCTION</span><span style="color:#61AFEF;"> example</span><span style="color:#C678DD;"> AS</span><span style="color:#98C379;"> &#39;org.apache.iotdb.udf.UDTFExample&#39;</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div></div></div><h4 id="method-2-cluster-automatically-installs-jar-packages-through-uri" tabindex="-1"><a class="header-anchor" href="#method-2-cluster-automatically-installs-jar-packages-through-uri"><span>Method 2: Cluster automatically installs jar packages through URI</span></a></h4><p>Prepare:<br> When registering using this method, it is necessary to upload the JAR package to the URI server in advance and ensure that the IoTDB instance executing the registration statement can access the URI server.</p><p>Registration statement:</p><div class="language-sql line-numbers-mode" data-highlighter="shiki" data-ext="sql" style="background-color:#282c34;color:#abb2bf;"><pre class="shiki one-dark-pro vp-code"><code class="language-sql"><span class="line"><span style="color:#C678DD;">CREATE</span><span style="color:#C678DD;"> FUNCTION</span><span style="color:#61AFEF;"> example</span><span style="color:#C678DD;"> AS</span><span style="color:#98C379;"> &#39;org.apache.iotdb.udf.UDTFExample&#39;</span><span style="color:#C678DD;"> USING</span><span style="color:#ABB2BF;"> URI </span><span style="color:#98C379;">&#39;http://jar/example.jar&#39;</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div></div></div><p>IoTDB will download JAR packages and synchronize them to the entire cluster.</p><h4 id="note" tabindex="-1"><a class="header-anchor" href="#note"><span>Note</span></a></h4><ol><li><p>Since UDF instances are dynamically loaded through reflection technology, you do not need to restart the server during the UDF registration process.</p></li><li><p>UDF function names are not case-sensitive.</p></li><li><p>Please ensure that the function name given to the UDF is different from all built-in function names. A UDF with the same name as a built-in function cannot be registered.</p></li><li><p>We recommend that you do not use classes that have the same class name but different function logic in different JAR packages. For example, in <code>UDF(UDAF/UDTF): udf1, udf2</code>, the JAR package of udf1 is <code>udf1.jar</code> and the JAR package of udf2 is <code>udf2.jar</code>. Assume that both JAR packages contain the <code>org.apache.iotdb.udf.ExampleUDTF</code> class. If you use two UDFs in the same SQL statement at the same time, the system will randomly load either of them and may cause inconsistency in UDF execution behavior.</p></li></ol><h3 id="_2-2-udf-deregistration" tabindex="-1"><a class="header-anchor" href="#_2-2-udf-deregistration"><span>2.2 UDF Deregistration</span></a></h3><p>The SQL syntax is as follows:</p><div class="language-sql line-numbers-mode" data-highlighter="shiki" data-ext="sql" style="background-color:#282c34;color:#abb2bf;"><pre class="shiki one-dark-pro vp-code"><code class="language-sql"><span class="line"><span style="color:#C678DD;">DROP</span><span style="color:#C678DD;"> FUNCTION</span><span style="color:#56B6C2;"> &lt;</span><span style="color:#ABB2BF;">UDF-</span><span style="color:#C678DD;">NAME</span><span style="color:#56B6C2;">&gt;</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div></div></div><p>Example: Uninstall the UDF from the above example:</p><div class="language-sql line-numbers-mode" data-highlighter="shiki" data-ext="sql" style="background-color:#282c34;color:#abb2bf;"><pre class="shiki one-dark-pro vp-code"><code class="language-sql"><span class="line"><span style="color:#C678DD;">DROP</span><span style="color:#C678DD;"> FUNCTION</span><span style="color:#ABB2BF;"> example</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div></div></div><h3 id="_2-3-show-all-registered-udfs" tabindex="-1"><a class="header-anchor" href="#_2-3-show-all-registered-udfs"><span>2.3 Show All Registered UDFs</span></a></h3><div class="language-sql line-numbers-mode" data-highlighter="shiki" data-ext="sql" style="background-color:#282c34;color:#abb2bf;"><pre class="shiki one-dark-pro vp-code"><code class="language-sql"><span class="line"><span style="color:#ABB2BF;">SHOW FUNCTIONS</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div></div></div><h3 id="_2-4-udf-configuration" tabindex="-1"><a class="header-anchor" href="#_2-4-udf-configuration"><span>2.4 UDF configuration</span></a></h3><ul><li>UDF configuration allows configuring the storage directory of UDF in <code>iotdb-system.properties</code></li></ul><div class="language-properties line-numbers-mode" data-highlighter="shiki" data-ext="properties" style="background-color:#282c34;color:#abb2bf;"><pre class="shiki one-dark-pro vp-code"><code class="language-properties"><span class="line"><span style="color:#7F848E;font-style:italic;"># UDF lib dir</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;">udf_lib_dir</span><span style="color:#ABB2BF;">=</span><span style="color:#98C379;">ext/udf</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><ul><li>-When using custom functions, there is a message indicating insufficient memory. Change the following configuration parameters in <code>iotdb-system.properties</code> and restart the service.</li></ul><div class="language-properties line-numbers-mode" data-highlighter="shiki" data-ext="properties" style="background-color:#282c34;color:#abb2bf;"><pre class="shiki one-dark-pro vp-code"><code class="language-properties"><span class="line"></span>
<span class="line"><span style="color:#7F848E;font-style:italic;"># Used to estimate the memory usage of text fields in a UDF query.</span></span>
<span class="line"><span style="color:#7F848E;font-style:italic;"># It is recommended to set this value to be slightly larger than the average length of all text</span></span>
<span class="line"><span style="color:#7F848E;font-style:italic;"># effectiveMode: restart</span></span>
<span class="line"><span style="color:#7F848E;font-style:italic;"># Datatype: int</span></span>
<span class="line"><span style="color:#C678DD;">udf_initial_byte_array_length_for_memory_control</span><span style="color:#ABB2BF;">=</span><span style="color:#98C379;">48</span></span>
<span class="line"></span>
<span class="line"><span style="color:#7F848E;font-style:italic;"># How much memory may be used in ONE UDF query (in MB).</span></span>
<span class="line"><span style="color:#7F848E;font-style:italic;"># The upper limit is 20% of allocated memory for read.</span></span>
<span class="line"><span style="color:#7F848E;font-style:italic;"># effectiveMode: restart</span></span>
<span class="line"><span style="color:#7F848E;font-style:italic;"># Datatype: float</span></span>
<span class="line"><span style="color:#C678DD;">udf_memory_budget_in_mb</span><span style="color:#ABB2BF;">=</span><span style="color:#98C379;">30.0</span></span>
<span class="line"></span>
<span class="line"><span style="color:#7F848E;font-style:italic;"># UDF memory allocation ratio.</span></span>
<span class="line"><span style="color:#7F848E;font-style:italic;"># The parameter form is a:b:c, where a, b, and c are integers.</span></span>
<span class="line"><span style="color:#7F848E;font-style:italic;"># effectiveMode: restart</span></span>
<span class="line"><span style="color:#C678DD;">udf_reader_transformer_collector_memory_proportion</span><span style="color:#ABB2BF;">=</span><span style="color:#98C379;">1:1:1</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="_2-5-udf-user-permissions" tabindex="-1"><a class="header-anchor" href="#_2-5-udf-user-permissions"><span>2.5 UDF User Permissions</span></a></h3><p>When users use UDF, they will be involved in the <code>USE_UDF</code> permission, and only users with this permission are allowed to perform UDF registration, uninstallation, and query operations.</p><p>For more user permissions related content, please refer to <a href="../User-Manual/Authority-Management_apache">Account Management Statements</a>.</p><h2 id="_3-udf-libraries" tabindex="-1"><a class="header-anchor" href="#_3-udf-libraries"><span>3. UDF Libraries</span></a></h2><p>Based on the ability of user-defined functions, IoTDB provides a series of functions for temporal data processing, including data quality, data profiling, anomaly detection, frequency domain analysis, data matching, data repairing, sequence discovery, machine learning, etc., which can meet the needs of industrial fields for temporal data processing.</p><p>You can refer to the <a class="route-link" href="/UserGuide/latest/SQL-Manual/UDF-Libraries_apache.html">UDF Libraries</a>document to find the installation steps and registration statements for each function, to ensure that all required functions are registered correctly.</p><h2 id="_4-udf-development" tabindex="-1"><a class="header-anchor" href="#_4-udf-development"><span>4. UDF development</span></a></h2><h3 id="_4-1-udf-development-dependencies" tabindex="-1"><a class="header-anchor" href="#_4-1-udf-development-dependencies"><span>4.1 UDF Development Dependencies</span></a></h3><p>If you use <a href="http://search.maven.org/" target="_blank" rel="noopener noreferrer">Maven</a>, you can search for the development dependencies listed below from the <a href="http://search.maven.org/" target="_blank" rel="noopener noreferrer">Maven repository</a> . Please note that you must select the same dependency version as the target IoTDB server version for development.</p><div class="language-xml line-numbers-mode" data-highlighter="shiki" data-ext="xml" style="background-color:#282c34;color:#abb2bf;"><pre class="shiki one-dark-pro vp-code"><code class="language-xml"><span class="line"><span style="color:#ABB2BF;">&lt;</span><span style="color:#E06C75;">dependency</span><span style="color:#ABB2BF;">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;"> &lt;</span><span style="color:#E06C75;">groupId</span><span style="color:#ABB2BF;">&gt;org.apache.iotdb&lt;/</span><span style="color:#E06C75;">groupId</span><span style="color:#ABB2BF;">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;"> &lt;</span><span style="color:#E06C75;">artifactId</span><span style="color:#ABB2BF;">&gt;udf-api&lt;/</span><span style="color:#E06C75;">artifactId</span><span style="color:#ABB2BF;">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;"> &lt;</span><span style="color:#E06C75;">version</span><span style="color:#ABB2BF;">&gt;1.0.0&lt;/</span><span style="color:#E06C75;">version</span><span style="color:#ABB2BF;">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;"> &lt;</span><span style="color:#E06C75;">scope</span><span style="color:#ABB2BF;">&gt;provided&lt;/</span><span style="color:#E06C75;">scope</span><span style="color:#ABB2BF;">&gt;</span></span>
<span class="line"><span style="color:#ABB2BF;">&lt;/</span><span style="color:#E06C75;">dependency</span><span style="color:#ABB2BF;">&gt;</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="_4-2-udtf-user-defined-timeseries-generating-function" tabindex="-1"><a class="header-anchor" href="#_4-2-udtf-user-defined-timeseries-generating-function"><span>4.2 UDTF(User Defined Timeseries Generating Function)</span></a></h3><p>To write a UDTF, you need to inherit the <code>org.apache.iotdb.udf.api.UDTF</code> class, and at least implement the <code>beforeStart</code> method and a <code>transform</code> method.</p><h4 id="interface-description" tabindex="-1"><a class="header-anchor" href="#interface-description"><span>Interface Description:</span></a></h4><table><thead><tr><th style="text-align:left;">Interface definition</th><th style="text-align:left;">Description</th><th>Required to Implement</th></tr></thead><tbody><tr><td style="text-align:left;">void validate(UDFParameterValidator validator) throws Exception</td><td style="text-align:left;">This method is mainly used to validate <code>UDFParameters</code> and it is executed before <code>beforeStart(UDFParameters, UDTFConfigurations)</code> is called.</td><td>Optional</td></tr><tr><td style="text-align:left;">void beforeStart(UDFParameters parameters, UDTFConfigurations configurations) throws Exception</td><td style="text-align:left;">The initialization method to call the user-defined initialization behavior before a UDTF processes the input data. Every time a user executes a UDTF query, the framework will construct a new UDF instance, and <code>beforeStart</code> will be called.</td><td>Required</td></tr><tr><td style="text-align:left;">Object transform(Row row) throws Exception</td><td style="text-align:left;">This method is called by the framework. This data processing method will be called when you choose to use the <code>MappableRowByRowAccessStrategy</code> strategy (set in <code>beforeStart</code>) to consume raw data. Input data is passed in by <code>Row</code>, and the transformation result should be returned.</td><td>Required to implement at least one <code>transform</code> method</td></tr><tr><td style="text-align:left;">void transform(Column[] columns, ColumnBuilder builder) throws Exception</td><td style="text-align:left;">This method is called by the framework. This data processing method will be called when you choose to use the <code>MappableRowByRowAccessStrategy</code> strategy (set in <code>beforeStart</code>) to consume raw data. Input data is passed in by <code>Column[]</code>, and the transformation result should be output by <code>ColumnBuilder</code>. You need to call the data collection method provided by <code>builder</code> to determine the output data.</td><td>Required to implement at least one <code>transform</code> method</td></tr><tr><td style="text-align:left;">void transform(Row row, PointCollector collector) throws Exception</td><td style="text-align:left;">This method is called by the framework. This data processing method will be called when you choose to use the <code>RowByRowAccessStrategy</code> strategy (set in <code>beforeStart</code>) to consume raw data. Input data is passed in by <code>Row</code>, and the transformation result should be output by <code>PointCollector</code>. You need to call the data collection method provided by <code>collector</code> to determine the output data.</td><td>Required to implement at least one <code>transform</code> method</td></tr><tr><td style="text-align:left;">void transform(RowWindow rowWindow, PointCollector collector) throws Exception</td><td style="text-align:left;">This method is called by the framework. This data processing method will be called when you choose to use the <code>SlidingSizeWindowAccessStrategy</code> or <code>SlidingTimeWindowAccessStrategy</code> strategy (set in <code>beforeStart</code>) to consume raw data. Input data is passed in by <code>RowWindow</code>, and the transformation result should be output by <code>PointCollector</code>. You need to call the data collection method provided by <code>collector</code> to determine the output data.</td><td>Required to implement at least one <code>transform</code> method</td></tr><tr><td style="text-align:left;">void terminate(PointCollector collector) throws Exception</td><td style="text-align:left;">This method is called by the framework. This method will be called once after all <code>transform</code> calls have been executed. In a single UDF query, this method will and will only be called once. You need to call the data collection method provided by <code>collector</code> to determine the output data.</td><td>Optional</td></tr><tr><td style="text-align:left;">void beforeDestroy()</td><td style="text-align:left;">This method is called by the framework after the last input data is processed, and will only be called once in the life cycle of each UDF instance.</td><td>Optional</td></tr></tbody></table><p>In the life cycle of a UDTF instance, the calling sequence of each method is as follows:</p><ol><li>void validate(UDFParameterValidator validator) throws Exception</li><li>void beforeStart(UDFParameters parameters, UDTFConfigurations configurations) throws Exception</li><li><code>Object transform(Row row) throws Exception</code> or <code>void transform(Column[] columns, ColumnBuilder builder) throws Exception</code> or <code>void transform(Row row, PointCollector collector) throws Exception</code> or <code>void transform(RowWindow rowWindow, PointCollector collector) throws Exception</code></li><li>void terminate(PointCollector collector) throws Exception</li><li>void beforeDestroy()</li></ol><blockquote><p>Note that every time the framework executes a UDTF query, a new UDF instance will be constructed. When the query ends, the corresponding instance will be destroyed. Therefore, the internal data of the instances in different UDTF queries (even in the same SQL statement) are isolated. You can maintain some state data in the UDTF without considering the influence of concurrency and other factors.</p></blockquote><h4 id="detailed-interface-introduction" tabindex="-1"><a class="header-anchor" href="#detailed-interface-introduction"><span>Detailed interface introduction:</span></a></h4><ol><li><strong>void validate(UDFParameterValidator validator) throws Exception</strong></li></ol><p>The <code>validate</code> method is used to validate the parameters entered by the user.</p><p>In this method, you can limit the number and types of input time series, check the attributes of user input, or perform any custom verification.</p><p>Please refer to the <a href="https://github.com/apache/iotdb/blob/rc/2.0.4/iotdb-api/udf-api/src/main/java/org/apache/iotdb/udf/api/customizer/parameter/UDFParameterValidator.java" target="_blank" rel="noopener noreferrer">Javadoc</a> for the usage of <code>UDFParameterValidator</code>.</p><ol start="2"><li><strong>void beforeStart(UDFParameters parameters, UDTFConfigurations configurations) throws Exception</strong></li></ol><p>This method is mainly used to customize UDTF. In this method, the user can do the following things:</p><ol><li>Use UDFParameters to get the time series paths and parse key-value pair attributes entered by the user.</li><li>Set the strategy to access the raw data and set the output data type in UDTFConfigurations.</li><li>Create resources, such as establishing external connections, opening files, etc.</li></ol><p>2.1 <strong>UDFParameters</strong></p><p><code>UDFParameters</code> is used to parse UDF parameters in SQL statements (the part in parentheses after the UDF function name in SQL). The input parameters have two parts. The first part is data types of the time series that the UDF needs to process, and the second part is the key-value pair attributes for customization. Only the second part can be empty.</p><p>Example:</p><div class="language-sql line-numbers-mode" data-highlighter="shiki" data-ext="sql" style="background-color:#282c34;color:#abb2bf;"><pre class="shiki one-dark-pro vp-code"><code class="language-sql"><span class="line"><span style="color:#C678DD;">SELECT</span><span style="color:#ABB2BF;"> UDF(s1, s2, </span><span style="color:#98C379;">&#39;key1&#39;</span><span style="color:#56B6C2;">=</span><span style="color:#98C379;">&#39;iotdb&#39;</span><span style="color:#ABB2BF;">, </span><span style="color:#98C379;">&#39;key2&#39;</span><span style="color:#56B6C2;">=</span><span style="color:#98C379;">&#39;123.45&#39;</span><span style="color:#ABB2BF;">) </span><span style="color:#C678DD;">FROM</span><span style="color:#D19A66;"> root</span><span style="color:#ABB2BF;">.</span><span style="color:#D19A66;">sg</span><span style="color:#ABB2BF;">.d;</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div></div></div><p>Usage:</p><div class="language-java line-numbers-mode" data-highlighter="shiki" data-ext="java" style="background-color:#282c34;color:#abb2bf;"><pre class="shiki one-dark-pro vp-code"><code class="language-java"><span class="line"><span style="color:#C678DD;">void</span><span style="color:#61AFEF;"> beforeStart</span><span style="color:#E06C75;">(</span><span style="color:#E5C07B;">UDFParameters</span><span style="color:#E06C75;"> parameters</span><span style="color:#ABB2BF;">,</span><span style="color:#E5C07B;"> UDTFConfigurations</span><span style="color:#E06C75;"> configurations) throws Exception {</span></span>
<span class="line"><span style="color:#E5C07B;"> String</span><span style="color:#E06C75;"> stringValue </span><span style="color:#56B6C2;">=</span><span style="color:#E5C07B;"> parameters</span><span style="color:#ABB2BF;">.</span><span style="color:#61AFEF;">getString</span><span style="color:#ABB2BF;">(</span><span style="color:#98C379;">&quot;key1&quot;</span><span style="color:#ABB2BF;">);</span><span style="color:#7F848E;font-style:italic;"> // iotdb</span></span>
<span class="line"><span style="color:#E5C07B;"> Float</span><span style="color:#E06C75;"> floatValue </span><span style="color:#56B6C2;">=</span><span style="color:#E5C07B;"> parameters</span><span style="color:#ABB2BF;">.</span><span style="color:#61AFEF;">getFloat</span><span style="color:#ABB2BF;">(</span><span style="color:#98C379;">&quot;key2&quot;</span><span style="color:#ABB2BF;">);</span><span style="color:#7F848E;font-style:italic;"> // 123.45</span></span>
<span class="line"><span style="color:#E5C07B;"> Double</span><span style="color:#E06C75;"> doubleValue </span><span style="color:#56B6C2;">=</span><span style="color:#E5C07B;"> parameters</span><span style="color:#ABB2BF;">.</span><span style="color:#61AFEF;">getDouble</span><span style="color:#ABB2BF;">(</span><span style="color:#98C379;">&quot;key3&quot;</span><span style="color:#ABB2BF;">);</span><span style="color:#7F848E;font-style:italic;"> // null</span></span>
<span class="line"><span style="color:#C678DD;"> int</span><span style="color:#E06C75;"> intValue </span><span style="color:#56B6C2;">=</span><span style="color:#E5C07B;"> parameters</span><span style="color:#ABB2BF;">.</span><span style="color:#61AFEF;">getIntOrDefault</span><span style="color:#ABB2BF;">(</span><span style="color:#98C379;">&quot;key4&quot;</span><span style="color:#ABB2BF;">, </span><span style="color:#D19A66;">678</span><span style="color:#ABB2BF;">);</span><span style="color:#7F848E;font-style:italic;"> // 678</span></span>
<span class="line"><span style="color:#7F848E;font-style:italic;"> // do something</span></span>
<span class="line"><span style="color:#E06C75;"> </span></span>
<span class="line"><span style="color:#7F848E;font-style:italic;"> // configurations</span></span>
<span class="line"><span style="color:#7F848E;font-style:italic;"> // ...</span></span>
<span class="line"><span style="color:#E06C75;">}</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>2.2 <strong>UDTFConfigurations</strong></p><p>You must use <code>UDTFConfigurations</code> to specify the strategy used by UDF to access raw data and the type of output sequence.</p><p>Usage:</p><div class="language-java line-numbers-mode" data-highlighter="shiki" data-ext="java" style="background-color:#282c34;color:#abb2bf;"><pre class="shiki one-dark-pro vp-code"><code class="language-java"><span class="line"><span style="color:#C678DD;">void</span><span style="color:#61AFEF;"> beforeStart</span><span style="color:#E06C75;">(</span><span style="color:#E5C07B;">UDFParameters</span><span style="color:#E06C75;"> parameters</span><span style="color:#ABB2BF;">,</span><span style="color:#E5C07B;"> UDTFConfigurations</span><span style="color:#E06C75;"> configurations) throws Exception {</span></span>
<span class="line"><span style="color:#7F848E;font-style:italic;"> // parameters</span></span>
<span class="line"><span style="color:#7F848E;font-style:italic;"> // ...</span></span>
<span class="line"><span style="color:#E06C75;"> </span></span>
<span class="line"><span style="color:#7F848E;font-style:italic;"> // configurations</span></span>
<span class="line"><span style="color:#E06C75;"> configurations</span></span>
<span class="line"><span style="color:#ABB2BF;"> .</span><span style="color:#61AFEF;">setAccessStrategy</span><span style="color:#ABB2BF;">(</span><span style="color:#C678DD;">new</span><span style="color:#61AFEF;"> RowByRowAccessStrategy</span><span style="color:#ABB2BF;">())</span></span>
<span class="line"><span style="color:#ABB2BF;"> .</span><span style="color:#61AFEF;">setOutputDataType</span><span style="color:#ABB2BF;">(</span><span style="color:#E5C07B;">Type</span><span style="color:#ABB2BF;">.</span><span style="color:#E5C07B;">INT32</span><span style="color:#ABB2BF;">);</span></span>
<span class="line"><span style="color:#E06C75;">}</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>The <code>setAccessStrategy</code> method is used to set the UDF&#39;s strategy for accessing the raw data, and the <code>setOutputDataType</code> method is used to set the data type of the output sequence.</p><p>2.2.1 <strong>setAccessStrategy</strong></p><p>Note that the raw data access strategy you set here determines which <code>transform</code> method the framework will call. Please implement the <code>transform</code> method corresponding to the raw data access strategy. Of course, you can also dynamically decide which strategy to set based on the attribute parameters parsed by <code>UDFParameters</code>. Therefore, two <code>transform</code> methods are also allowed to be implemented in one UDF.</p><p>The following are the strategies you can set:</p><table><thead><tr><th style="text-align:left;">Interface definition</th><th style="text-align:left;">Description</th><th>The <code>transform</code> Method to Call</th></tr></thead><tbody><tr><td style="text-align:left;">MappableRowByRowStrategy</td><td style="text-align:left;">Custom scalar function<br>The framework will call the <code>transform</code> method once for each row of raw data input, with k columns of time series and 1 row of data input, and 1 column of time series and 1 row of data output. It can be used in any clause and expression where scalar functions appear, such as select clauses, where clauses, etc.</td><td>void transform(Column[] columns, ColumnBuilder builder) throws ExceptionObject transform(Row row) throws Exception</td></tr><tr><td style="text-align:left;">RowByRowAccessStrategy</td><td style="text-align:left;">Customize time series generation function to process raw data line by line.<br>The framework will call the <code>transform</code> method once for each row of raw data input, inputting k columns of time series and 1 row of data, and outputting 1 column of time series and n rows of data.<br> When a sequence is input, the row serves as a data point for the input sequence.<br> When multiple sequences are input, after aligning the input sequences in time, each row serves as a data point for the input sequence.<br>(In a row of data, there may be a column with a <code>null</code> value, but not all columns are <code>null</code></td><td>void transform(Row row, PointCollector collector) throws Exception</td></tr><tr><td style="text-align:left;">SlidingTimeWindowAccessStrategy</td><td style="text-align:left;">Customize time series generation functions to process raw data in a sliding time window manner.<br>The framework will call the <code>transform</code> method once for each raw data input window, input k columns of time series m rows of data, and output 1 column of time series n rows of data.<br>A window may contain multiple rows of data, and after aligning the input sequence in time, each window serves as a data point for the input sequence. <br>(Each window may have i rows, and each row of data may have a column with a <code>null</code> value, but not all of them are <code>null</code>)</td><td>void transform(RowWindow rowWindow, PointCollector collector) throws Exception</td></tr><tr><td style="text-align:left;">SlidingSizeWindowAccessStrategy</td><td style="text-align:left;">Customize the time series generation function to process raw data in a fixed number of rows, meaning that each data processing window will contain a fixed number of rows of data (except for the last window).<br>The framework will call the <code>transform</code> method once for each raw data input window, input k columns of time series m rows of data, and output 1 column of time series n rows of data.<br>A window may contain multiple rows of data, and after aligning the input sequence in time, each window serves as a data point for the input sequence. <br>(Each window may have i rows, and each row of data may have a column with a <code>null</code> value, but not all of them are <code>null</code>)</td><td>void transform(RowWindow rowWindow, PointCollector collector) throws Exception</td></tr><tr><td style="text-align:left;">SessionTimeWindowAccessStrategy</td><td style="text-align:left;">Customize time series generation functions to process raw data in a session window format.<br>The framework will call the <code>transform</code> method once for each raw data input window, input k columns of time series m rows of data, and output 1 column of time series n rows of data.<br>A window may contain multiple rows of data, and after aligning the input sequence in time, each window serves as a data point for the input sequence.<br> (Each window may have i rows, and each row of data may have a column with a <code>null</code> value, but not all of them are <code>null</code>)</td><td>void transform(RowWindow rowWindow, PointCollector collector) throws Exception</td></tr><tr><td style="text-align:left;">StateWindowAccessStrategy</td><td style="text-align:left;">Customize time series generation functions to process raw data in a state window format.<br>he framework will call the <code>transform</code> method once for each raw data input window, inputting 1 column of time series m rows of data and outputting 1 column of time series n rows of data.<br>A window may contain multiple rows of data, and currently only supports opening windows for one physical quantity, which is one column of data.</td><td>void transform(RowWindow rowWindow, PointCollector collector) throws Exception</td></tr></tbody></table><h4 id="interface-description-1" tabindex="-1"><a class="header-anchor" href="#interface-description-1"><span>Interface Description:</span></a></h4><ul><li><p><code>MappableRowByRowStrategy</code> and <code>RowByRowAccessStrategy</code>: The construction of <code>RowByRowAccessStrategy</code> does not require any parameters.</p></li><li><p><code>SlidingTimeWindowAccessStrategy</code></p></li></ul><p>Window opening diagram:</p><img style="width:100%;max-width:800px;max-height:600px;margin-left:auto;margin-right:auto;display:block;" src="/img/UserGuide/Process-Data/UDF-User-Defined-Function/timeWindow.png"><p><code>SlidingTimeWindowAccessStrategy</code>: <code>SlidingTimeWindowAccessStrategy</code> has many constructors, you can pass 3 types of parameters to them:</p><ul><li>Parameter 1: The display window on the time axis</li></ul><p>The first type of parameters are optional. If the parameters are not provided, the beginning time of the display window will be set to the same as the minimum timestamp of the query result set, and the ending time of the display window will be set to the same as the maximum timestamp of the query result set.</p><ul><li>Parameter 2: Time interval for dividing the time axis (should be positive)</li><li>Parameter 3: Time sliding step (not required to be greater than or equal to the time interval, but must be a positive number)</li></ul><p>The sliding step parameter is also optional. If the parameter is not provided, the sliding step will be set to the same as the time interval for dividing the time axis.</p><p>The relationship between the three types of parameters can be seen in the figure below. Please see the <a href="https://github.com/apache/iotdb/blob/rc/2.0.4/iotdb-api/udf-api/src/main/java/org/apache/iotdb/udf/api/customizer/strategy/SlidingTimeWindowAccessStrategy.java" target="_blank" rel="noopener noreferrer">Javadoc</a> for more details.</p><div style="text-align:center;"><img style="width:100%;max-width:800px;max-height:600px;margin-left:auto;margin-right:auto;display:block;" src="/img/github/99787878-47b51480-2b5b-11eb-8ed3-84088c5c30f7.png"></div><blockquote><p>Note that the actual time interval of some of the last time windows may be less than the specified time interval parameter. In addition, there may be cases where the number of data rows in some time windows is 0. In these cases, the framework will also call the <code>transform</code> method for the empty windows.</p></blockquote><ul><li><code>SlidingSizeWindowAccessStrategy</code></li></ul><p>Window opening diagram:</p><img style="width:100%;max-width:800px;max-height:600px;margin-left:auto;margin-right:auto;display:block;" src="/img/UserGuide/Process-Data/UDF-User-Defined-Function/countWindow.png"><p><code>SlidingSizeWindowAccessStrategy</code>: <code>SlidingSizeWindowAccessStrategy</code> has many constructors, you can pass 2 types of parameters to them:</p><ul><li>Parameter 1: Window size. This parameter specifies the number of data rows contained in a data processing window. Note that the number of data rows in some of the last time windows may be less than the specified number of data rows.</li><li>Parameter 2: Sliding step. This parameter means the number of rows between the first point of the next window and the first point of the current window. (This parameter is not required to be greater than or equal to the window size, but must be a positive number)</li></ul><p>The sliding step parameter is optional. If the parameter is not provided, the sliding step will be set to the same as the window size.</p><ul><li><code>SessionTimeWindowAccessStrategy</code></li></ul><p>Window opening diagram: <strong>Time intervals less than or equal to the given minimum time interval <code>sessionGap</code> are assigned in one group.</strong></p><img style="width:100%;max-width:800px;max-height:600px;margin-left:auto;margin-right:auto;display:block;" src="/img/UserGuide/Process-Data/UDF-User-Defined-Function/sessionWindow.png"><p><code>SessionTimeWindowAccessStrategy</code>: <code>SessionTimeWindowAccessStrategy</code> has many constructors, you can pass 2 types of parameters to them:</p><ul><li><p>Parameter 1: The display window on the time axis.</p></li><li><p>Parameter 2: The minimum time interval <code>sessionGap</code> of two adjacent windows.</p></li><li><p><code>StateWindowAccessStrategy</code></p></li></ul><p>Window opening diagram: <strong>For numerical data, if the state difference is less than or equal to the given threshold <code>delta</code>, it will be assigned in one group.</strong></p><img style="width:100%;max-width:800px;max-height:600px;margin-left:auto;margin-right:auto;display:block;" src="/img/UserGuide/Process-Data/UDF-User-Defined-Function/stateWindow.png"><p><code>StateWindowAccessStrategy</code> has four constructors.</p><ul><li>Constructor 1: For numerical data, there are 3 parameters: the time axis can display the start and end time of the time window and the threshold <code>delta</code> for the allowable change within a single window.</li><li>Constructor 2: For text data and boolean data, there are 3 parameters: the time axis can be provided to display the start and end time of the time window. For both data types, the data within a single window is same, and there is no need to provide an allowable change threshold.</li><li>Constructor 3: For numerical data, there are 1 parameters: you can only provide the threshold delta that is allowed to change within a single window. The start time of the time axis display time window will be defined as the smallest timestamp in the entire query result set, and the time axis display time window end time will be defined as The largest timestamp in the entire query result set.</li><li>Constructor 4: For text data and boolean data, you can provide no parameter. The start and end timestamps are explained in Constructor 3.</li></ul><p>StateWindowAccessStrategy can only take one column as input for now.</p><p>Please see the <a href="https://github.com/apache/iotdb/blob/rc/2.0.4/iotdb-api/udf-api/src/main/java/org/apache/iotdb/udf/api/customizer/strategy/StateWindowAccessStrategy.java" target="_blank" rel="noopener noreferrer">Javadoc</a> for more details.</p><p>2.2.2 <strong>setOutputDataType</strong></p><p>Note that the type of output sequence you set here determines the type of data that the <code>PointCollector</code> can actually receive in the <code>transform</code> method. The relationship between the output data type set in <code>setOutputDataType</code> and the actual data output type that <code>PointCollector</code> can receive is as follows:</p><table><thead><tr><th style="text-align:left;">Output Data Type Set in <code>setOutputDataType</code></th><th style="text-align:left;">Data Type that <code>PointCollector</code> Can Receive</th></tr></thead><tbody><tr><td style="text-align:left;">INT32</td><td style="text-align:left;">int</td></tr><tr><td style="text-align:left;">INT64</td><td style="text-align:left;">long</td></tr><tr><td style="text-align:left;">FLOAT</td><td style="text-align:left;">float</td></tr><tr><td style="text-align:left;">DOUBLE</td><td style="text-align:left;">double</td></tr><tr><td style="text-align:left;">BOOLEAN</td><td style="text-align:left;">boolean</td></tr><tr><td style="text-align:left;">TEXT</td><td style="text-align:left;">java.lang.String and org.apache.iotdb.udf.api.type.Binar`</td></tr></tbody></table><p>The type of output time series of a UDTF is determined at runtime, which means that a UDTF can dynamically determine the type of output time series according to the type of input time series.<br> Here is a simple example:</p><div class="language-java line-numbers-mode" data-highlighter="shiki" data-ext="java" style="background-color:#282c34;color:#abb2bf;"><pre class="shiki one-dark-pro vp-code"><code class="language-java"><span class="line"><span style="color:#C678DD;">void</span><span style="color:#61AFEF;"> beforeStart</span><span style="color:#E06C75;">(</span><span style="color:#E5C07B;">UDFParameters</span><span style="color:#E06C75;"> parameters</span><span style="color:#ABB2BF;">,</span><span style="color:#E5C07B;"> UDTFConfigurations</span><span style="color:#E06C75;"> configurations) throws Exception {</span></span>
<span class="line"><span style="color:#7F848E;font-style:italic;"> // do something</span></span>
<span class="line"><span style="color:#7F848E;font-style:italic;"> // ...</span></span>
<span class="line"><span style="color:#E06C75;"> </span></span>
<span class="line"><span style="color:#E06C75;"> configurations</span></span>
<span class="line"><span style="color:#ABB2BF;"> .</span><span style="color:#61AFEF;">setAccessStrategy</span><span style="color:#ABB2BF;">(</span><span style="color:#C678DD;">new</span><span style="color:#61AFEF;"> RowByRowAccessStrategy</span><span style="color:#ABB2BF;">())</span></span>
<span class="line"><span style="color:#ABB2BF;"> .</span><span style="color:#61AFEF;">setOutputDataType</span><span style="color:#ABB2BF;">(</span><span style="color:#E5C07B;">parameters</span><span style="color:#ABB2BF;">.</span><span style="color:#61AFEF;">getDataType</span><span style="color:#ABB2BF;">(</span><span style="color:#D19A66;">0</span><span style="color:#ABB2BF;">));</span></span>
<span class="line"><span style="color:#E06C75;">}</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><ol start="3"><li><strong>Object transform(Row row) throws Exception</strong></li></ol><p>You need to implement this method or <code>transform(Column[] columns, ColumnBuilder builder) throws Exception</code> when you specify the strategy of UDF to read the original data as <code>MappableRowByRowAccessStrategy</code>.</p><p>This method processes the raw data one row at a time. The raw data is input from <code>Row</code> and output by its return object. You must return only one object based on each input data point in a single <code>transform</code> method call, i.e., input and output are one-to-one. It should be noted that the type of output data points must be the same as you set in the <code>beforeStart</code> method, and the timestamps of output data points must be strictly monotonically increasing.</p><p>The following is a complete UDF example that implements the <code>Object transform(Row row) throws Exception</code> method. It is an adder that receives two columns of time series as input.</p><div class="language-java line-numbers-mode" data-highlighter="shiki" data-ext="java" style="background-color:#282c34;color:#abb2bf;"><pre class="shiki one-dark-pro vp-code"><code class="language-java"><span class="line"><span style="color:#C678DD;">import</span><span style="color:#E5C07B;"> org.apache.iotdb.udf.api.UDTF</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"><span style="color:#C678DD;">import</span><span style="color:#E5C07B;"> org.apache.iotdb.udf.api.access.Row</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"><span style="color:#C678DD;">import</span><span style="color:#E5C07B;"> org.apache.iotdb.udf.api.customizer.config.UDTFConfigurations</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"><span style="color:#C678DD;">import</span><span style="color:#E5C07B;"> org.apache.iotdb.udf.api.customizer.parameter.UDFParameterValidator</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"><span style="color:#C678DD;">import</span><span style="color:#E5C07B;"> org.apache.iotdb.udf.api.customizer.parameter.UDFParameters</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"><span style="color:#C678DD;">import</span><span style="color:#E5C07B;"> org.apache.iotdb.udf.api.customizer.strategy.MappableRowByRowAccessStrategy</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"><span style="color:#C678DD;">import</span><span style="color:#E5C07B;"> org.apache.iotdb.udf.api.type.Type</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;">public</span><span style="color:#C678DD;"> class</span><span style="color:#E5C07B;"> Adder</span><span style="color:#C678DD;"> implements</span><span style="color:#E5C07B;"> UDTF</span><span style="color:#ABB2BF;"> {</span></span>
<span class="line"><span style="color:#C678DD;"> private</span><span style="color:#E5C07B;"> Type</span><span style="color:#E06C75;"> dataType</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;"> @</span><span style="color:#E5C07B;">Override</span></span>
<span class="line"><span style="color:#C678DD;"> public</span><span style="color:#C678DD;"> void</span><span style="color:#61AFEF;"> validate</span><span style="color:#ABB2BF;">(</span><span style="color:#E5C07B;">UDFParameterValidator</span><span style="color:#E06C75;font-style:italic;"> validator</span><span style="color:#ABB2BF;">)</span><span style="color:#C678DD;"> throws</span><span style="color:#E5C07B;"> Exception</span><span style="color:#ABB2BF;"> {</span></span>
<span class="line"><span style="color:#ABB2BF;"> validator</span></span>
<span class="line"><span style="color:#ABB2BF;"> .</span><span style="color:#61AFEF;">validateInputSeriesNumber</span><span style="color:#ABB2BF;">(</span><span style="color:#D19A66;">2</span><span style="color:#ABB2BF;">)</span></span>
<span class="line"><span style="color:#ABB2BF;"> .</span><span style="color:#61AFEF;">validateInputSeriesDataType</span><span style="color:#ABB2BF;">(</span><span style="color:#D19A66;">0</span><span style="color:#ABB2BF;">, </span><span style="color:#E5C07B;">Type</span><span style="color:#ABB2BF;">.</span><span style="color:#E5C07B;">INT64</span><span style="color:#ABB2BF;">)</span></span>
<span class="line"><span style="color:#ABB2BF;"> .</span><span style="color:#61AFEF;">validateInputSeriesDataType</span><span style="color:#ABB2BF;">(</span><span style="color:#D19A66;">1</span><span style="color:#ABB2BF;">, </span><span style="color:#E5C07B;">Type</span><span style="color:#ABB2BF;">.</span><span style="color:#E5C07B;">INT64</span><span style="color:#ABB2BF;">);</span></span>
<span class="line"><span style="color:#ABB2BF;"> }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;"> @</span><span style="color:#E5C07B;">Override</span></span>
<span class="line"><span style="color:#C678DD;"> public</span><span style="color:#C678DD;"> void</span><span style="color:#61AFEF;"> beforeStart</span><span style="color:#ABB2BF;">(</span><span style="color:#E5C07B;">UDFParameters</span><span style="color:#E06C75;font-style:italic;"> parameters</span><span style="color:#ABB2BF;">, </span><span style="color:#E5C07B;">UDTFConfigurations</span><span style="color:#E06C75;font-style:italic;"> configurations</span><span style="color:#ABB2BF;">)</span><span style="color:#ABB2BF;"> {</span></span>
<span class="line"><span style="color:#ABB2BF;"> dataType </span><span style="color:#56B6C2;">=</span><span style="color:#E5C07B;"> parameters</span><span style="color:#ABB2BF;">.</span><span style="color:#61AFEF;">getDataType</span><span style="color:#ABB2BF;">(</span><span style="color:#D19A66;">0</span><span style="color:#ABB2BF;">);</span></span>
<span class="line"><span style="color:#ABB2BF;"> configurations</span></span>
<span class="line"><span style="color:#ABB2BF;"> .</span><span style="color:#61AFEF;">setAccessStrategy</span><span style="color:#ABB2BF;">(</span><span style="color:#C678DD;">new</span><span style="color:#61AFEF;"> MappableRowByRowAccessStrategy</span><span style="color:#ABB2BF;">())</span></span>
<span class="line"><span style="color:#ABB2BF;"> .</span><span style="color:#61AFEF;">setOutputDataType</span><span style="color:#ABB2BF;">(dataType);</span></span>
<span class="line"><span style="color:#ABB2BF;"> }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;"> @</span><span style="color:#E5C07B;">Override</span></span>
<span class="line"><span style="color:#C678DD;"> public</span><span style="color:#E5C07B;"> Object</span><span style="color:#61AFEF;"> transform</span><span style="color:#ABB2BF;">(</span><span style="color:#E5C07B;">Row</span><span style="color:#E06C75;font-style:italic;"> row</span><span style="color:#ABB2BF;">)</span><span style="color:#C678DD;"> throws</span><span style="color:#E5C07B;"> Exception</span><span style="color:#ABB2BF;"> {</span></span>
<span class="line"><span style="color:#C678DD;"> return</span><span style="color:#E5C07B;"> row</span><span style="color:#ABB2BF;">.</span><span style="color:#61AFEF;">getLong</span><span style="color:#ABB2BF;">(</span><span style="color:#D19A66;">0</span><span style="color:#ABB2BF;">) </span><span style="color:#56B6C2;">+</span><span style="color:#E5C07B;"> row</span><span style="color:#ABB2BF;">.</span><span style="color:#61AFEF;">getLong</span><span style="color:#ABB2BF;">(</span><span style="color:#D19A66;">1</span><span style="color:#ABB2BF;">);</span></span>
<span class="line"><span style="color:#ABB2BF;"> }</span></span>
<span class="line"><span style="color:#ABB2BF;">}</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><ol start="4"><li><strong>void transform(Column[] columns, ColumnBuilder builder) throws Exception</strong></li></ol><p>You need to implement this method or <code>Object transform(Row row) throws Exception</code> when you specify the strategy of UDF to read the original data as <code>MappableRowByRowAccessStrategy</code>.</p><p>This method processes the raw data multiple rows at a time. After performance tests, we found that UDTF that process multiple rows at once perform better than those UDTF that process one data point at a time. The raw data is input from <code>Column[]</code> and output by <code>ColumnBuilder</code>. You must output a corresponding data point based on each input data point in a single <code>transform</code> method call, i.e., input and output are still one-to-one. It should be noted that the type of output data points must be the same as you set in the <code>beforeStart</code> method, and the timestamps of output data points must be strictly monotonically increasing.</p><p>The following is a complete UDF example that implements the <code>void transform(Column[] columns, ColumnBuilder builder) throws Exception</code> method. It is an adder that receives two columns of time series as input.</p><div class="language-java line-numbers-mode" data-highlighter="shiki" data-ext="java" style="background-color:#282c34;color:#abb2bf;"><pre class="shiki one-dark-pro vp-code"><code class="language-java"><span class="line"><span style="color:#C678DD;">import</span><span style="color:#E5C07B;"> org.apache.iotdb.tsfile.read.common.block.column.Column</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"><span style="color:#C678DD;">import</span><span style="color:#E5C07B;"> org.apache.iotdb.tsfile.read.common.block.column.ColumnBuilder</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"><span style="color:#C678DD;">import</span><span style="color:#E5C07B;"> org.apache.iotdb.udf.api.UDTF</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"><span style="color:#C678DD;">import</span><span style="color:#E5C07B;"> org.apache.iotdb.udf.api.customizer.config.UDTFConfigurations</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"><span style="color:#C678DD;">import</span><span style="color:#E5C07B;"> org.apache.iotdb.udf.api.customizer.parameter.UDFParameterValidator</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"><span style="color:#C678DD;">import</span><span style="color:#E5C07B;"> org.apache.iotdb.udf.api.customizer.parameter.UDFParameters</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"><span style="color:#C678DD;">import</span><span style="color:#E5C07B;"> org.apache.iotdb.udf.api.customizer.strategy.MappableRowByRowAccessStrategy</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"><span style="color:#C678DD;">import</span><span style="color:#E5C07B;"> org.apache.iotdb.udf.api.type.Type</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;">public</span><span style="color:#C678DD;"> class</span><span style="color:#E5C07B;"> Adder</span><span style="color:#C678DD;"> implements</span><span style="color:#E5C07B;"> UDTF</span><span style="color:#ABB2BF;"> {</span></span>
<span class="line"><span style="color:#C678DD;"> private</span><span style="color:#E5C07B;"> Type</span><span style="color:#E06C75;"> type</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;"> @</span><span style="color:#E5C07B;">Override</span></span>
<span class="line"><span style="color:#C678DD;"> public</span><span style="color:#C678DD;"> void</span><span style="color:#61AFEF;"> validate</span><span style="color:#ABB2BF;">(</span><span style="color:#E5C07B;">UDFParameterValidator</span><span style="color:#E06C75;font-style:italic;"> validator</span><span style="color:#ABB2BF;">)</span><span style="color:#C678DD;"> throws</span><span style="color:#E5C07B;"> Exception</span><span style="color:#ABB2BF;"> {</span></span>
<span class="line"><span style="color:#ABB2BF;"> validator</span></span>
<span class="line"><span style="color:#ABB2BF;"> .</span><span style="color:#61AFEF;">validateInputSeriesNumber</span><span style="color:#ABB2BF;">(</span><span style="color:#D19A66;">2</span><span style="color:#ABB2BF;">)</span></span>
<span class="line"><span style="color:#ABB2BF;"> .</span><span style="color:#61AFEF;">validateInputSeriesDataType</span><span style="color:#ABB2BF;">(</span><span style="color:#D19A66;">0</span><span style="color:#ABB2BF;">, </span><span style="color:#E5C07B;">Type</span><span style="color:#ABB2BF;">.</span><span style="color:#E5C07B;">INT64</span><span style="color:#ABB2BF;">)</span></span>
<span class="line"><span style="color:#ABB2BF;"> .</span><span style="color:#61AFEF;">validateInputSeriesDataType</span><span style="color:#ABB2BF;">(</span><span style="color:#D19A66;">1</span><span style="color:#ABB2BF;">, </span><span style="color:#E5C07B;">Type</span><span style="color:#ABB2BF;">.</span><span style="color:#E5C07B;">INT64</span><span style="color:#ABB2BF;">);</span></span>
<span class="line"><span style="color:#ABB2BF;"> }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;"> @</span><span style="color:#E5C07B;">Override</span></span>
<span class="line"><span style="color:#C678DD;"> public</span><span style="color:#C678DD;"> void</span><span style="color:#61AFEF;"> beforeStart</span><span style="color:#ABB2BF;">(</span><span style="color:#E5C07B;">UDFParameters</span><span style="color:#E06C75;font-style:italic;"> parameters</span><span style="color:#ABB2BF;">, </span><span style="color:#E5C07B;">UDTFConfigurations</span><span style="color:#E06C75;font-style:italic;"> configurations</span><span style="color:#ABB2BF;">)</span><span style="color:#ABB2BF;"> {</span></span>
<span class="line"><span style="color:#ABB2BF;"> type </span><span style="color:#56B6C2;">=</span><span style="color:#E5C07B;"> parameters</span><span style="color:#ABB2BF;">.</span><span style="color:#61AFEF;">getDataType</span><span style="color:#ABB2BF;">(</span><span style="color:#D19A66;">0</span><span style="color:#ABB2BF;">);</span></span>
<span class="line"><span style="color:#E5C07B;"> configurations</span><span style="color:#ABB2BF;">.</span><span style="color:#61AFEF;">setAccessStrategy</span><span style="color:#ABB2BF;">(</span><span style="color:#C678DD;">new</span><span style="color:#61AFEF;"> MappableRowByRowAccessStrategy</span><span style="color:#ABB2BF;">()).</span><span style="color:#61AFEF;">setOutputDataType</span><span style="color:#ABB2BF;">(type);</span></span>
<span class="line"><span style="color:#ABB2BF;"> }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;"> @</span><span style="color:#E5C07B;">Override</span></span>
<span class="line"><span style="color:#C678DD;"> public</span><span style="color:#C678DD;"> void</span><span style="color:#61AFEF;"> transform</span><span style="color:#ABB2BF;">(</span><span style="color:#E5C07B;">Column</span><span style="color:#ABB2BF;">[] </span><span style="color:#E06C75;font-style:italic;">columns</span><span style="color:#ABB2BF;">, </span><span style="color:#E5C07B;">ColumnBuilder</span><span style="color:#E06C75;font-style:italic;"> builder</span><span style="color:#ABB2BF;">)</span><span style="color:#C678DD;"> throws</span><span style="color:#E5C07B;"> Exception</span><span style="color:#ABB2BF;"> {</span></span>
<span class="line"><span style="color:#C678DD;"> long</span><span style="color:#ABB2BF;">[] </span><span style="color:#E06C75;">inputs1</span><span style="color:#56B6C2;"> =</span><span style="color:#ABB2BF;"> columns[</span><span style="color:#D19A66;">0</span><span style="color:#ABB2BF;">].</span><span style="color:#61AFEF;">getLongs</span><span style="color:#ABB2BF;">();</span></span>
<span class="line"><span style="color:#C678DD;"> long</span><span style="color:#ABB2BF;">[] </span><span style="color:#E06C75;">inputs2</span><span style="color:#56B6C2;"> =</span><span style="color:#ABB2BF;"> columns[</span><span style="color:#D19A66;">1</span><span style="color:#ABB2BF;">].</span><span style="color:#61AFEF;">getLongs</span><span style="color:#ABB2BF;">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;"> int</span><span style="color:#E06C75;"> count</span><span style="color:#56B6C2;"> =</span><span style="color:#ABB2BF;"> columns[</span><span style="color:#D19A66;">0</span><span style="color:#ABB2BF;">].</span><span style="color:#61AFEF;">getPositionCount</span><span style="color:#ABB2BF;">();</span></span>
<span class="line"><span style="color:#C678DD;"> for</span><span style="color:#ABB2BF;"> (</span><span style="color:#C678DD;">int</span><span style="color:#E06C75;"> i</span><span style="color:#56B6C2;"> =</span><span style="color:#D19A66;"> 0</span><span style="color:#ABB2BF;">; i </span><span style="color:#56B6C2;">&lt;</span><span style="color:#ABB2BF;"> count; i++) {</span></span>
<span class="line"><span style="color:#E5C07B;"> builder</span><span style="color:#ABB2BF;">.</span><span style="color:#61AFEF;">writeLong</span><span style="color:#ABB2BF;">(inputs1[i] </span><span style="color:#56B6C2;">+</span><span style="color:#ABB2BF;"> inputs2[i]);</span></span>
<span class="line"><span style="color:#ABB2BF;"> }</span></span>
<span class="line"><span style="color:#ABB2BF;"> }</span></span>
<span class="line"><span style="color:#ABB2BF;">}</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><ol start="5"><li><strong>void transform(Row row, PointCollector collector) throws Exception</strong></li></ol><p>You need to implement this method when you specify the strategy of UDF to read the original data as <code>RowByRowAccessStrategy</code>.</p><p>This method processes the raw data one row at a time. The raw data is input from <code>Row</code> and output by <code>PointCollector</code>. You can output any number of data points in one <code>transform</code> method call. It should be noted that the type of output data points must be the same as you set in the <code>beforeStart</code> method, and the timestamps of output data points must be strictly monotonically increasing.</p><p>The following is a complete UDF example that implements the <code>void transform(Row row, PointCollector collector) throws Exception</code> method. It is an adder that receives two columns of time series as input. When two data points in a row are not <code>null</code>, this UDF will output the algebraic sum of these two data points.</p><div class="language-java line-numbers-mode" data-highlighter="shiki" data-ext="java" style="background-color:#282c34;color:#abb2bf;"><pre class="shiki one-dark-pro vp-code"><code class="language-java"><span class="line"><span style="color:#C678DD;">import</span><span style="color:#E5C07B;"> org.apache.iotdb.udf.api.UDTF</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"><span style="color:#C678DD;">import</span><span style="color:#E5C07B;"> org.apache.iotdb.udf.api.access.Row</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"><span style="color:#C678DD;">import</span><span style="color:#E5C07B;"> org.apache.iotdb.udf.api.collector.PointCollector</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"><span style="color:#C678DD;">import</span><span style="color:#E5C07B;"> org.apache.iotdb.udf.api.customizer.config.UDTFConfigurations</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"><span style="color:#C678DD;">import</span><span style="color:#E5C07B;"> org.apache.iotdb.udf.api.customizer.parameter.UDFParameters</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"><span style="color:#C678DD;">import</span><span style="color:#E5C07B;"> org.apache.iotdb.udf.api.customizer.strategy.RowByRowAccessStrategy</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"><span style="color:#C678DD;">import</span><span style="color:#E5C07B;"> org.apache.iotdb.udf.api.type.Type</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;">public</span><span style="color:#C678DD;"> class</span><span style="color:#E5C07B;"> Adder</span><span style="color:#C678DD;"> implements</span><span style="color:#E5C07B;"> UDTF</span><span style="color:#ABB2BF;"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;"> @</span><span style="color:#E5C07B;">Override</span></span>
<span class="line"><span style="color:#C678DD;"> public</span><span style="color:#C678DD;"> void</span><span style="color:#61AFEF;"> beforeStart</span><span style="color:#ABB2BF;">(</span><span style="color:#E5C07B;">UDFParameters</span><span style="color:#E06C75;font-style:italic;"> parameters</span><span style="color:#ABB2BF;">, </span><span style="color:#E5C07B;">UDTFConfigurations</span><span style="color:#E06C75;font-style:italic;"> configurations</span><span style="color:#ABB2BF;">)</span><span style="color:#ABB2BF;"> {</span></span>
<span class="line"><span style="color:#ABB2BF;"> configurations</span></span>
<span class="line"><span style="color:#ABB2BF;"> .</span><span style="color:#61AFEF;">setOutputDataType</span><span style="color:#ABB2BF;">(</span><span style="color:#E5C07B;">TSDataType</span><span style="color:#ABB2BF;">.</span><span style="color:#E5C07B;">INT64</span><span style="color:#ABB2BF;">)</span></span>
<span class="line"><span style="color:#ABB2BF;"> .</span><span style="color:#61AFEF;">setAccessStrategy</span><span style="color:#ABB2BF;">(</span><span style="color:#C678DD;">new</span><span style="color:#61AFEF;"> RowByRowAccessStrategy</span><span style="color:#ABB2BF;">());</span></span>
<span class="line"><span style="color:#ABB2BF;"> }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;"> @</span><span style="color:#E5C07B;">Override</span></span>
<span class="line"><span style="color:#C678DD;"> public</span><span style="color:#C678DD;"> void</span><span style="color:#61AFEF;"> transform</span><span style="color:#ABB2BF;">(</span><span style="color:#E5C07B;">Row</span><span style="color:#E06C75;font-style:italic;"> row</span><span style="color:#ABB2BF;">, </span><span style="color:#E5C07B;">PointCollector</span><span style="color:#E06C75;font-style:italic;"> collector</span><span style="color:#ABB2BF;">)</span><span style="color:#C678DD;"> throws</span><span style="color:#E5C07B;"> Exception</span><span style="color:#ABB2BF;"> {</span></span>
<span class="line"><span style="color:#C678DD;"> if</span><span style="color:#ABB2BF;"> (</span><span style="color:#E5C07B;">row</span><span style="color:#ABB2BF;">.</span><span style="color:#61AFEF;">isNull</span><span style="color:#ABB2BF;">(</span><span style="color:#D19A66;">0</span><span style="color:#ABB2BF;">) </span><span style="color:#56B6C2;">||</span><span style="color:#E5C07B;"> row</span><span style="color:#ABB2BF;">.</span><span style="color:#61AFEF;">isNull</span><span style="color:#ABB2BF;">(</span><span style="color:#D19A66;">1</span><span style="color:#ABB2BF;">)) {</span></span>
<span class="line"><span style="color:#C678DD;"> return</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"><span style="color:#ABB2BF;"> }</span></span>
<span class="line"><span style="color:#E5C07B;"> collector</span><span style="color:#ABB2BF;">.</span><span style="color:#61AFEF;">putLong</span><span style="color:#ABB2BF;">(</span><span style="color:#E5C07B;">row</span><span style="color:#ABB2BF;">.</span><span style="color:#61AFEF;">getTime</span><span style="color:#ABB2BF;">(), </span><span style="color:#E5C07B;">row</span><span style="color:#ABB2BF;">.</span><span style="color:#61AFEF;">getLong</span><span style="color:#ABB2BF;">(</span><span style="color:#D19A66;">0</span><span style="color:#ABB2BF;">) </span><span style="color:#56B6C2;">+</span><span style="color:#E5C07B;"> row</span><span style="color:#ABB2BF;">.</span><span style="color:#61AFEF;">getLong</span><span style="color:#ABB2BF;">(</span><span style="color:#D19A66;">1</span><span style="color:#ABB2BF;">));</span></span>
<span class="line"><span style="color:#ABB2BF;"> }</span></span>
<span class="line"><span style="color:#ABB2BF;">}</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><ol start="6"><li><strong>void transform(RowWindow rowWindow, PointCollector collector) throws Exception</strong></li></ol><p>You need to implement this method when you specify the strategy of UDF to read the original data as <code>SlidingTimeWindowAccessStrategy</code> or <code>SlidingSizeWindowAccessStrategy</code>.</p><p>This method processes a batch of data in a fixed number of rows or a fixed time interval each time, and we call the container containing this batch of data a window. The raw data is input from <code>RowWindow</code> and output by <code>PointCollector</code>. <code>RowWindow</code> can help you access a batch of <code>Row</code>, it provides a set of interfaces for random access and iterative access to this batch of <code>Row</code>. You can output any number of data points in one <code>transform</code> method call. It should be noted that the type of output data points must be the same as you set in the <code>beforeStart</code> method, and the timestamps of output data points must be strictly monotonically increasing.</p><p>Below is a complete UDF example that implements the <code>void transform(RowWindow rowWindow, PointCollector collector) throws Exception</code> method. It is a counter that receives any number of time series as input, and its function is to count and output the number of data rows in each time window within a specified time range.</p><div class="language-java line-numbers-mode" data-highlighter="shiki" data-ext="java" style="background-color:#282c34;color:#abb2bf;"><pre class="shiki one-dark-pro vp-code"><code class="language-java"><span class="line"><span style="color:#C678DD;">import</span><span style="color:#E5C07B;"> java.io.IOException</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"><span style="color:#C678DD;">import</span><span style="color:#E5C07B;"> org.apache.iotdb.udf.api.UDTF</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"><span style="color:#C678DD;">import</span><span style="color:#E5C07B;"> org.apache.iotdb.udf.api.access.Row</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"><span style="color:#C678DD;">import</span><span style="color:#E5C07B;"> org.apache.iotdb.udf.api.access.RowWindow</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"><span style="color:#C678DD;">import</span><span style="color:#E5C07B;"> org.apache.iotdb.udf.api.collector.PointCollector</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"><span style="color:#C678DD;">import</span><span style="color:#E5C07B;"> org.apache.iotdb.udf.api.customizer.config.UDTFConfigurations</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"><span style="color:#C678DD;">import</span><span style="color:#E5C07B;"> org.apache.iotdb.udf.api.customizer.parameter.UDFParameters</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"><span style="color:#C678DD;">import</span><span style="color:#E5C07B;"> org.apache.iotdb.udf.api.customizer.strategy.SlidingTimeWindowAccessStrategy</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"><span style="color:#C678DD;">import</span><span style="color:#E5C07B;"> org.apache.iotdb.udf.api.type.Type</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;">public</span><span style="color:#C678DD;"> class</span><span style="color:#E5C07B;"> Counter</span><span style="color:#C678DD;"> implements</span><span style="color:#E5C07B;"> UDTF</span><span style="color:#ABB2BF;"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;"> @</span><span style="color:#E5C07B;">Override</span></span>
<span class="line"><span style="color:#C678DD;"> public</span><span style="color:#C678DD;"> void</span><span style="color:#61AFEF;"> beforeStart</span><span style="color:#ABB2BF;">(</span><span style="color:#E5C07B;">UDFParameters</span><span style="color:#E06C75;font-style:italic;"> parameters</span><span style="color:#ABB2BF;">, </span><span style="color:#E5C07B;">UDTFConfigurations</span><span style="color:#E06C75;font-style:italic;"> configurations</span><span style="color:#ABB2BF;">)</span><span style="color:#ABB2BF;"> {</span></span>
<span class="line"><span style="color:#ABB2BF;"> configurations</span></span>
<span class="line"><span style="color:#ABB2BF;"> .</span><span style="color:#61AFEF;">setOutputDataType</span><span style="color:#ABB2BF;">(</span><span style="color:#E5C07B;">TSDataType</span><span style="color:#ABB2BF;">.</span><span style="color:#E5C07B;">INT32</span><span style="color:#ABB2BF;">)</span></span>
<span class="line"><span style="color:#ABB2BF;"> .</span><span style="color:#61AFEF;">setAccessStrategy</span><span style="color:#ABB2BF;">(</span><span style="color:#C678DD;">new</span><span style="color:#61AFEF;"> SlidingTimeWindowAccessStrategy</span><span style="color:#ABB2BF;">(</span></span>
<span class="line"><span style="color:#E5C07B;"> parameters</span><span style="color:#ABB2BF;">.</span><span style="color:#61AFEF;">getLong</span><span style="color:#ABB2BF;">(</span><span style="color:#98C379;">&quot;time_interval&quot;</span><span style="color:#ABB2BF;">),</span></span>
<span class="line"><span style="color:#E5C07B;"> parameters</span><span style="color:#ABB2BF;">.</span><span style="color:#61AFEF;">getLong</span><span style="color:#ABB2BF;">(</span><span style="color:#98C379;">&quot;sliding_step&quot;</span><span style="color:#ABB2BF;">),</span></span>
<span class="line"><span style="color:#E5C07B;"> parameters</span><span style="color:#ABB2BF;">.</span><span style="color:#61AFEF;">getLong</span><span style="color:#ABB2BF;">(</span><span style="color:#98C379;">&quot;display_window_begin&quot;</span><span style="color:#ABB2BF;">),</span></span>
<span class="line"><span style="color:#E5C07B;"> parameters</span><span style="color:#ABB2BF;">.</span><span style="color:#61AFEF;">getLong</span><span style="color:#ABB2BF;">(</span><span style="color:#98C379;">&quot;display_window_end&quot;</span><span style="color:#ABB2BF;">)));</span></span>
<span class="line"><span style="color:#ABB2BF;"> }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;"> @</span><span style="color:#E5C07B;">Override</span></span>
<span class="line"><span style="color:#C678DD;"> public</span><span style="color:#C678DD;"> void</span><span style="color:#61AFEF;"> transform</span><span style="color:#ABB2BF;">(</span><span style="color:#E5C07B;">RowWindow</span><span style="color:#E06C75;font-style:italic;"> rowWindow</span><span style="color:#ABB2BF;">, </span><span style="color:#E5C07B;">PointCollector</span><span style="color:#E06C75;font-style:italic;"> collector</span><span style="color:#ABB2BF;">)</span><span style="color:#ABB2BF;"> {</span></span>
<span class="line"><span style="color:#C678DD;"> if</span><span style="color:#ABB2BF;"> (</span><span style="color:#E5C07B;">rowWindow</span><span style="color:#ABB2BF;">.</span><span style="color:#61AFEF;">windowSize</span><span style="color:#ABB2BF;">() </span><span style="color:#56B6C2;">!=</span><span style="color:#D19A66;"> 0</span><span style="color:#ABB2BF;">) {</span></span>
<span class="line"><span style="color:#E5C07B;"> collector</span><span style="color:#ABB2BF;">.</span><span style="color:#61AFEF;">putInt</span><span style="color:#ABB2BF;">(</span><span style="color:#E5C07B;">rowWindow</span><span style="color:#ABB2BF;">.</span><span style="color:#61AFEF;">windowStartTime</span><span style="color:#ABB2BF;">(), </span><span style="color:#E5C07B;">rowWindow</span><span style="color:#ABB2BF;">.</span><span style="color:#61AFEF;">windowSize</span><span style="color:#ABB2BF;">());</span></span>
<span class="line"><span style="color:#ABB2BF;"> }</span></span>
<span class="line"><span style="color:#ABB2BF;"> }</span></span>
<span class="line"><span style="color:#ABB2BF;">}</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><ol start="7"><li><strong>void terminate(PointCollector collector) throws Exception</strong></li></ol><p>In some scenarios, a UDF needs to traverse all the original data to calculate the final output data points. The <code>terminate</code> interface provides support for those scenarios.</p><p>This method is called after all <code>transform</code> calls are executed and before the <code>beforeDestory</code> method is executed. You can implement the <code>transform</code> method to perform pure data processing (without outputting any data points), and implement the <code>terminate</code> method to output the processing results.</p><p>The processing results need to be output by the <code>PointCollector</code>. You can output any number of data points in one <code>terminate</code> method call. It should be noted that the type of output data points must be the same as you set in the <code>beforeStart</code> method, and the timestamps of output data points must be strictly monotonically increasing.</p><p>Below is a complete UDF example that implements the <code>void terminate(PointCollector collector) throws Exception</code> method. It takes one time series whose data type is <code>INT32</code> as input, and outputs the maximum value point of the series.</p><div class="language-java line-numbers-mode" data-highlighter="shiki" data-ext="java" style="background-color:#282c34;color:#abb2bf;"><pre class="shiki one-dark-pro vp-code"><code class="language-java"><span class="line"><span style="color:#C678DD;">import</span><span style="color:#E5C07B;"> java.io.IOException</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"><span style="color:#C678DD;">import</span><span style="color:#E5C07B;"> org.apache.iotdb.udf.api.UDTF</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"><span style="color:#C678DD;">import</span><span style="color:#E5C07B;"> org.apache.iotdb.udf.api.access.Row</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"><span style="color:#C678DD;">import</span><span style="color:#E5C07B;"> org.apache.iotdb.udf.api.collector.PointCollector</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"><span style="color:#C678DD;">import</span><span style="color:#E5C07B;"> org.apache.iotdb.udf.api.customizer.config.UDTFConfigurations</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"><span style="color:#C678DD;">import</span><span style="color:#E5C07B;"> org.apache.iotdb.udf.api.customizer.parameter.UDFParameters</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"><span style="color:#C678DD;">import</span><span style="color:#E5C07B;"> org.apache.iotdb.udf.api.customizer.strategy.RowByRowAccessStrategy</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"><span style="color:#C678DD;">import</span><span style="color:#E5C07B;"> org.apache.iotdb.udf.api.type.Type</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;">public</span><span style="color:#C678DD;"> class</span><span style="color:#E5C07B;"> Max</span><span style="color:#C678DD;"> implements</span><span style="color:#E5C07B;"> UDTF</span><span style="color:#ABB2BF;"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;"> private</span><span style="color:#E5C07B;"> Long</span><span style="color:#E06C75;"> time</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"><span style="color:#C678DD;"> private</span><span style="color:#C678DD;"> int</span><span style="color:#E06C75;"> value</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;"> @</span><span style="color:#E5C07B;">Override</span></span>
<span class="line"><span style="color:#C678DD;"> public</span><span style="color:#C678DD;"> void</span><span style="color:#61AFEF;"> beforeStart</span><span style="color:#ABB2BF;">(</span><span style="color:#E5C07B;">UDFParameters</span><span style="color:#E06C75;font-style:italic;"> parameters</span><span style="color:#ABB2BF;">, </span><span style="color:#E5C07B;">UDTFConfigurations</span><span style="color:#E06C75;font-style:italic;"> configurations</span><span style="color:#ABB2BF;">)</span><span style="color:#ABB2BF;"> {</span></span>
<span class="line"><span style="color:#ABB2BF;"> configurations</span></span>
<span class="line"><span style="color:#ABB2BF;"> .</span><span style="color:#61AFEF;">setOutputDataType</span><span style="color:#ABB2BF;">(</span><span style="color:#E5C07B;">TSDataType</span><span style="color:#ABB2BF;">.</span><span style="color:#E5C07B;">INT32</span><span style="color:#ABB2BF;">)</span></span>
<span class="line"><span style="color:#ABB2BF;"> .</span><span style="color:#61AFEF;">setAccessStrategy</span><span style="color:#ABB2BF;">(</span><span style="color:#C678DD;">new</span><span style="color:#61AFEF;"> RowByRowAccessStrategy</span><span style="color:#ABB2BF;">());</span></span>
<span class="line"><span style="color:#ABB2BF;"> }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;"> @</span><span style="color:#E5C07B;">Override</span></span>
<span class="line"><span style="color:#C678DD;"> public</span><span style="color:#C678DD;"> void</span><span style="color:#61AFEF;"> transform</span><span style="color:#ABB2BF;">(</span><span style="color:#E5C07B;">Row</span><span style="color:#E06C75;font-style:italic;"> row</span><span style="color:#ABB2BF;">, </span><span style="color:#E5C07B;">PointCollector</span><span style="color:#E06C75;font-style:italic;"> collector</span><span style="color:#ABB2BF;">)</span><span style="color:#ABB2BF;"> {</span></span>
<span class="line"><span style="color:#C678DD;"> if</span><span style="color:#ABB2BF;"> (</span><span style="color:#E5C07B;">row</span><span style="color:#ABB2BF;">.</span><span style="color:#61AFEF;">isNull</span><span style="color:#ABB2BF;">(</span><span style="color:#D19A66;">0</span><span style="color:#ABB2BF;">)) {</span></span>
<span class="line"><span style="color:#C678DD;"> return</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"><span style="color:#ABB2BF;"> }</span></span>
<span class="line"><span style="color:#C678DD;"> int</span><span style="color:#E06C75;"> candidateValue</span><span style="color:#56B6C2;"> =</span><span style="color:#E5C07B;"> row</span><span style="color:#ABB2BF;">.</span><span style="color:#61AFEF;">getInt</span><span style="color:#ABB2BF;">(</span><span style="color:#D19A66;">0</span><span style="color:#ABB2BF;">);</span></span>
<span class="line"><span style="color:#C678DD;"> if</span><span style="color:#ABB2BF;"> (time </span><span style="color:#56B6C2;">==</span><span style="color:#D19A66;"> null</span><span style="color:#56B6C2;"> ||</span><span style="color:#ABB2BF;"> value </span><span style="color:#56B6C2;">&lt;</span><span style="color:#ABB2BF;"> candidateValue) {</span></span>
<span class="line"><span style="color:#ABB2BF;"> time </span><span style="color:#56B6C2;">=</span><span style="color:#E5C07B;"> row</span><span style="color:#ABB2BF;">.</span><span style="color:#61AFEF;">getTime</span><span style="color:#ABB2BF;">();</span></span>
<span class="line"><span style="color:#ABB2BF;"> value </span><span style="color:#56B6C2;">=</span><span style="color:#ABB2BF;"> candidateValue;</span></span>
<span class="line"><span style="color:#ABB2BF;"> }</span></span>
<span class="line"><span style="color:#ABB2BF;"> }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;"> @</span><span style="color:#E5C07B;">Override</span></span>
<span class="line"><span style="color:#C678DD;"> public</span><span style="color:#C678DD;"> void</span><span style="color:#61AFEF;"> terminate</span><span style="color:#ABB2BF;">(</span><span style="color:#E5C07B;">PointCollector</span><span style="color:#E06C75;font-style:italic;"> collector</span><span style="color:#ABB2BF;">)</span><span style="color:#C678DD;"> throws</span><span style="color:#E5C07B;"> IOException</span><span style="color:#ABB2BF;"> {</span></span>
<span class="line"><span style="color:#C678DD;"> if</span><span style="color:#ABB2BF;"> (time </span><span style="color:#56B6C2;">!=</span><span style="color:#D19A66;"> null</span><span style="color:#ABB2BF;">) {</span></span>
<span class="line"><span style="color:#E5C07B;"> collector</span><span style="color:#ABB2BF;">.</span><span style="color:#61AFEF;">putInt</span><span style="color:#ABB2BF;">(time, value);</span></span>
<span class="line"><span style="color:#ABB2BF;"> }</span></span>
<span class="line"><span style="color:#ABB2BF;"> }</span></span>
<span class="line"><span style="color:#ABB2BF;">}</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><ol start="8"><li><strong>void beforeDestroy()</strong></li></ol><p>The method for terminating a UDF.</p><p>This method is called by the framework. For a UDF instance, <code>beforeDestroy</code> will be called after the last record is processed. In the entire life cycle of the instance, <code>beforeDestroy</code> will only be called once.</p><h3 id="_4-3-udaf-user-defined-aggregation-function" tabindex="-1"><a class="header-anchor" href="#_4-3-udaf-user-defined-aggregation-function"><span>4.3 UDAF (User Defined Aggregation Function)</span></a></h3><p>A complete definition of UDAF involves two classes, <code>State</code> and <code>UDAF</code>.</p><h4 id="state-class" tabindex="-1"><a class="header-anchor" href="#state-class"><span>State Class</span></a></h4><p>To write your own <code>State</code>, you need to implement the <code>org.apache.iotdb.udf.api.State</code> interface.</p><h4 id="interface-description-2" tabindex="-1"><a class="header-anchor" href="#interface-description-2"><span>Interface Description:</span></a></h4><table><thead><tr><th>Interface Definition</th><th>Description</th><th>Required to Implement</th></tr></thead><tbody><tr><td>void reset()</td><td>To reset the <code>State</code> object to its initial state, you need to fill in the initial values of the fields in the <code>State</code> class within this method as if you were writing a constructor.</td><td>Required</td></tr><tr><td>byte[] serialize()</td><td>Serializes <code>State</code> to binary data. This method is used for IoTDB internal <code>State</code> passing. Note that the order of serialization must be consistent with the following deserialization methods.</td><td>Required</td></tr><tr><td>void deserialize(byte[] bytes)</td><td>Deserializes binary data to <code>State</code>. This method is used for IoTDB internal <code>State</code> passing. Note that the order of deserialization must be consistent with the serialization method above.</td><td>Required</td></tr></tbody></table><h4 id="detailed-interface-introduction-1" tabindex="-1"><a class="header-anchor" href="#detailed-interface-introduction-1"><span>Detailed interface introduction:</span></a></h4><ol><li><strong>void reset()</strong></li></ol><p>This method resets the <code>State</code> to its initial state, you need to fill in the initial values of the fields in the <code>State</code> object in this method. For optimization reasons, IoTDB reuses <code>State</code> as much as possible internally, rather than creating a new <code>State</code> for each group, which would introduce unnecessary overhead. When <code>State</code> has finished updating the data in a group, this method is called to reset to the initial state as a way to process the next group.</p><p>In the case of <code>State</code> for averaging (aka <code>avg</code>), for example, you would need the sum of the data, <code>sum</code>, and the number of entries in the data, <code>count</code>, and initialize both to 0 in the <code>reset()</code> method.</p><div class="language-java line-numbers-mode" data-highlighter="shiki" data-ext="java" style="background-color:#282c34;color:#abb2bf;"><pre class="shiki one-dark-pro vp-code"><code class="language-java"><span class="line"><span style="color:#C678DD;">class</span><span style="color:#E5C07B;"> AvgState</span><span style="color:#C678DD;"> implements</span><span style="color:#E5C07B;"> State</span><span style="color:#ABB2BF;"> {</span></span>
<span class="line"><span style="color:#C678DD;"> double</span><span style="color:#E06C75;"> sum</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;"> long</span><span style="color:#E06C75;"> count</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;"> @</span><span style="color:#E5C07B;">Override</span></span>
<span class="line"><span style="color:#C678DD;"> public</span><span style="color:#C678DD;"> void</span><span style="color:#61AFEF;"> reset</span><span style="color:#ABB2BF;">()</span><span style="color:#ABB2BF;"> {</span></span>
<span class="line"><span style="color:#ABB2BF;"> sum </span><span style="color:#56B6C2;">=</span><span style="color:#D19A66;"> 0</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"><span style="color:#ABB2BF;"> count </span><span style="color:#56B6C2;">=</span><span style="color:#D19A66;"> 0</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"><span style="color:#ABB2BF;"> }</span></span>
<span class="line"><span style="color:#E06C75;"> </span></span>
<span class="line"><span style="color:#7F848E;font-style:italic;"> // other methods</span></span>
<span class="line"><span style="color:#ABB2BF;">}</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><ol start="2"><li><strong>byte[] serialize()/void deserialize(byte[] bytes)</strong></li></ol><p>These methods serialize the <code>State</code> into binary data, and deserialize the <code>State</code> from the binary data. IoTDB, as a distributed database, involves passing data among different nodes, so you need to write these two methods to enable the passing of the State among different nodes. Note that the order of serialization and deserialization must be the consistent.</p><p>In the case of <code>State</code> for averaging (aka <code>avg</code>), for example, you can convert the content of State to <code>byte[]</code> array and read out the content of State from <code>byte[]</code> array in any way you want, the following shows the code for serialization/deserialization using <code>ByteBuffer</code> introduced by Java8:</p><div class="language-java line-numbers-mode" data-highlighter="shiki" data-ext="java" style="background-color:#282c34;color:#abb2bf;"><pre class="shiki one-dark-pro vp-code"><code class="language-java"><span class="line"><span style="color:#ABB2BF;">@</span><span style="color:#E5C07B;">Override</span></span>
<span class="line"><span style="color:#C678DD;">public</span><span style="color:#C678DD;"> byte</span><span style="color:#E06C75;">[] </span><span style="color:#61AFEF;">serialize</span><span style="color:#E06C75;">() {</span></span>
<span class="line"><span style="color:#E5C07B;"> ByteBuffer</span><span style="color:#E06C75;"> buffer </span><span style="color:#56B6C2;">=</span><span style="color:#E5C07B;"> ByteBuffer</span><span style="color:#ABB2BF;">.</span><span style="color:#61AFEF;">allocate</span><span style="color:#ABB2BF;">(</span><span style="color:#E5C07B;">Double</span><span style="color:#ABB2BF;">.</span><span style="color:#E5C07B;">BYTES</span><span style="color:#56B6C2;"> +</span><span style="color:#E5C07B;"> Long</span><span style="color:#ABB2BF;">.</span><span style="color:#E5C07B;">BYTES</span><span style="color:#ABB2BF;">);</span></span>
<span class="line"><span style="color:#E5C07B;"> buffer</span><span style="color:#ABB2BF;">.</span><span style="color:#61AFEF;">putDouble</span><span style="color:#ABB2BF;">(sum);</span></span>
<span class="line"><span style="color:#E5C07B;"> buffer</span><span style="color:#ABB2BF;">.</span><span style="color:#61AFEF;">putLong</span><span style="color:#ABB2BF;">(count);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;"> return</span><span style="color:#E5C07B;"> buffer</span><span style="color:#ABB2BF;">.</span><span style="color:#61AFEF;">array</span><span style="color:#ABB2BF;">();</span></span>
<span class="line"><span style="color:#E06C75;">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#ABB2BF;">@</span><span style="color:#E5C07B;">Override</span></span>
<span class="line"><span style="color:#C678DD;">public</span><span style="color:#C678DD;"> void</span><span style="color:#61AFEF;"> deserialize</span><span style="color:#E06C75;">(</span><span style="color:#C678DD;">byte</span><span style="color:#E06C75;">[] bytes) {</span></span>
<span class="line"><span style="color:#E5C07B;"> ByteBuffer</span><span style="color:#E06C75;"> buffer </span><span style="color:#56B6C2;">=</span><span style="color:#E5C07B;"> ByteBuffer</span><span style="color:#ABB2BF;">.</span><span style="color:#61AFEF;">wrap</span><span style="color:#ABB2BF;">(bytes);</span></span>
<span class="line"><span style="color:#E06C75;"> sum </span><span style="color:#56B6C2;">=</span><span style="color:#E5C07B;"> buffer</span><span style="color:#ABB2BF;">.</span><span style="color:#61AFEF;">getDouble</span><span style="color:#ABB2BF;">();</span></span>
<span class="line"><span style="color:#E06C75;"> count </span><span style="color:#56B6C2;">=</span><span style="color:#E5C07B;"> buffer</span><span style="color:#ABB2BF;">.</span><span style="color:#61AFEF;">getLong</span><span style="color:#ABB2BF;">();</span></span>
<span class="line"><span style="color:#E06C75;">}</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h4 id="udaf-classes" tabindex="-1"><a class="header-anchor" href="#udaf-classes"><span>UDAF Classes</span></a></h4><p>To write a UDAF, you need to implement the <code>org.apache.iotdb.udf.api.UDAF</code> interface.</p><h4 id="interface-description-3" tabindex="-1"><a class="header-anchor" href="#interface-description-3"><span>Interface Description:</span></a></h4><table><thead><tr><th>Interface definition</th><th>Description</th><th>Required to Implement</th></tr></thead><tbody><tr><td>void validate(UDFParameterValidator validator) throws Exception</td><td>This method is mainly used to validate <code>UDFParameters</code> and it is executed before <code>beforeStart(UDFParameters, UDTFConfigurations)</code> is called.</td><td>Optional</td></tr><tr><td>void beforeStart(UDFParameters parameters, UDAFConfigurations configurations) throws Exception</td><td>Initialization method that invokes user-defined initialization behavior before UDAF processes the input data. Unlike UDTF, configuration is of type <code>UDAFConfiguration</code>.</td><td>Required</td></tr><tr><td>State createState()</td><td>To create a <code>State</code> object, usually just call the default constructor and modify the default initial value as needed.</td><td>Required</td></tr><tr><td>void addInput(State state, Column[] columns, BitMap bitMap)</td><td>Update <code>State</code> object according to the incoming data <code>Column[]</code> in batch, note that last column <code>columns[columns.length - 1]</code> always represents the time column. In addition, <code>BitMap</code> represents the data that has been filtered out before, you need to manually determine whether the corresponding data has been filtered out when writing this method.</td><td>Required</td></tr><tr><td>void combineState(State state, State rhs)</td><td>Merge <code>rhs</code> state into <code>state</code> state. In a distributed scenario, the same set of data may be distributed on different nodes, IoTDB generates a <code>State</code> object for the partial data on each node, and then calls this method to merge it into the complete <code>State</code>.</td><td>Required</td></tr><tr><td>void outputFinal(State state, ResultValue resultValue)</td><td>Computes the final aggregated result based on the data in <code>State</code>. Note that according to the semantics of the aggregation, only one value can be output per group.</td><td>Required</td></tr><tr><td>void beforeDestroy()</td><td>This method is called by the framework after the last input data is processed, and will only be called once in the life cycle of each UDF instance.</td><td>Optional</td></tr></tbody></table><p>In the life cycle of a UDAF instance, the calling sequence of each method is as follows:</p><ol><li>State createState()</li><li>void validate(UDFParameterValidator validator) throws Exception</li><li>void beforeStart(UDFParameters parameters, UDAFConfigurations configurations) throws Exception</li><li>void addInput(State state, Column[] columns, BitMap bitMap)</li><li>void combineState(State state, State rhs)</li><li>void outputFinal(State state, ResultValue resultValue)</li><li>void beforeDestroy()</li></ol><p>Similar to UDTF, every time the framework executes a UDAF query, a new UDF instance will be constructed. When the query ends, the corresponding instance will be destroyed. Therefore, the internal data of the instances in different UDAF queries (even in the same SQL statement) are isolated. You can maintain some state data in the UDAF without considering the influence of concurrency and other factors.</p><h4 id="detailed-interface-introduction-2" tabindex="-1"><a class="header-anchor" href="#detailed-interface-introduction-2"><span>Detailed interface introduction:</span></a></h4><ol><li><strong>void validate(UDFParameterValidator validator) throws Exception</strong></li></ol><p>Same as UDTF, the <code>validate</code> method is used to validate the parameters entered by the user.</p><p>In this method, you can limit the number and types of input time series, check the attributes of user input, or perform any custom verification.</p><ol start="2"><li><strong>void beforeStart(UDFParameters parameters, UDAFConfigurations configurations) throws Exception</strong></li></ol><p>The <code>beforeStart</code> method does the same thing as the UDAF:</p><ol><li>Use UDFParameters to get the time series paths and parse key-value pair attributes entered by the user.</li><li>Set the strategy to access the raw data and set the output data type in UDAFConfigurations.</li><li>Create resources, such as establishing external connections, opening files, etc.</li></ol><p>The role of the <code>UDFParameters</code> type can be seen above.</p><p>2.2 <strong>UDTFConfigurations</strong></p><p>The difference from UDTF is that UDAF uses <code>UDAFConfigurations</code> as the type of <code>configuration</code> object.</p><p>Currently, this class only supports setting the type of output data.</p><div class="language-java line-numbers-mode" data-highlighter="shiki" data-ext="java" style="background-color:#282c34;color:#abb2bf;"><pre class="shiki one-dark-pro vp-code"><code class="language-java"><span class="line"><span style="color:#C678DD;">void</span><span style="color:#61AFEF;"> beforeStart</span><span style="color:#E06C75;">(</span><span style="color:#E5C07B;">UDFParameters</span><span style="color:#E06C75;"> parameters</span><span style="color:#ABB2BF;">,</span><span style="color:#E5C07B;"> UDAFConfigurations</span><span style="color:#E06C75;"> configurations) throws Exception {</span></span>
<span class="line"><span style="color:#7F848E;font-style:italic;"> // parameters</span></span>
<span class="line"><span style="color:#7F848E;font-style:italic;"> // ...</span></span>
<span class="line"></span>
<span class="line"><span style="color:#7F848E;font-style:italic;"> // configurations</span></span>
<span class="line"><span style="color:#E06C75;"> configurations</span></span>
<span class="line"><span style="color:#ABB2BF;"> .</span><span style="color:#61AFEF;">setOutputDataType</span><span style="color:#ABB2BF;">(</span><span style="color:#E5C07B;">Type</span><span style="color:#ABB2BF;">.</span><span style="color:#E5C07B;">INT32</span><span style="color:#ABB2BF;">);</span><span style="color:#E06C75;"> }</span></span>
<span class="line"><span style="color:#E06C75;">}</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>The relationship between the output type set in <code>setOutputDataType</code> and the type of data output that <code>ResultValue</code> can actually receive is as follows:</p><table><thead><tr><th>The output type set in <code>setOutputDataType</code></th><th>The output type that <code>ResultValue</code> can actually receive</th></tr></thead><tbody><tr><td>INT32</td><td>int</td></tr><tr><td>INT64</td><td>long</td></tr><tr><td>FLOAT</td><td>float</td></tr><tr><td>DOUBLE</td><td>double</td></tr><tr><td>BOOLEAN</td><td>boolean</td></tr><tr><td>TEXT</td><td>org.apache.iotdb.udf.api.type.Binary</td></tr></tbody></table><p>The output type of the UDAF is determined at runtime. You can dynamically determine the output sequence type based on the input type.</p><p>Here is a simple example:</p><div class="language-java line-numbers-mode" data-highlighter="shiki" data-ext="java" style="background-color:#282c34;color:#abb2bf;"><pre class="shiki one-dark-pro vp-code"><code class="language-java"><span class="line"><span style="color:#C678DD;">void</span><span style="color:#61AFEF;"> beforeStart</span><span style="color:#E06C75;">(</span><span style="color:#E5C07B;">UDFParameters</span><span style="color:#E06C75;"> parameters</span><span style="color:#ABB2BF;">,</span><span style="color:#E5C07B;"> UDAFConfigurations</span><span style="color:#E06C75;"> configurations) throws Exception {</span></span>
<span class="line"><span style="color:#7F848E;font-style:italic;"> // do something</span></span>
<span class="line"><span style="color:#7F848E;font-style:italic;"> // ...</span></span>
<span class="line"><span style="color:#E06C75;"> </span></span>
<span class="line"><span style="color:#E06C75;"> configurations</span></span>
<span class="line"><span style="color:#ABB2BF;"> .</span><span style="color:#61AFEF;">setOutputDataType</span><span style="color:#ABB2BF;">(</span><span style="color:#E5C07B;">parameters</span><span style="color:#ABB2BF;">.</span><span style="color:#61AFEF;">getDataType</span><span style="color:#ABB2BF;">(</span><span style="color:#D19A66;">0</span><span style="color:#ABB2BF;">));</span></span>
<span class="line"><span style="color:#E06C75;">}</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><ol start="3"><li><strong>State createState()</strong></li></ol><p>This method creates and initializes a <code>State</code> object for UDAF. Due to the limitations of the Java language, you can only call the default constructor for the <code>State</code> class. The default constructor assigns a default initial value to all the fields in the class, and if that initial value does not meet your requirements, you need to initialize them manually within this method.</p><p>The following is an example that includes manual initialization. Suppose you want to implement an aggregate function that multiply all numbers in the group, then your initial <code>State</code> value should be set to 1, but the default constructor initializes it to 0, so you need to initialize <code>State</code> manually after calling the default constructor:</p><div class="language-java line-numbers-mode" data-highlighter="shiki" data-ext="java" style="background-color:#282c34;color:#abb2bf;"><pre class="shiki one-dark-pro vp-code"><code class="language-java"><span class="line"><span style="color:#C678DD;">public</span><span style="color:#E5C07B;"> State</span><span style="color:#61AFEF;"> createState</span><span style="color:#E06C75;">() {</span></span>
<span class="line"><span style="color:#E5C07B;"> MultiplyState</span><span style="color:#E06C75;"> state </span><span style="color:#56B6C2;">=</span><span style="color:#C678DD;"> new</span><span style="color:#61AFEF;"> MultiplyState</span><span style="color:#E06C75;">()</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"><span style="color:#E5C07B;"> state</span><span style="color:#ABB2BF;">.</span><span style="color:#E5C07B;">result</span><span style="color:#56B6C2;"> =</span><span style="color:#D19A66;"> 1</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"><span style="color:#C678DD;"> return</span><span style="color:#E06C75;"> state</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"><span style="color:#E06C75;">}</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><ol start="4"><li><strong>void addInput(State state, Column[] columns, BitMap bitMap)</strong></li></ol><p>This method updates the <code>State</code> object with the raw input data. For performance reasons, also to align with the IoTDB vectorized query engine, the raw input data is no longer a data point, but an array of columns <code>Column[]</code>. Note that the last column (i.e. <code>columns[columns.length - 1]</code>) is always the time column, so you can also do different operations in UDAF depending on the time.</p><p>Since the input parameter is not of a single data point type, but of multiple columns, you need to manually filter some of the data in the columns, which is why the third parameter, <code>BitMap</code>, exists. It identifies which of these columns have been filtered out, so you don&#39;t have to think about the filtered data in any case.</p><p>Here&#39;s an example of <code>addInput()</code> that counts the number of items (aka count). It shows how you can use <code>BitMap</code> to ignore data that has been filtered out. Note that due to the limitations of the Java language, you need to do the explicit cast the <code>State</code> object from type defined in the interface to a custom <code>State</code> type at the beginning of the method, otherwise you won&#39;t be able to use the <code>State</code> object.</p><div class="language-java line-numbers-mode" data-highlighter="shiki" data-ext="java" style="background-color:#282c34;color:#abb2bf;"><pre class="shiki one-dark-pro vp-code"><code class="language-java"><span class="line"><span style="color:#C678DD;">public</span><span style="color:#C678DD;"> void</span><span style="color:#61AFEF;"> addInput</span><span style="color:#E06C75;">(</span><span style="color:#E5C07B;">State</span><span style="color:#E06C75;"> state</span><span style="color:#ABB2BF;">,</span><span style="color:#E5C07B;"> Column</span><span style="color:#E06C75;">[] columns</span><span style="color:#ABB2BF;">,</span><span style="color:#E5C07B;"> BitMap</span><span style="color:#E06C75;"> bitMap) {</span></span>
<span class="line"><span style="color:#E5C07B;"> CountState</span><span style="color:#E06C75;"> countState </span><span style="color:#56B6C2;">=</span><span style="color:#E06C75;"> (CountState) state</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;"> int</span><span style="color:#E06C75;"> count </span><span style="color:#56B6C2;">=</span><span style="color:#E06C75;"> columns[</span><span style="color:#D19A66;">0</span><span style="color:#E06C75;">]</span><span style="color:#ABB2BF;">.</span><span style="color:#61AFEF;">getPositionCount</span><span style="color:#ABB2BF;">();</span></span>
<span class="line"><span style="color:#C678DD;"> for</span><span style="color:#E06C75;"> (</span><span style="color:#C678DD;">int</span><span style="color:#E06C75;"> i </span><span style="color:#56B6C2;">=</span><span style="color:#D19A66;"> 0</span><span style="color:#ABB2BF;">;</span><span style="color:#E06C75;"> i </span><span style="color:#56B6C2;">&lt;</span><span style="color:#E06C75;"> count</span><span style="color:#ABB2BF;">;</span><span style="color:#E06C75;"> i</span><span style="color:#ABB2BF;">++</span><span style="color:#E06C75;">) {</span></span>
<span class="line"><span style="color:#C678DD;"> if</span><span style="color:#E06C75;"> (bitMap </span><span style="color:#56B6C2;">!=</span><span style="color:#D19A66;"> null</span><span style="color:#56B6C2;"> &amp;&amp;</span><span style="color:#56B6C2;"> !</span><span style="color:#E5C07B;">bitMap</span><span style="color:#ABB2BF;">.</span><span style="color:#61AFEF;">isMarked</span><span style="color:#ABB2BF;">(i)</span><span style="color:#E06C75;">) {</span></span>
<span class="line"><span style="color:#C678DD;"> continue</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"><span style="color:#E06C75;"> }</span></span>
<span class="line"><span style="color:#C678DD;"> if</span><span style="color:#E06C75;"> (</span><span style="color:#56B6C2;">!</span><span style="color:#E06C75;">columns[</span><span style="color:#D19A66;">0</span><span style="color:#E06C75;">]</span><span style="color:#ABB2BF;">.</span><span style="color:#61AFEF;">isNull</span><span style="color:#ABB2BF;">(i)</span><span style="color:#E06C75;">) {</span></span>
<span class="line"><span style="color:#E5C07B;"> countState</span><span style="color:#ABB2BF;">.</span><span style="color:#E5C07B;">count</span><span style="color:#ABB2BF;">++;</span></span>
<span class="line"><span style="color:#E06C75;"> }</span></span>
<span class="line"><span style="color:#E06C75;"> }</span></span>
<span class="line"><span style="color:#E06C75;">}</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><ol start="5"><li><strong>void combineState(State state, State rhs)</strong></li></ol><p>This method combines two <code>State</code>s, or more precisely, updates the first <code>State</code> object with the second <code>State</code> object. IoTDB is a distributed database, and the data of the same group may be distributed on different nodes. For performance reasons, IoTDB will first aggregate some of the data on each node into <code>State</code>, and then merge the <code>State</code>s on different nodes that belong to the same group, which is what <code>combineState</code> does.</p><p>Here&#39;s an example of <code>combineState()</code> for averaging (aka avg). Similar to <code>addInput</code>, you need to do an explicit type conversion for the two <code>State</code>s at the beginning. Also note that you are updating the value of the first <code>State</code> with the contents of the second <code>State</code>.</p><div class="language-java line-numbers-mode" data-highlighter="shiki" data-ext="java" style="background-color:#282c34;color:#abb2bf;"><pre class="shiki one-dark-pro vp-code"><code class="language-java"><span class="line"><span style="color:#C678DD;">public</span><span style="color:#C678DD;"> void</span><span style="color:#61AFEF;"> combineState</span><span style="color:#E06C75;">(</span><span style="color:#E5C07B;">State</span><span style="color:#E06C75;"> state</span><span style="color:#ABB2BF;">,</span><span style="color:#E5C07B;"> State</span><span style="color:#E06C75;"> rhs) {</span></span>
<span class="line"><span style="color:#E5C07B;"> AvgState</span><span style="color:#E06C75;"> avgState </span><span style="color:#56B6C2;">=</span><span style="color:#E06C75;"> (AvgState) state</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"><span style="color:#E5C07B;"> AvgState</span><span style="color:#E06C75;"> avgRhs </span><span style="color:#56B6C2;">=</span><span style="color:#E06C75;"> (AvgState) rhs</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E5C07B;"> avgState</span><span style="color:#ABB2BF;">.</span><span style="color:#E5C07B;">count</span><span style="color:#56B6C2;"> +=</span><span style="color:#E5C07B;"> avgRhs</span><span style="color:#ABB2BF;">.</span><span style="color:#E5C07B;">count</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"><span style="color:#E5C07B;"> avgState</span><span style="color:#ABB2BF;">.</span><span style="color:#E5C07B;">sum</span><span style="color:#56B6C2;"> +=</span><span style="color:#E5C07B;"> avgRhs</span><span style="color:#ABB2BF;">.</span><span style="color:#E5C07B;">sum</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"><span style="color:#E06C75;">}</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><ol start="6"><li><strong>void outputFinal(State state, ResultValue resultValue)</strong></li></ol><p>This method works by calculating the final result from <code>State</code>. You need to access the various fields in <code>State</code>, derive the final result, and set the final result into the <code>ResultValue</code> object.IoTDB internally calls this method once at the end for each group. Note that according to the semantics of aggregation, the final result can only be one value.</p><p>Here is another <code>outputFinal</code> example for averaging (aka avg). In addition to the forced type conversion at the beginning, you will also see a specific use of the <code>ResultValue</code> object, where the final result is set by <code>setXXX</code> (where <code>XXX</code> is the type name).</p><div class="language-java line-numbers-mode" data-highlighter="shiki" data-ext="java" style="background-color:#282c34;color:#abb2bf;"><pre class="shiki one-dark-pro vp-code"><code class="language-java"><span class="line"><span style="color:#C678DD;">public</span><span style="color:#C678DD;"> void</span><span style="color:#61AFEF;"> outputFinal</span><span style="color:#E06C75;">(</span><span style="color:#E5C07B;">State</span><span style="color:#E06C75;"> state</span><span style="color:#ABB2BF;">,</span><span style="color:#E5C07B;"> ResultValue</span><span style="color:#E06C75;"> resultValue) {</span></span>
<span class="line"><span style="color:#E5C07B;"> AvgState</span><span style="color:#E06C75;"> avgState </span><span style="color:#56B6C2;">=</span><span style="color:#E06C75;"> (AvgState) state</span><span style="color:#ABB2BF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C678DD;"> if</span><span style="color:#E06C75;"> (</span><span style="color:#E5C07B;">avgState</span><span style="color:#ABB2BF;">.</span><span style="color:#E5C07B;">count</span><span style="color:#56B6C2;"> !=</span><span style="color:#D19A66;"> 0</span><span style="color:#E06C75;">) {</span></span>
<span class="line"><span style="color:#E5C07B;"> resultValue</span><span style="color:#ABB2BF;">.</span><span style="color:#61AFEF;">setDouble</span><span style="color:#ABB2BF;">(</span><span style="color:#E5C07B;">avgState</span><span style="color:#ABB2BF;">.</span><span style="color:#E5C07B;">sum</span><span style="color:#56B6C2;"> /</span><span style="color:#E5C07B;"> avgState</span><span style="color:#ABB2BF;">.</span><span style="color:#E5C07B;">count</span><span style="color:#ABB2BF;">);</span></span>
<span class="line"><span style="color:#E06C75;"> } </span><span style="color:#C678DD;">else</span><span style="color:#E06C75;"> {</span></span>
<span class="line"><span style="color:#E5C07B;"> resultValue</span><span style="color:#ABB2BF;">.</span><span style="color:#61AFEF;">setNull</span><span style="color:#ABB2BF;">();</span></span>
<span class="line"><span style="color:#E06C75;"> }</span></span>
<span class="line"><span style="color:#E06C75;">}</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><ol start="7"><li><strong>void beforeDestroy()</strong></li></ol><p>The method for terminating a UDF.</p><p>This method is called by the framework. For a UDF instance, <code>beforeDestroy</code> will be called after the last record is processed. In the entire life cycle of the instance, <code>beforeDestroy</code> will only be called once.</p><h3 id="_4-4-maven-project-example" tabindex="-1"><a class="header-anchor" href="#_4-4-maven-project-example"><span>4.4 Maven Project Example</span></a></h3><p>If you use Maven, you can build your own UDF project referring to our <strong>udf-example</strong> module. You can find the project <a href="https://github.com/apache/iotdb/tree/master/example/udf" target="_blank" rel="noopener noreferrer">here</a>.</p><h2 id="_5-contribute-universal-built-in-udf-functions-to-iotdb" tabindex="-1"><a class="header-anchor" href="#_5-contribute-universal-built-in-udf-functions-to-iotdb"><span>5. Contribute universal built-in UDF functions to iotdb</span></a></h2><p>This part mainly introduces how external users can contribute their own UDFs to the IoTDB community.</p><h3 id="_5-1-prerequisites" tabindex="-1"><a class="header-anchor" href="#_5-1-prerequisites"><span>5.1 Prerequisites</span></a></h3><ol><li><p>UDFs must be universal.</p><p>The &quot;universal&quot; mentioned here refers to: UDFs can be widely used in some scenarios. In other words, the UDF function must have reuse value and may be directly used by other users in the community.</p><p>If you are not sure whether the UDF you want to contribute is universal, you can send an email to <code>dev@iotdb.apache.org</code> or create an issue to initiate a discussion.</p></li><li><p>The UDF you are going to contribute has been well tested and can run normally in the production environment.</p></li></ol><h3 id="_5-2-what-you-need-to-prepare" tabindex="-1"><a class="header-anchor" href="#_5-2-what-you-need-to-prepare"><span>5.2 What you need to prepare</span></a></h3><ol><li>UDF source code</li><li>Test cases</li><li>Instructions</li></ol><h3 id="_5-3-contribution-content" tabindex="-1"><a class="header-anchor" href="#_5-3-contribution-content"><span>5.3 Contribution Content</span></a></h3><h4 id="_5-3-1-udf-source-code" tabindex="-1"><a class="header-anchor" href="#_5-3-1-udf-source-code"><span>5.3.1 UDF Source Code</span></a></h4><ol><li>Create the UDF main class and related classes in <code>iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin</code> or in its subfolders.</li><li>Register your UDF in <code>iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/BuiltinTimeSeriesGeneratingFunction.java</code>.</li></ol><h4 id="_5-3-2-test-cases" tabindex="-1"><a class="header-anchor" href="#_5-3-2-test-cases"><span>5.3.2 Test Cases</span></a></h4><p>At a minimum, you need to write integration tests for the UDF.</p><p>You can add a test class in <code>integration-test/src/test/java/org/apache/iotdb/db/it/udf</code>.</p><h4 id="_5-3-3-instructions" tabindex="-1"><a class="header-anchor" href="#_5-3-3-instructions"><span>5.3.3 Instructions</span></a></h4><p>The instructions need to include: the name and the function of the UDF, the attribute parameters that must be provided when the UDF is executed, the applicable scenarios, and the usage examples, etc.</p><p>The instructions for use should include both Chinese and English versions. Instructions for use should be added separately in <code>docs/zh/UserGuide/Operation Manual/DML Data Manipulation Language.md</code> and <code>docs/UserGuide/Operation Manual/DML Data Manipulation Language.md</code>.</p><h4 id="_5-3-4-submit-a-pr" tabindex="-1"><a class="header-anchor" href="#_5-3-4-submit-a-pr"><span>5.3.4 Submit a PR</span></a></h4><p>When you have prepared the UDF source code, test cases, and instructions, you are ready to submit a Pull Request (PR) on <a href="https://github.com/apache/iotdb" target="_blank" rel="noopener noreferrer">Github</a>. You can refer to our code contribution guide to submit a PR: <a href="https://iotdb.apache.org/Community/Development-Guide.html" target="_blank" rel="noopener noreferrer">Development Guide</a>.</p><p>After the PR review is approved and merged, your UDF has already contributed to the IoTDB community!</p><h2 id="_6-common-problem" tabindex="-1"><a class="header-anchor" href="#_6-common-problem"><span>6. Common problem</span></a></h2><p>Q1: How to modify the registered UDF?</p><p>A1: Assume that the name of the UDF is <code>example</code> and the full class name is <code>org.apache.iotdb.udf.ExampleUDTF</code>, which is introduced by <code>example.jar</code>.</p><ol><li>Unload the registered function by executing <code>DROP FUNCTION example</code>.</li><li>Delete <code>example.jar</code> under <code>iotdb-server-2.0.x-all-bin/ext/udf</code>.</li><li>Modify the logic in <code>org.apache.iotdb.udf.ExampleUDTF</code> and repackage it. The name of the JAR package can still be <code>example.jar</code>.</li><li>Upload the new JAR package to <code>iotdb-server-2.0.x-all-bin/ext/udf</code>.</li><li>Load the new UDF by executing <code>CREATE FUNCTION example AS &quot;org.apache.iotdb.udf.ExampleUDTF&quot;</code>.</li></ol></div><!----><!----><!----></div><footer class="vp-page-meta"><div class="vp-meta-item edit-link"><a class="auto-link external-link vp-meta-label" href="https://github.com/apache/iotdb-docs/edit/main/src/UserGuide/latest/User-Manual/User-defined-function_apache.md" aria-label="Found Error? Edit this page on GitHub" rel="noopener noreferrer" target="_blank"><!--[--><svg xmlns="http://www.w3.org/2000/svg" class="icon edit-icon" viewBox="0 0 1024 1024" fill="currentColor" aria-label="edit icon" name="edit"><path d="M430.818 653.65a60.46 60.46 0 0 1-50.96-93.281l71.69-114.012 7.773-10.365L816.038 80.138A60.46 60.46 0 0 1 859.225 62a60.46 60.46 0 0 1 43.186 18.138l43.186 43.186a60.46 60.46 0 0 1 0 86.373L588.879 565.55l-8.637 8.637-117.466 68.234a60.46 60.46 0 0 1-31.958 11.229z"></path><path d="M728.802 962H252.891A190.883 190.883 0 0 1 62.008 771.98V296.934a190.883 190.883 0 0 1 190.883-192.61h267.754a60.46 60.46 0 0 1 0 120.92H252.891a69.962 69.962 0 0 0-69.098 69.099V771.98a69.962 69.962 0 0 0 69.098 69.098h475.911A69.962 69.962 0 0 0 797.9 771.98V503.363a60.46 60.46 0 1 1 120.922 0V771.98A190.883 190.883 0 0 1 728.802 962z"></path></svg><!--]-->Found Error? Edit this page on GitHub<!----></a></div><div class="vp-meta-item git-info"><div class="update-time"><span class="vp-meta-label">Last Updated: </span><time class="vp-meta-info" datetime="2025-11-13T10:04:03.000Z" data-allow-mismatch>11/13/25, 10:04 AM</time></div><!----></div></footer><!----><!----><!----><!--]--></main><!--]--><footer class="site-footer"><span id="doc-version" style="display:none;">latest</span><p class="copyright-text">Copyright © 2026 The Apache Software Foundation.
Apache IoTDB, IoTDB, Apache, the Apache feather logo, and the Apache IoTDB project logo are either registered trademarks or trademarks of The Apache Software Foundation in all countries</p><p style="text-align:center;margin-top:10px;color:#909399;font-size:12px;margin:0 30px;"><strong>Having questions?</strong> Connect with us on QQ, WeChat, or Slack. <a href="https://github.com/apache/iotdb/issues/1995">Join the community</a></p></footer></div><!--]--><!--]--><!--[--><!----><!--]--><!--]--></div>
<script type="module" src="/assets/app-pmkzp6TS.js" defer></script>
</body>
</html>