Emil C
Emil C

Reputation: 342

Call MongoDB function from Java

I'm trying to call a stored JavaScript function from the MongoDB Java driver.

I have been following this guide to store the function on the DB server and I'm able to call the function from the mongo shell and have the result returned.

However I cannot figure out how to call the same function in Java?

According to this http://api.mongodb.org/java/current/com/mongodb/DB.html#doEval-java.lang.String-java.lang.Object...- there's a method called doEval

I have also tried to use it with this method:

public static String callFunction() {

    try (MongoClient client = new MongoClient("localhost")) {
        com.mongodb.DB db = client.getDB("TestDB");
        return db.doEval("echoFunction", 3).toString();
    }
}

But when I call the method this is what I get:

{ "retval" : { "$code" : "function (x) {\n    return x;\n}"} , "ok" : 1.0}

and I would expect to get the number 3 back in this case.

Another problem with the above code is that the method client.getDB() is deprecated. As I understand it the new method to call is client.getDatabase() and it returns a MongoDatabase object, but according to the API there is no method to execute a function.

So my question is: Is it possible to execute a stored JavaScript function on the database server from Java and get back the result of that function? And if it is possible, I would appreciate some help on how to do it?

Thank you.

Edit:

According to a comment on Calling server js function on mongodb from java:

"It seems like getNextSequence is a function written in the mongo javascript shell. Neither the database (mongod) nor the Java side knows this function exists and neither is able to interprete the Javascript code the function contains. You will have to reimplement it in Java. "

The function I'm trying to implement is a bit more complex than the example above - it's supposed to return a collection of documents and that does not seems to be working using the db.doEval method.

So I guess the comment is correct?

Upvotes: 6

Views: 13003

Answers (3)

You can do all this with java driver.

MongoClient mongoClient = new MongoClient();
MongoDatabase mdb = mongoClient.getDatabase("TestDB");

/* run this <code snippet> in bootstrap */
BsonDocument echoFunction = new BsonDocument("value",
        new BsonJavaScript("function(x1) { return x1; }"));

BsonDocument myAddFunction = new BsonDocument("value",
        new BsonJavaScript("function (x, y){ return x + y; }"));

mdb.getCollection("system.js").updateOne(
        new Document("_id", "echoFunction"),
        new Document("$set", echoFunction),
        new UpdateOptions().upsert(true));

mdb.getCollection("system.js").updateOne(
        new Document("_id", "myAddFunction"),
        new Document("$set", myAddFunction),
        new UpdateOptions().upsert(true));

mdb.runCommand(new Document("$eval", "db.loadServerScripts()"));
/* end </code snippet> */

Document doc1 = mdb.runCommand(new Document("$eval", "echoFunction(5)"));
System.out.println(doc1);

The result is also:

Document{{retval=5.0, ok=1.0}}

Upvotes: 7

Alexander Makarov
Alexander Makarov

Reputation: 165

I resolved the same issue in the following way:

I run a command in mongoShell to create my stored JavaScript functions:

db.system.js.save(
   {
     _id: "echoFunction" ,
     value : function(x1) { return x1; }
   }
)

db.system.js.save(
   {
     _id : "myAddFunction" ,
     value : function (x, y){ return x + y; }
   }
);


db.system.js.save(
    {
    _id: "fullFillCollumns" ,
    value : function () { 
        for (i = 0; i < 2000; i++) {
        db.numbers.save({num:i}); } }
    }
);

To execute this functions from MongoDB Java Driver:

MongoClient mongoClient = new MongoClient();
MongoDatabase db = mongoClient.getDatabase("databaseName");

db.runCommand(new Document("$eval", "fullFillCollumns()"));

Document doc1 = db.runCommand(new Document("$eval", "echoFunction(5)"));
System.out.println(doc1);

Document doc2 = db.runCommand(new Document("$eval", "myAddFunction(5,8)"));
System.out.println(doc2); 

I see that the collection numbers were created and filled with values. In the IntellijIdea console I see:

Document{{retval=5.0, ok=1.0}}
Document{{retval=13.0, ok=1.0}}

Upvotes: 1

rsutormin
rsutormin

Reputation: 1649

You should do this instead:

        return db.doEval("echoFunction(3)").toString();

If you use just function name in eval you only refer to JavaScript variable on server side storing code of function. It doesn't execute it. When you use parentheses you request to actually execute a function. If you need to send something more complex than a number I would advise to use JSON serializer.

Upvotes: 1

Related Questions