IBM Cloud Docs
注释查询语言参考

注释查询语言参考

注释查询语言 (AQL) 是用于创建 IBM Watson® Knowledge Studio 高级规则抽取器的主要语言。

  • 数据模型:AQL 的数据模型类似于 SQL 数据库 (例如 DB2®) 所使用的标准关系模型。 AQL 中的所有数据都存储在元组,一个或多个列的数据记录或字段中。 元组的集合构成视图。 视图中的所有元组都必须具有相同的模式(即字段在所有元组中的名称和类型)。
  • 执行模型:运行时组件的执行模型是每次执行一个文档。 运行时组件收到文档的集合,然后对每个文档运行抽取器,以从该文档中抽取信息。
  • AQL 语句:使用 AQL 语句可以创建模块、视图、表、字典和函数,并随后加以使用。
  • 内置函数:AQL 具有可在抽取规则中使用的内置函数集合。
  • create function 语句:要对抽取的值执行 AQL 不支持的操作,可以定义定制函数以在抽取规则中使用,这种函数称为用户定义的函数 (UDF)。

注释查询语言 (AQL) 语法

与许多编程语言一样,AQL 也基于通用句法和语法。

编程语言的词法结构是一组基本规则,用于定义该语言的记号或基本组成部分,例如其保留字、标识、常量等。

AQL 的语法类似于 SQL 的语法,但存在以下若干重要差异:

  • AQL 区分大小写。

  • AQL 目前不支持高级 SQL 功能,例如相关子查询和递归查询。

  • AQL 具有新的语句类型 extract,SQL 中不存在这种语句类型。

  • AQL 不允许关键字(保留字)作为视图名称、列名或函数名。

  • AQL 允许正则表达式用 Perl 语法表示,但这并不是必需的。 正则表达式以斜杠 (/) 开头并以斜杠 (/) 结尾,如 Perl 语法中所示。 AQL 还允许使用以单引号 (') 开头且以单引号 (') 结尾的正则表达式。 例如,可以使用 /regex/ 而不是 'regex' 作为 AQL 中的正则表达式。

  • 标识:标识用于定义 AQL 对象的名称,包括模块、视图、表、字典、函数、属性和函数参数的名称。

  • 保留字:保留字是在 AQL 结构的上下文中具有固定含义的词,无法对其重新定义。 关键字是在语言语法中具有特殊含义的保留字。

  • 常量:常量是固定值,可以是下列其中一种数据类型:String、Integer、Float 或 Boolean。

  • 注释:通过注释可使用基本描述来扩充 AQL 代码,以帮助他人理解代码并生成自我描述的已编译模块。

  • 表达式:AQL 表达式是一个或多个标量值以及求值为单个标量值的函数的组合。

标识

标识用于定义 AQL 对象的名称,包括模块、视图、表、字典、函数、属性和函数参数的名称。

