本文将介绍怎样使用 Spring Data MongoDB 提供的 ScriptOperations,在 MongoDB 上执行 JavaScript 脚本。
MongoDB 允许通过直接发送脚本或调用存储的脚本在服务器上运行 JavaScript 函数。
MongoDB 4.2 移除了被 ScriptOperations 使用的 eval 命令的支持,并且没有提供其他替代方案。因此,ScriptOperations 不在推荐使用,该接口的声明如下:
package org.springframework.data.mongodb.core; import java.util.Set; import org.springframework.data.mongodb.core.script.ExecutableMongoScript; import org.springframework.data.mongodb.core.script.NamedMongoScript; import org.springframework.lang.Nullable; /** * Script operations on {@link com.mongodb.client.MongoDatabase} level. Allows interaction with server side JavaScript functions. * * @author Christoph Strobl * @author Oliver Gierke * @since 1.7 * @deprecated since 2.2. The {@code eval} command has been removed without replacement in MongoDB Server 4.2.0. */ @Deprecated public interface ScriptOperations { //... }
官方已经不推荐使用了,但还是有必要看看它是干什么的。
ScriptOperations 可以通过 MongoTemplate 的 scriptOps() 方法获取,ScriptOperations 并为 JavaScript 的使用提供基本的抽象。scriptOps() 方法定义如下:
ScriptOperations scriptOps() 返回可以在 MongoDatabase 级别上执行的 ScriptOperations 对象。
ScriptOperations 接口定义的主要方法如下:
Object all(String scriptName, Object... args) 用它的名字调用 JavaScript。
Object execute(ExecutableMongoScript script, Object... args) 执行脚本,通过它的名字调用它或直接发送它。
boolean exists(String scriptName) 检查 MongoDatabase 中是否存在给定名称的服务器端 JavaScript。
Set<String> getScriptNames() 返回可以调用的 JavaScript 函数的名称。
NamedMongoScript register(ExecutableMongoScript script) 存储给定的 ExecutableMongoScript 对象,生成一个名称,以便随后可以通过名称来调用。
NamedMongoScript register(NamedMongoScript script) 在数据库中注册给定的名称 MongoScript。
(1)直接运行 JavaScript 脚本,使用 ExecutableMongoScript 定义一个可执行的 JavaScript 脚本,代码如下:
ScriptOperations scriptOps = template.scriptOps(); // 定义一个脚本 ExecutableMongoScript echoScript = new ExecutableMongoScript("function(x) { return x; }"); // 直接运行脚本 scriptOps.execute(echoScript, "directly execute script");
(2)通过脚本名称运行脚本,代码如下:
ScriptOperations scriptOps = template.scriptOps(); // 定义一个脚本 ExecutableMongoScript echoScript = new ExecutableMongoScript("function(x) { return x; }"); // 使用 echo 名称注册 echoScript 脚本 scriptOps.register(new NamedMongoScript("echo", echoScript)); // 通过 echo 名称执行脚本 scriptOps.call("echo", "execute script via name");
下面是通过 ScriptOperations 执行脚本的完整代码,如下:
package com.hxstrive.springdata.mongodb; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.ScriptOperations; import org.springframework.data.mongodb.core.script.ExecutableMongoScript; import org.springframework.data.mongodb.core.script.NamedMongoScript; /** * ScriptOperations 操作 JavaScript 脚本 * @author hxstrive.com */ @SpringBootTest class ScriptOperationsDemo { @Autowired private MongoTemplate mongoTemplate; @Test public void directlyExecuteScript() { // 获取 ScriptOperations 对象 ScriptOperations scriptOps = mongoTemplate.scriptOps(); // 创建一个简单的脚本 // 将接收的参数 x 前后添加固定字符,然后返回 ExecutableMongoScript echoScript = new ExecutableMongoScript("function(x) { return '[' + x + ']'; }"); // 执行脚本 Object result = scriptOps.execute(echoScript, "directly execute script"); System.out.println(result); // 结果: // [directly execute script] // 注意:笔者运行的 MongoDB 版本为 3.4.15,在 5.0.14 版本运行抛出如下错误: // com.mongodb.MongoCommandException: Command failed with error 59 (CommandNotFound): // 'no such command: '$eval'' on server localhost:27017. // The full response is {"ok": 0.0, "errmsg": "no such command: '$eval'", "code": 59, // "codeName": "CommandNotFound"} } @Test public void directlyExecuteScript2() { ScriptOperations scriptOps = mongoTemplate.scriptOps(); // 定义一个脚本 ExecutableMongoScript echoScript = new ExecutableMongoScript("function(x) { return '[' + x + ']'; }"); // 使用 echo 名称注册 echoScript 脚本 scriptOps.register(new NamedMongoScript("echo", echoScript)); // 通过 echo 名称执行脚本 Object result = scriptOps.call("echo", "execute script via name"); System.out.println(result); // 结果: // [execute script via name] } }
其中,Person 实体类的代码如下:
package com.hxstrive.springdata.mongodb.entity; import lombok.Builder; import lombok.Data; import lombok.ToString; @Data @ToString @Builder public class Person { /** 用户ID */ private int id; /** 用户姓名 */ private String name; /** 年龄 */ private int age; /** 电子邮件 */ private String email; }