Apache Drill:如何创建新功能?
Apache Drill允許用戶使用ANSI SQL探索任何類型的數據。 這很棒,但是Drill的作用遠遠不止于此,它允許您創建自定義函數來擴展查詢引擎。 這些自定義功能具有任何Drill基本操作的所有性能,但是允許執行這些性能會使編寫這些功能比您期望的要復雜一些。
在本文中,我將使用一個非常基本的示例逐步說明如何創建和部署新功能。 請注意,您可以在文檔中找到許多有關“ 鉆取自定義功能”的信息 。
讓我們創建一個新函數,使您能夠屏蔽字符串中的某些字符,并使它變得非常簡單。 新功能將允許用戶從頭開始隱藏x個字符,然后替換為他們選擇的任何字符。 看起來像:
MASK( 'PASSWORD' , '#' , 4 ) => ####WORD- 您可以在以下Github存儲庫中找到完整的項目。
 
如前所述,我們可以想象很多高級功能,但是我的目標是著重于編寫自定義功能的步驟,而不是功能的作用。
先決條件
為此,您將需要:
- Java Developer Kit 7或更高版本
 - Apache Drill 1.1或更高版本
 - Maven 3.0或更高版本
 
依存關系
以下Drill依賴項應添加到您的Maven項目中
<dependency><groupId>org.apache.drill.exec</groupId><artifactId>drill-java-exec</artifactId><version>1.1.0</version> </dependency>資源
Mask函數是DrillSimpleFunc的實現。
開發人員可以創建兩種類型的自定義函數:
- 簡單函數:這些函數具有一行作為輸入,并產生單個值作為輸出
 - 聚合函數:接受多行作為輸入并產生一個值作為輸出
 
簡單功能通常稱為UDF,代表用戶定義的功能。 聚合功能稱為UDAF,代表用戶定義的聚合功能。
在此示例中,我們只需要轉換每一行上一列的值,因此一個簡單的函數就足夠了。
創建功能
第一步是實現DrillSimpleFunc接口。
package org.apache.drill.contrib.function;import org.apache.drill.exec.expr.DrillSimpleFunc; import org.apache.drill.exec.expr.annotations.FunctionTemplate;@FunctionTemplate(name="mask",scope= FunctionTemplate.FunctionScope.SIMPLE,nulls = FunctionTemplate.NullHandling.NULL_IF_NULL ) public class SimpleMaskFunc implements DrillSimpleFunc{public void setup() {}public void eval() {} }函數的行為由注釋驅動(第6-10行)*函數的名稱 *函數的范圍 ,在我們的情況下為簡單*值為NULL時的操作,在這種情況下,Reverse將僅返回NULL
現在,我們需要使用setup()和eval()方法來實現函數的邏輯。
- setup是不言自明的,在我們這里,我們不需要設置任何東西。
 - eval是該功能的核心。 如您所見,此方法沒有任何參數,并返回void。 那么它是怎樣工作的?
 
實際上,該函數將動態生成(請參閱DrillSimpleFuncHolder ),并且輸入參數和輸出保持器是通過注釋的保持器定義的。 讓我們來看看這個。
import io.netty.buffer.DrillBuf; import org.apache.drill.exec.expr.DrillSimpleFunc; import org.apache.drill.exec.expr.annotations.FunctionTemplate; import org.apache.drill.exec.expr.annotations.Output; import org.apache.drill.exec.expr.annotations.Param; import org.apache.drill.exec.expr.holders.IntHolder; import org.apache.drill.exec.expr.holders.NullableVarCharHolder; import org.apache.drill.exec.expr.holders.VarCharHolder;import javax.inject.Inject;@FunctionTemplate(name = "mask",scope = FunctionTemplate.FunctionScope.SIMPLE,nulls = FunctionTemplate.NullHandling.NULL_IF_NULL ) public class SimpleMaskFunc implements DrillSimpleFunc {@ParamNullableVarCharHolder input;@Param(constant = true)VarCharHolder mask;@Param(constant = true)IntHolder toReplace;@OutputVarCharHolder out;@InjectDrillBuf buffer;public void setup() {}public void eval() {}}我們需要定義函數的參數。 在這種情況下,我們有3個參數,每個參數都使用@Param批注定義。 另外,我們還必須使用@Output注釋定義返回的值。
我們的mask函數的參數為??:
- 可為空的字符串
 - 掩碼字符或字符串
 - 從第一個字符開始替換的字符數
 
該函數返回:
- 一串
 