存在两种类型的区分大小写的 AQL 标识:

  • 简单标识

    简单标识必须以小写 (a-z) 或大写字母 (A-Z) 或下划线字符 (_) 开头。 后续字符可以是小写或大写字母,下划线字符或数字 (0-9)。 简单标识必须与任何 AQL 关键字不同。

  • 加双引号的标识

    双引号标识以双引号字符 (") 开头和结尾。 可以在开头和结尾双引号字符之间使用任何字符。 加双引号的标识不能包含句点 (.) 字符。 如果名称中出现双引号字符,那么必须通过在其前面添加反斜杠字符 (\) 来对其进行转义,例如 \”

保留字

保留字是在 AQL 结构的上下文中具有固定含义的词,无法对其重新定义。 关键字是在语言语法中具有特殊含义的保留字。

以下由 AQL 保留的关键字不能用作标识,因为这些关键字在语言中各自都有明确定义的用途:

  • all
  • allow
  • allow_empty
  • always
  • and
  • annotate
  • as
  • ascending
  • ascii
  • attribute
  • between
  • blocks
  • both
  • by
  • called
  • case
  • cast
  • ccsid
  • character
  • characters
  • columns
  • consolidate
  • content\_type
  • count
  • create
  • default
  • descending
  • detag
  • detect
  • deterministic
  • dictionary
  • dictionaries
  • document
  • element
  • else
  • empty\_fileset
  • entries
  • exact
  • export
  • external
  • external_name
  • extract
  • fetch
  • file
  • first
  • flags
  • folding
  • from
  • function
  • group
  • having
  • import
  • in
  • include
  • infinity
  • inline_match
  • input
  • into
  • insensitive
  • java
  • language
  • left
  • lemma_match
  • like
  • limit
  • mapping
  • matching_regex
  • minus
  • module
  • name
  • never
  • not
  • null
  • on
  • only
  • order
  • output
  • part_of_speech
  • parts_of_speech
  • parameter
  • pattern
  • point
  • points
  • priority
  • regex
  • regexes
  • retain
  • required
  • return
  • right
  • rows
  • select
  • separation
  • set
  • specific
  • split
  • table
  • tagger
  • then
  • token
  • Token
  • tokens
  • unicode
  • union
  • up
  • using
  • values
  • view
  • views
  • when
  • where
  • with

以下保留字是内置标量类型的名称:

  • Text
  • Span
  • Integer
  • Float
  • String
  • Boolean
  • ScalarList

以下其他保留名称不能用作标识:

  • Dictionary
  • Regex
  • Consolidate
  • Block
  • BlockTok
  • Sentence
  • Tokenize
  • RegexTok
  • PosTag

常量

常量是固定值,可以是下列其中一种数据类型:StringIntegerFloatBoolean

常量在 selectextract 子句的 select 列表中使用,或者在内置或 UDF 函数和谓词中用作自变量。 AQL 支持以下类型的常量:

  • 字符串常量

    用单引号 (‘) 括起的字符串,例如‘a string’。

  • 整数常量

    未用引号括起的 32 位有符号整数值,例如 10 或 -1。

  • 浮点常量

    未用引号括起的单精度 32 位浮点值,例如 3.14 或 -1.2。

  • 布尔值常量

    未用引号括起的值 true 或 false。

  • 空值常量

    未用引号括起的值 null。

注释

通过注释可使用基本描述来扩充 AQL 代码,以帮助他人理解代码并生成自我描述的已编译模块。

注释允许 AQL 开发者使用基本描述来扩充 AQL 代码,以便于理解 AQL 源代码,并生成自我描述的已编译 AQL 模块。 AQL 中支持三种注释:

  • 单行注释

    单行注释以双连字符 (--) 开头。

  • 多行注释

    多行注释以斜杠和星号 (/*) 开头,以星号和斜杠 (*) 结尾。 不能嵌套多行注释。 例如,不允许使用以下嵌套的多行注释:

    /*
    A comment with a /*nested comment*/
    */
    
  • AQL Doc 注释

    通过 AQL Doc 注释,可以使用浅显易懂的语言描述模块或 AQL 对象,并且可描述不同的方面,以便其他用户能够理解上下文。 与会被 AQL 编译器忽略的单行注释和多行注释不同,AQL Doc 注释会在已编译模块(.tam 文件)的元数据中进行序列化,并且可供外部使用。

    AQL Doc 中针对语句和模块的所有注释都具有以下格式:

    • 注释为纯文本(不支持 HTML 标记)。

    • 注释以正斜杠开头,后跟两个星号 (/**) ,并以星号正斜杠 (*/) 结尾。 (可选) 每行可以以星号 (*) 开头。

    • 可以在星号前面使用任意数量的空格。

    • 可以在每行开头或在可选星号后面使用以 @ 符号为前缀的特殊标记。

    • 不能嵌套 AQL Doc 注释。 AQL Doc 系统中支持两个级别的详细程度。 在描述每个工件的语句和语法的主题中详细说明了用于记录该工件的格式。

    • 模块级别注释

      模块级别注释包含在名为 module.info 的特殊文件中,该文件直接位于模块文件夹下。 这些注释应该描述模块的语义以及模块的 Document 视图的模式。

    • 语句级别注释

      语句级别注释包含在源 AQL 文件中,紧接在用于创建 AQL 对象的语句之前。 在一个语句的 AQL Doc 注释与该语句本身之间,允许使用单行注释和多行注释。

      可以使用 AQL Doc 注释来记录以下顶级 AQL 语句:

      • create external view 语句
      • create external table 语句
      • create external dictionary 语句
      • create 函数
      • detag 语句
      • select... into 语句 AQL Doc 注释在模块的已编译表示中进行序列化。

表达式

AQL 表达式是一个或多个标量值以及求值为单个标量值的函数的组合。

表达式可以是以下四种类型之一:

  • 常量
  • 列引用
  • 标量函数调用
  • 聚集函数调用

常量

表达式可以是类型为 IntegerFloatString 的常量,如以下示例所示:

select 'positive' as polarity

该表达式是字符串常量 positive

列引用

表达式可以是列引用,如以下示例所示:

create view Person as
select F.name as firstname, L.name as lastname
from FirstName F, LastName L
where FollowsTok(F.name, L.name, 0, 0);

此视图标识了可以解释为个人全名的文本(例如,“Samuel Davis”和“Vicky Rosenberg”)。 表达式 F.nameL.name 是列引用表达式,分别用于返回视图 FL 的 name 列。 from 语句将视图 FL 定义为视图 FirstNameLastName(用于定义有效的名字和姓氏,在本示例中未显示)的局部名。

标量函数调用

表达式可以由一个或多个标量函数调用组成,每个标量函数调用都可以包含自变量,自变量也可以是类型为常量、列引用或标量函数调用的表达式。 标量函数调用可以是其中一个内置标量函数,也可以是用户定义的标量函数。 假设有以下示例:

create view FamilyMembers as
    select ToLowerCase(GetText(P.lastname)) as lastname, List( (GetText(P.firstname))) as firstnames
    from Person P
    group by GetText(P.lastname);

此视图通过将具有相同姓氏的人员分组在一起并显示所有姓名(其中姓氏使用小写字符)来标识潜在的家庭成员(例如,lastname keaton, firstnames (Elyse, Alex, Diane))。 GetText 函数调用的输出用作 ToLowerCase 函数调用的自变量,用于以小写形式显示姓名。 此示例中的scalar函数调用表达式为:ToLowerCase(GetText(P.lastname)ToLowerCase(GetText(P.firstname))GetText(P.lastname)

聚集函数调用

表达式可以是聚集函数调用。 此表达式类型可以将类型为列引用或类型为标量函数调用的另一个表达式作为自变量。 以下示例显示了聚集函数调用,其中包含类型为列引用的表达式:

create view CountLastNames as
select Count(Person.lastname)
from Person;

此表达式仅仅是 Count(Person.lastname),用于统计文档中的 Person.lastname 注释数。 以上示例中存在的类型为标量函数调用的表达式的聚集函数调用示例为 List(GetText(P.firstname)),其中 List 聚集函数将 GetText 标量函数作为自变量来生成名字的列表。 聚集函数调用表达式仅允许作为 select 语句的 select 列表中的表达式。 select 语句的 extract 列表中不允许使用聚集函数调用表达式,也不允许将聚集函数调用表达式作为标量或聚集函数调用的自变量。

数据模型

AQL 的数据模型类似于 SQL 数据库 (例如 DB2®) 所使用的标准关系模型。 AQL 中的所有数据都存储在元组,一个或多个列的数据记录或字段中。 元组的集合构成视图。 视图中的所有元组都必须具有相同的模式(即字段在所有元组中的名称和类型)。

输入文档的内容表示为名为 Document 的特殊视图。

元组的字段必须属于 AQL 的以下某种内置数据类型:

  • 布尔值

    具有 true 或 false 值的数据类型。

  • Float

    单精度浮点数。

  • integer

    32 位带符号整数。

  • ScalarList

    相同标量类型(Integer、Float、Text 或 Span)的值的集合。 数据类型为 ScalarList 的值可以作为 AQL 内置聚集函数 List() 或 UDF 的结果来获取。

  • Span

    Span 是 Text 对象的相邻区域,通过其在 Text 对象中的开始和结束偏移量来标识。 假定输入文本为:

    Amelia Earhart is a pilot.
    

    Span [0-6] 范围的文本为 Amelia

    可以将此 Span 可视化为:

    0A1m2e3l4i5a6
    

    与此类似,Span [20-25] 范围的文本为 pilot

    Span [x-x] 表示一个字符结尾与下一个字符开头之间的范围。 在上一个示例中, [ 0-0 ] 是字符 A前的空字符串。 同样, [ 3-3 ] 的 Span 是字符 el之间的空字符串。

    类型为 Span 的值可以作为 extract 语句、内置标量函数或 UDF 的结果来获取。

  • Text

    用于表示字符序列的 AQL 数据类型。 Text 对象包含 Unicode 字符串,这称为其字符串值。 如果一个字符串是通过并置另一个 Text 对象的不相连子字符串来构成的,那么该字符串还包含对原始 Text 对象的引用以及相关的偏移量映射信息。 如果这两个 Text 对象的所有组成部分都对应相等,那么将认为这两个对象相等。

  • 比较类型为 Span 和 Text 的值 优先级划分因子会影响类型为 Span 和 Text 的值之间的比较。

比较类型为 Span 和 Text 的值

优先级划分因子会影响类型为 Span 和 Text 的值之间的比较。

类型为 Span 和 Text 的值可通过以下方式进行比较:

  • 值为 null 的 Span 的排序始终低于其他值。
  • Text 值的排序始终高于 Span 值。
  • 类型为 Text 的值首先按其字符串值的词法顺序排序,然后依次按其原始 Text 顺序和偏移量映射信息(如果适用)进行排序。
  • 类型为 Span 的值首先按其底层 Text 对象排序,接着按其开始偏移量排序,然后按其结束偏移量排序。 开始偏移量较小的 Span 的排序较低。 如果两个 Span 的开始偏移量相同,那么结束偏移量较小的 Span 排序较低。

执行模型

运行时组件的执行模型是每次执行一个文档。 运行时组件收到文档的集合,然后对每个文档运行抽取器,以从该文档中抽取信息。

抽取器由一个或多个 AQL 模块组成,用于创建视图集合,其中每个视图定义一个关系。 其中一些视图指定为输出视图,其他视图为非输出视图。 非输出视图可以包含导入到模块或从模块导出的某些视图。 请务必注意,输出视图和导出的视图是正交关系。 例如,导出的视图不适合作为输出视图。 除了这些视图外,独特的 Document 视图还表示此抽取器注释的输入文档。

Document 视图

在 AQL 模块级别,Document 视图是一个特殊视图,用于表示由该模块注释的当前文档。 将两个或多个模块组合在一起构成抽取器时,所有模块的 Document 模式的无重复并集就是所需的 Document 视图。 使用 require document with columns 语句可指定 Document 视图的模式。 如果在模块中不存在此语句,那么假定 Document 视图的缺省模式为 (text Text, label Text):

  • text

    当前文档的文本内容。

  • label

    正在注释的当前文档的标签。

关键字 Document 保留作为 Document 视图的标识,这在执行期间会自动填充。 因此,您无法使用该名称来定义其他视图或表。 但是,可以将 Document 这个词用作属性名称和别名的标识。

AQL 语句

使用 AQL 语句可以创建模块、视图、表、字典和函数,并随后加以使用。

AQL 中支持以下语句:

  • 用于创建模块和声明模块之间交互的语句

    module 语句

    export 语句

    import 语句

  • 用于创建 AQL 对象(视图、字典、表或函数)的语句

    create dictionary 和 create external dictionary 语句

    create table 语句

    create view 语句

    create external view 语句

    detag 语句

    extract 语句

    select 语句

    require document with columns 语句

    set default dictionary language 语句

    create function 语句

AQL 语句中的语法规范包含方括号 ([ ])。 这些规范指示在编写语句时使用相应的语法时,方括号及其保留的构造是可选的。 此外,它们还作为占位符来定义如何指定构造或自变量的其他实例。

module 语句

使用 module 语句可创建具有自包含的必需资源的模块。 您可以将这些资源作为 AQL 对象导出到其他模块,也可以从其他模块将这些资源作为 AQL 对象导入。

语法

module <module-name\>;

描述

  • <module-name\>

    声明当前文件是名为 <module-name\>的模块的一部分。 模块名称必须是简单标识。 不允许使用加双引号的标识作为模块名称。

    模块中的每个 AQL 文件都必须有恰好一个模块声明,并且此声明必须是每个文件中的第一个语句。 此声明用于建立与模块名称同名的名称空间。 此模块内的 AQL 文件中声明的所有视图(以及其他对象,例如字典、表和函数)都位于此单个名称空间中。 这些对象可供此名称空间中的所有 AQL 文件访问。

    声明为模块 <module-name\> 一部分的所有文件都必须位于名为 <module-name\> 的文件夹中,以强制使用此名称空间。 此模块文件夹中的 AQL 文件没有排序。 AQL 编译器会检查模块的所有 AQL 文件,并确定模块中声明的所有视图、字典、表和函数的正确编译顺序。

用法说明

  • 如果底层 AQL 文件不是模块(模块化 AQL)的一部分,并且是以兼容性方式进行编译的,那么不支持此语句。
  • 不允许视图之间存在循环依赖性。
  • AQL 模块不支持子模块。
  • 将忽略顶级模块文件夹的子文件夹中的 AQL 文件。

示例

示例 1:样本模块

在以下示例中,视图 TheMentions 属于名为 sample 的模块。

module sample;

create dictionary GetTheDict as ('The', 'the');

create view TheMentions as
  extract dictionary 'GetTheDict' on D.text as theMention
  from Document D;

export 语句

AQL 中的 export 语句用于从当前模块导出 AQL 对象,以便可以在其他模块中导入和使用该对象。

语法

export view|dictionary|table|function <object-name\>;

描述

  • view|dictionary|table|function

    定义要导出的对象的类型。 对象类型可以是视图、字典、表或函数。

  • <object-name\>

    定义要导出的对象的名称。 <object-name\> 可以是简单标识或双引号标识。

用法说明

  • 无法导出任何导入的 AQL 对象。 通过创建新视图,可以产生重新导出当前模块中的视图或表的效果:

    select * from <imported_view_name_or_table_name>;
    
  • 示例中显示的 export viewoutput view 语句相互之间是正交关系。 换言之,输出视图并不会自动成为导出的视图,而必须使用 export 语句显式导出。 导出的视图并不会自动成为输出视图,而必须使用 output view 语句显式输出。 在示例中,export 语句尝试导出视图 PersonName.FirstName,这是导入的视图。 此尝试会导致错误,这意味着开发者必须将导入的视图复制到新视图中,然后改为导出该视图。

示例

示例 1:创建视图和字典,然后将其导出以在其他模块中使用

此示例创建视图 FirstName 和 NotFirstName。 视图 FirstName 收集有关 FirstNamesDictionary 字典中表示的名字的信息。 另一个视图收集排除名字后剩余的名称。 需要两个字典,以便更轻松地抽取文本。 一个字典 FirstNamesDictionary 包含要搜索的所有名字。 另一个字典 LastNamesDictionary 包含要搜索的姓氏。 FirstNamesDictionary 字典已导出,以便可以在其他模块中使用该字典。 还导出了 FirstName 和 NotFirstName 视图,以便可以在其他模块(例如,示例 2 中的模块 person)中导入和使用这两个视图。

module personName;

create dictionary FirstNamesDictionary as
('Smith', 'John', 'Mary', 'Sam', 'George');

-- export dictionary statement

export dictionary FirstNamesDictionary;


create view FirstName as
  extract dictionary 'FirstNamesDictionary'
  on D.text as firstName
from Document D;

-- export view statement

export view FirstName;

create dictionary LastNamesDictionary as
('Stewart', 'Johnson', 'Smith', 'Hopkins', 'George');

create view NotFirstName as
  select F.firstName as notFirstName from FirstName F
  where ContainsDict('LastNamesDictionary', F.firstName);

-- export view statement

export view NotFirstName;

示例 2:导入要使用的视图,但因导出不正确,导致了错误

此示例显示了导入两个视图。 这些视图是从示例 1 中的模块 personName 导出的。 现在,模块人员可以导入和引用这些视图。 但是,此模块尝试导出其所导入的相同视图 FirstName,因此导致了错误。

module person;

-- Form 1

import view FirstName from module personName as PersonFirstName;

-- Form 2

import view NotFirstName from module personName;


-- ERROR
-- Reason: A view that is imported from one module
-- cannot be exported in the current module.
export view personName.FirstName;


output view PersonFirstName;

导致此代码中出现该错误的原因是,从一个模块导入的视图无法在当前模块中导出。 此外,除非使用 output view 语句定义输出视图,否则导出的视图并不会自动成为输出视图。

import 语句

可以使用 import 语句在当前模块的上下文中引用从其他模块导出的对象。

语法

import view|dictionary|table|function <object-name\>
     from module <module-name\> [as <alias\>];

描述

  • view\|dictionary\|table\|function

    标识要导入的 AQL 对象的类型。 对象的类型是必需的。

  • <object-name\>

    <object-name\> 可以是简单标识或双引号标识。

  • <module-name\>

    <module-name\> 必须是简单标识。

  • <alias\>

    此格式的 import 语句 (也称为 alias import) 将 <alias\> 名称下的指定 AQL 对象 (而不是原始名称) 导入到当前模块的名称空间中。 可以使用未限定别名或使用通过当前模块名称(即,导入 AQL 对象的模块)限定的别名来引用导入的元素。 不能使用 originalModule.elementName,因为 alias import 语句仅使用别名导入元素,而不会使用限定原始名称导入元素。

用法说明

  • 不使用别名规范的 import 语句会将指定的 AQL 对象导入到当前模块。 它使 AQL 对象可由当前模块中使用其限定名 <original\_module\_name\>.<object-name\>定义的 AQL 语句访问。

  • 此 import 语句仅用于从当前模块以外的模块导入 AQL 对象。 在该模块的 AQL 文件中声明的对象对于该同一模块中的其他任何 AQL 文件都可见。 import 语句是将对象放置在当前模块的上下文中,而不是当前文件的上下文中。 因此,模块 A 中 1.aql 导入的视图对于该同一模块中的 2.aql 中可见,而无需任何其他 import 语句。

  • 所有 import 语句都必须紧跟在模块声明之后,但必须位于其他所有类型的语句之前。 只有从任何模块显式导出的 AQL 对象才能在其他模块中导入。 如果不遵循此需求,会生成编译错误。

  • import view 语句与任何 create view 语句或同一模块中(不仅仅是当前文件中)的其他 import 语句之间发生命名冲突时,会产生编译错误。 除了视图外,此限制还适用于对其他对象执行的 import 语句。

  • AQL 编译器遵循先前版本的 AQL 中使用的命名约定:

    • 模块不能包含同名视图和表。
    • 允许使用与表或视图同名的字典。
    • 允许使用与表、视图或字典同名的函数。
  • 模块中一个或多个 AQL 文件中的不同 import 语句对不同 AQL 对象提供相同名称时,会引入编译错误。

  • 模块中的 AQL 文件尝试不使用 import view 语句来引用模块的其他导出视图时,会引入编译错误。 这同样适用于字典、表或函数。

  • 模块中的两个 AQL 文件使用两个不同的别名(例如,A 和 B)从另一个模块导入同一视图 X 时,这两个别名会视为同义。 此规则还适用于表、字典和函数。

示例

示例 1:创建视图,然后将其导出,并导入到其他模块中。

此示例创建两个视图:FirstNameNotFirstName。 视图 FirstName 收集有关 FirstNamesDictionary 字典中表示的名字的信息。 另一个视图收集排除名字后剩余的名称。 需要两个字典,以便更轻松地抽取文本。 一个字典 FirstNamesDictionary 包含要搜索的所有名字。 另一个字典 LastNamesDictionary 包含要搜索的姓氏。 导出了 FirstNameNotFirstName 视图,以便可以在其他模块(例如,示例 2 和 3 中的模块 person)中导入和使用这两个视图。

module personName;

create dictionary FirstNamesDictionary as
('Smith', 'John', 'Mary', 'Sam', 'George');

create view FirstName as
extract dictionary 'FirstNamesDictionary'
on D.text as firstName
from Document D;

export view FirstName;

create dictionary LastNamesDictionary as
('Stewart', 'Johnson', 'Smith', 'Hopkins', 'George');

create view NotFirstName as
select F.firstName as notFirstName from FirstName F
where ContainsDict('LastNamesDictionary', F.firstName);

export view NotFirstName;

示例 2:使用 alias import 导入视图 FirstName

此示例导入在示例 1 中创建和导出的其中一个视图。 然后,将输出 PersonFirstName 视图,以便可以查看其结果。

样本 import 语句称为 alias import。 此语句将视图 FirstName(不带模块限定符)导入到当前模块 person 的名称空间。 导入的视图只能通过别名 PersonFirstName 来访问,而不能通过其他任何表单进行访问。 例如,无法将导入的视图作为 personName.FirstName 引用,因为此视图仅通过别名导入。

module person;

`import view FirstName from module personName as PersonFirstName;`

output view PersonFirstName;

示例 3:不使用 alias import 导入视图 NotFirstname

此样本 import 语句将限定名 personName.NotFirstName(而不是该视图的未限定名称)导入到当前模块 person 的名称空间。 请始终使用限定名来引用导入的视图。 其他任何引用方式都会被标记为编译器错误。

module person;

`import view NotFirstName from module personName;`


output view personName.NotFirstName;

import module 语句

可以使用 import module 语句来导入和复用现有 AQL 模块。

语法

 import module <module-name\>;

描述

  • <module-name\>

    指定要导入的模块。 <module-name\> 必须是简单标识。

用法说明

  • 此 import 语句仅用于从当前模块以外的其他模块导入 AQL 对象。

  • 所有 import 语句都必须紧跟在模块声明之后,但必须位于其他所有类型的语句之前。 只有从任何模块显式导出的 AQL 对象才能在其他模块中导入。 如果不遵循此需求,会生成编译错误。

  • import view 语句与任何 create view 或同一模块中(不仅仅是当前文件中)的其他 import 语句之间发生命名冲突时,会产生编译错误。 除了视图外,此限制还适用于对其他 AQL 对象执行的 import 语句。

  • 模块中的 AQL 文件尝试不使用 import view 语句来引用模块的其他导出视图时,会引入编译错误。 这同样适用于字典、表或函数。

  • 如果模块中的两个 AQL 文件使用两个不同的别名(例如,A 和 B)从另一个模块导入同一视图 X,这两个别名会视为同义。 此规则还适用于表、字典和函数。

示例

在此示例中,import 语句会导入已导出的视图 personName.FirstNamepersonName.NotFirstName 的限定名。 未由模块 personName 导出的任何视图都不会在执行 import 语句的过程中导入。

  • 示例 1:同时导入 FirstName 和 NotFirstName 视图

    此示例显示了从模块 personName 导出的所有视图。 在 export 语句的示例部分中创建了视图 FirstNameNotFirstName

    module personOther;
    
    -- The following statement would import both views FirstName and NotFirstName.
    import module personName;
    

set default dictionary language 语句

set default dictionary language 语句允许抽取器开发者定制包含模块的缺省字典匹配语言集。

语法

 set default dictionary language as '<language codes\>';

描述

  • <language codes\>

    对于在未显式指定 with language as 的情况下声明的模块,指定用于编译和匹配其字典的语言。 <language codes\> 集必须是以逗号分隔的列表,每个语言代码周围都没有空格。 不遵循此需求可能会导致编译错误。

    此语句会影响以下字典:

    • 在当前模块中使用 create dictionarycreate external dictionary 语句(该语句不包含 with language as 子句)显式声明的字典。
    • 来自外部文件的字典。
    • extract pattern 语句的模式规范中,类型为 'string' 的原子和类型为 <'string' [match parameters]> 的原子没有显式 with language as 规范。 当模块中没有此语句时,运行时组件缺省为德语,西班牙语,英语,法语,意大利语和未指定语言 x 的语言集。 它定义为集合: [de,es,en,fr,it,x_unspecified]。 在一个模块中,此语句只能存在一个实例。

用法说明

  • 可以更新 set default dictionary language 语句,以扩大抽取器涵盖的语言的范围。 这种添加语言的能力有助于简化定制以及现有抽取器的复用。

示例

示例 1:指定要用于匹配字典条目的语言

module Dictionaries;

-- Set the default dictionary matching language
--  for this module to English and French

set default dictionary language as 'en,fr';


/**
* Dictionary of English and French names. Because no language clause
* exists in dictionary definition, module level dictionary matching
* setting will be applied.
*/

create dictionary EnglishFrenchNames
from file 'en_fr_names.dict';

/**
* Dictionary of Italian names. Language clause in the dictionary
* definition will override the module level dictionary matching setting.
*/

create dictionary ItalianNames
with language as 'it'
as
(
'firstEntry','secondEntry'
);


/**
* View to extract pattern: Phone, followed by one to three tokens,
* followed by Email. Language clause at the atom level will override
* the module level dictionary setting.
*/

create view PhoneEmailPattern as
extract pattern <'Phone'[ with language as 'en']>
  <Token> {1,3} 'Email'
as match
from Document D;

output view PhoneEmailPattern;

require document with columns 语句

通过使用 require document with columns 语句,可以在编译时定义特殊视图 Document 的模式。 此模式定义指定了需要位于 Document 视图内每个元组中的必需字段及其类型的列表。

语法

require document with columns  <columnName\> <columnType\>
     [and <columnName\> <columnType\>]*;

描述

  • <columnName\>

    指定要在模式中使用的列的名称。 <columnName\> 是属性标识,它是简单标识或双引号标识。

  • <columnType\>

    指定要在 Document 视图的模式中使用的列的类型。 <columnType\> 可以是下列其中一种数据类型: 整数,浮点数,布尔值或文本。

  • [ and <columnName\> <columnType\> ]*

    Document 视图的模式指定其他列名和类型。 它们遵循与 <columnName\><columnType\>相同的规则。

在较早版本的 AQL 中,特殊视图 Document 的模式预定义为包含单个字段 (text Text) 或两个字段 (text text, label Text)。 选择使用其中哪个模式在运行时决定。 通过使用 require document with columns 语句,可以在编译时覆盖缺省输入文档模式。

用法说明

  • 对于模块化 AQL 代码,任何 require document with columns 语句的作用域都是定义了该语句的模块。
  • 每个 AQL 文件只允许使用一个 require document with columns 语句。 在单个模块或通用模块中,可以有零个、一个或多个具有 require document with columns 语句的 AQL 文件。 模块中的所有 AQL 文件会在整个模块级别合并其 require document with columns 语句,以构成模块范围的 require document with columns。 此语句定义该模块的 Document 视图的模式。 如果模块或通用模块的 AQL 文件都不包含 require 语句,那么该模块会将缺省模式用于视图 Document。 此模式由两列组成:(text Text, label Text)。 如果模块中至少有一个 AQL 文件具有一个 Document 语句,那么不会为特殊视图 require document with columns 建立缺省列。
  • 将多个模块组合在一起构成抽取器时,整个抽取器的 Document 视图的模式由每个模块的 Document 模式的无重复并集进行定义。 如果在多个 require document with columns 语句中找到的任何列的类型需求在各个模块之间存在冲突,那么会引发异常。 例如,一个模块需要类型为 Y 的列 X,而正在装入的另一个模块需要类型为 Z 的列 X。
  • 如果提供的输入文档元组不包含所有必需的列,那么在运行抽取器时会引发异常。 此外,如果列不符合其相应的必需类型,也会引发异常。
  • 模块中存在 require document with columns 语句时,所引用的特殊视图 Document 的每一列必须至少在一个 require document with columns 语句中进行声明。 该语句可以在同一模块内的不同 AQL 文件中找到。 但是,所有此类 require document with columns 语句都将在模块级别进行合并,以构成模块范围的 require document with columns 语句。

示例

示例 1:具有类似列类型的 require document 语句

以下示例定义的文档模式包含同一类型的四个字段。 此 AQL 样本期望数据集合的每个文档元组包含文档模式中定义的四列。

请参阅 JSON 文档格式,以获取有关如何创建符合模式的文档的详细信息。

module person;

-- Require document statement with similar field types

require document with columns
  inputDocumentText Text
  and inputDocumentLabel Text
  and documentAuthor Text
  and documentCreationDate Text;

示例 2:具有不同列类型的 require document 语句

以下样本定义的文档模式包含字段类型不同的列。

module sample;

-- Require document statement with varying field types

require document with columns
  bookText Text
  and purchaseCount Integer
  and bookPrice Float
  and isBookPopular Boolean;

示例 3:合并文档模式

此示例描述如何合并使用 first.aql、last.aql 和 socialsecurity.aql 文件的文档模式。

first.aql:
    module person;
    require document with columns firstName Text;


last.aql:
    module person;
    require document with columns lastName Text;


socialsecurity.aql
    module person;
    require document with columns lastName Text
        and socialSecurityNo Integer;

合并后的模式为 (firstName Text, lastName Text, socialSecurityNo Integer)。

create view 语句

AQL 抽取器的顶级组件是其视图。 视图是用于定义(但不一定计算)一组元组的逻辑语句。

语法

create view 语句可以采用三种形式之一。 最简单的形式是定义由单个 selectextract 语句的元组组成的逻辑视图。 第二种是多连接形式,定义包含通过多个 selectextract 语句的多集并集引发的元组的视图。 第三种形式是定义新视图,其中包含两个 selectextract 语句的元组之间的差集。

create view <viewname\> as  <select or extract statement\>;
create view <viewname\> as  (<select or extract statement\>) union all (<select or extract statement\>)...;
create view <viewname\> as  (<select or extract statement\>) minus (<select or extract statement\>);

描述

  • <viewname\>

    <viewname\> 可以是简单标识或双引号标识。 不能包含句点字符。

  • <select or extract statement\>

    selectextract 语句创建用于计算包含视图的元组的输出。

用法说明

  • 视图名称区分大小写。 例如,Person、PERSON 和 person 是不同的视图名称。
  • 同一 AQL 模块中的两个视图不能共享名称,这会使视图重复。 但是,同名的两个视图可以存在于两个不同的模块中,因为其标准名称是唯一的。
  • 缺省情况下,create view 语句定义的视图是非输出视图,直到将其指定为输出视图为止。
  • selectextract 形式的 union allminus 语句必须具有兼容的输出模式。 如果两个模式的列数相同,列名的顺序相同,并且具有兼容的数据类型,那么对于 unionminus 操作,会将这两个模式视为兼容:
    • 同一数据类型的字段兼容 unionminus
    • Span 和 Text 数据类型兼容 unionminus。 对于 Span 与 Text 类型之间的并集,输出类型为 Span。 但是,Text 类型的对象不会自动转换为 Span 类型 - 仅当函数调用需要时才会执行自动转换。
    • 不管底层的标量类型如何,两个 ScalarLists 都兼容 unionminus

示例

示例 1:使用 select 或 extract 语句创建视图

在以下示例中,视图 Phone 使用 extract 语句来准备其元组。 视图 PhoneNumber 使用 select 语句从视图 Phone 中选取特定字段。

create view Phone as extract
  regexes /\+?([1-9]\d{2}\)\d{3}-\d{4}/
  and /\+?[Xx]\.?\d{4,5}/
  on D.text as num
from Document D;

create view PhoneNumber as
select P.num as num, LeftContextTok(P.num, 3) as lc
from Phone P;

示例 2:使用 Union All 语句创建视图

视图 AllPhoneNums 通过 PhoneExtension 视图的元组准备并集化集。 并集化的两个视图具有相同的模式。

create view Phone as
extract
    regex /\+?([1-9]\d{2}\)\d{3}-\d{4}/
    on D.text as match
from Document D;

create view Extension as
  extract
    regex /\+?[Xx]\.?\d{4,5}/
    on D.text as match
from Document D;

create view AllPhoneNums as
  (select P.match from Phone P)
union all
  (select E.match from Extension E);

示例 3:使用 Minus 语句创建视图

以下示例显示了如何使用 minus 从一组元组中过滤掉不需要的元组。

create view Organization as
  (select * from OrganizationCandidates)
minus
  (select * from InvalidOrganizations);

示例 4:minus 的模式兼容性

请务必注意,基于不同目标文本的范围的类型不同。 请参考以下 AQL 示例,其中使用 String 字面值说明了此差异。

create view OneString as
  select 'a string' as match
  from Document D;

create view TheSameString as
  select 'a string' as match
  from Document D;

create view Subtraction as
  (select R.match from OneString R)
minus
  (select R.match from TheSameString R);

输出是将 'a string' 作为字段值的一组记录,而不是预期的空元组列表输出。

虽然 OneStringTheSameString 视图的内容看起来相同,但实际文本值具有不同的底层 AQL 对象。 OneString.match 的类型为“Span over OneString.match”。 TheSameString.match 的类型为“Span over TheSameString.match”。 由于字段类型不同,因此这两个视图不兼容,无法用于比较。

要获取所需的空元组列表输出,必须比较相同类型的值。 在以下示例中,GetString() 函数将 Span 对象转换为 String 对象,以将兼容类型传递给 minus 操作。

create view Subtraction as
(select GetString(R.match) from OneString R)
minus
(select GetString(R.match) from TheSameString R);

记录具有 AQL Doc 的 create view 语句

create view 语句的 AQL Doc 注释包含以下信息:

  • 有关视图的常规描述。
  • @field,表示视图中的每个列名。

示例

/**
* Extracts all spans that match a phone number pattern from
* the input documents. It uses a regular expression to match
* phone number patterns.
* @field num phone number
* @field lc 3 tokens to the left of phone number*/

create view PhoneNumber as
select P.num as num, LeftContextTok(P.num, 3) as lc
from
(
extract
    regexes /\+?([1-9]\d{2}\)\d{3}-\d{4}/ and /\+?[Xx]\.?\d{4,5}/
    on D.text as num
from Document D
) P;

output view 语句

output view 语句用于将视图定义为输出视图。 运行时组件仅输出标记为输出视图的视图元组。 AQL 编译器仅编译标记为输出或导出的视图,或者可从标记为输出或导出的视图进行访问的视图。

语法

output view <view-name\> [as '<alias\>'];

描述

  • <view-name\>

    要输出的视图的名称,该名称在当前模块的名称空间中是已知的。 <view-name\> 是简单标识或双引号标识。 不能输出内置视图 Document

  • [as '<alias\>']*

    定义输出视图的 <alias\> 名称。 未指定可选别名时,视图使用以下名称进行输出:

    • 在模块化 AQL 中,视图以名称 <module-name\>.<view-name\> 输出,其中 <module-name\> 是最初定义视图的模块的名称 (这可能与输出视图的模块不同)。
    • 在 AQL 1.4 或更低版本中,视图以名称 <view-name\> 输出 当您为不同域定制抽取器时, output ... as <alias\> 语句很有用。 在定义输出视图时使用 <alias\> 名称可确保输出名称在定制的不同实现之间完全相同。

    不能在另一个 selectexport 语句中使用 <alias\> 名称。 必须使用单引号将 <alias\> 括起来,并且 <alias\> 名称可以包含句点。

用法说明

  • AQL 抽取器运行时,会为定义为输出视图的每个视图计算合成元组。 此外,还会计算任何非输出视图的元组,但仅当计算输出视图的合成元组需要这些元组时才会执行此计算。
  • 在模块化 AQL 中,output view 语句会使用标准视图名称(通过其模块名称限定)输出视图的元组。 请参考以下示例,其中 output view Person; 语句会生成 personModule.Person 视图的输出:
module personModule;

create view Person as
  extract dictionary 'FamousPeopleDict' on D.text as match
  from Document D;

output view Person;

此行为适用于没有别名的任何视图输出,而不管视图是在定义了该视图的模块中进行输出,还是在导入了该视图的模块中进行输出,甚至是在使用 alias import 导入该视图时进行输出。 例如,在输出视图 MyPerson 中,此示例会导致视图使用其原始限定名 personModule.Person 进行输出,而不是使用其本地别名 MyPerson 进行输出。

module employeeModule;

import view Person from module personModule as MyPerson;

output view MyPerson;
  • 构建抽取器的库时,如果其中相同类型的实体可以有许多不同的实现(具体取决于输入文档的应用领域或语言),那么 output alias 语句非常有用。 定义输出视图时使用别名的主要优点是可在各个输出视图之间确保一致的命名法。 如果有多个模块,每个模块会输出语义类似的视图,那么在处理这些模块时,用户的程序逻辑需要一致的命名法。 在模块化 AQL 中,output view 语句中使用别名时,视图的元组会使用指定的别名进行输出。 例如,以下代码将使用别名 PersonAlias 输出结果,并且该别名未使用模块前缀进行限定。
module personModule;

create view Person as
  extract dictionary 'FamousPeopleDict' on D.text as match
  from Document D;

output view Person as 'PersonAlias';

示例

以下示例包含两个模块:personModuleFrench 和 personModuleEnglish。 每个模块输出一个视图,分别名为 PersonNameFrench 和 PersonNameEnglish。 假设有类似的模块,每个模块输出的视图是人名抽取器的语义变体。 通过此视图针对指定输入语言的定制中的差异,针对不同语言定制了这些模块。 最终,无论处理哪个模块,用户可能都希望在程序使用的模块中,所寻求的输出视图名为 PersonName。 这是正常的期望,因为针对语言、领域或其他用途定制的每个模块都应该生成不同的结果。 底层语义类似时,这些模块的使用者就无需改变其程序的算法来适应不同的输出视图名称。

在此示例中,由于使用了别名 PersonName,因此使用者无需改变所寻求的视图名称。 但是,结果可能因处理的模块而异。 例如,在示例中,生成的匹配项基于法语(示例 1)和基于英语(示例 2)。

示例 1:生成的匹配项基于法语

以下示例定义了视图 PersonNameFrench,并使用与实现无关的别名 'PersonName' 来输出该视图。

module personModuleFrench;

create dictionary FrenchNames as
  ('Jean', 'Pierre', 'Voltaire', 'Francois', 'Marie', 'Juliette');

create view PersonNameFrench as
  extract dictionary 'FrenchNames'
  on D.text as name
  from Document D;

output view PersonNameFrench as 'PersonName';

示例 2:生成的匹配项基于英语

以下示例定义了视图 PersonNameEnglish,并使用与实现无关的别名 'PersonName' 来输出该视图。

module personModuleEnglish;

create dictionary EnglishNames as
  ('John', 'Peter', 'Andrew', 'Francis', 'Mary', 'Juliet');

create view PersonNameEnglish as
  extract dictionary 'EnglishNames'
  on D.text as name
  from Document D;

output view PersonNameEnglish as 'PersonName';

示例模块的使用者可以通过别名 'PersonName' 来访问输出元组。 使用者无需知道从中访存结果的实际模块。

extract 语句

extract 语句用于直接从文本中抽取基本特征。

语法

extract <select list>,
  <extraction specification>
from <from list>
[having <having clause>]
[consolidate on <column> [using '<policy>']]
[limit <maximum number of output tuples for each document>];

描述

  • <select list\>

    输出表达式的逗号分隔列表。 这些输出表达式的结果将作为 extract 语句的输出返回,同时还会返回对抽取规范求值而生成的元组。 <select list\> 的格式与 select 语句的 <select list\> 相同。

  • <extraction specification\>

    将抽取规范应用于 <from list\>中定义的视图中的所有元组。 这将根据在 extract 语句中为元组列指定的相应别名来重命名这些元组列。 可以使用下列其中一个抽取规范:

    • 正则表达式
    • 字典
    • 拆分
    • 词性
    • 序列模式
  • <from list\>

    逗号分隔列表,用作要从中选择特性的元组的源。 <from list\> 的格式类似于 select 语句的 <from list\> 的格式。 但是,如果 extract 语句没有模式规范,那么 <from list\> 可以包含单个项。

  • [having <having clause\>]

    指定应用于每个抽取的输出元组的过滤谓词 (在 <having clause\>中)。 <having clause\> 中指定的字段名称是指 <select list\>中指定的任何别名。 此子句是可选的。

  • [consolidate on <column\>[using '<policy\>' ]]

    定义如何处理合并 <policy\>中定义的重叠范围。 在此规范中, <column\> 必须是作为 extract 语句一部分的输出字段的名称。 此子句是可选的。

  • [limit<maximum number of output tuples for each document\>]

    将每个文档中的输出元组数限制为指定的最大值。 此子句是可选的。

用法说明

extract 语句的语义如下所示:

  • 对应用于输入关系的每个元组的抽取规范求值。 对于抽取生成的每个结果,将生成一个输出元组,其中包含抽取的值以及 <select list\>中指定的原始元组的任何列。
  • 根据在 <select list\><extraction specification\>中指定的别名重命名输出元组的列。
  • 将可选的 having 子句中的任何谓词应用于生成的输出元组。
  • 根据可选的 consolidation 子句合并传递谓词的元组,并将生成的元组添加到输出。
  • 如果存在可选的 limit 子句,那么会将输出限制为只显示每个文档的指定元组数。

from 语句的 extract pattern 子句的语义不同于没有模式规范的其他形式的 extract 语句。 如果 <from list\> 中的至少一个视图不包含特定文档上的任何元组,那么 extract 语句的输出为空。 此输出为空的原因是输入视图中元组的所有组合集为空。

extract pattern 语句的特殊用例中,from 子句可作为占位符,用于声明模式规范中涉及的关系的名称。 语句的语义仅受模式规范的影响。 尤其是,即使某些输入视图为空,该语句的输出也可以为非空。

示例

示例 1:从预定义视图中抽取电话号码

此样本 extract 语句对预定义视图 Document 表示的输入文本中的美国电话号码的正则表达式求值。 然后,将输出限制为只显示每个文档中识别到的前三个电话号码。 having 子句中的字段名称引用 extract 语句开头处的别名。

create view PhoneNumbers as
  extract
    D.text as documentText,
    regex /\d{3}-\d{3}-\d{4}/ on D.text as phoneNumber
from Document D
having MatchesRegex(/\d{3}.*/, phoneNumber)
limit 3;

示例 2:抽取首字母大写单词的块

在此示例中,extract 语句识别到由两个到三个首字母大写单词构成的块。 在 AQL 中,块是指相邻范围的记号,在本例中是两到三个记号。 此示例还使用合并策略从元组的输出集内排除包含在较大块中的块。

create view CapitalizedWord as
  extract
    regex /[A-Z][a-z]*/
            with flags 'CANON_EQ'
        on 1 token in D.text
        as word
from Document D;

create view TwoToThreeCapitalizedWords as
  extract blocks
  with count between 2 and 3
  and separation 0 tokens
  on CW.word as capswords
from CapitalizedWord CW
consolidate on capswords using 'ContainedWithin';

consolidate 子句通过抽取块规范应用于 capswords 字段。 不同之处在于,consolidation 子句引用的目标字段是 extract 语句的输出字段。 select 语句的目标字段是输入字段。 此行为类似于 having 子句的行为。

示例 3:嵌套的 extractselect 语句作为视图名称

extract 语句的输入视图可以是视图名称(如示例 2 所示),也可以是嵌套的 extractselect 语句,如以下示例所示:

create view SampleExtract as
  extract
  regex /foo/ on E.foobar as foo
  from
    (extract regex /foobar/ on D.text as foobar
  from Document D) E;

示例 4:使用 select 列表抽取语句

在此示例中,将抽取模式的匹配项,同时从输入视图中选择多个属性。

create view Person as
  extract.F.first as first, M.initial as middle, L.last as last
          pattern ('Mr.'|'Ms.'|'Miss')? (<F.first> <M.initial>? <L.last>)
                    return group 0 as reference
                    and group 1 as salutation
                    and group 2 as name
  from FirstName F, MiddleInitial M, LastName L;
  • 正则表达式 使用正则表达式抽取规范可识别在整个输入文本中正则表达式包含的匹配模式。
  • 字典 使用字典抽取规范可从输入文本中抽取字符串字典中包含的字符串。
  • 拆分 使用拆分抽取规范可将一个大范围拆分为若干较小的范围。
  • 使用块抽取规范可识别整个输入文本中相邻范围的块。
  • 词性 使用词性抽取规范可识别不同词性在整个输入文本中的位置。
  • 序列模式 使用模式抽取规范可对整个输入文档以及从输入文档中抽取的其他范围执行模式匹配。

正则表达式

使用正则表达式抽取规范可识别在整个输入文本中正则表达式包含的匹配模式。

语法

regex[es] /<regex1>/
         [and /<regex2>/ and ... and /<regex n>/]
       [with flags '<flags string>']
on <token spec>] <name>.<column>
<grouping spec>

描述

  • regex[es] /<regex1\>/

    指定要在抽取中使用的正则表达式。 缺省情况下,AQL 对正则表达式使用 Perl 语法,这意味着正则表达式字面值会用两个正斜杠 (//) 字符括起。 正则表达式转义序列优先于其他转义字符。 AQL 允许使用采用 SQL 字符串语法的正则表达式,因此可以将美国电话号码的正则表达式表示为以下任一示例:

    /\d{3}-\d{5}/
    
    '\\d{3}-\\d{5}'
    
  • [and /<regex2\>/ and ... and /<regex n\>/ ]

    列出要在抽取中使用的更多正则表达式。

  • [with flags '<flags string\>']

    指定用于控制正则表达式匹配的标志组合。 此参数是可选的。 这些标志对应于 Java™ 实现中定义的标志子集。 如果未提供标志字符串,那么缺省情况下 AQL 仅使用 DOTALL 标志。

    要指定多个标志,请使用 | 字符分隔各标志。 例如,要指定多行匹配、不区分大小写的匹配以及 Unicode 大小写折叠,请使用标志字符串 'MULTILINE|CASE_INSENSITIVE|UNICODE'

  • [<token spec\>]

    指示是否仅根据记号边界匹配正则表达式。

    ... [[between <number> and] <number> token[s] in] ...
    

    记号约束是规范的可选部分。 如果省略了记号约束,那么 AQL 会返回输入文本中每个字符位置的最长非重叠匹配项。 如果存在记号约束,那么 extract 语句将根据每个记号边界返回最长匹配项,但长度不超过指定的记号范围长度。 每个返回的匹配项必须在记号开头处开始,并在记号结尾处结束。 如果存在多个重叠匹配项,extract 语句会返回所有这些匹配项。

    记号边界的位置取决于运行时组件用于对文档进行记号化的记号化器。 如果引擎使用的是标准记号化器,那么记号会定义为词字符序列或单个标点符号字符。

    例如,假设有以下字符串:

    "The fish are pretty," said the boy.
    

    系统会在以下位置标识记号边界:

    ["][The] [fish] [are] [pretty][,]["] [said] [the] [boy][.]
    
  • <name\>.<column\>

    应用了正则表达式的视图名称和列名。

  • <grouping spec\>

    确定如何处理正则表达式中的组捕获。 捕获组是正则表达式匹配中通过原始表达式中的括号进行标识的区域。 例如,表达式 (fish)(cakes) 具有三个捕获组:

    • 组 0 是整个匹配项 fishcakes
    • 组 1 是 fish
    • 组 2 是 cakes。 在语法的 return 子句中指定组标识时,每个组标识必须对应于在语法中指定为 extract regex 子句一部分的每个正则表达式中的有效组。

    下面是分组规范的格式:

    return
        group <number> as <name>
        [and group <number> as <name>]*
    

    要仅返回组 0 (整个匹配项) ,可以使用较短的备用格式,如示例 1 中所示。 此格式等同于 return group 0 as <name><name> 可以是简单标识或双引号标识。

用法说明

  • 通常,AQL 支持的功能与 Java 5 正则表达式实现的相同,如“类模式:java.util.regex”中所述。 运行时组件包含多个正则表达式引擎实现,包括 Java 的内置实现。 在编译期间,优化器会检查每个正则表达式,并选择可以运行表达式的最快引擎。

  • 备用执行引擎对于特定极端情况可能具有略微不同的语义。 尤其是,AQL 不保证对备选项的求值顺序。

    例如,假设某个 extract 语句通过文本“fisherman”与正则表达式 /fish|fisherman/ 相匹配。 该语句可能与“fish”或“fisherman”相匹配,具体取决于内部使用的正则表达式引擎。

AQL 标志字符串 Java 标志 描述
CANON_EQ CANON_EQ 规范等效性:同一字符的不同 Unicode 编码视为等效。
CASE_INSENSITIVE CASE_INSENSITIVE 执行不区分大小写的匹配。缺省情况下,不区分大小写的匹配假定仅匹配 US-ASCII 字符集的字符。 可以通过使用此标志指定 UNICODE 标志,以启用 UNICODE 相关的不区分大小写的匹配。
UNICODE UNICODE_CASE 如果指定了不区分大小写的匹配,请使用 Unicode 大小写折叠以与 Unicode 标准一致的方式,确定在不区分大小写的比较中两个字符是否等效。 缺省情况下,不区分大小写的匹配假定仅匹配 US-ASCII 字符集的字符。:如果在不同时使用 CASE_INSENSITIVE 标志的情况下使用此标志,其行为是未定义的。
DOTALL DOTALL 使点字符 . 与所有字符 (包括换行符) 匹配。
LITERAL LITERAL 将表达式视为字面值字符序列,忽略正常的正则表达式转义序列。
MULTILINE MULTILINE 使字符 ^$ 与任何行的开头和结尾相匹配,而不是与整个输入文本的开头和结尾相匹配。
UNIX_LINES UNIX_LINES 仅将 UNIX™ 换行符 视为换行符,忽略回车符 \r
  • 遵循以下准则可使抽取器运行速度更快并且更易于维护:
    • 避免使用很长的复杂正则表达式,而使用与 AQL 语句组合在一起的较小、较简单的正则表达式。
    • 在正则表达式中避免不必要地使用先行断言和后行断言。 通常,可以通过向 extract 语句的 having 子句添加谓词来实现相同的效果。
    • 尽可能使用正则表达式抽取规范中的记号约束。

示例

示例 1:使用规范的 Unicode 等效性来确定匹配项

此示例显示了如何查找不是名字的首字母大写单词。 规范的 Unicode 字符等效性用于确定匹配项。 请注意 ‘CANON_EQ’ 标志的用法,并注意对记号执行的是正则表达式:

create dictionary FirstNamesDict as
  (
  'Aaron', 'Matthew', 'Peter'
  );
create view NotFirstName as
  extract
    regex /[A-Z][a-z]*/
    with flags 'CANON_EQ'
    on 1 token in D.text
    as word
from Document D
  having Not(ContainsDict('FirstNamesDict', word));

示例 2:使用捕获组

以下示例演示了在 extract regex 语句中使用捕获组。 该代码使用捕获组来抽取美国电话号码的字段:

create view Phone as
  extract regex /(\d{3})-(\d{3}-\d{4})/
    on between 4 and 5 tokens in D.text
    return
      group 1 as areaCode
      and group 2 as restOfNumber
      and group 0 as fullNumber
from Document D;

示例 3:对输入文本应用多个正则表达式

可以在同一 extract regex 语句中使用 regexes 语法来指定多个正则表达式。

create view PhoneNum as
  extract regexes
    /(\d{3})-(\d{3}-\d{4})/ and /[Xx]\d{3,5}/
        on between 1 and 5 tokens in D.text as num
from Document D;

示例 4:误用分组规范

此代码样本中的正则表达式既不包含 group -1,也不包含 group 3000。 这将导致编译错误。

create view ErrorExample as
    extract regex /(\d+)/
    on D.text
    return group -1 as wrongGroup and
          group 3000 as nonExistentGroup
from Document D;

字典

使用字典抽取规范可从输入文本中抽取字符串字典中包含的字符串。

语法

    dictionar[y|ies]
        '<dictionary>'
        [and '<dictionary>' and ... and '<dictionary>']
        [with flags '<flags string>']

描述

  • '<dictionary\>'

    引用使用 create dictionary 语句、create external dictionary 语句或文件系统上的字典文件创建的字典。

  • [and '<dictionary\>' and ... and '<dictionary\>']

    引用要用于抽取的其他字典。

  • [with flags'<flags string\>']

    控制字典匹配。 目前,支持两个选项:

    • 完全匹配

      提供区分大小写的完全匹配。

    • IgnoreCase

      提供不区分大小写的匹配。

如果未指定任何标志,那么字典将根据创建时指定的任何标志进行匹配。 如果在创建期间未指定任何标志,那么字典将使用 IgnoreCase 标志进行匹配。

用法说明

  • 字典始终根据记号边界求值。 具体而言,如果字典条目的第一个记号与文本区域的第一个记号相匹配,条目的第二个记号与文本区域的第二个记号相匹配,依此类推,那么该条目与该文本区域相匹配。 将忽略两个连续记号之间的字符。

    例如,假定使用的是基于空格的简单记号化模型,该模型适用于英语等语言。 此外,假定输入文本为“Let’s go fishing!” 如果字典包含术语 go fish,那么文本 Let's go fishing! 中没有任何匹配项。 但是,如果字典包含 go fishing 条目(请注意,go 和 fishing 之间有两个空格),那么文本 Let's go fishing! 中有一个匹配项。 空格指定 gofishing 是两个不同的记号。 如果在输入文本中的两个记号 gofishing 之间存在一个或多个空格字符,那么有一个匹配项。

    对于字典条目与输入文本的每个匹配项,extract dictionary 语句都会生成输出元组。

示例

示例 1:从字典文件中抽取术语

使用包含常见名字和姓氏的字典文件以及区分大小写的匹配来查找人名。

create view Name as
  extract
    dictionaries
        'first.dict'
        and 'last.dict'
    with flags 'Exact'
        on D.text
        as name
from Document D;

下面是 last.dict 的样本内容:

#Dictionary for surnames
Anthony
Aparicio
Cate
Lehmann
Radcliff

下面是 first.dict 的样本内容:

#Dictionary for given names
Aaron
Candra
Freeman
Mathew
Matthew
Zoraida

请注意:

  • extract dictionary 语句中直接引用的字典文件系统无法显式配置为使用一组语言,以便在运行时编译和应用字典。 如果模块包含 set default dictionary language 语句,请改为使用此语句来指定语言集。

    因此,建议不要在 extract dictionary 语句中直接引用字典文件,未来可能会停止使用此功能。 首选做法是使用 create dictionary from file 语句显式定义字典对象,然后在 extract 语句中使用该字典。

  • 编译器和运行时组件会尝试在配置的搜索路径下查找 AQL 中引用的字典文件。

示例 2:从内联字典中抽取术语

使用内联字典和不区分大小写的缺省匹配来查找连词。

create dictionary ConjunctionDict as
  (
    'and', 'or', 'but', 'yet'
  );

create view Conjunction as
  extract
    dictionary 'ConjunctionDict'
        on D.text
        as name
from Document D;

拆分

使用拆分抽取规范可将一个大范围拆分为若干较小的范围。

语法

split using <name>.<split point column>
    [retain [right|left|both] split point[s]]
    on <name>.<column to split>
    as <output name>

描述

拆分抽取规范采用两个自变量:

  • 包含较长目标范围的文本的列。
  • 包含拆分点的列。

拆分算法的运作方式是对输入视图执行两轮操作。 第一轮操作按目标列对所有输入元组分组。 第二轮操作会遍历每个组中的元组,并使用拆分列的每个值拆分目标列。

  • <name\>.<split point column\>

    指定用于抽取的拆分点。

  • [retain [right|left|both] split point[s]]

    指定如何处理每个结果的左右端点。 此自变量是可选的。

    • 如果指定了 retain left split point,那么每个输出范围还包含左侧的拆分点(如果此类拆分点存在)。
    • 如果指定了 retain right split point,那么系统会使每个输出范围包含右侧的拆分点。
    • 拆分抽取还接受空值作为拆分点。 对于每个此类值,抽取会返回包含整个输入范围的元组。
  • <name\>.<column to split\>

    指定用于抽取的目标列。

  • <output name\>

    定义抽取输出的名称。

示例

示例 1:拆分点和 retain 子句

如果拆分点是短语 fish are swimming in the fish pond 中单词 fish 的所有实例,那么 retain 子句的各种版本会生成以下结果:

  • 省略了 retain 子句

    " are swimming in the " 和 " pond"

  • retain right split point

    " are swimming in the fish" 和 " pond"

  • retain left split point

    "fish are swimming in the " 和 "fish pond"

  • retain both split points

    "fish are swimming in the fish" 和 "fish pond"

示例 2:拆分抽取

此示例将文档拆分为句子。 它首先使用正则表达式来标识句子边界,然后使用拆分抽取规范根据句子边界拆分文档文本。

create dictionary AbbreviationsDict as
  (
  'Cmdr.',
  'Col.',
  'DR.',
  'Mr.',
  'Miss.');

create view Sentences as
  extract
    split using B.boundary
        retain right split point
        on B.text
        as sentence
  from (
    extract
        D.text as text,
        regex /(([\.\?!]+\s)|(\n\s*\n))/
        on D.text as boundary
        from Document D
    -- Filter the candidate boundaries.
      having Not(ContainsDict('AbbreviationsDict',
            CombineSpans(LeftContextTok(boundary, 1), boundary)))
      ) B;

使用块抽取规范可识别整个输入文本中相邻范围的块。

语法

blocks
    with count [between <min\> and] <max\>
    and separation [between 0 and] <max\> (tokens| characters)
    on <name\>.<column containing spans\>
    as <output name\>

描述

  • with count [between<min\> and] <max\>

    指定可以组成一个块的范围数。 <min\><max\> 值指定可构成块的最小和最大范围数。

  • [between 0 and] <max\>

    指定范围之间允许的间距,超过此间距后,这些范围即不会再视为相邻范围。

  • (tokens| characters)

    指定范围的间距表示的是记号数还是字符数。

  • <name\>.<column containing spans\>

    要应用块运算符的视图名称和列名。

  • <output name\>

    指定块运算符的输出的名称。

用法说明

  • 如果输入扫描在输入范围中包含多个重叠块,那么块抽取语句会返回所有可能的块。 使用合并可过滤掉冗余块。
  • 使用块抽取规范的 extract 语句会生成多个块,其中每个块由多个输入元组中某个特定字段的值聚集组成。 因此,其 select 列表不能包含其输入视图中的字段。

示例

示例 1:抽取某个字符范围内的词块

在以下代码中,视图 TwoToThreeCapitalizedWords 标识了包含彼此相距 100 个字符之内的两个到三个首字母大写单词的块。

create view CapitalizedWords as
  extract
    regex /[A-Z][a-z]*/
        with flags 'CANON_EQ'
        on 1 token in D.text
        as word
from Document D;

create view TwoToThreeCapitalizedWords as
  extract blocks
    with count between 2 and 3
    and separation between 0 and 100 characters
    on CW.word as capswords
from CapitalizedWords CW;

示例 2:抽取某个记号范围内的词块

以下代码标识了恰好包含彼此相距五个记号之内的两个首字母大写单词的块。

create view TwoCapitalizedWords as
extract blocks
    with count 2
    and separation between 0 and 5 tokens
    on CW.word as capswords
from CapitalizedWords CW;

词性

使用词性抽取规范可识别不同词性在整个输入文本中的位置。

语法

part_of_speech
 '<part of speech spec>'
 [and '<part of speech spec>']*
 [with language '<language code>']
 [and mapping from <mapping table name>]
 on <input column> as <output column\>
 from <input view>

描述

  • '<part of speech spec\>'

    标识要从输入文本中抽取的词性。 '<part of speech spec\>' 是下列其中一个字符串:

    • 包含由多语言记号化器生成的词性标记的逗号分隔列表
    • 内部词性名称和标志的组合,如映射表所定义
  • [and '<part of speech spec\>']*

    标识用于抽取的其他词性标记。

  • [with language '<language code\>']

    指定要在抽取中使用的语言。 <language code\> 是两个字母的小写语言代码,例如 "en" 或 "ja"。 如果省略此自变量,那么会假定词性抽取的语言为英语。

  • [and mapping from <mapping table name\>]

    指定 AQL 表的名称,该表将原始词性标记(如“NOUN”)映射到高级别词性和标志的组合。 虽然可选映射表可以具有可变的名称,但是词性映射表必须具有以下列名:

    • 标记

      用于保存多语言记号化器词性标记的列。

    • basetag

      用于保存相应内部标记的列。

    • flagstr

      用于保存与所指示词性关联的标记的逗号分隔列表的列。

    映射表必须在使用该映射表的 extract create table 语句所在的模块中使用 part_of_speech 语句进行定义。 映射表不能是导入的表,也不能是外部表。

    create table POSMapping_EN(tag Text, basetag Text, flagstr Text)
    as values
     ('CCONJ','CONJ','coordinating'),
     ('SCONJ','CONJ','subordinating');
    
  • <input column\>

    指定要从中抽取词性信息的输入视图的列。

  • <output column\>

    指定带有所指示词性的记号的范围将发送到的列的名称。

  • <input view\>

    指定要从中抽取词性信息的输入视图。

用法说明

  • 仅当使用多语言记号化器时,词性抽取才有效。 如果系统使用标准记号化器,那么 part_of_speech 抽取会生成错误。

语言的词性标记

对于所有支持的语言,多语言记号化器使用下表中列出的词性标记。

标记 描述
ADJ 形容词
ADP 介系词
ADV 副词
AUX 助动词
CCONJ 并列连词
DET 限定词
INTJ 感叹词
NOUN 名词
NUM 数词
PART 小品词
PRON 代词
PROPN 专有名词
PUNCT 标点符号
SCONJ 从属连词
SYM 符号
VERB 动词
X 其他

示例

示例 1:在 extract 语句中直接使用词性标记

视图 EnglishNoun 会抽取英语名词(单数或不可数)或专有名词(单数)。

create view EnglishNoun
as extract parts_of_speech 'NOUN' and 'PROPN'
with language 'en' on D.text
as noun from Document D;

序列模式

使用模式抽取规范可对整个输入文档以及从输入文档中抽取的其他范围执行模式匹配。

语法

序列模式的一般语法是首先指定要在文本中匹配的模式,然后指定要由抽取器返回的内容。 序列模式的最后一部分指定作为模式的输入的内容;这可能是先前定义的视图中的列,也可能是整个文档文本。

pattern <pattern specification> [return clause] [with inline_match on <viewname.colname>]

描述

  • <pattern specification\>

    <pattern specification\> 由多个原子组成。 单个原子可以是已定义视图中的列、固定字符串或正则表达式。 可以将原子指定为可选和重复,并指定原子之间的记号间隔。

    模式规范是包含 extract 子句的较大 AQL 语句的一部分。

    下面是一个简单的示例,说明如何创建视图来包含先前定义的视图中的三个相邻匹配项。 在此示例中,将返回整个组合,即 group 0 引用的内容:

    create view Money as
    extract pattern <C.match> <N.match> <Q.match>
    return group 0 as  match
    from Currency C, Number N, Quantifier Q;
    

    如果原子无需彼此完全相邻,那么可以使用原子之间的记号间隔来允许更多匹配项。 此示例查找在相距 0 到 2 个记号之内的位置后跟电话号码的人员提及项。 请注意 <Token>{0,2} 构造,它指示允许人员和电话注释之间存在 0 到 2 标记的间隔。

    create view Phone as
    extract regex /(\d{3})-(\d{3}-\d{4})/
      on between 4 and 5 tokens in D.text
      return
       group 1 as areaCode
       and group 2 as restOfNumber
       and group 0 as fullNumber
    from Document D;
    create view PersonPhone as
    extract
      pattern (<P.name>) <Token>{0,2} (<Ph.fullNumber>)
      return group 0 as match
       and group 1 as person
       and group 2 as phone
    from Person P, Phone Ph;
    

    记号间隔构造限制为在序列表达式中执行。 此外,序列中的每个记号间隔的前后必须为“非记号间隔”表达式。 因此,extract pattern 语句会生成以下异常:

    -> pattern consisting only of a token gap is an error
    extract pattern <Token> as match from ...
    -> pattern beginning with a token gap is an error
    extract pattern <Token> {0,2} <Ph.phone> as match from ...
    -> pattern ending with a token gap is an error
    extract pattern <P.name> <Token> ?  as match from ...
    -> group consisting only of a token gap is an error
    extract pattern <P.name> (<Token>)  <Ph.phone> as match from ...
    

    使用 (min,max) 语法可指示每个原子重复的次数。 也可以使用? 用于指示 Atom 或重复 Atom 是可选的语法。 原子及其关于重复性和可选性的指示组合在一起以创建序列。

    下面是显示如何重复元素的更复杂示例。 通过识别一个到三个首字母大写单词(后跟“Hotel”或“hotel”记号)来查找候选酒店名称。

    create view CapsWord as
    
    extract
        regex /[A-Z][a-z]*/
           on 1 token in D.text
           as word
    from Document D;
    
    create view HotelCandidate as
    extract
      pattern <CW.word>{1,3} /[Hh]otel/ as hotelname
    from CapsWord CW;
    

    您还可以使用 | 运算符来指示原子之间的选项,如 extract pattern <A.match>| <B.match> <C.match> as match from Apple A, Bacon B, Chocolate C;中所示。 此模式可以解释为“与任一 A.match 相匹配,或者与 B.match 后跟 C.match 的序列相匹配”。 您可以在示例 1 中查看使用 | 运算符的完整示例。

    创建模式后,每个与 <pattern specification> 匹配的模式将根据模式规范的 return 子句以及 extract 语句开头的可选 <select list> 来构造输出结果。 结果会根据 having 语句的 consolidatelimitextract 子句进行过滤及合并。 例如,如果模式规范存在多个重叠匹配项,那么将返回所有可能的匹配项,并且您可以使用 consolidation 子句来过滤掉冗余输出。

    请参考先前的示例,但现在的目标是除去包含单词“Sheraton”的匹配项,并通过除去包含在更大匹配项中的匹配项来合并生成的匹配项。 例如,我们不希望在同一文本范围内找到“Best Garden Hotel”和“Garden Hotel”。

    create view HotelCandidate as
    extract
      pattern <CW.word>{1,3} /[Hh]otel/ as hotelname
    from CapsWord CW
    having Not(ContainsRegex(/.*Sheraton.*/,hotelname))
    consolidate on hotelname using 'ContainedWithin';
    

既然您已熟悉语法和一些示例,下图概述了模式规范的完整语法。 您开始构建模式以了解如何组织要构建的模式的结构时,请参阅此完整语法。

如果您熟悉基于 POSIX 字符的正则表达式,那么您将发现语法是类似的。 在本例中,语法允许元素之间使用空格,此外还定义了为了满足 AQL 用途可以使用的元素。请注意,在本例中,术语 Alternation 表示选项。 在元素之间使用竖线指示存在选项,选项可以使用 ( ) 进行分组。

Pattern   -> Alternation
Alternation  -> Sequence | Sequence | ... | Sequence
Sequence   -> Optional Optional ... Optional
Optional   -> Repeat | Repeat ? Repeat     -> Atom | Atom { min, max }
Atom     -> <view_name.column_name>
     'string'
   <'string' [match parameters]>
     /regex/
     <Token>
     Group
Group -> ( Pattern )

具体而言,原子可以具有六种格式:

  • <view_name.column_name\>

    指定在 extract pattern 语句的 from 列表中指定的其中一个视图、表或表函数引用中的列。

  • 'string'

    使用 AQL 缺省字典匹配语义来指定与指定字符串的匹配项。

  • <'string' [match parameters]\>

    使用 [match parameters] 指定的字典匹配语义来指定与指定字符串的匹配项。 [match parameters] 的格式为 case (exact | insensitive)。 此格式可指定用于确定字符串匹配项的大小写折叠的类型。 要指定区分大小写的完全匹配项,请选择 exact。 要指定匹配项不区分大小写,请选择缺省值 insensitive。

  • /regex/

    指定基于字符的正则表达式匹配项,使其只能与文档文本中的单个记号相匹配。 此外,语法允许在序列表达式中指定特殊记号间隔构造,以指示 minmax 个记号内的匹配项。

  • <token\>

    任何记号的匹配项。

  • [return clause]

    根据 return 子句为模式表达式的每个匹配项生成抽取的值。 return 子句的语义与 return 语句中 extract regex 子句的语义相同。

  • [with inline_match on <viewname.colname\>]

    对于原子(例如,字符串和正则表达式原子),with inline_match 子句会确定系统用于字符串或正则表达式抽取的文本对象。 例如,如果子句为 with inline_match on Email.subject,那么模式规范中内联定义的所有字典和正则表达式都将应用于 Email 视图的 subject 字段。 如果不存在 with inline_match,那么缺省情况下会对整个 Document.text 运行字符串和正则表达式抽取。 在这种情况下,viewname 必须是在当前模块中定义或从其他模块导入的视图或表的名称。with inline_match 子句中不允许引用表函数。

  • [with language as <language code(s)\>]

    对于要用于对字符串求值的语言,指定两字母语言代码(例如 en(英语)或 zh(中文))的逗号分隔列表。 对于其语言代码未包含在此字符串中的文档,不会有任何匹配项。 如果省略了语言参数,那么求值语言缺省为下列其中一个语言集:

    • 通过包含模块中的 set default language 语句(如果已声明)指定的语言集。
    • 包含德语 (de)、西班牙语 (es)、英语 (en)、法语 (fr)、意大利语 (it) 和未指定语言 (x_unspecified) 的语言集。

用法说明

  • extract pattern 语句的语义受模式规范的影响。 每个匹配项根据模式规范的 return 子句以及 extract 语句顶部的 select 列表来构造输出结果。 结果会根据 extract 语句的 havingconsolidatelimit 子句进行过滤及合并。 如果模式规范存在多个重叠匹配项,那么模式抽取将输出所有可能的匹配项。 使用合并可过滤掉冗余输出。

  • from 语句的 extract pattern 子句的语义不同于没有模式规范的其他形式的 extract 语句。 extract 语句的常规语义要求针对 <from list\>中定义的每个视图组合对抽取规范进行求值。 如果 <from list\> 中的至少一个视图未包含特定文档的任何结果,那么 extract 语句的输出为空,因为输入视图中的所有结果组合的集合为空。 在 extract pattern 语句的特殊用例中,from 子句可作为占位符,用于声明模式规范中涉及的关系的名称。 语句的语义仅受模式规范的影响。 尤其是,即使某些输入视图为空,该语句的输出也可以为非空。

  • 使用序列模式抽取的 extract 语句可以结转 from 列表中任何视图的列,但仅当视图名称未显示在模式规范的重复元素中时才可执行此操作。 例如,CapsWordOneToThree 语句会生成编译错误。 发生此错误的原因是 extract 语句顶部的前转列 CW.type 属于模式规范的 repeat 元素 <CW.word>{1,3} 中的视图名称 CW

    create view CapsWord as
    extract 'UpperCase' as type,
        regex /[A-Z].*/ on 1 token in D.text as word
    from Document D;
    
    ---> This results in and error due to the repeating element CW.word
    create view CapsWordOneToThree as
    extract CW.type as type,
        pattern <CW.word>{1,3} as match
    from CapsWord CW;
    
    output view CapsWordOneToThree;
    

    对于从模式规范的备选项或可选元素中显示的视图名称结转的列,当文本中不存在相应的备选项或可选元素时,输出列的值为 null。 说明此点的示例位于示例 1 的 Person 视图中。

  • 在重复元素下出现的组不能在语句的 return 子句中输出。 例如,以下语句会导致异常:

    create view CapsWordOneToThree as
      extract
             pattern (<CW.word>){1,3}
            return group 0 as fullmatch
                   and group 1 as word   -- not allowed due to repeat
      from CapsWord CW;
    

示例

示例 1:具有捕获组的序列模式

此示例的目标是通过识别名字、后跟中间名首字母缩写(可选),再跟姓氏的出现实例来查找人名,以及查找整个匹配项(可选择在前面添加常用称呼)。 此外,抽取器会将整个匹配项作为引用返回,将第一组作为称呼返回,将第二组作为姓名返回,并结转各个输入视图中的名字、中间名首字母缩写和姓氏的值。

create view MiddleInitial as
extract regex /\b([\p{Lu}\p{M}*]\.\s*){1,5}\b/
            on between 1 and 10 tokens in D.text as initial
from Document D;

create view Person as
extract F.first as first,
        M.initial as middle,
        L.last as last,
        pattern ('Mr.'|'Ms.'|'Miss')? (<F.first> <M.initial>? <L.last>)
return group 0 as reference
  and group 1 as salutation
  and group 2 as name
from FirstName F, MiddleInitial M, LastName L;

由于子模式表达式 ('Mr.'|'Ms.'|'Miss')? 是可选的,因此当文本中不存在称呼时,称呼输出列的值为 null 。 同样,由于模式子表达式 <M.initial>? 是可选的,因此当中间初始不存在时,输出列中间的值为 null

示例 2:具有字符串匹配项和匹配参数的序列模式

此示例的目标是通过检查文档的标题注释来查找已知项目的会议记录的出现实例。 请注意,with inline_match 子句指定根据视图 Title 的匹配字段来执行字符串匹配,而不是对整个文档文本执行字符串匹配。

create view Project as
extract
regex /[Pp]roject\s?\w*/ on D.text as name
from Document D;


create view Title as
extract regex /[A-z][a-z]+.*/
on between 1 and 20 tokens in D.text as match
from Document D;


create view MeetingNote as
extract
pattern <'Meeting Notes:'[with case exact]> (<P.name>)
return group 0 as match
  and group 1 as projectname
with inline_match on Title.match
from Project P;

示例 3:即使输入视图为空,也会返回非空结果的序列模式

即使输入视图 LastName 为空,以下语句也会生成结果。 模式规范的第二部分 <L.name\>? 包含可选元素。 模式规范的语义旨在输出由 FirstName.name 范围或 FirstName.name 范围(其后紧跟 LastName.name 范围)组成的所有范围。 因此,对于视图 LastName 为空的文档,该语句的结果包含由从该文档中识别到的单个 FirstName.name 范围组成的所有范围。

create dictionary FirstNamesDict as
(
  'Aaron', 'Matthew', 'Peter'
);
create dictionary LastNamesDict as
(
  'Anthony', 'Lehman', 'Radcliff'
);

create view LastName as
  extract dictionary 'LastNamesDict'
  on D.text as last
from Document D
having MatchesRegex(/((\p{L}\p{M}*)+\s+)?\p{Lu}\p{M}*.{1,20}/, last);

create view FirstName as
  extract dictionary 'FirstNamesDict'
  on D.text as first
from Document D
having MatchesRegex(/\p{Lu}\p{M}*.{1,20}/, first);

create view PersonName as
extract pattern <F.first> <L.last>? as fullName
from FirstName F, LastName L;

select 语句

AQL 中的 select 语句提供了强大的机制来使用各种规范构造和组合元组集。

语法

select 语句在结构方面类似于 SQL SELECT 语句:

select `<select list>`
  from `<from list>`
  [where `<where clause>`]
  [consolidate on `<column>`
     [using '`<policy>`' [with priority
         from `<column> ` [priority order]]]]
  [group by `<group by list>`]
  [order by `<order by list>`]
  [limit `<maximum number of output tuples for each document>`];

描述

  • <select list\>

    输出表达式的逗号分隔列表。

  • <from list\>

    逗号分隔列表,用作要选择的元组的源。

  • [where <where clause\>]

    针对从 from 子句的关系中所有元组的笛卡尔积生成的每个元组,定义要对其应用的谓词。 此子句是可选的。

  • [consolidate on<column\>[using '<policy\>' [with priority from <column\> priority order]]]

    定义用于管理重叠范围的合并策略。 此子句是可选的。

  • [group by<group by list\>]

    按指定字段的公共值对从同一文档生成的元组分组。 此子句是可选的。

  • [order by<order by list\>]

    select 语句从每个文档生成的输出元组排序。 排序是基于排序依据列表(表达式的逗号分隔列表)的值。 此子句是可选的。

  • [limit <maximum number of output tuples for each document\>]

    将每个文档的输出元组数限制为指定的最大值。 此子句是可选的。

用法说明

select 语句的语义如下所示:

  • 通过采用 from 列表中关系的笛卡尔积来确定输入数据(以元组形式)。
  • 对于生成的每个输入元组,通过在(可选)where 子句中应用谓词,对其进行过滤。
  • 如果存在可选的 group by 子句,将按分组依据列表中指定的值对从同一文档生成的元组分组,并计算 select 列表中 aggregate 函数的结果。
  • 根据(可选)consolidation 子句中定义的策略,合并任何重叠元组。 如果存在可选的 order by 子句,将按排序依据列表的值对这些元组排序。
  • 计算每个元组上 select 列表中的所有表达式,并对 as 子句指定的列重命名。
  • 如果存在可选的 limit 子句,那么会将输出元组数限制为每个文档的指定元组数。

示例

下面是如何使用 select 语句来抽取与模式匹配的电话号码的示例。 假定已经定义了 PhoneNumbers 视图,该视图抽取模式为 XXX-XXX-XXXX 的美国电话号码。 此 select 语句对输入文本中模式为 444-888-XXXX 的正则表达式求值。 该视图具有输出列 documentTextphoneNumber。 此外,输出限制为只显示每个文档中识别到的此电话号码模式的第一个出现实例。

create view PhoneNumbersPattern1 as
select D.documentText, D.phoneNumber
from PhoneNumbers D
where MatchesRegex(/444-888-\d{4}/,D.phoneNumber)
limit 1;

另一个示例是如何使用 select 语句来查找人员及其相应电话号码的近似映射。 假定已经定义了视图 Person,并且该视图具有列 person 和视图 PhoneNumbers。 此 select 语句对 where 子句求值,以查找包含人员提及项并在相距 1 到 3 个词或记号之内的位置后跟电话号码的文本范围。 此语句的输入由 from 列表中 PersonPhoneNumbers 视图的连接表示。

create view PersonPhone as
select P1.documentText, P1.person, P2.phoneNumber, CombineSpans(P1.person,P2.phoneNumber) as personPhoneSpan
from Person P1, PhoneNumbers P2
where FollowsTok(P1.person,P2.phoneNumber,1,3);

personPhoneSpan 列将包含用于提供近似人员-电话映射的匹配范围。

personPhoneSpan
John : 433-999-1000
Martha Mob 433-999-1001
  • select 列表 AQL 的 selectextract 语句中的 select 列表包含输出表达式的逗号分隔列表。
  • from 列表 AQL 中的 selectextract 语句的第二部分是 from 列表。 from 列表是逗号分隔列表,用作要选择或抽取的元组的源。
  • where 子句 可选的 where 子句针对从 from 子句的关系中所有元组的笛卡尔积生成的每个元组,定义要对其应用的谓词。
  • consolidate on 子句 可选的 consolidate on 子句指定如何解决 selectextract 语句输出的元组上的重叠范围。 使用此子句时,具有非重叠范围的元组不受影响。
  • group by 子句 group by 语句的可选 select 子句指示运行时组件按指定字段的公共值对从同一文档生成的元组分组。
  • order by 子句 可选的 order by 子句指示运行时组件根据排序依据列表(表达式的逗号分隔集)中的值,对 select 语句从每个文档生成的输出元组排序。
  • limit 子句 可选的 limit 子句指定 select 语句为文档生成的输出元组数的限制。
  • select... into 语句 select ... into 语句用于在单个语句中定义视图,并指定这是输出视图。

select 列表

AQL 的 selectextract 语句中的 select 列表包含输出表达式的逗号分隔列表。

语法

每个 select 表达式都必须为下列其中一种格式:

select
   <viewname>.<colname> as <alias> |
   <viewname>.* |
   <expr> as <alias> |
     case
     when <predfunction1()> then <expr1>
      when <predfunction2()> then <expr2>...
     when <predfunctionn()>
      then <exprn>
     [else <expr\_default>]
      as <name>

描述

  • <viewname\>.<colname\> as <alias\>

    • <viewname\>

      指定要从中选择列的视图。

    • <colname\>

      指定该视图中的列。

    • <alias\>

      指定表示所选字段已知的名称。 此字段是可选字段, 可选择作为每个输出元组的一部分。 如果未指定 <alias\> ,那么缺省情况下列的名称为 <colname\>。 可以是简单标识,也可以是加双引号的标识。

  • <viewname\>.*

    指定视图的名称。 此语法指示指定视图的所有列都必须结转到最外围的 selectextract 语句。

    与 SQL 一样,AQL 也允许简写的 select * 语句。 此语句的效果是从 from 语句的 select 子句中指定的所有输入中选择所有列。 但是,不支持简写的 extract * 语句。

  • <expr\> as <alias\>

    表示将表达式分配给包含该表达式的视图的属性。

    • <expr\>

      指定由标量函数调用、聚集函数调用或常量组成的表达式。

    • <name\>

      表示用于保存由 <expr\> 指定为 <alias\>的表达式的结果的列的名称。 如果未指定 <alias\> ,那么缺省情况下列的名称为 <name\>。 可以是简单标识,也可以是加双引号的标识。

  • when<function1()\> then <expr1\> when <function2()\> then <expr2\> ... when <functionN()\> then <exprn\> [else ] <expr_default\> as <name\>

    • <function1()\>, <function2()\>, <functionN()\>

      指定返回类型 Boolean 的标量函数。

    • <expr1\>, <expr2\>, <exprn\>, <expr_default\>

      指定由标量函数调用组成,并且必须返回同一类型的表达式。

    如果 <function1()\> 的结果为 true ,那么 case 表达式的结果为 <expr1\>的结果,并且不会对任何后续 when 子句进行求值。 否则,会以相同方式对后续的 when 子句(如果有)求值。

    如果未满足 when 子句的任何条件,那么此 case 表达式的结果将是缺省表达式 <expr\_default\>的结果。 此表达式在可选的 else 子句中指定。 如果不存在 [else] 子句,那么此 case 表达式的结果为 null

用法说明

  • 不支持以下语句:

    select * from Document;
    

    在发出此 Document 语句的 .aql 文件的当前上下文或作用域中,可能无法完全了解 select 视图的内容。 缺少有关内容的信息的原因是,此特殊 require document with columns 视图在模块级别使用时,当前 .aql 文件外部提供的多个 Document 语句可能改变了此视图的最终模式定义。 简写的 Document.* 语句不是有效的 AQL 构造。

  • 您可以从 Document 视图中显式选择字段。 以下示例显示了如何从 Document 视图中有效地显式选择字段:

    select D.label as label,
      D.text as text
    from Document D;
    

示例

以下示例说明了各种形式的 select 列表。

示例 1:使用常量显式分配值

此示例显示了将常量值分配给 select 列表中的视图属性。 名为 polarity 的字段指示 PS.match 的极性是正还是负(请注意,常量值是显式分配给此属性)。

create view PositiveSentimentsWithPolarity as
select
  'positive' as polarity,
  PS.match as sentiment
from
  PositiveSentiments PS;
create view NegativeSentimentsWithPolarity as
select
  'negative' as polarity,
  NS.match as sentiment
from
  NegativeSentiments NS;

示例 2:使用函数调用显式分配值

以下示例说明了如何将函数调用的结果显式分配给 select 列表中的视图属性。

create view Citizenship as
select
  P.Name as name,
  MatchesDict('USCities.dict', P.birthPlace) as isUSCitizen
from
  Person P;

示例 3:通过字典抽取获得 select 列表表达式

以下示例说明了 select 列表表达式可以如何从字典抽取的结果中选取类型为 Span 的值。

create view PersonNames as
select N.match as name
from
  (extract
  dictionary 'firstNames.dict'
  on D.text
  as match
  from Document D
)N;

示例 4:case 表达式示例

此第一个示例显示了如何指定对特定字段的空值处理:

create view School as
select
case
when Not(NotNull(P.education)) then 'Unknown'
else GetString(P.education)
as name
from Person P;

此示例说明了如何对数据进行分类:

create view Company as
select
PM.name as productname,
case
when ContainsRegex (/IBM/,PM.name) then 'IBM'
when ContainsDict ('OpenSourceDict',PM.name) then 'OSS'
else 'Unknown'
as name
from ProductMatches PM;

from 列表

AQL 中的 selectextract 语句的第二部分是 from 列表。 from 列表是逗号分隔列表,用作要选择或抽取的元组的源。

语法

from <from list item> <name>  [, <from list item> <name>]

描述

  • <from list item\>

    视图、表、表函数引用或嵌套的 AQL 语句。 AQL 中的所有嵌套语句都必须用括号括起。

  • <name\>

    select 语句或 extract 语句中限定作用域的 <from list item\>的局部名。 局部名可以是简单标识,也可以是加双引号的标识。 包含空格、标点符号字符或 AQL 关键字的局部名必须用双引号括起。

示例

示例 1:具有视图和嵌套语句的 from 列表

此示例显示了引用视图和嵌套的 extract 语句的 from 列表。 此示例为语句的结果分配了局部名 FN。 此示例还为 LastName 视图的输出分配了局部名 Last Name

create dictionary LastNamesDict as
  (
    'Anthony', 'Lehman', 'Radcliff'
  );

create view LastName as
  extract dictionary 'LastNamesDict'
  on D.text as lastname
from Document D;

create view FromList as
  select *
    from
    (extract dictionary 'first.dict' on D.text
      as firstname from Document D) FN,
      LastName "Last Name"
    where Follows(FN.firstname,
      "Last Name".lastname, 0, 1);

以下名称包含在外部字典 first.dict 中:

#Dictionary for given names
Aaron
Candra
Freeman
Mathew
Matthew
Zoraida

where 子句

可选的 where 子句针对从 from 子句的关系中所有元组的笛卡尔积生成的每个元组,定义要对其应用的谓词。

语法

select <select list>
  from <from list>
[where <where clause>]

描述

  • <where clause\>

    指定一个或多个谓词。 where 子句中的任何谓词包含属于 from 列表的多个视图中的字段时,都将执行连接。 此谓词必须是一组内置谓词函数或其他返回 Boolean 数据类型的用户定义函数的联合:

    function1() and function2()
    and ... and functionn()
    

    where 子句是可选的,如果不存在要应用的谓词,那么可以从 select 语句中省略此子句。

示例

示例 1:在 WHERE 子句中使用谓词来过滤掉连接的元组

此示例显示的 where 子句仅查找由有效名字在相距 0-1 个字符之内的位置后跟有效姓氏所组成的短语。

-- a view containing words that are valid given names
create view FirstName as
  extract dictionary 'first.dict'
  on D.text as firstname
from Document D;

-- a view containing words that are valid surnames
create view LastName as
  extract dictionary 'last.dict'
  on D.text as lastname
from Document D;

-- a view containing phrases consisting of valid given names
-- followed within 0-1 characters by valid surnames.
create view FullName as
  select *
  from
  FirstName FN,
  LastName LN
  where
  Follows (FN.firstname, LN.lastname, 0, 1);

以下名称包含在外部字典 first.dict 中:

#Dictionary for given names
Aaron
Candra
Freeman
Mathew
Matthew
Zoraida

以下名称包含在外部字典 last.dict 中:

#Dictionary for surnames
Anthony
Lehman
Radcliff

consolidate on 子句

可选的 consolidate on 子句指定如何解决 selectextract 语句输出的元组上的重叠范围。 使用此子句时,具有非重叠范围的元组不受影响。

语法

以下代码是此子句的一般结构示例:

consolidate on <target>
  [using '<policy>'[ with priority from <priority_column>
    [ <priority_order> ]]]

描述

  • <target\>

    from 子句中指定视图中的列,或者指定由标量函数调用(包含在 from 子句中指定为自变量的视图的列)组成的表达式。

  • '<policy\>'

    指定 Text Analytics 支持的下列其中一个合并策略:

    • ContainedWithin

      此策略是缺省值。 如果范围 A 和 B 重叠,并且 A 完全包含 B,那么此策略将从输出中除去包含范围 B 的元组。 如果 A 和 B 相同,那么会除去其中一项。 选择除去哪个元组是任意的。

    • NotContainedWithin

      如果范围 A 和 B 重叠,并且 A 完全包含 B,那么此策略将从输出中除去范围 A。 如果 A 和 B 相同,那么会除去其中一项。 选择除去哪个元组是任意的。

    • ContainsButNotEqual

      此策略与 ContainedWithin 相同,但会保留完全相等的范围。

    • ExactMatch

      如果一组范围涵盖同一文本区域,那么此策略会返回其中恰好一个范围。 其他所有范围都保持不变。

    • LeftToRight

      此策略按从左到右的顺序依次处理范围。 发生重叠时,会保留最左侧最长的非重叠范围。 此策略模拟大多数正则表达式引擎的重叠处理策略。

  • <priority\_column\>

    指定类型为 Text、String、Integer 或 Float 的列。 只能在使用 LeftToRight 合并策略时指定。

  • <priority\_order\>

    指定升序或降序。 只能在使用 LeftToRight 合并策略时指定。 升序可确保如果元组 T1 的优先级为 1,元组 T2 的优先级为 2,那么 T1 的优先级高于 T2。 与之相反,如果优先级为降序,那么 T2 的优先级更高。 优先级顺序的缺省值为升序。

用法说明

  • 存在 priority 子句时,合并的语义遵循以下顺序:
    • 从左到右依次处理范围,在范围重叠时,保留最左侧的范围。
    • 如果有多个重叠范围在同一偏移量开始,将根据优先级顺序来保留优先级最高的重叠范围。
    • 通过在优先级相同的范围中保留最长的范围,中断剩余的关系。
  • 合并将空值视为完全相同。 具有空 <consolidate target\> 的所有输入将生成单个输出元组,该元组是在这些输入中随机选择的。 此行为类似于合并目标列中具有相同范围的元组的做法。 但例外情况是,如果策略为 ContainsButNotEqual,那么不会生成单个输出元组。 在此情况下,空 <consolidate target\> 将输出具有空合并目标的所有输入。

示例

示例 1:基于单列进行合并

此示例指示系统检查所有输出元组的 Person.name 字段,并使用 ContainedWithin 合并策略来解决重叠问题。

consolidate on Person.name
  using 'ContainedWithin'

示例 2:基于包含多列的表达式进行合并

此示例指示系统检查将 CombineSpans 标量函数应用于每个输出元组中的 Person.firstnamePerson.lastname 字段的结果。 此示例使用 ContainedWithin 合并策略来解决重叠问题。

consolidate on
  CombineSpans(Person.firstname, Person.lastname)
  using 'ContainedWithin'

示例 3:使用 LeftToRight 策略和优先级顺序进行合并

假设从输入文本 John Doe 中抽取以下术语元组:

match: `John`,
priority: `1`

match: `John Doe`,
priority: `2`

这两个范围具有相同的开始偏移量。 使用 LeftToRight 策略以及升序优先级顺序进行合并时,将保留元组 (match: John, priority: 1),因为此元组的优先级更高。 使用降序优先级顺序进行合并时,将保留元组 (match: John Doe, priority: 2),如以下示例所示:

create view ConsolidatePeopleWithPrioritiesAscending as
  select P.match as match, P.weight as weight
  from People P
  consolidate on P.match
  using 'LeftToRight'
  with priority from P.weight
  ascending;

group by 子句

group by 语句的可选 select 子句指示运行时组件按指定字段的公共值对从同一文档生成的元组分组。

语法

select <select list>
from <from list>
[where <where clause>]
...
[group by <group by list>]

描述

  • <group by list\>

    指定包含 from 子句和标量函数调用中视图的列的表达式逗号分隔列表。 应用 group by 子句时,共享所有 group by 表达式的公共值的每组元组都会生成代表整个组的单个输出元组。

    未显示在 group by 子句中的字段或表达式不能出现在 select 列表中,除非在聚集函数调用中使用了该字段或表达式。 列表中表达式的顺序无关紧要。

    group by 子句将所有空值视为完全相同。 具有空值的列上的 Group by 将生成单个组。

示例

示例 1:计算聚集值

使用 group by 子句可计算聚集值。 此示例统计文档中每个名字的出现次数。 在此示例中,Count 是聚集函数。

create view SampleView as
  select
    GetText(P.firstname) as name,
    Count(GetText(P.firstname)) as occurrences
  from
    (extract
    dictionary 'first.dict'
    on D.text as firstname
    from Document D
    ) P
  group by GetText(P.firstname);

在本例中,first.dict 是包含以下条目的外部字典:

#Dictionary for given names
Aaron
Candra
Freeman
Matthew
Zoraida

以下步骤描述了此语句的语义:

  1. from 子句中的子查询生成的元组,按其 firstname 字段的文本内容分组。
  2. 对于每个组,统计具有非空 firstname 值的元组数。 对于每个此类组生成单个输出元组,其中包含两个值:名字和该组中的元组数。

示例 2:将不同字段分组在一起的问题

此示例说明了无效的语句。

select GetText(P.firstname) as first,
  GetText(P.lastname) as last,
  Count(P.firstname) as occurrences
from Person P
group by GetText(P.firstname);

不接受在 select 列表中包含 GetText(P.lastname),因为具有相同 firstname 值的元组可能具有不同的 lastname 值,这会导致不确定性。

order by 子句

可选的 order by 子句指示运行时组件根据排序依据列表(表达式的逗号分隔集)中的值,对 select 语句从每个文档生成的输出元组排序。

语法

select ...
  [order by <order by list>]

描述

  • <order by list\>

    指定表达式的逗号分隔列表。

    排序是基于表达式的逗号分隔列表的值。 order by 子句支持返回数字(Integer 或 Float)、Text 或 Span 数据类型的表达式。 如果 order by 子句中的表达式返回的类型为 Span,那么将通过比较相关范围值来比较结果元组。 在以下示例中,将比较 person 字段的范围值。

    order by P.person
    
    

    order by 子句将空值视为未排序(空值之间)。 空值的排序低于其他对象。

示例

示例 1:按多个表达式排序

假定 person 是类型为 Span 的字段。 以下 order by 子句指定语句返回每个文档中的元组。 根据词法,元组先按 person 字段的文本,再按 person 字段的开头进行排序。

order by GetText(P.person), GetBegin(P.person)

limit 子句

可选的 limit 子句指定 select 语句为文档生成的输出元组数的限制。

语法

select <select list>
  from <from list>
  ...
  [limit <maximum number of output tuples for each document>];

描述

  • <maximum number of output tuples for each document\>

    指定每个文档的最大输出元组数。 如果 limit 值大于或等于可以返回的元组总数,那么将返回所有元组。

示例

示例 1:限制返回数

此示例返回每个文档中的前三个人名:

create view SampleView as
  select *
  from Person P
  order by GetBegin(P.name)
  limit 3;

select... into 语句

select ... into 语句用于在单个语句中定义视图,并指定这是输出视图。

语法

select <select list>
into <output view name>
from <from list>
[where <where clause>]
[consolidate on <column> [using '<policy>' [with priority from <column> [priority order]]]]
[group by <group by list>]
[order by <order by list>]
[limit <maximum number of output tuples for each document>];

描述

  • <output view name\>

    指定语句所定义的输出视图的名称。 select ... into 语句与 select 语句相同,但附加的 into <output view name\> 子句除外。

示例

示例 1:定义视图

此示例定义了名为 PersonPhone 的视图,同时将此视图指定为输出视图。

select P.name as name,
  Ph.number as phoneNumber
into PersonPhone
from Person P, Phone Ph;

此示例等同于以下两个语句:

create view PersonPhone as
  select P.name as name,
    Ph.number as phoneNumber
  from Person P, Phone Ph;

output view PersonPhone;

detag 语句

AQL 中的 detag 语句提供了用于在运行 AQL 抽取器之前从 HTML 或 XML 文档中去除所有标记的函数。

detag 语句还可以保留标记的原始位置以及这些标记中存储的任何值。 detag 语句从文档中除去标记时,运行时组件会记住已去除标记的文本的偏移量与原始标记源的偏移量之间的映射。 Remap 函数是一个特殊的内置函数,用于将已去除标记的文本中的范围映射回原始源中的等效范围。

语法

detag <input view name>.<text column>
 as <output view name>
[detect content_type (always|never)]
[annotate
 element '<element name>' as <auxiliary view name>
 [with attribute '<attribute name>' as <column name>]
 [and attribute '<attribute name>' as <column name>]
 [, element ...]];

描述

  • <input view name\>.<text column\>

    • <input view name\>

      指定要对其执行 detag 进程的输入视图的名称。 <input view name\> 可以是简单标识或双引号标识。

    • <text column\>

      指定要对其执行 detag 进程的输入视图的文本字段。 <text column\> 可以是简单标识或双引号标识。

  • <output view name\>

    指定包含已去除标记的文本的输出视图的名称。 输出视图包含名为 text 的单个列,用于保存已去除标记的文本。 <output view name\> 可以是简单标识或双引号标识。

  • always|never

    指定是否在处理 detag 语句之前验证内容是为 HTML 还是 XML。 通过拆离器运行非 HTML 和非 XML 文本时,如果文本包含 XML 特殊字符 (例如 <>&) ,那么可能会发生问题。 如果缺少 detect content_type 子句,那么缺省值为 always,并且系统始终会检测内容。

    • always

      指定在尝试操作之前始终执行验证,以避免解析非 HTML 或 XML 的文档时发生问题。 如果 <text column\> 的值似乎未包含标记,那么系统将跳过当前文档的拆离。

    • never

      指定在尝试执行 detag 操作之前从不执行验证。 即使文本不包含任何 HTML 或 XML 内容,系统也会尝试对目标文本执行去除标记操作。

  • <element name\>

    指定要注释的 HTML 或 XML 元素的名称。 可选的 annotate 子句可以指示运行时组件通过创建一个或多个视图来记住有关已除去的标记的信息。

  • <auxiliary view name\>

    指定创建用于保存原始标记及其属性的视图的名称。 可以是简单标识,也可以是加双引号的标识。

  • <attribute name\>

    HTML 或 XML 元素的属性的名称。

  • <column name\>

    <auxiliary view name\> 中用于存储 <attribute name\>值的列的名称。 可以是简单标识,也可以是加双引号的标识。

示例

示例 1:指定 detag 输出视图和辅助视图

在此示例中,将创建 DetaggedDoc 视图以保存 text 视图的 Document 属性中原始文本的已去除标记的版本。 除了创建 DetaggedDoc 视图外,annotate 子句还会创建名为 Anchor 的辅助视图。 此辅助视图有两列。 一列名为 match,用于包含锚点文本。 另一列名为 linkTarget,用于包含作为文本的链接实际目标。 match 列中的范围基于 DetaggedDoc 视图的 text 值。

detag Document.text as DetaggedDoc
annotate
  'a' as Anchor
  with attribute 'href' as linkTarget;

示例 2:使用 Remap 函数

以下示例说明了如何使用 Remap 函数将已去除标记的文本中的范围映射回原始源中的等效范围。

-- Strip out tags from each document, provided that the document
-- is in HTML or XML format.
-- Remember the locations and content of all <A> and <META> tags
-- in the original source document.
detag Document.text as DetaggedDoc
detect content_type always
annotate
  element 'a' as Anchor
    with attribute 'href' as target,
  element 'meta' as Meta
    with attribute 'name' as name
    and attribute 'content' as content;

output view DetaggedDoc;

-- Create a view containing all lists of keywords in the
-- document's META tags.
create view MetaKeywordsLists as
select M.content as list
from Meta M
where MatchesRegex(/keywords/, 'CASE_INSENSITIVE', M.name)
  and NotNull(M.content);

-- Create a dictionary of "interesting" web sites
create dictionary InterestingSitesDict as
(
  'ibm.com', 'slashdot.org'
);

-- Create a view containing all anchor tags whose targets contain
-- a match of the "interesting sites" dictionary.
create view InterestingLinks as
select A.match as anchortext, A.target as href
from Anchor A
where ContainsDict('InterestingSitesDict', A.target);

-- Find all capitalized words in the anchor text of links to
-- "interesting" web sites.
create view InterestingWords as
extract I.href as href,
  regex /[A-Z][a-z]+/ on 1 token in I.anchortext as word
from InterestingLinks I;

-- Map spans in the InterestingWords view back to the original
-- HTML or XML source of the document.
create view InterestingWordsHTML as
select I.href as href, Remap(I.word) as word
from InterestingWords I;

记录具有 AQL Doc 的 detag 语句

detag 语句的 AQL Doc 注释包含以下信息:

  • 有关语句功能的常规描述。
  • @field,表示要对其执行 detag 进程的输入视图的每个文本字段。
  • @auxView,指定视图名称。
  • @auxViewField,指定视图的标准列名。
/**
* Detags the input document
* @field text the detagged text of the document
* @auxView Anchor stores the anchor points from tagged doc
* @auxViewField Anchor.linkTarget stores the href attribute of anchor tag
*/

detag Document.text as DetaggedDoc
  annotate element 'a' as Anchor
  with attribute 'href' as linkTarget;

create dictionarycreate external dictionary 语句

create dictionarycreate external dictionary 语句用于定义词或短语的字典,以通过 extract 语句或谓词函数识别输入文本中匹配的术语。 create dictionary 语句允许在源 AQL 代码中指定字典内容,并且字典内容会在模块的已编译表示(.tam 文件)中进行序列化。 create external dictionary 语句允许在实例化抽取器时指定字典内容,而不是在源 AQL 代码中指定字典内容,并且不必重新编译模块。 因此,外部字典是功能强大的构造,允许 AQL 开发者在已编译的模块中公开定制点。

可以通过三个源创建字典:

  • 字典文件
  • 内联字典声明
  • 使用 create table 语句和 create external table 语句创建的表。

语法

内部 create dictionary 语句具有三种语法形式:from filefrom table 和内联格式。

  • 内部字典

    From file:

    create dictionary <dictionary name>
    
     from file '<file name>'
     [with language as '<language code(s)>']
     [and case (exact | insensitive)]
     [and lemma_match];
    

    From table:

    create dictionary <dictionary name>
    
      from table <table name>
      with entries from <column name>
      [and language as '<language code(s)>']
      [and case (exact | insensitive)]
      [and lemma_match];
    

    内联格式

    create dictionary <dictionary name>
    [with language as '<language code(s)>']
    [and case (exact | insensitive)]
    [and lemma_match]
     as
        (
        '<entry 1>', '<entry 2>', ... , '<entry n>'
        )
    ;
    
  • 外部字典

    create external dictionary <dictionary-name>
    required [true|false]
    [with language as '<language codes\>']
    [and case (exact | insensitive )]
    [and lemma_match];
    

描述

  • <dictionary name\>

    指定新的内部或外部字典的名称。 可以是简单标识,也可以是加双引号的标识。

  • '<file name\>'

    指定包含字典条目的文件的名称。 字典文件是回车符分隔的文本文件,其中每行一个字典条目。 字典文件中的条目可以包含多个记号。

  • <table name\>

    指定要从中添加字典条目的表的名称。 字典无法通过从其他模块导入的表进行创建。

  • <column name\>

    指定要从中添加字典条目的表中列的名称。

  • required [true|false]

    指定是否需要外部字典的外部内容才能运行模块。

    • true

      如果子句为 required true,那么必须提供包含外部内容的文件的位置 URI。 指定的文件必须包含内容。 如果未提供 URI,或者文件不包含内容,那么运行时组件会抛出异常。

    • false

      如果子句为 required false,那么即使未提供此字典的外部内容的 URI,也可以成功运行模块。 如果未提供 URI,那么运行时组件会将其视为空字典。

    现在不推荐使用 create external dictionary <dictionary-name\> allow_empty ,这将生成编译器警告。

  • '<language code(s)\>'

    针对要用于对字典求值的语言或外部字典的文档语言,指定两字母语言代码(例如 en(英语)或 zh(中文))的逗号分隔列表。 对于其语言代码未包含在此字符串中的文档,字典不会生成任何结果。

    如果省略了语言参数,那么字典语言缺省为下列其中一个语言集:

    • 通过包含模块中的 set default language 语句(如果已声明)指定的语言集。
    • 包含德语 (de)、西班牙语 (es)、英语 (en)、法语 (fr)、意大利语 (it) 和未指定语言 (x_unspecified) 的语言集。
  • lemma_match

    使用词元化在文档中查找与字典术语类似的词的匹配项。

    词元化是确定给定词的词元的过程。 词元是一个词,可以用作单个给定术语的匹配项。 例如,术语“go”可以与术语“goes”、“going”、“gone”或“went”相匹配。 此过程涉及复杂的任务,例如理解上下文并确定句子中某个词的词性。 IBM 多语言记号化器为其提供词性支持的所有语言都可以使用词元化。

    词元匹配仅对使用 lemma match 子句声明的字典执行。

    使用 lemma_match 子句的字典抽取的语义如下所示:

    • 计算输入文档中每个记号的词元化格式。
    • 将根据词元化的文档对字典求值。 不能将 lemma_match 选项与 case exact 选项一起使用。 如果同时使用这两个选项,将返回编译器错误。
  • case (exact | insensitive)

    指定字典在确定特定文档区域是否匹配时执行的大小写折叠类型。

    • exact

      指定区分大小写的完全匹配。

    • insensitive

      指定匹配项不区分大小写。 此选项是缺省值。

  • '<entry 1\>', '<entry 2\>', ... , '<entry n\>'

    指定要包含在内联字典中的字符串。 内联字典中的条目可以包含一个或多个记号。

用法说明

  • 建议使用 from filefrom table 格式,尤其是预期要修改条目,或者有许多条目时。 通过使用这两种格式,可以修改字典的内容,而无需修改代码。

  • create dictionary 语句由模块化 AQL 编译器处理时,对 create dictionary ... from file 语法中指定的字典文件位置的引用必须相对于发出此 create dictionary 语句的模块的根目录。

  • 在字典文件中可以通过在注释前面添加字符 # 来指定注释。 注释可以在一行中的任意位置开始。

  • 如果要指定跨多行的注释,必须在每行前面添加注释字符。 如果注释字符是字典条目的一部分,那么必须使用反斜杠字符 (\) 对其进行转义,例如 \#。 如果反斜杠字符是字典条目的一部分,那么必须对反斜杠本身进行转义,如 \\ 中所示。

  • 对于外部字典,在装入模块期间,必须指定正在装入的模块所需外部字典的 URI 列表。

  • 字典词元化:现有字典匹配语义与词元化语义之间的主要差异是,后者是针对词元化形式的文档(而不是原始形式的文档)执行匹配。

    属于启用了词元匹配的字典的字典条目具有以下先决条件:

    • 字典条目可以包含一个或多个记号,其中每个条目记号都是一个词元。 要创建词元的字典,可以使用 GetLemma 标量函数。
    • 字典条目的记号应该用空格分隔。 如果记号包含空格,那么应该使用反斜杠 (\) 字符来对空格进行转义。
  • 下表显示了 create external dictionary 语句与 create dictionary 语句之间的差异:

create external dictionary create dictionary
  • 为字典定义占位符,其内容在初始化时提供。
  • 需要字典的内容在编译时可用。
  • 在模块的已编译表示 (.tam) 中进行序列化。

示例

示例 1:创建外部字典

外部字典 PersonPositiveClues 应该在装入时使用外部文件中的值进行填充。 此外,该字典还应该与其标志所指定的某些西方语言相匹配。

module PersonModuleEnglish;

create external dictionary PersonPositiveClues
  allow_empty false
  with case exact;

export dictionary PersonPositiveClues;

示例 2:词元化

假设字典已启用了词元匹配,并包含两个条目:go shop 和 went shopping。 文档包含文本 Anna went shopping。 词元化形式的输入文档为 Anna go shop。 词元匹配将返回 went shopping 作为 go shop 条目的匹配项。 原始文档文本不会与字典条目进行比较,只有词元化的文档文本才会与字典条目进行比较。 因此,文档中不存在 went shopping 条目的匹配项。

记录具有 AQL Doc 的 create dictionarycreate external dictionary 语句

create dictionary 语句的 AQL Doc 注释包含以下信息:

有关字典的常规描述。

/**
* A dictionary of terms used to greet people.
*
*/

create dictionary GreetingDict as
(
  'regards', 'regds', 'hello', 'hi', 'thanks', 'best', 'subj', 'to', 'from'
);

create external dictionary 语句的 AQL Doc 注释包含有关所创建字典的常规描述。 以下字符串说明了格式:

/**
 * Customizable dictionary of given names.
 * Evaluated on English, French, Italian, German, Portuguese, Spanish text.
 */
create external dictionary CustomFirstNames_WesternEurope
  allow_empty true;

create table 语句

create table 语句用于创建 AQL 表。

AQL 中的 create table 语句用于定义静态查找表,以使用更多信息扩充注释。

语法

create table <table name> (
    <colname> <type> [,  <colname> <type>]* )
 as values
    ( <value> [, <value>]*),
    ...
    ( <value> [, <value>]*);

描述

  • <table name\>

    指定要创建的表的名称。 <table name\> 可以是简单标识或双引号标识。

  • <colname\>

    指定要创建的列的名称。

  • <type\>

    指定关联列的 AQL 数据类型。 所有列的类型都必须是 Text、Integer、Float 或 Boolean。

  • <value\>

    指定要在创建的表中填充的元组。

示例

示例 1:创建公司名称表

在此示例中,create table 语句向公司名称注释添加了更多位置元数据:

-- Create a dictionary of company names
create dictionary CompanyNames as
  ('IBM', 'BigCorp', 'Initech');

-- Find all matches of the company names dictionary.
create view Company as
  extract
    dictionary 'CompanyNames' on D.text as company
  from Document D;


-- Create a table that maps company names to locations of
-- corporate headquarters.
create table NameToLocation
  (name Text, location Text) as
  values
  ('IBM', 'USA'),
  ('BigCorp', 'Apex'),
  ('Initech', 'Dallas'),
  ('Acme Fake Company Names', 'Somewhere');

-- Use the table to augment the Company view with location
-- information.
create view CompanyLoc as
  select N2C.location as loc,
          C.company as company
  from Company C, NameToLocation N2C
  where Equals(GetText(C.company), GetText(N2C.name));

output view CompanyLoc;

记录具有 AQL Doc 的 create table 语句

create table 语句的 AQL Doc 注释包含以下信息:

  • 有关表的常规描述。
  • @field,表示此表的模式中的每个列名。
/** Create a table that maps company names to locations
/** of corporate headquarters.
* @field name name of the company
* @field location location of corporate headquarters
*/

create table NameToLocation
  (name Text, location Text) as
 values
  ('IBM', 'USA'),
  ('Enron', 'UK'),
  ('Initech', 'Dallas'),
  ('Acme Fake Company Names', 'Somewhere');

create external table 语句

已编译的模块在所有输入文档中运行时,可以使用 create external table 语句来指定具有已确定内容的表。 您可在装入时提供表内容,而不是在源 AQL 代码中提供表内容,并且不必重新编译模块。

外部表是功能强大的构造,允许 AQL 开发者在已编译的模块中公开定制点。

语法

create external table <table-name\>
  (<colname\> <type\>
   [,  <colname\> <type\>]* )
   allow_empty <true|false>;

描述

  • <table-name\>

    指定要创建的外部表的名称。 <table-name\> 可以是简单标识或双引号标识。

  • <colname\>

    指定要创建的列的名称。

  • <type\>

    指定关联列的 AQL 数据类型。 所有列的类型都必须是 Text、Integer、Float 或 Boolean。

  • [, <colname\> <type\>]*

    指定要在外部表中使用的其他列和 AQL 对象。

  • allow_empty [true|false]

    指定 allow_empty 子句的值。

    • true

      如果子句为 allow_empty true,那么即使未提供此表的外部内容的 URI,也可以成功运行模块。 如果未提供 URI,那么运行时组件会将其视为空表。

    • false

      如果子句为 allow_empty false,那么必须提供包含外部内容的文件的位置 URI。 指定的文件必须包含内容。 如果未提供 URI,或者文件不包含内容,那么运行时组件会抛出异常。

用法说明

  • 模块的已编译表示包含有关模块定义的外部对象(视图、字典和表)的元数据。
  • 在装入模块期间,必须指定正在装入的模块所需外部表的 URI 列表。
  • 外部表内容支持的格式是包含头的一个 CSV (.csv) 文件。

下表显示了 create external table 语句与 create table 语句之间的差异:

create external table create table
  • 为表定义占位符,其内容在初始化时提供。
  • 需要表的内容在编译时可用。
  • 在模块的已编译表示 (.tam) 中进行序列化。

示例

示例 1:创建在装入时填充的外部表

外部表 PersonNegativeClues 应该在装入时进行填充,因为有 allow_empty false 标志。

module PersonModuleFrench;

create external table PersonNegativeClues (name Text)
  allow_empty false;

export table PersonNegativeClues;

示例 2:使用外部表创建字典

字典还可以通过外部表进行创建,这类似于通过使用 create table 语句声明的内联表创建字典。

create external table Product (nickName Text, formalName Text)
allow_empty false;

/**
  * Dictionary of product nicknames, from the nickName field
  * of the customizable external table Product.
  */
create dictionary ProductDict
from table Product
with entries from nickName;

记录具有 AQL Doc 的 create external table 语句

create external table 语句的 AQL Doc 注释包含以下信息:

  • 有关表的常规描述。
  • @field,表示此表的模式中的每个列名。
/** Create a table that maps company names to locations of corporate headquarters.
* @field name name of the company
* @field location location of corporate headquarters
*/
create external table Company2Location
  (name Text, location Text)
   allow_empty false;

create external view 语句

除了用于保存文本和标签内容的预定义 create external view 视图外,AQL 中的 Document 语句还允许将有关文档的更多元数据指定为新视图。

语法

create external view <view_name> (
        <colname> <type> [, <colname> <type>]*
        )
external_name '<view_external_name>';

描述

  • <view_name\>

    指定外部视图的内部名称。 在 AQL 规则中,外部视图通过此名称进行引用。 <view_name\> 可以是简单标识或双引号标识。 <view_name\> 不能包含句点字符。

  • <colname\>

    指定要在外部视图中定义的列的名称。

  • <type\>

    指定关联列的数据类型。 外部视图列支持的数据类型为 Text、Span、Integer 和 Float。

  • '<view_external_name\>'

    指定外部视图的外部名称。 在外部视图中填充元组的外部系统通过外部名称引用外部视图。 '<view_external_name\>' 必须是包含在单引号 ("ExternalName") 中的字符串常量。

示例

为了说明外部视图,假设有一个示例应用程序需要您识别电子邮件中的人名。

示例 1:识别电子邮件中的人名

假定电子邮件的文本为“Ena, please send me the document ASAP”。 虽然人可能能够根据电子邮件文本理解 Ena 是人名,但编写用于以高精度识别一般文本中人名的 AQL 规则可能过于保守,根据 Ena 是首字母大写单词这一事实,无法以高置信度得出相同结论。

提高规则覆盖范围的一种方法是使用电子邮件的发件人收件人抄送字段中的词作为更多证据。

如果电子邮件是发给“Ena Smith”的,并且应用程序使此信息可供抽取器使用,那么抽取器开发者可以编写更多 AQL 规则来提升抽取器的覆盖范围(基于电子邮件通常发送给人员这一领域知识)。

例如,可以编写 AQL 规则来识别电子邮件元数据字段中的人员记号。 然后,在确定电子邮件文本中的首字母大写记号是否为人名时,可以将此信息用作重要线索。 通常,电子邮件元数据并不是实际电子邮件的一部分,但应用程序可以使用外部视图使这些元数据可供抽取器使用。

在运行时,对于必须处理的每个电子邮件,应用程序可以将电子邮件文本作为文档文本(用于填充 Document 视图)传递。 此外,应用程序还可以使用相应定义的外部视图来传递额外的元数据。

以下语句定义名为 EmailMetadata 的外部视图。 该外部视图的模式包含类型为 Text 的三个字段。 在运行时,视图 EmailMetadata 会自动从名为 EmailMetadataSrc 的外部类型进行填充。 然后,可以在 AQL 规则中引用 EmailMetadata 视图,这类似于引用其他任何视图的做法。

create external view EmailMetadata
  (fromAddress Text, toAddress Text, ccAddress Text)
external_name 'EmailMetadataSrc';

记录具有 AQL Doc 的 create external view 语句

create external view 语句的 AQL Doc 注释包含以下信息:

  • 有关视图的常规描述。
  • @field,表示视图中的每个列名。
/**
* The external view named EmailMetadata, containing three fields
* of type Text. At run time, the view EmailMetadata is
* automatically populated from an external type named
* EmailMetadataSrc.
*
* @field from the fromAddress field of the email
* @field to the toAddress field of the email
* @field cc the ccAddress field of the email
*/

create external view EmailMetadata(fromAddress Text, toAddress Text, ccAddress Text)
external_name 'EmailMetadataSrc';

外部工件的文件格式

支持三种类型的外部工件:外部视图、外部字典和外部表。

外部字典

下面定义了包含外部字典条目的文件的格式:

  • 回车符分隔的文本文件。
  • 每行一个字典条目。
  • 建议的文件扩展名为 .dict,但也可以支持其他文件扩展名。
  • 字典中的条目可以包含多个记号。
  • 注释通过在注释内容前面添加字符 # 来指定。
  • 注释可以在一行中的任意位置开始。
  • 多行注释必须在每行开头包含 # 字符。
  • 字典条目可以包含注释字符,但每个注释字符都须使用反斜杠字符进行转义。 例如,\#

外部表

外部表内容支持的文件格式是包含头的 .csv 文件。

以下示例显示了 create external table 语句以及指定此外部表的内容的 .csv 文件。

create external table Company2Location
  (name Text, location Text)
   allow_empty false;

.csv 文件的第一行包含头。 其余行包含数据。

name,location
IBM,USA
Infosys,India
LG,Korea
Vodafone,UK

外部视图

可以通过以下方式指定外部视图的内容:

  • 如果运行抽取器,那么仅当使用 JSON 输入格式时,才能为数据集合指定外部视图内容。

内置函数

AQL 具有可在抽取规则中使用的内置函数集合。

  • 聚集函数 聚集函数用于对一组输入值实现操作(例如,计数、数学运算和其他运算)。 这些函数仅返回一个结果。
  • 谓词函数 谓词函数通过特定谓词的输入自变量来测试该谓词,并返回相应的 Boolean 值。
  • 标量函数 标量函数通过字段值对一组输入元组执行操作,并返回非 Boolean 值,例如 Span、Text 或 Integer 类型的值。 这些函数可以在 select 语句或 select 语句的 extract 列表中使用。 还可以用作谓词函数的输入。

聚集函数

聚集函数用于对一组输入值实现操作(例如,计数、数学运算和其他运算)。 这些函数仅返回一个结果。

这些函数可以在 select 语句的 select 列表中使用,但不能在 extract 语句中使用。

以下示例是一般形式的聚集函数调用:

Aggregate_Function_Name(argument)

argument 可以是:

  • 表达式,包含 from 子句中视图的列,或者包含涉及 from 子句中视图列的标量函数的组合。

    在除了说明的情况之外的大多数情况下,将忽略自变量的空值。

  • 字符 *,适用于 Count(*) 聚集函数的特殊用例。

    在这种情况下,将对输出的所有行进行计数,包括空值。

聚集函数 自变量类型 返回类型 返回值
Avg(expression) Integer、Float Float 所有输入值的平均值,如果未选择任何行,将为 null
计数 (\ *) integer 所有输入行数
Count(expression) 任何 integer 所有非空输入值的数量
List(expression) Integer、Float、Text、Span 与输入自变量类型相同的标量值的列表 非空输入值的未排序列表:这是包,而不是集,因此可能包含重复项。 如果只选择了空值,那么为空列表
Max(expression) Integer、Float、Text、Span 与自变量类型相同 所有输入值中的最大元素,如果未选择任何行,将为 null
Min(expression) Integer、Float、Text、Span 与自变量类型相同 所有输入值中的最小元素,如果未选择任何行,将为 null
Sum(expression) Integer、Float 与自变量类型相同 所有输入值的和,如果未选择任何行,将为 null

当前版本的限制:

当前版本的 AQL 支持通过 aggregate 函数列表来创建标量值。

示例

以下示例说明了聚集函数可以如何统计人名注释数,或计算与文档中识别到的每个不同姓氏关联的名字集:

-- identify occurrences of given names in the document
create view FirstName as
extract dictionary 'firstNames.dict' on D.text as name
from Document D;

-- identify occurrences of surnames in the document
create view LastName as
extract dictionary 'lastNames.dict' on D.text as name
from Document D;

-- identify complete person names in the document
create view Person as
select F.name as firstname, L.name as lastname
from FirstName F, LastName L
where FollowsTok(F.name, L.name, 0, 0);

-- count the number of person annotations in the document
create view CountPerson as
select Count(*)
from Person;

-- for each distinct surname, output a list of given names associated with it in the document
create view FamilyMembers as
select GetText(P.lastname) as lastname, List(GetText(P.firstname)) as firstnames
from Person P
group by GetText(P.lastname);

以下示例说明了 Min 和 Max 函数的用法:

-- Extract stop words from input text
create view StopWords as
extract
regex /\s(the|in|a|an|as|to|from)\s/ on D.text as match
from Document D;


-- Count the number of times each stop word matched above, was used in the text
create view StopWordsCount as
select
GetText(S.match) as stopword,
Count(S.match) as stopwordcount
from StopWords S
group by GetText(S.match);

-- Retrieve the most used and least used stop word count
create view StopWordUsageCount as
        select Min(S.stopwordcount) as least, Max(S.stopwordcount) as most
from StopWordsCount S;

谓词函数

谓词函数通过特定谓词的输入自变量来测试该谓词,并返回相应的 Boolean 值。

谓词函数的输入自变量除了包含字典、正则表达式等外,还可包含其他标量函数或聚集函数的返回值。 这些函数可以在 where 语句的 select 子句以及 extract 语句的 having 子句中使用。

还有

And 函数接受可变数量的 Boolean 自变量,并返回所有输入参数中逻辑 AND 运算的结果。

在逻辑 AND 运算过程中,AQL 优化器不会尝试优化对此函数的自变量求值的顺序。 如果任何输入为 null,那么结果为 null

假设有以下查询格式:

select ...
from ...
where And(predicate1, predicate2);

因此,使用 AND 运算的查询格式的运行速度通常比以下形式的相同查询慢得多:

select ...
from ...
where predicate1 and predicate2;

请尽可能使用 SQL 样式和关键字,而不要使用此函数。

包含

Contains 函数采用两个范围作为自变量:

Contains(<span1>, <span2>)

如果 TRUE 完全包含 span1,那么此函数会返回 span2。 如果 span2 的开始位置等于 span1 的开始位置或位于其之后,并且结束位置等于 span1 的结束位置或位置其之前,那么将完全包含 span2。 如果任一自变量为 null,那么此函数会返回 null

ContainsDict

ContainsDict 函数用于检查某个范围的文本是否包含给定字典中的任何条目。 此函数接受以下内容作为输入自变量:一个字典、一个可选的标志指定以及一个要进行求值的范围。

ContainsDict('<dictionary>', ['<flags>', ]<span>)

如果范围包含字典的一个或多个匹配项,那么 ContainsDict 函数会返回 TRUE。 标志可以为 ExactIgnoreCase

  • 如果使用 Exact,那么将对字典中的每个术语执行区分大小写的匹配。
  • 如果使用 IgnoreCase,那么对字典中的每个术语执行的匹配不区分大小写。
  • 如果未指定任何标志,那么字典将根据创建时指定的任何标志进行匹配。 如果在创建期间未指定任何标志,那么字典将使用 IgnoreCase 标志进行匹配。

如果范围为 null,那么此函数会返回 null

以下示例说明了 ContainsDict 函数的用法:

create dictionary EmployeePhoneDict as
(
 '121-222-2346', '121-234-1198', '121-235-8891'
);

create view PhoneNum as
extract regex /(\d{3})-(\d{3}-\d{4})/
    on between 4 and 5 tokens in D.text
    return
        group 1 as areaCode
        and group 2 as restOfNumber
        and group 0 as number
from Document D;

create view PhoneNumbers as
select P.number as number
from PhoneNum P
where ContainsDict('EmployeePhoneDict',P.number);

字典始终根据记号边界求值。 例如,如果字典由术语 fish 组成,那么在文本 Let's go fishing! 中没有任何匹配项。

ContainsDicts

ContainsDicts 函数用于检查某个范围的文本是否包含任何给定字典中的任何条目。 此函数接受以下内容作为输入自变量:两个或更多字典、一个可选的标志指定以及一个要进行求值的范围。

ContainsDicts('<dictionary>','<dictionary>','<dictionary>', ['<flags>', ]<span>)

如果范围包含其中至少一个指定字典中的一个或多个匹配项,那么 ContainsDicts 函数会返回 TRUE。 标志可以为 ExactIgnoreCase

  • 如果使用 Exact,那么将对字典中的每个术语执行区分大小写的匹配。
  • 如果使用 IgnoreCase,那么对字典中的每个术语执行的匹配不区分大小写。
  • 如果未指定任何标志,那么字典将根据创建时指定的任何标志进行匹配。 如果在创建期间未指定任何标志,那么字典将使用 IgnoreCase 标志进行匹配。

如果任一自变量为 null 或这两个自变量均为 null,那么此函数会返回 null

以下示例说明了包含 ContainsDicts 标志的 Exact 函数的用法:

create view PersonWithFirstName as
select P.reference as reference
from Person P
where ContainsDicts(
'FirstNamesUsedGlobally',
'FirstNamesUsedInGermanyLong',
'NickNamesUsedGlobally',
'FirstNamesUsedInGermanyShort',
'FirstNamesUsedInItaly',
'FirstNamesUsedInFrance',
'Exact',
P.reference);

ContainsRegex

ContainsRegex 函数用于检查某个范围的文本是否与给定正则表达式相匹配。 此函数接受以下内容:一个要与其匹配的正则表达式、一个可选的标志指定以及一个要与其匹配的输入范围。

ContainsRegex(/<regular expression>/, ['<flags>', ]<span>)

如果该范围的文本(作为单独的 Java™ 字符串)包含正则表达式的一个或多个匹配项,那么此函数会返回 TRUE。 如果范围为 null,那么此函数会返回 null。 可选的标志会影响匹配行为,这类似于 Java 正则表达式中使用的标志。

标志字符串是通过使用 | 作为分隔符组合以下一个或多个标志构成的:

  • CANON_EQ
  • CASE_INSENSITIVE
  • DOTALL
  • LITERAL
  • MULTILINE
  • UNICODE(不使用 CASE_INSENSITIVE 时无意义)
  • UNIX_LINES

标志字符串的示例如下:

'UNICODE | CASE_INSENSITIVE'

假设有以下示例,其中 ContainsRegex 识别产品名称及其任一侧的版本号提及项。 与 MatchesRegex 的示例不同,在此示例中,版本号匹配项并不是使用 regex 严格识别的,而是根据包含与 regex 相匹配的记号的产品名称提及项周围的上下文识别的。


-- dictionary of product names
create dictionary ProductNamesDict as
(
  'IBM WebSphere Application Server',
  'Microsoft Windows',
  'Apple Mac OS',
  'IBM Rational Application Developer',
  'Apache HTTP Server',
  'Eclipse',
  'Google Android'
);

-- extract product names from input text
create view ProductNames as
extract
  dictionary 'ProductNamesDict'
  on D.text as name
from Document D;

-- gather context around product name mention
create view ProductNamesWithContext as
select
  P.name as name,
  LeftContext(P.name, 5) as leftctxt,
  RightContext(P.name, 5) as rightctxt
from ProductNames P;

-- use a regex to identify products with version number mentions on either sides of the product mention
create view ProductsWithVersionNumbers as
(
  select
    P.name as productname,
    P.leftctxt as productversion
  from ProductNamesWithContext P
  where ContainsRegex (/v\d((\.\d)+)?/, P.leftctxt)
)
union all
(
  select
    P.name as productname,
    P.rightctxt as productversion
  from ProductNamesWithContext P
  where ContainsRegex (/v\d((\.\d)+)?/, P.rightctxt)
);

Equals

Equals 函数采用任意类型的两个自变量:

Equals(<arg1>, <arg2>)

如果两个范围的开始和结束偏移量相同,并且包含相同的文本,那么这两个范围视为相等。 如果任一自变量为 null 或这两个自变量均为 null,那么此函数会返回 null

以下示例说明了 Equals 函数的用法。

-- Select phone number spans whose text is equal to 001-543-2217
create view PhoneNumber as
select P.number as number
from PhoneNum P
where Equals('001-543-2217',GetText(P.number));

Follows

Follows 谓词函数采用两个 Span 自变量和两个 Integer 自变量:

Follows(<span1>, <span2>, <minchar>, <maxchar>)

如果 TRUE 结束位置与 span1 开始位置之间的字符数介于 span2minchar(含 minchar 和 maxchar)之间,那么此函数会返回 maxchar。 如果任何自变量为 null,那么此函数会返回 null

FollowsTok

FollowsTok 谓词函数是 Follows 的一个版本;但是,FollowsTok 距离自变量计算的是记号数,而不是字符数:

FollowsTok(<span1>, <span2>, <mintok>, <maxtok>)

如果 FollowsTok 结束位置与 TRUE 开始位置之间的记号数介于 span1span2(含 mintok 和 maxtok)之间,那么 mintok 函数会返回 maxtok。 如果任何自变量为 null,那么此函数会返回 null

GreaterThan

GreaterThan 谓词函数采用任意类型的两个自变量:

GreaterThan(<arg1>, <arg2>)

如果 <arg1> 大于 <arg2>,那么此函数将返回 TRUE 。 如果任一自变量为 FALSE,那么此函数会返回 null

IsNull

IsNull 函数用于测试数据是否为 null。 此函数采用任何类型的单个自变量,如果单个自变量为 TRUE,那么此函数会返回 null,否则会返回 FALSE。 此谓词的行为和已定义的 NotNull 谓词不同于在输入为 null 时返回 null 的其他所有谓词。

MatchesDict

MatchesDict 函数采用一个字典(如在字典抽取中)、一个可选的标志指定以及一个范围作为自变量:

MatchesDict('<dictionary>', ['<flags>', ]<span>)

如果范围与字典中的一个或多个术语完全匹配,那么 MatchesDict 函数会返回 TRUE。 标志可以为 ExactIgnoreCase

  • 如果使用 Exact,那么将对字典中的每个术语执行区分大小写的匹配。
  • 如果使用 IgnoreCase,那么对字典中的每个术语执行的匹配不区分大小写。
  • 如果未指定任何标志,那么字典将根据创建时指定的任何标志进行匹配。 如果在创建期间未指定任何标志,那么字典将使用 IgnoreCase 标志进行匹配。

如果任何自变量为 null,那么此函数会返回 null

字典始终根据记号边界求值。 例如,如果字典由术语 fish 组成,那么在文本 Let's go fishing! 中没有任何匹配项。

MatchesRegex

MatchesRegex 函数的语法类似于 ContainsRegex。 与 ContainsRegex 函数不同,仅当作为单独 Java 字符串采用的范围的整个文本与正则表达式相匹配时,MatchesRegex 函数才会返回 TRUE。 如果任何自变量为 null,那么此函数会返回 null。 可选的标志会影响匹配行为,这类似于 Java 正则表达式中使用的标志。

MatchesRegex(/<regular expression>/, ['<flags>', ]<span>)

标志字符串是通过使用 | 作为分隔符组合以下部分标志构成的:

  • CANON_EQ
  • CASE_INSENSITIVE
  • DOTALL
  • LITERAL
  • MULTILINE
  • UNICODE(不使用 CASE_INSENSITIVE 时无意义)
  • UNIX_LINES

标志字符串的示例如下:

'UNICODE | CASE_INSENSITIVE'

假设有以下示例,其中 MatchesRegex 用于识别产品名称及其右侧的版本号提及项。 与 ContainsRegex 部分中的示例不同,在此示例中,确切的版本号被识别为紧跟在产品名称提及项之后的记号。

-- gather right context around product name mention
create view ProductNamesWithContext as
select
  P.name as name,
  RightContext(P.name, 5) as rightctxt
from ProductNames P;

-- use a regex to identify products with version number mentions to the right
create view ProductsWithVersionNumbers as
select
  P.name as productname,
  P.rightctxt as productversion
from ProductNamesWithContext P
where MatchesRegex (/v\d((\.\d)+)?/, P.rightctxt);

Not

Not 函数采用单个 Boolean 自变量,并返回其补充项。 如果自变量为 null,那么此函数会返回 null

NotNull

NotNull 函数采用任何类型的单个自变量。

顾名思义,如果自变量的值不为 null,那么 NotNull 函数会返回 TRUE;如果自变量为 FALSE,那么会返回 null

或者

Or 函数采用可变数量的非 null Boolean 自变量。 如果任何自变量为 null,那么此函数会返回 null

如果其中任一自变量求值为 Or,那么 TRUE 函数会返回 TRUE

Overlaps

Overlaps 函数采用两个 Span 自变量:

Overlaps(<span1>, <span2>)

如果两个输入范围在文档文本中重叠,那么此函数会返回 TRUE。 如果任一自变量为 null,那么此函数会返回 null

标量函数

标量函数通过字段值对一组输入元组执行操作,并返回非 Boolean 值,例如 Span、Text 或 Integer 类型的值。 这些函数可以在 select 语句或 select 语句的 extract 列表中使用。 还可以用作谓词函数的输入。

如果需要 Span 对象,而提供的是 Text 对象,那么会基于此 Text 对象,使用覆盖该 Text 对象的整个长度的开始和结束偏移量来自动生成转换的 Span 对象。

如果需要 Text 对象,而提供的是 Span 对象,那么会基于该 Span 对象的文本值自动生成转换的 Text 对象。

Chomp

Chomp 函数类似于 Perl 中的 Chomp 运算符,但 Chomp 跨范围(而不是字符串)运行:

Chomp(<span1>)

以下示例说明了 Chomp 函数的用法。

detag Document.text as DetaggedDoc
annotate
element 'a' as Anchor
with attribute 'href' as linkTarget;

create view Links as
select Chomp(A.linkTarget) as link
from Anchor A;

如果输入范围在开头或结尾包含任何空格,那么 Chomp 函数会将范围缩减到足够小,以除去空格。 然后,此函数会返回没有前导或尾部空格的新范围。 如果输入范围没有前导或尾部空格,那么 Chomp 函数会返回相同的范围。 如果输入范围为 null,那么 Chomp 会返回 null

CombineSpans

CombineSpans 函数采用两个范围作为输入,如果这两个范围基于同一文本对象,那么此函数会返回完全涵盖两个输入范围的最短范围。

CombineSpans(['IgnoreOrder',] <span1>, <span2>)

CombineSpans 函数对其输入范围的顺序很敏感,除非使用 IgnoreOrder 参数。 使用可选的 IgnoreOrder 参数时,将忽略两个范围的顺序。

以下示例说明了 CombineSpans 函数的用法。

create view FullName as
     select
             CombineSpans('IgnoreOrder',F.name, L.name) as fullName
     from
             FirstName F,
             LastName L
     where
             FollowsTok(F.name, L.name, 0,0);

此函数的语义如下所示:

  • 如果 span1span2null,或者这两个范围基于不同的 Text 对象,那么此函数会返回 null
  • 否则,如果 span1 小于 span2,或者使用了 IgnoreOrder 参数,那么此函数会返回涵盖 span1span2 的最短范围。
  • 否则,如果 span1 大于 span2,并且未使用 IgnoreOrder,那么此函数会返回运行时错误。

根据 Span 的定义,CombineSpans 函数自变量的不同场景如下所示:

  • 范围 2 始终在范围 1 之后。 换言之,保持从左到右的顺序:

    CombineSpans([0,7], [3,7]) returns the span [0,7]
    CombineSpans([0,7], [8,10]) returns the span [0,10]
    CombineSpans([0,7], [3,6]) returns the span [0,7]
    CombineSpans([0,7], [0,7]) returns the span [0,7]
    
  • 范围 2 不在范围 1 之后。 换言之, 维护从左到右顺序:

    CombineSpans(‘IgnoreOrder’, [0,10], [0,7]) returns the span [0,10]
    CombineSpans(‘IgnoreOrder’, [3,6], [0,7]) returns the span [0,7]
    CombineSpans(‘IgnoreOrder’, [3,7], [0,7]) returns the span [0,7]
    CombineSpans(‘IgnoreOrder’, [8,10], [0,7]) returns the span [0,10]
    CombineSpans([3,6], [0,7]) will result in Runtime error as the IgnoreOrder flag has not been specified.
    

GetBegin 和 GetEnd

GetBegin 函数采用单个 Span 自变量,并返回输入范围的开始偏移量。

例如,

GetBegin([5, 10])

会返回值 5

与此类似,GetEnd 函数用于返回其输入范围的结束偏移量。

以下示例说明了 GetBeginGetEnd 函数的用法。

create view PersonOffsets as
select GetBegin(P.name) as offsetBegin, GetEnd(P.name) as offsetEnd
from Person P;

对于这两个函数,如果自变量为 null,那么函数会返回 null

GetLanguage

GetLanguage 函数采用单个 Span 自变量,并返回范围的源文本的两字母语言代码。 如果自变量为 null,那么此函数会返回 null

仅当数据源使用合适的语言来标记文本字段时,此语句才会生成有意义的结果。

GetLemma

GetLemma 函数采用单个 Span 或 Text 对象作为自变量,并返回包含词元化形式的输入范围的字符串。 如果自变量为 null,那么此函数会返回 null。 具有用于词元匹配的字典条目时,此函数可以确定记号化器返回的词元化形式的各种记号。 例如,对于范围 went shopping,GetLemma 会返回词元字符串 go shop

此函数的结果遵循以下规则:

  • 如果输入范围在令牌开始时开始,在令牌结束时结束,那么结果包含以第一个令牌的引理开头的词元序列,后跟一个空格,后跟第二个令牌的引理,后跟一个空格,依此类推 (例如,狗猫鱼鸟 ...)。 如果记号的词元由空格组成,请使用反斜杠字符 (\) 对空格进行转义。
  • 如果输入范围以空格开头或结尾(例如,在两个记号之间开始或在两个记号之间结束),那么此函数会忽略开头和尾部空格。
  • 如果输入范围从记号中间开始或在记号中间结束,那么输出将由以下内容组成(顺序如下),各项之间用空格分隔:
    • 表面形式的第一个部分记号(如果存在)。
    • 词元序列,对应于第一个到最后一个完整记号。 如果任何完整记号的词元由空格组成,请使用反斜杠字符 (\) 对空格进行转义。
    • 表面形式的最后一个部分记号(如果存在)。

如果使用的记号化器无法生成词元,那么此函数会返回错误。

可以使用 GetLemma() 函数来创建词元化形式的字典。 请对包含其词元化形式要包括在字典中的术语的输入调用 GetLemma()

GetLength

GetLength 函数采用单个 Span 自变量,并返回输入范围的长度。 如果自变量为 null,那么此函数会返回 null

例如,

GetLength([5, 12])

会返回值 7。

GetLengthTok

GetLengthTok 函数采用单个 Span 自变量,并返回记号中输入范围的长度。 如果输入自变量为 null,那么此函数会返回 null

GetString

GetString 函数采用单个 AQL 对象作为其自变量,并返回通过 Text 对象的字符串表示构成的该对象。

对于 Span 和 Text 自变量,返回的值与 GetText() 返回的值不同。 对于 Text 对象,返回的值包含用单引号括起的文本字符串。 对于 Span 对象,返回的值还包含用方括号括起的偏移量。

对于标量列表,此函数会返回列表中元素的 GetString() 值,并用分号连接各值。 对于 Integer、Float、Boolean 和 String 自变量,此函数会将自变量的值作为字符串返回。 对于值为 null 的自变量,此函数会返回 null

GetText

GetText 函数采用单个 Span 或 Text 对象作为自变量。 对于范围输入,此函数会根据该范围标记的实际文本字符串来返回文本对象。 对于文本输入,此函数会返回输入文本对象。 如果输入为 null,那么此函数会返回 null。 例如:

GetText([5, 12])

该范围返回文档中字符位置 5 - 12 的子字符串。

GetText 函数有两个主要用途。

测试两个范围所标记的文本之间的字符串是否相等。

-- Create a dictionary of company names
create dictionary CompanyNames as
('IBM', 'BigCorp', 'Initech');

-- Find all matches of the company names dictionary.
create view Company as
extract
    dictionary 'CompanyNames' on D.text as company
from Document D;


-- Create a table that maps company names to locations of
-- corporate headquarters.
create table NameToLocation (name Text, location Text) as
values
    ('IBM', 'USA'),
    ('BigCorp', 'Apex'),
    ('Initech', 'Dallas'),
    ('Acme Fake Company Names', 'Somewhere');

-- Use the table to augment the Company view with location
-- information.
create view CompanyLoc as
select N2C.location as loc, C.company as company
from Company C, NameToLocation N2C
where Equals(GetText(C.company), GetText(N2C.name));

output view CompanyLoc;

将一个文档拆分为多个更小的子文档。

例如,如果主文档是由多个博客条目组成的博客,那么可以使用 GetText 为每个博客条目创建一个子文档。

detag Document.text as DetaggedBlog
annotate
    element 'blog' as Blog
    with attribute 'name' as title;


create view BlogEntry as
select B.match as entry, B.title as title
from Blog B;

-- Turn each tuple in the BlogEntry view into a sub-document
create view BlogEntryDoc as
select GetText(B.title) as title, GetText(B.entry) as body
from BlogEntry B;

output view BlogEntryDoc;

--Dictionary for Companies
create dictionary CompanyNameDict as
(
    'A Corporation', 'B Corporation'
);

-- Run an extraction over the sub-documents.
-- The spans that this "extract" statement creates will have
-- offsets relative to the blog entries themselves, as opposed
-- to the original multi-entry document.
create view CompanyName as
extract dictionary 'CompanyNameDict' on B.body as name
from BlogEntryDoc B;

output view CompanyName;

LeftContext 和 RightContext

LeftContext 函数采用 Span 和 Integer 对象作为输入:

LeftContext(<input span>, <nchars>)

LeftContext(<input span\>, <nchars\>) 函数返回一个新范围,该范围包含紧靠 <input span\>左侧的文档的 nchars 字符。 如果输入范围从文档开头开始少于 <nchars\> 个字符,那么 LeftContext() 将返回从文档开头开始并一直持续到输入范围开始的范围。

例如,LeftContext([20, 30], 10) 会返回范围 [10, 20]。 范围 LeftContext([5, 10], 10) 会返回 [0, 5]。

如果输入从文档的第一个字符开始,那么 LeftContext() 会返回长度为零的范围。 与此类似,RightContext 函数会返回其输入范围右侧的文本。 对于这两个函数,如果任一自变量为 null,那么函数会返回 null

LeftContextTok 和 RightContextTok

LeftContextTokRightContextTok 函数是 LeftContextRightContext 的变体版本,采用记号数作为距离:

LeftContextTok(<input span>, <num tokens>)
RightContextTok(<input span>, <num tokens>)

以下示例说明了 RightContextTok 函数的用法。

create view Salutation as
extract
 regex /Mr\.|Ms\.|Miss/
  on D.text as salutation
  from Document D;

--Select the token immediately following a Salutation span
create view NameCandidate as
select RightContextTok(S.salutation, 1) as name
from Salutation S;

对于这两个函数,如果任一自变量为 null,那么函数会返回 null

Remap

Remap 函数采用单个 Span 自变量:

Remap(<span>)

如果输入范围基于的文本对象是通过转换另一个文本对象生成的,那么 Remap 函数会将该范围转换为基于原始“源”文本的范围。

例如,如果范围 N.name 基于已去除标记的文档,该文档是通过 AQL detag 语句运行 HTML 所生成的,那么

Remap(<N.name>)

会返回基于原始 HTML 的等效范围。

如果通过对空文档运行 detag 语句生成了 Span 自变量,那么此函数会将范围重新映射到文档的开头(换言之,就是 Document.text[0-0])。 此外,如果 detag 语句生成了空字符串,那么此函数也会将范围重新映射到文档开头。 AQL 中生成此类派生文本对象的唯一部分是 detag 语句。

以下示例说明了 Remap 函数的用法:

-- Detag the HTML document and annotate the anchor tags
detag Document.text as DetagedDoc
    annotate
    element 'a' as Anchor;

-- Remap the Anchor Tags
create view AnchorTag as
select Remap(A.match) as anchor
from Anchor A;

如果 Remap 的自变量不是派生的文本对象,也不是基于派生的文本对象的范围,那么此函数会生成错误。 如果自变量为 null,那么此函数会返回 null

SpanBetween

SpanBetween 函数采用两个范围作为输入,如果这两个范围基于同一文本对象,那么此函数会返回恰好涵盖这两个范围之间文本的范围;如果这两个范围基于不同的文本对象,那么会返回 null

SpanBetween(['IgnoreOrder',] <span1>, <span2>)

使用可选的 IgnoreOrder 参数时,AQL 编译器将忽略两个范围的顺序。

如果两个跨度之间不存在任何文本,那么 SpanBetween 将返回从 <span1>末尾开始的空跨度。

CombineSpans类似, SpanBetween 对其输入的顺序敏感,除非您使用 IgnoreOrder 参数。 因此,

SpanBetween([5, 10], [50, 60])

会返回范围 [10, 50],而

SpanBetween([50, 60], [5, 10])

会返回范围 [60, 60]

如果 SpanBetween 的自变量为 null,那么此函数会返回 null

SpanIntersection

SpanIntersection 函数采用两个范围作为输入,如果这两个范围基于同一文本对象,那么此函数会返回涵盖这两个输入所涵盖的文本的范围;如果这两个范围基于不同的文本对象,那么会返回 null

SpanIntersection(<span1>, <span2>)

例如,

SpanIntersection([5, 20], [10, 60])

会返回范围 [10, 20],而

SpanIntersection([5, 20], [7, 10])

会返回范围 [7, 10]

如果这两个范围不重叠,那么 SpanIntersection 会返回 null。 如果任一范围输入为 null,那么此函数会返回 null

SubSpanTok

SubSpanTok 函数采用一个范围及该范围中的一对偏移量作为输入:

SubSpanTok(<span>, <first_tok>, <last_tok>)

如函数名称所示, <first_tok><last_tok> 自变量是标记中的距离,根据系统配置为使用的任何记号化器。

SubSpanTok 函数会返回一个新范围,其中涵盖输入范围内所指示的记号范围(含两端值)。 如果指定的记号范围从输入范围内开始,并且超出输入范围的结束位置,那么将返回包含的记号范围部分。 如果 <first_tok> 表示超出目标范围的距离,那么 SubSpanTok 将返回从输入范围开始的零长度范围。

如果任何输入为 null,那么此函数会返回 null

ToLowerCase

ToLowerCase 函数采用单个对象作为其自变量,并返回该对象的小写字符串表示。 转换为字符串的执行方式与 GetString() 函数执行转换的方式相同。

此函数的主要用途是执行不区分大小写的等值连接:

where Equals(ToLowerCase(C.company), ToLowerCase(N2C.name))

如果输入对象为 null,那么此函数会返回 null

create function 语句

要对抽取的值执行 AQL 不支持的操作,可以定义定制函数以在抽取规则中使用,这种函数称为用户定义的函数 (UDF)。

AQL 支持用户定义的标量函数和用户定义的表函数。 Java™ 和 PMML 是 UDF 唯一支持的实现语言。 标量函数返回单个标量值,而表函数返回一个或多个元组(也就是一个表)。

通过执行以下四个步骤来实现用户定义的函数 (UDF):

  1. 实现函数

    AQL 支持使用 Java 或 PMML 实现的用户定义的函数 (UDF)。

  2. 在 AQL 中声明函数。

    通过 create function 语句,可以使用户定义的标量函数和 PMML 文件中的机器学习模型可供 AQL 使用。

  3. 在 AQL 中使用函数。

    用户定义的函数与 AQL 语句和子句一起使用。

  4. 调试 UDF。

    由于基于 Java 的用户定义的函数 (UDF) 在 Java 类中作为公共方法实现,因此可采用调试 Java 程序的相同方式来调试 UDF。

实现用户定义的函数

AQL 支持使用 Java™ 或 PMML 实现的用户定义的函数 (UDF)。

此部分特别着重说明使用 Java 实现的 UDF。 对于使用 PMML 实现的 UDF,机器学习模型会存储在 PMML XML 文件中。 请参阅 PMML 文档以了解如何创建这些模型: [http://dmg.org/pmml/v4-1/GeneralStructure.html]

您可以在 Java 类中将标量 UDF 作为公共方法实现。 还可以在扩展 API com.ibm.avatar.api.udf.TableUDFBase 的 Java 类中将表 UDF 作为公共方法实现。 此外,表 UDF 可以选择性覆盖超类 com.ibm.avatar.api.udf.TableUDFBase 的 initState() 方法。

如果 UDF 具有类型为 Span、Text 或 ScalarList 的输入参数,或者 UDF 的返回类型为 Span、Text 或 ScalarList,那么 Java 类必须导入 com.ibm.avatar.algebra.datamodel.Span、com.ibm.avatar.algebra.datamodel.Text 或 com.ibm.avatar.algebra.datamodel.ScalarList 类才能编译。

表函数需要额外 API 来提供输出模式信息。 这些 API 属于基类 com.ibm.systemt.api.udf.TableUDFbase。 如果子类包含多个表函数,那么会为每个实例创建单独的 Java 对象。

要使 UDF 代码能够从其 JAR 文件中检索非类资源,唯一支持的方法是 getResourceAsStream()。 不支持使用其他方法(例如,getResource()getResources()getSystemResource())来访问资源。 例如,包含 com.ibm.myproject.udfs 包中 my.properties 属性文件的 UDF JAR 文件可以使用以下 Java 语句对其进行访问:

InputStream in = this.getClass().getClassLoader().
  getResourceAsStream(“com/ibm/myproject/udfs/my.properties”);

使用 Java 实现的 UDF 的生命周期

在对抽取器进行编译 (CompileAQL.compile() API)、实例化 (OperatorGraph.createOG() API) 和验证 (OperatorGraph.validateOG() API) 时将执行以下操作,对于 AQL 中的每个 create function 语句恰好执行一次:

  1. 使用全新的类装入器来装入包含 UDF 方法的 Java 类。 该类会在相应的 create function 语句中指定的 UDF JAR 中进行搜索。 装入此类时所需的其他任何类也会在同一 UDF JAR 中进行搜索,如果找不到所需类,那么会将搜索委派给已实例化运行时的类装入器。
  2. 创建该类的实例。
  3. 对于表 UDF,调用 initState() 方法。

由于这些步骤是针对每个 create function 语句执行的,因此如果 Java 类包含多个 UDF 方法(以及在 AQL 的不同 create function 语句中使用的所有方法),那么将多次装入该类,每次都会在单独的类装入器中执行(步骤 1)。 接下来,将创建该类的多个实例(步骤 2),然后对每个实例调用一次 initState() 方法(步骤 3)。 每个 UDF 都会生成单独的类装入器的原因是,为了允许不同的 UDF 使用同一类(或库)的不同版本。

在运行时 (OperatorGraph.execute() API),不会重新装入 UDF 类,因为该类已在抽取器实例化期间装入。 将根据需要调用实现 UDF 的 Java 方法,以计算输出的各部分。 抽取器用于单个线程时,这意味着对于每个输入文档,存在的调用次数可能为零,也可能有很多次(并且极有可能在 Operator Graph 对象的整个生命周期中有多次调用)。 抽取器在多个线程中共享时,不同的线程可以在大约同一时间(使用不同的输入)调用此方法。

如果需要对 UDF 代码的特定部分进行恰好一次求值(例如,为了初始化在各个调用中 UDF 方法所需的数据结构),那么不应将该代码放在 UDF 求值方法中,因为该方法极有可能在抽取器的整个生命周期中执行多次(与 OperatorGraph 对象一样),并且当抽取器在多个线程中共享时,可能会几乎同时调用此方法。 请改为执行以下操作:

  1. 对于标量 UDF,遵循标准 Java 编程原则。 例如,将代码放在静态块中。
  2. 对于表 UDF,将代码放在 initState() 方法中,或者遵循标准 Java 编程原则。 例如,将代码放在静态块中。

执行上述操作时,请记住,在抽取器编译、实例化和验证期间,可能会多次装入该类,如以上步骤 1-3 中所述。 如果用于初始化 UDF 的代码放在静态块中,那么每次装入类时(可能会多次装入),都会执行该代码,这会在编译和运算符图形实例化期间引入开销。 如果开销较大,请遵循以下最佳做法:

  • 要最大限度降低编译时的开销,最佳做法是将消耗大量编译时间的资源(如初始化时间很长的大型字典或大型 UDF)放在单独的 AQL 模块中,并将其导出。 尝试仅编译发生更改的 AQL 模块以及依赖于这些模块的其他模块。
  • 要最大限度降低运算符图形实例化和验证期间的开销:
    1. 如果单个 UDF 方法需要初始化代码,请将该 UDF 方法放在单独的 Java 类中(并像之前那样将高开销实例化代码放在静态初始化程序中,或者使用可确保代码仅执行一次的其他某种机制)。
    2. 如果初始化代码由多个 UDF 方法共享,那么运行时和 AQL 不会提供显式机制来确保初始化代码恰好执行一次。 在此类情况下,如果初始化时间太长,那么唯一的解决方案是将共享资源和初始化代码放在系统类路径上。 换言之,就是将初始化代码放在新类 MySeparateClass.java 中,并将初始化结果存储在此类的静态变量中,例如 MySeparateClass.myVar。 将 MySeparateClass.java 以及初始化期间所需的任何资源一起打包成 JAR 文件,然后将该 JAR 放在系统类路径上。 UDF 方法可以使用 MySeparateClass.myVar 静态变量来引用已初始化的模型。 现在,初始化代码将恰好执行一次 - 在系统类装入器装入 MySeparateClass.java 类时执行。

提示:

模块的已编译表示(.tam 文件)包含 UDF 的序列化二进制代码。 因此,如果两个不同模块中的 create function 语句引用了相同的 UDF 代码,那么该 UDF 代码会在这两个模块的已编译表示中进行序列化。 换言之,UDF 代码会序列化两次。 在此类情况下,可以通过创建充当 UDF 库的单独模块来避免冗余,然后在其他模块中复用该库。 要创建 UDF 库,请执行以下步骤:

  1. 创建一个模块。
  2. 将所有 UDF JAR 文件放入该模块中。
  3. 使用 create function 语句定义所有必需的函数。
  4. 使用 export function 语句将这些函数全部导出,以便可以在其他模块中将其导入并加以使用。

示例

示例 1:实现标量 UDF

此示例显示了名为 toUpperCase 的标量 UDF 的实现。 此 UDF 采用类型为 Span 的值作为输入,并输出由输入 Span 的文本内容(转换为大写)组成的字符串值。

package com.ibm.biginsights.textanalytics.udf;
import com.ibm.avatar.algebra.datamodel.Span;

/**
  * @param s input Span
  * @return all upper-case version of the text of the input Span
  */
public String toUpperCase(Span s) {
  return s.getText().toUpperCase();
}

示例 2:实现将表用作输入的标量 UDF

此示例显示了名为 TableConsumingScalarFunc 的标量 UDF 的实现。 此 UDF 采用两个元组列表作为输入,并输出连接两个输入元组列表的内容的字符串值。

import com.ibm.avatar.algebra.datamodel.TupleList;

/**
  * Example implementation of a user-defined scalar function using a table as input
*/
public class TableConsumingScalarFunc
{

  /**
    * Main entry point to the `scalar` function. This function takes two lists of tuples and concatenates them into a single string.
    *
    * @param arg1 first set of tuples to merge
    * @param arg2 second set of tuples to merge
    * @return the two sets of tuples, concatenated
    */
  public String eval (TupleList arg1, TupleList arg2)
  {
    StringBuilder sb = new StringBuilder ();

    sb.append("Input1: ");
    sb.append(arg1.toPrettyString ());
    sb.append("\nInput2: ");
    sb.append(arg2.toPrettyString ());

    return sb.toString ();
  }
}

示例 3:实现将表用作输入的表 UDF

此示例显示了名为 TableConsumingTableFunc 的表 UDF 的实现。 此 UDF 采用两个元组列表作为输入,并输出单个元组列表,其中包含第一个输入中的元组与第二个输入中元组的混合。 请注意,该实现从基类 com.ibm.avatar.api.udf.TableUDFBase 扩展,该基类提供了用于获取输入和输出模式的 API。

package com.ibm.test.udfs;

import java.lang.reflect.Method;

import com.ibm.avatar.algebra.datamodel.AbstractTupleSchema;
import com.ibm.avatar.algebra.datamodel.FieldCopier;
import com.ibm.avatar.algebra.datamodel.Tuple;
import com.ibm.avatar.algebra.datamodel.TupleList;
import com.ibm.avatar.algebra.datamodel.TupleSchema;
import com.ibm.avatar.api.exceptions.TableUDFException;
import com.ibm.avatar.api.udf.TableUDFBase;

/**
  * Example implementation of a user-defined table function
*/
public class TableConsumingTableFunc extends TableUDFBase
{

  /** Accessors for copying fields from input tuples to output tuples. */
  private FieldCopier arg1Copier, arg2Copier;

  /**
    * Main entry point to the `table` function. This function takes two lists of tuples and generates a new list of wide
    * tuples, where element i of the returned list is created by joining element i of the first input with element i of
    * the second input.
    *
    * @param arg1 first set of tuples to merge
    * @param arg2 second set of tuples to merge
    * @return the two sets of tuples, interleaved
    */
  public TupleList eval (TupleList arg1, TupleList arg2)
  {

    TupleSchema retSchema = getReturnTupleSchema ();
    TupleList ret = new TupleList (retSchema);

    // We skip any records that go off the end
    int numRecs = Math.min (arg1.size (), arg2.size ());

    for (int i = 0; i < numRecs; i++) {
      Tuple retTuple = retSchema.createTup ();

      Tuple inTup1 = arg1.getElemAtIndex (i);
      Tuple inTup2 = arg2.getElemAtIndex (i);

      arg1Copier.copyVals (inTup1, retTuple);
      arg2Copier.copyVals (inTup2, retTuple);

      // System.err.printf ("%s + %s = %s\n", inTup1, inTup2, retTuple);

      ret.add (retTuple);
    }

    return ret;
  }

  /**
    * Initialize the internal state of the `table` function. In this case, we create accessors to copy fields from input
    * tuples to output tuples.
    *
    * @see com.ibm.avatar.api.udf.TableUDFBase#initState() for detailed description
    */
  @Override
  public void initState () throws TableUDFException
  {
    // Create accessors to do the work of copying fields from input tuples to output tuples
    AbstractTupleSchema arg1Schema = getRuntimeArgSchema ().getFieldTypeByIx (0).getRecordSchema ();
    AbstractTupleSchema arg2Schema = getRuntimeArgSchema ().getFieldTypeByIx (1).getRecordSchema ();
    TupleSchema retSchema = getReturnTupleSchema ();

    // Create offsets tables for a straightforward copy.
    String[] srcs1 = new String[arg1Schema.size ()];
    String[] dests1 = new String[arg1Schema.size ()];
    String[] srcs2 = new String[arg2Schema.size ()];
    String[] dests2 = new String[arg2Schema.size ()];

    for (int i = 0; i < srcs1.length; i++) {
      srcs1[i] = arg1Schema.getFieldNameByIx (i);
      dests1[i] = retSchema.getFieldNameByIx (i);
    }
    for (int i = 0; i < srcs2.length; i++) {
      srcs2[i] = arg2Schema.getFieldNameByIx (i);
      dests2[i] = retSchema.getFieldNameByIx (i + srcs1.length);
    }

    arg1Copier = retSchema.fieldCopier (arg1Schema, srcs1, dests1);
    arg2Copier = retSchema.fieldCopier (arg2Schema, srcs2, dests2);
  }

  /**
    * Check the validity of tuple schemas given in the AQL “create function”.
    *
    * @see com.ibm.avatar.api.udf.TableUDFBase#validateSchema(TupleSchema, TupleSchema, TupleSchema, Method, Boolean) for
    *      description of arguments
    */
  @Override
  public void validateSchema (TupleSchema declaredInputSchema, TupleSchema runtimeInputSchema,
    TupleSchema returnSchema, Method methodInfo, boolean compileTime) throws TableUDFException
  {
    // The output schema should contain the columns of the two input schemas in order.
    AbstractTupleSchema arg1Schema = declaredInputSchema.getFieldTypeByIx (0).getRecordSchema ();
    AbstractTupleSchema arg2Schema = declaredInputSchema.getFieldTypeByIx (1).getRecordSchema ();

    System.err.printf ("TableConsumingTableFunc: Input schemas are %s and %s\n", arg1Schema, arg2Schema);

    // First check sizes
    if (returnSchema.size () != arg1Schema.size () + arg2Schema.size ()) { throw new TableUDFException (
      "Schema sizes don't match (%d + %d != %d)", arg1Schema.size (), arg2Schema.size (), returnSchema.size ()); }

    // Then check types
    for (int i = 0; i < arg1Schema.size (); i++) {
      if (false == (arg1Schema.getFieldTypeByIx (i).equals (returnSchema.getFieldTypeByIx (i)))) { throw new TableUDFException (
        "Field type %d of output doesn't match corresponding field of first input arg (%s != %s)", i,
        returnSchema.getFieldTypeByIx (i), arg1Schema.getFieldTypeByIx (i)); }
    }

    for (int i = 0; i < arg2Schema.size (); i++) {
      if (false == (arg2Schema.getFieldTypeByIx (i).equals (returnSchema.getFieldTypeByIx (i + arg1Schema.size ())))) { throw new TableUDFException (
        "Field type %d of output doesn't match corresponding field of first input arg (%s != %s)", i
          + arg1Schema.size (), returnSchema.getFieldTypeByIx (i + arg1Schema.size ()), arg2Schema.getFieldTypeByIx (i)); }
    }
  }

}

声明用户定义的函数

通过 create function 语句,可以使用户定义的标量函数和 PMML 文件中的机器学习模型可供 AQL 使用。

语法

create function 语句的一般语法如下所示:

create function <function-name>(<input-schema-definition>)
return <return-type> [like <column-name>] | table ( <output-schema-definition)
external_name <ext-name>
language [java | pmml]
[deterministic | not deterministic]
[return null on null input | called on null input];
<input-schema-definition>
<column-name> <data-type> | table (<output-schema-definition>) as locator [,<column-name> <data-type> | table (<output-schema-definition>) as locator ]*
<output-schema-definition>
<column-name> <data-type> [,<column-name> <data-type>]*

描述

  • <function-name\>

    <function-name\> 声明 UDF 的 AQL 名称。 在 AQL 代码中使用此名称引用 UDF。

  • <input-schema-definition\>

    指定 UDF 的输入参数。 输入参数具有指定为 <column-name\>的名称,并且可以是标量类型或表定位器。 当语言为 PMML 时,该函数必须采用名为 params 的单个表作为自变量。

  • <column-name\>

    指定 UDF 的输入或输出中列的名称。

  • <data-type\>

    UDF 的输入标量参数的类型、UDF 的输入表的模式中列的类型,或 UDF 的输出表的模式中列的类型。 <data-type\> 的可能值为 Integer , Float , String , Text , Span , Boolean 或 ScalarList。

  • table (<output-schema-definition\>) as locator

    此输入类型允许函数将给定 AQL 视图的整个内容(对当前文档进行计算的结果)作为输入。 locator 参数将视图或表作为自变量引用。

  • <return-type\>

    对于标量 UDF , <return-type\> 指定 UDF 返回的标量值的类型。 返回类型的可能的值为 Integer、Float、String、Text、Span、Boolean 或 ScalarList。 如果返回类型为 Integer,那么实现 UDF 的 Java™ 函数会返回类型为 Integer 的对象。 UDF 实现无法返回基本类型 int。 如果返回类型为 Text 或 Span,请指定从中派生返回类型的输入参数。 可以使用可选的 like 规范来指定输入参数,因为范围始终来自底层列。 如果返回类型为 ScalarList,请指定要从中推断 ScalarList 中标量类型的输入参数。 当语言为 PMML 时,该函数必须返回表。

  • <output-schema-definition\>

    对于表 UDF , <output-schema-definition\> 指定 UDF 返回的表的输出模式,包括列名及其类型。

  • <ext-name\>

    external_name 指定在何处查找 UDF 的实现。 当语言为 Java时,它是格式为 '<jar-file-name\>:<fully-qualified-class-name\>!<method-name\>'的字符串,由三个部分组成:

    • JAR 文件名:编译模块化 AQL 时,UDF JAR 文件的位置引用必须相对于进行引用的模块的根目录。
    • 类名:包含 UDF 实现的标准类名。
    • 方法名称:方法必须是类的公共方法。 方法特征符必须与 create function 语句中指定的参数类型相匹配。 运行时组件不会执行自动类型转换。 当语言为 PMML 时,external_name 子句将指定字符串,该字符串将是 PMML 文件相对于模块根目录的位置。 当前实现支持使用 PMML 标准 V4.1 表达的模型,并使用 V1.0.22 org.jpmml 库对其求值。
  • language

    language 规范指向 UDF 的实现语言。 运行时组件仅支持使用 Java™ 实现的 UDF。

  • deterministic/not deterministic

    可选的 deterministic/not deterministic 指定函数是否为无状态函数。 确定性函数针对同一组输入值始终返回相同的值。

  • return null on null input

    可选的 return null on null input 指定当一个或多个输入值为 null 时的函数行为。 如果指定了 return null on null input,那么系统会对 null 输入返回 null,而不调用 UDF。 如果指定了 called on null input,那么即使输入值为 null,也会调用 UDF。

使用 PMML 实现的 UDF 的用法说明

从 PMML 文件创建的函数采用名为 params 的单个表作为其自变量,并输出表。 此函数的实现将在 create function 语句中声明的输入和输出模式与 PMML 文件中指定的模式之间映射输入字段。 换言之,即 DataDictionary 部分(描述可以在该模型生成和使用的输入和输出记录中出现的字段的名称和类型)、MiningSchema 部分(指示在表示特征向量的每个元组中,包含了在 DataDictionary 部分中定义的哪些命名文件)和 Output 部分(指示在模型输出的外部表示的每个元组中,包含了 DataDictionary 部分中定义的哪些命名字段)。 这些函数必须为表函数;表中的每一行都将传递到 PMML 模型,并生成输出行。

这些模式信息是必需的,因为 PMML 和 AQL 类型的系统并不完全匹配。 例如,PMML 有多种类型可表示时间戳记,而 AQL 目前需要用户将时间戳记编码为字符串值。 通过模式信息,了解 AQL 但不了解 PMML 的开发者可以理解 AQL 规则集。

AQL 编译器将根据 PMML 文件的输入模式来检查输入模式,以确保这两种模式兼容。 如果这两种模式包含同名但类型不兼容的字段,那么编译会失败,并显示相应的错误消息。 如果函数的输入或输出模式包含多余列或缺少列,那么 resulting 函数会忽略这些列,而不会生成错误。 AQL 定义和 PMML 文件中的列名顺序可能不同。 如果某个列名同时出现在输入和输出模式中,但未出现在 PMML 模式中,那么该列的值会传递到函数的输出。

示例

示例 1:使用 Java 声明将标量值作为输入的标量 UDF

以下示例显示了用于声明名为 create function 的函数的 udfCombineSpans 语句。 该用户定义的函数采用两个范围作为输入,并返回类似于第一个输入范围的合并范围。 实际 UDF Java™ 函数打包成名为 udfs.jar 的 JAR 文件,位于 udfjars 目录下。 实现该函数的方法为 combineSpans 类中的 com.ibm.test.udfs.MiscScalarFunc。 此方法还声明了 udfSpanGreaterThan 函数,如果范围大于指定的大小,此函数会返回 true

/**
* A function to combine two spans and return the resultant span from beginOffset of first span, to endOffset of second span
* @param p1 first input span
* @param p2 second input span
* @return a span from beginOffset of p1 till endOffset of p2
*/

create function udfCombineSpans(p1 Span, p2 Span)
return Span like p1
external_name 'udfjars/udfs.jar:com.ibm.test.udfs.MiscScalarFunc!combineSpans'
language java
deterministic
return null on null input;

/**
* A function to compare an input Span's size against an input size
* @param span input span
* @param size input size to be compared against
* @return Boolean true if input span's size is greater than input argument size, else false
*/

create function udfSpanGreaterThan(span Span, size Integer)
return Boolean
external_name 'udfjars/udfs.jar:com.ibm.test.udfs.MiscScalarFunc!spanGreaterThan'
language java
deterministic;

下一个示例显示了如何声明名为 udfCompareNames 的函数,其中为该函数提供了名称列表 nameList 和单个名称 myName。 输出是类似于输入列表的列表,该列表仅包含 nameList 中与 myName 类似的条目。

/**
* A function to compare an input string against a list of names
* and returns a list of entries from nameList similar to myName.
* @param nameList a list of names
* @param myName a name to compare against the list
* @return a list of entries from nameList similar to myName
*/

create function udfCompareNames(nameList ScalarList, myName String)
return ScalarList like nameList
external_name 'udfjars/udfs.jar:com.ibm.test.udfs.MiscScalarFunc!compareNames'
language java
deterministic;

示例 2:使用 Java 声明将表作为输入的标量 UDF

以下示例说明了 table as locator 输入类型的用法。 此示例显示了用于声明名为 MyScalarFunc 的函数的 create function 语句。 此 UDF 的 Java™ 实现打包在 com.ibm.test.udfs.TableConsumingScalarFunc 类中。

此 UDF 采用两个元组列表作为输入,并输出连接两个输入元组列表的内容的字符串值。 此 UDF 的 Java™ 实现在实现用户定义的函数中进行了说明。

-- Declare a simple scalar function that turns two tables into a big string
create function MyScalarFunc(
    firstArg table( spanVal Span ) as locator,
    secondArg table( spanVal Span, strVal Text ) as locator
)
return String
external_name
  'udfjars/udfs.jar:com.ibm.test.udfs.TableConsumingScalarFunc!eval'
language java
deterministic
called on null input;

示例 3:使用 Java 声明表 UDF

以下示例说明了 return table 子句的用法。 此示例显示了用于声明名为 MyScalarFunc 的表函数的 create function 语句。 此 UDF 的 Java™ 实现打包在 com.ibm.test.udfs.TableConsumingTableFunc 类中。 此 UDF 采用两个元组列表作为输入,并输出将输入列表合并在一起的元组列表。 此 UDF 的 Java™ 实现在实现用户定义的函数中进行了说明。

-- Declare a simple table function that "zips together" two input tables
create function MyTableFunc(
    firstArg table( spanVal Span ) as locator,
    secondArg table( spanVal Span, strVal Text ) as locator
)
return table( outSpan1 Span, outSpan2 Span, outStr Text)
external_name
  'udfjars/udfs.jar:com.ibm.test.udfs.TableConsumingTableFunc!eval'
language java
deterministic
called on null input;

示例 4:声明使用 PMML 实现的表 UDF

以下示例显示了用于使用 PMML 语言声明名为 IrisDecisionTree 的表函数的 create function 语句。 此 UDF 的 PMML 实现在 IrisTree.xml 文件中指定,如以下示例所示。 存储在 IrisTree.xml 中的模型是决策树模型。

-- Scoring function based on a decision tree model stored in IrisTree.xml
create function IrisDecisionTree(
    params table(
        sepal_length Float,
        sepal_width Float,
        petal_length Float,
        petal_width Float
    ) as locator
)
return table( class Text, actual_class Text,
    -- This PMML file also defines additional output fields
    "Probability_Iris-setosa" Float,
    "Probability_Iris-versicolor" Float,
    "Probability_Iris-virginica" Float)
external_name 'IrisTree.xml'
language pmml;

IrisTree.xml

<?xml version="1.0"?>
<PMML version="3.2" xmlns="http://www.dmg.org/PMML-3_2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.dmg.org/PMML-3_2 http://www.dmg.org/v3-2/pmml-3-2.xsd">
  <Header copyright="Copyright (c) 2012 DMG" description="RPart Decision Tree Model">
  <Extension name="user" value="DMG" extender="Rattle/PMML"/>
  <Application name="Rattle/PMML" version="1.2.29"/>
  <Timestamp>2012-09-27 12:46:08</Timestamp>
  </Header>
  <DataDictionary numberOfFields="5">
  <DataField name="class" optype="categorical" dataType="string">
    <Value value="Iris-setosa"/>
    <Value value="Iris-versicolor"/>
    <Value value="Iris-virginica"/>
  </DataField>
  <DataField name="sepal_length" optype="continuous" dataType="double">
    <Interval closure="closedClosed" leftMargin="4.3" rightMargin="7.9"/>
  </DataField>
  <DataField name="sepal_width" optype="continuous" dataType="double">
    <Interval closure="closedClosed" leftMargin="2" rightMargin="4.4"/>
  </DataField>
  <DataField name="petal_length" optype="continuous" dataType="double">
    <Interval closure="closedClosed" leftMargin="1" rightMargin="6.91"/>
  </DataField>
  <DataField name="petal_width" optype="continuous" dataType="double">
    <Interval closure="closedClosed" leftMargin="0.1" rightMargin="2.5"/>
  </DataField>
  </DataDictionary>
  <TreeModel modelName="RPart_Model" functionName="classification" algorithmName="rpart" splitCharacteristic="binarySplit" missingValueStrategy="defaultChild">
  <MiningSchema>
    <MiningField name="class" usageType="predicted"/>
    <MiningField name="sepal_length" usageType="supplementary"/>
    <MiningField name="sepal_width" usageType="supplementary"/>
    <MiningField name="petal_length" usageType="active"/>
    <MiningField name="petal_width" usageType="supplementary"/>
  </MiningSchema>
  <Output>
    <OutputField name="class" optype="categorical" dataType="string" feature="predictedValue"/>
    <OutputField name="Probability_Iris-setosa" optype="continuous" dataType="double" feature="probability" value="Iris-setosa"/>
    <OutputField name="Probability_Iris-versicolor" optype="continuous" dataType="double" feature="probability" value="Iris-versicolor"/>
    <OutputField name="Probability_Iris-virginica" optype="continuous" dataType="double" feature="probability" value="Iris-virginica"/>
  </Output>
  <Node id="1" score="Iris-virginica" recordCount="105" defaultChild="3">
    <True/>
    <ScoreDistribution value="Iris-setosa" recordCount="33" confidence="0.314285714285714"/>
    <ScoreDistribution value="Iris-versicolor" recordCount="35" confidence="0.333333333333333"/>
    <ScoreDistribution value="Iris-virginica" recordCount="37" confidence="0.352380952380952"/>
    <Node id="2" score="Iris-setosa" recordCount="33">
    <SimplePredicate field="petal_length" operator="lessThan" value="2.6"/>
    <ScoreDistribution value="Iris-setosa" recordCount="33" confidence="1"/>
    <ScoreDistribution value="Iris-versicolor" recordCount="0" confidence="0"/>
    <ScoreDistribution value="Iris-virginica" recordCount="0" confidence="0"/>
    </Node>
    <Node id="3" score="Iris-virginica" recordCount="72" defaultChild="7">
    <SimplePredicate field="petal_length" operator="greaterOrEqual" value="2.6"/>
    <ScoreDistribution value="Iris-setosa" recordCount="0" confidence="0"/>
    <ScoreDistribution value="Iris-versicolor" recordCount="35" confidence="0.486111111111111"/>
    <ScoreDistribution value="Iris-virginica" recordCount="37" confidence="0.513888888888889"/>
    <Node id="6" score="Iris-versicolor" recordCount="37">
      <SimplePredicate field="petal_length" operator="lessThan" value="4.85"/>
      <ScoreDistribution value="Iris-setosa" recordCount="0" confidence="0"/>
      <ScoreDistribution value="Iris-versicolor" recordCount="34" confidence="0.918918918918919"/>
      <ScoreDistribution value="Iris-virginica" recordCount="3" confidence="0.0810810810810811"/>
    </Node>
    <Node id="7" score="Iris-virginica" recordCount="35">
      <SimplePredicate field="petal_length" operator="greaterOrEqual" value="4.85"/>
      <ScoreDistribution value="Iris-setosa" recordCount="0" confidence="0"/>
      <ScoreDistribution value="Iris-versicolor" recordCount="1" confidence="0.0285714285714286"/>
      <ScoreDistribution value="Iris-virginica" recordCount="34" confidence="0.971428571428571"/>
    </Node>
    </Node>
  </Node>
  </TreeModel>
</PMML>

记录具有注释的 create function 语句

create function 语句的 AQL Doc 注释包含以下信息:

  • 有关函数的常规描述。
  • @param 描述,指定函数中使用的每个参数名称。 此描述指示参数的类型:是标量类型还是表。 如果参数是表,将描述表的模式,包括列名和列类型,顺序为这些列在预期作为输入的表的模式中的显示顺序。
  • @return 描述,指定函数返回的信息。 如果函数返回表,那么指定表的输出模式,包括列名和列类型,顺序为这些列在输出表的模式中的显示顺序。
/**
* A function to compare an input string against a list of names
* and returns a list of entries from nameList similar to myName.
* @param nameList a list of names
* @param myName a name to compare against the list
* @return a list of entries from nameList similar to myName
*/

create function udfCompareNames(nameList ScalarList, myName String)
  return ScalarList like nameList
  external_name 'udfjars/udfs.jar:com.ibm.test.udfs.MiscScalarFunc!compareNames'
  language java
  deterministic;

使用用户定义的函数

用户定义的函数与 AQL 语句和子句一起使用。

用户定义的标量函数与 AQL 语句和子句一起使用,类似于内置函数。 具体而言,标量 UDF 可以在 selectwherehavinggroup byorder by 子句中使用,使用方式与内置标量函数(例如,GetBeginLeftContext)相同。 返回 Boolean 类型的用户定义的标量函数可用作 wherehaving 子句的谓词。

用户定义的表函数(表 UDF)可以在 from 语句或 select 语句的 extract 子句中使用。

示例

示例 1:使用通过 Java 实现的将标量值用作输入的标量 UDF

此示例演示了如何使用在udfCombineSpans声明用户定义的函数udfSpanGreaterThan中声明的 函数。

create function udfCombineSpans(p1 Span, p2 Span)
return Span like p1
external_name 'udfjars/udfs.jar:com.ibm.test.udfs.MiscScalarFunc!combineSpans'
language java
deterministic
return null on null input;

create function udfSpanGreaterThan(span Span, size Integer)
return Boolean
external_name 'udfjars/udfs.jar:com.ibm.test.udfs.MiscScalarFunc!spanGreaterThan'
language java
deterministic;

-- identify occurrences of given names in the document
create dictionary FirstNamesDict from file 'firstNames.dict';

create view FirstName as
extract dictionary 'FirstNamesDict' on D.text as name
from Document D;

-- Use a UDF to merge the name that is longer than 7 characters with the text to its right in a
-- way that is appropriate to the application.
create view NameAndContext as
select udfCombineSpans(F.name, RightContext(F.name, 50)) as name
from FirstName F
where udfSpanGreaterThan(F.name, 7);

示例 2:使用通过 Java 实现的将表用作输入的标量 UDF

以下示例显示了如何使用将表作为输入的标量函数。 此示例说明了 MyScalarFunc 表 UDF 函数的用法,该函数的 Java™ 实现在实现用户定义的函数中进行了说明。 它们是相同的列类型,但具有不同的列名。

-- Declare a simple scalar function that turns two tables into a big string
create function MyScalarFunc(
    firstArg table( spanVal Span ) as locator,
    secondArg table( spanVal Span, strVal Text ) as locator
)
return String
external_name
  'udfjars/udfs.jar:com.ibm.test.udfs.TableConsumingScalarFunc!eval'
language java
deterministic
called on null input;

-- Create two views to serve as inputs to the `table` function.
-- Note that column names don't match, but types do.
create view FirstInputView as
extract regex /\d+/ on 1 token in D.text as match
from Document D;

create view SecondInputView as
select S.match as spanCol, 'Dummy string' as textCol
from
    (extract regex /[A-Z][a-z]+/ on 1 token in D.text as match
    from Document D) S;

-- Call the `scalar` function defined above from the select list.
create view ScalarFuncOutput as
select MyScalarFunc(FirstInputView, SecondInputView) as func
from Document D;

示例 3:使用通过 Java 实现的表 UDF

此示例说明了 MyTableFunc 表 UDF 函数的用法,该函数的 Java™ 实现在实现用户定义的函数中进行了说明。

-- Declare a simple table function that "zips together" two input tables
create function MyTableFunc(
    firstArg table( spanVal Span ) as locator,
    secondArg table( spanVal Span, strVal Text ) as locator
)
return table( outSpan1 Span, outSpan2 Span, outStr Text)
external_name
  'udfjars/udfs.jar:com.ibm.test.udfs.TableConsumingTableFunc!eval'
language java
deterministic
called on null input;

-- Create two views to serve as inputs to the `table` function.
-- Note that column names don't match, but types do.
create view FirstInputView as
extract regex /\d+/ on 1 token in D.text as match
from Document D;


create view SecondInputView as
select S.match as spanCol, 'Dummy string' as textCol
from
    (extract regex /[A-Z][a-z]+/ on 1 token in D.text as match
    from Document D) S;

-- Use the `table` function
create view TabFuncOutput as
select T.outSpan1 as span1, T.outSpan2 as span2
from MyTableFunc(FirstInputView, SecondInputView) T;

与示例 2 类似,该示例定义了两个视图:FirstInputView(模式为 (match Span))和 SecondInputView(模式为 (spanCol Span, textCol Text))。 该示例中的最后一个视图 TabFuncOutputtable 子句中调用包含这两个输入视图的 from 函数 MyTableFunc。 如示例 2 中所述,只要输入视图和表定位器的模式中的列数和列类型相匹配,那么视图与函数的表定位器自变量即兼容。 但是,列名不必相匹配。

最后,select 子句将废弃 MyTableFunc 的输出表中的 outStr 列,而仅保留前两列 outSpan1outSpan2

示例 4:使用通过 PMML 实现的表 UDF

此示例说明了在声明用户定义的函数的示例 4 中声明的 IrisDecisionTree 表 UDF 函数的用法。

-- External view to hold the input records
create external view IrisData(
    sepal_length Float,
    sepal_width Float,
    petal_length Float,
    petal_width Float
)
external_name 'IrisData';
--Invoke the function on the input data records
create view IrisDecisionTreeOutput as
select * from IrisDecisionTree(IrisData);

output view IrisDecisionTreeOutput;