SoftInstigate / restheart

Rapid API Development with MongoDB
https://restheart.org
GNU Affero General Public License v3.0
807 stars 171 forks source link

How to call function in restheart? #87

Closed singhvikram704 closed 8 years ago

singhvikram704 commented 8 years ago

Hi,

I want to insert a document in collection with auto increment id ,As body used below giving error:- { " _id":getNextSequence("userid"), "name": "Sarah C" }

How to call function??

I am able to make it by shell, robomongo (as explained here:- http://stackoverflow.com/questions/21215303/sequence-in-mongodb).

Want to use by resheart.

Thanks in advance

ujibang commented 8 years ago

Hi @singhvikram704

as discussed here calling-server-js-function-on-mongodb-from-java, I don't think you can invoke a javascript function from RESTHeart. The function is actually interpreted by the mongo shell javascript engine and it is not stored in the database.

With RESTHeart you can achieve the same (in this case an auto increment id) using a request transformer.

A simple custom transformer can add the _id property to POST requests mimic your javascript function.

ujibang commented 8 years ago

We can store a javascript function on the server:

https://docs.mongodb.org/manual/tutorial/store-javascript-function-on-server/

Investigating...

ujibang commented 8 years ago

It seems that you can only use server side javascript functions in queries (using the $where operator) and in mapReduce operations:

From https://docs.mongodb.org/manual/tutorial/store-javascript-function-on-server/

Once you save a function in the system.js collection, you can use the function from any JavaScript context; e.g. $where operator, mapReduce command or db.collection.mapReduce().

From https://docs.mongodb.org/manual/core/server-side-javascript/

MongoDB provides the following commands, methods, and operator that perform server-side execution of JavaScript code:

  • mapReduce....
  • $where operator that evaluates a JavaScript expression or a function in order to query for documents.

I stored a javascript function on the server with the mongo shell:

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

I can use myAddFunction in queries (forget about the no sense example) and it works:

http -a a:a GET 127.0.0.1:8080/test/coll?filter='{"$where": "myAddFunction(1,2)===3"}'

However I cannot use it in write (PUT,POST,PATCH) operations.

singhvikram704 commented 8 years ago

Hi @ujibang

I created collection "counters" { "_id" : "userid", "seq" : 26 }

I created function as:

db.system.js.save({ _id : "getId" , value : function (name) { var ret = db.counters.findAndModify({ query: { _id: name }, update: { $inc: { seq: 1 } }, new: true }); return ret.seq; } });

Trying to call via restheart with url:- http://localhost:8080/management/counters?filter={"$where": "getId('userid')"}

But getting error:- "exception message": "Query failed with error code 16722 and error message 'ReferenceError: db is not defined' on server 127.0.0.1:27017"

am i doing anything wrong?

ujibang commented 8 years ago

I don't think this is the right approach.

First you cannot access db

From https://docs.mongodb.org/manual/reference/operator/query/where/#op._S_where

map-reduce operations, the group command, and $where operator expressions cannot access certain global functions or properties, such as db, that are available in the mongo shell.

Also take into account that the function is evaluated for each document of the collection, so the counter would be incremented by the total number of documents in the collection.

the $where provides greater flexibility, but requires that the database processes the JavaScript expression or function for each document in the collection.

By the way, what is the request supposed to return with that filter expression?

singhvikram704 commented 8 years ago

Hi @ujibang

need incremented(by 1) value of "seq" attribute on each request of document from collection "counters" as mentioned above. If we can, please let me know the way with example as you given for calling system function. Thanks in advance.

ujibang commented 8 years ago

I suggest you a different approach: a RESTHeart transformer that actually increment the counter on every GET request.

Something like:

public class GetCounter implements Transformer {
    tranform(final HttpServerExchange exchange, final RequestContext context, DBObject contentToTransform, final DBObject args) {
        if (context.getMethod() == GET) {
             // execute the $inc update
        }
    }
}

Full documentation at https://softinstigate.atlassian.net/wiki/x/i4CM

Or simply do PATCH http://localhost:8080/management/counters/userid {"$inc": { "seq": 1}}