概述
JavaScript 是 Fess 自 15.9 版本起的默认脚本语言。 它运行于 Sai(CodeLibs 开发的 Nashorn 分支, Fess 已将其用于 DI XML 表达式的 解析)之上,脚本以 ECMAScript 6 执行。其标识符为 javascript ,也可使用别名 js 和 sai 指定。
脚本的求值方式
Fess 的脚本引擎会先尝试将脚本文本编译为单个”表达式”。只有在解析失败时, 才会将该文本重新编译为”语句”块。
因此,仅返回一个值的简单表达式:
以及在顶层包含 return 语句的脚本:
两者都能正常运行。后者在纯 JavaScript 中通常会导致语法错误,因为顶层不允许使用 return 。但由于它无法编译为表达式,因此会被重新解释为语句块,作为有效脚本 执行。
在每行都被视为单个表达式的场景(如数据存储脚本)中,不能使用由多条语句组成的 脚本。而在整个脚本被求值的场景(如计划任务)中,则可以自由使用多行语句、 let / const 变量声明以及控制结构。
Warning
作为语句块编译的脚本,只有在包含显式 return 时才会返回值。当脚本文本无法解析为表达式 时,它会被包装进函数并作为语句块执行,而没有 return 的块其求值结果为 null 。 仅仅在末尾加一个分号就足以越过这条界线:
| 脚本 | 结果 | 原因 |
|---|---|---|
content.length() | 11 | 解析为表达式,表达式的值即为结果 |
content.length(); | null | 只能解析为语句块,而其中没有 return |
var x = 1; x + 2 | null | 只能解析为语句块,而其中没有 return |
在 Groovy 中这三者都会返回值,因为最后求值的语句的值就是脚本的返回值。JavaScript 没有 这条规则。
这是迁移中唯一一处不产生任何错误、任何日志行,除了字段悄然变空之外没有其他症状的差异: 脚本返回 null 的数据存储映射,只是不设置该字段而已。数据存储的 字段名=表达式 每一行请写成不带末尾分号的表达式,并为每个计划任务脚本写上显式的 return 。
基本语法
下文中末尾不带分号的行是**表达式**,可在任何位置使用,包括数据存储的 字段名=表达式 行。 let / const 声明、 if 块和循环是**语句**,只能用于整个脚本被求值的场景(如计划 任务),并且脚本必须包含显式的 return 才会产生值。请参阅上文”脚本的求值方式”。
变量声明
字符串操作
集合操作
条件分支
循环处理
数据存储脚本
数据存储设置中的脚本示例。
Note
在数据存储脚本中,每行 字段名=表达式 均作为独立的单一表达式求值。 因此,不能使用 let / const 变量声明语句,也不能使用一次性设置多个字段的多行控制结构(如 if 块)。 使用Java类时,请以完全限定类名(FQCN)写成单一表达式,条件分支则在各字段中使用三元运算符(例如: url=data.published ? data.url : null )。 此外,这里使用的变量名 data 仅为示例,实际变量名取决于所使用的数据存储连接器。详情请参阅 数据存储爬取 。 表达式请写成不带末尾分号的形式:只能解析为语句块的行其求值结果为 null ,该字段将不会被设置。请参阅 脚本的求值方式 。
基本映射
URL生成
内容加工
日期处理
可用对象
脚本中可用的对象因执行上下文而异。
| 上下文 | 对象 | 说明 |
|---|---|---|
| 所有上下文 | container | DI容器。通过 container.getComponent("...") 访问组件 |
| 计划任务 | executor | 任务执行控制( JobExecutor )。任务停止支持所必需 |
| 数据存储 | (连接器特定) | 各数据存储提供的数据记录变量。变量名取决于连接器 |
| 路径映射 | url , matcher | 待转换的URL字符串及正则表达式匹配结果( Matcher )。可在替换字符串带有已注册引擎名前缀(如 javascript: ,别名 js: 、 sai: )时使用 |
| 文档权重 | (文档字段) | 目标文档的各字段均可作为变量使用(用于条件表达式和权重值表达式) |
计划任务脚本
计划任务中使用的JavaScript脚本示例。 在计划任务中,container 和 executor 可用。 将 executor 传递给任务的 execute() 方法可启用任务停止控制。
Note
计划任务脚本作为一个完整的脚本整体求值。 脚本引擎会先尝试将其编译为表达式,仅在失败时才重新解释为语句块,因此可以使用多行语句、 let / const 声明、控制结构以及顶层的 return 语句(详见上文”脚本的求值方式”)。 以下「使用Java类」「访问Fess组件」「错误处理」「调试与日志输出」的示例均基于此完整脚本的上下文。
执行爬取任务
条件爬取
顺序执行多个任务
使用Java类
在JavaScript脚本中,借助 Sai(Nashorn)的 Java 互操作机制,可以直接使用 Java 标准库和 Fess 的类。JavaScript 没有 import 语句,因此类名始终以完全限定名 (FQCN)书写。
日期与时间
文件操作
HTTP通信
Warning
访问外部资源会影响性能, 请将其控制在最小限度。
访问Fess组件
可以使用 container 访问Fess的组件。
系统帮助器
获取配置值
执行搜索
错误处理
JavaScript 没有 import 语句,因此不存在 Groovy 那样的位置限制。 可以使用 try-catch 捕获异常,控制任务的错误行为。
调试与日志输出
日志输出
调试输出
如需快速查看变量内容,可以使用 JSON.stringify 将其转换为字符串后输出到日志。
从Groovy迁移
将现有 Groovy 脚本移植到 JavaScript 时,请注意以下差异。
算术运算的精度
JavaScript 的数值运算始终以双精度浮点数处理。例如,以下表达式在 Groovy 中返回 整数 34 ,而在 JavaScript 中返回浮点数 34.0 。
另一方面,通过 Java 互操作调用的方法,其返回值会保留 Java 一侧的类型,因此 content.length() 仍然返回整数。
需要改写的Groovy专用语法
以下 Groovy 专用语法在 JavaScript 中需要改写。
| Groovy | JavaScript | 说明 |
|---|---|---|
1000L | 1000 | long 类型字面量的 L 后缀不再需要,直接书写数字字面量即可 |
["a", "b"] as String[] | ["a", "b"] | JavaScript 数组在传递给接受 |
Java互操作
Java 互操作的写法与 Nashorn 相同,与 Groovy 几乎没有差别。 new java.io.File(...)、 java.lang.System.getProperty(...)、 new org.codelibs.fess.job.IndexExportJob() 等完全限定构造函数调用均可直接解析。
ES6语法
由于 Fess 的 JavaScript 引擎以 ECMAScript 6 运行,可以使用 let / const、 箭头函数、模板字符串、解构赋值、for...of、class 等 ES6 语法。但可选链 ( ?. )和空值合并运算符( ?? )属于 ES2020 及以后的语法,无法使用。
最佳实践
保持简单: 避免复杂逻辑,编写易读的代码
默认值: 使用逻辑 OR 运算符(
||)代替 Elvis 运算符异常处理: 使用适当的try-catch处理意外错误
日志输出: 输出日志以便于调试
性能: 最小化外部资源访问
数值运算: 需要整数的场合,直接使用 Java 互操作方法调用的结果,或按需显式转换
参考信息
脚本概述 - 脚本概述
Groovy脚本指南 - Groovy 脚本指南(插件)
数据存储爬取 - 数据存储配置指南
调度器 - 调度器配置指南