對于每個參數,您都必須使用一個holder類。 對于String ,這由VarCharHolder或NullableVarCharHolder 21、24,30行管理,該行提供了一種緩沖區,可以有效地管理較大的對象。 由于我們正在處理VarChar您還必須注入另一個緩沖區,該緩沖區將用于輸出行33-。 請注意,Drill實際上并不將Java堆用于查詢中正在處理的數據,而是將這些數據保留在堆外,并為我們管理生命周期,而無需使用Java垃圾收集器。
因為我們有了適當的類(輸入/輸出對象),所以我們差不多完成了,只需要實現eval()方法本身,并使用這些對象即可。
public void eval() {// get the value and replace withString maskValue = org.apache.drill.exec.expr.fn.impl.StringFunctionHelpers.getStringFromVarCharHolder(mask);String stringValue = org.apache.drill.exec.expr.fn.impl.StringFunctionHelpers.toStringFromUTF8(input.start, input.end, input.buffer);int numberOfCharToReplace = Math.min(toReplace.value, stringValue.length());// build the mask substringString maskSubString = com.google.common.base.Strings.repeat(maskValue, numberOfCharToReplace);String outputValue = (new StringBuilder(maskSubString)).append(stringValue.substring(numberOfCharToReplace)).toString();// put the output value in the out bufferout.buffer = buffer;out.start = 0;out.end = outputValue.getBytes().length;buffer.setBytes(0, outputValue.getBytes()); }代碼很簡單:
- 獲取面具本身-第4行
 - 獲取值–第5行
 - 獲取要替換的字符數–第7行
 - 生成帶有掩碼值的新字符串-第10/11行
 - 創建并填充輸出緩沖區–第14至17行
 
但是,對于習慣于閱讀Java代碼的人來說,這段代碼確實有些奇怪。 之所以會出現這種奇怪現象,是因為在查詢中執行的最終代碼實際上會即時生成。 這使Drill可以利用Java的即時(JIT)編譯器來達到最大速度。 要使此工作有效,您必須遵守一些基本規則:
- 不要使用import,而是使用完全限定的類名 ,這是在第10行使用Strings類完成的。 (來自Apache Drill中打包的Google Guava API)
 - 該ValueHolders類,在我們的例子VarCharHolder和IntHolder應該被操縱結構一樣,所以你必須調用輔助方法,例如getStringFromVarCharHolder和toStringFromUTF8 。 調用諸如toString類的toString將導致非常嚴重的問題。
 
現在,我們準備部署和測試此新功能。
包
再一次,由于Drill將生成源代碼,因此您必須以在classpath中存在函數的類和源代碼的方式來準備軟件包 。 這不同于通常打包Java代碼的方式,但是Drill能夠進行必要的代碼生成是必需的。 Drill使用編譯后的代碼訪問注釋,并使用源代碼進行代碼生成。
一種簡單的方法是使用maven構建項目,尤其是在pom.xml文件中使用如下所示的maven-source-plugin :
<plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-source-plugin</artifactId><version>2.4</version><executions><execution><id>attach-sources</id><phase>package</phase><goals><goal>jar-no-fork</goal></goals></execution></executions> </plugin>現在,當您使用mvn package構建時,Maven將生成2個jar:
- 帶有類和資源的默認jar( drill-simple-mask-1.0.jar )
 - 第二個帶有源的jar( drill-simple-mask-1.0-sources.jar )
 
最后,您必須在項目的resources文件夾中添加drill-module.conf文件,以告訴Drill您的jar包含自定義函數。 如果您沒有為功能設置特定的配置,則可以將此文件保留為空。
一切準備就緒,您現在可以打包和部署新功能,只需打包并將Jars復制到Drill 3rd party文件夾中即可; $ DRILL_HOME / jars / 3rdparty,其中$ DRILL_HOME是您的Drill安裝文件夾。
mvn clean packagecp target/*.jar $DRILL_HOME/jars/3rdparty重新開始練習。
跑 !
現在,您應該可以在查詢中使用您的函數了:
SELECT MASK(first_name, '*' , 3) FIRST , MASK(last_name, '#', 7) LAST FROM cp.`employee.json` LIMIT 5; +----------+------------+ | FIRST | LAST | +----------+------------+ | ***ri | ###### | | ***rick | ####### | | ***hael | ###### | | ***a | #######ez | | ***erta | ####### | +----------+------------+結論
在這個簡單的項目中,您學習了如何編寫,部署和使用自定義的Apache Drill Function。 現在,您可以擴展它以創建自己的函數。
擴展Apache Drill(使用自定義功能,存儲插件或格式)時,要記住的一件事是Drill運行時動態生成大量代碼。 這意味著在編寫和部署擴展時可能必須使用非常特定的模式。 使用我們的基本功能,這意味著我們必須:
- 部署類和源
 - 使用完全合格的班級名稱
 - 使用值持有者類和輔助方法來操縱參數*
 
翻譯自: https://www.javacodegeeks.com/2015/07/apache-drill-how-to-create-a-new-function.html
總結
以上是生活随笔為你收集整理的Apache Drill:如何创建新功能?的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: 支付宝余额佳可以直接使用买东西吗?
 - 下一篇: cdi 2.7.5_集成测试CDI 1.