Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suggestion: user-defined functions #3740

Open
elifarley opened this issue Feb 5, 2015 · 8 comments
Open

Suggestion: user-defined functions #3740

elifarley opened this issue Feb 5, 2015 · 8 comments

Comments

@elifarley
Copy link

I think that user-defined functions that run on the cluster would be a nice feature to have.

Examples:

r.table('my-table').filter(r.func('my-func-1')).run(conn)
@Tryneus
Copy link
Member

Tryneus commented Feb 5, 2015

Once we get #3636 in, it would be trivial for users to add queries to a separate table, and you could theoretically do something like:

r.db('queries').table('app').get('filter_query').eval().run(conn)

This isn't quite what you suggested here, but it's close. It would be useful if we could parameterize the query, or store some subset of the Term tree from a given query - but I suppose that depends on how #3636 is settled.

@danielmewes danielmewes added this to the backlog milestone Feb 5, 2015
@marshall007
Copy link
Contributor

This is similar to #3739 and as @danielmewes mentioned over there you can accomplish something similar on the client-side.

@Tryneus
Copy link
Member

Tryneus commented Feb 9, 2015

Here's what I would propose for this (note that this is similar to what was suggested in #1863, but allows for runtime parameterization):

  • Implement New pseudo type for ReQL queries in ReQL #3636, so we have a pseudotype for queries
  • Add a term r.bind (or r.rql or something):
    • r.bind takes a single argument - a function with n arguments
    • r.bind returns a ReQL query (saved as a pseudotype) which can be stored in a Datum
    • Example: r.bind(lambda x, y: r.table(x).insert({'value':y}))
  • Add a term r.eval (or r.call or something):
    • r.eval takes n arguments plus a ReQL query (a FUNC term or a pseudotype) that takes n arguments
    • r.eval compiles and runs the query, using the provided values as parameters, and returns the result of the query

@thelinuxlich
Copy link

It would be amazing to have something like r.cron("* * */1 * *", r.rql(....))

@Tryneus
Copy link
Member

Tryneus commented Feb 13, 2015

@thelinuxlich, I think that would be a cool use case for inserting into the rethinkdb.jobs table, something like:

r.db("rethinkdb").table("jobs").insert(
    { "type": "periodic_query", "info": { "query": r.rql(...) } })

@thelinuxlich
Copy link

@Tryneus I was about to mention the jobs table but I was unsure if it fits the case :)

@danielmewes
Copy link
Member

I'm not sure if the jobs table is a good place for storing cron jobs. Currently jobs only contains things that are currently running, and having scheduled tasks in there would be a pretty big jump from that.

We should probably have a separate pseudo table (tasks or scheduled_queries or something) for that.

@danielmewes
Copy link
Member

Another proposal by @skizzerz #849 (comment)


Bumping this issue, as the thought just occurred to me not too long ago that a client-visible macroing system would be incredibly useful to make it easier to use very complex queries, especially when you want to run the same query both manually in data explorer as well as multiple places throughout an application.

My thoughts on how this would look are as follows (names chosen to be consistent with the db/table/index commands):

r.macroCreate(macroName, function) → object

This would create a macro with the given macroName using the given function. The function can return anything and can take parameters, which are passed in when executing the macro. Macro names are case-sensitive, must be unique, and are global across all databases.

Example: Creates a macro to compute a golfer's score for a game.

r.macroCreate('getscore',
    function (player, net) {
        return r.branch(
            net.default(true),
            player('gross_score').sub(player('course_handicap'),
            player('gross_score')
        );
    }
).run(conn, callback);

r.macroDrop(macroName) → object

Deletes a previously-created macro.

Example

r.macroDrop('getscore').run(conn, callback);

r.macroRename(oldName, newName) → object

Renames a macro.

Example

r.macroRename('getscore', 'getScore').run(conn, callback);

r.do([args]*, macroName) → any

any.do([args]*, macroName) → any

This adds two overloads to the existing do command to execute a macro. In both overloads, the given macroName is executed as if the macro function itself were present in the do command instead of simply the name.

For the first overload, if args are specified, they are passed in exactly as they would be if a function were passed to r.do. For the second overload, any specified arguments are passed after the previous result (in other words, the first argument would be the result of executing the part before .do, and any other arguments are as specified in the command).

Examples

r.do(
    r.table('players').get('f19b5f16-ef14-468f-bd48-e194761df255'),
    true,
    'getscore'
).run(conn, callback);

// In this example, the "player" argument is the result of .get() and the "net" argument
// is the first argument passed into .do()
r.table('players').get('f19b5f16-ef14-468f-bd48-e194761df255').do(true, 'getscore').run(conn, callback);

If overloading do is not desirable (and the syntax is rather clunky in order to make it fit with existing overloads), consider the following commands instead that achieve the same purpose:

r.macro(macroName, [args]*) → any
any.macro(macroName, [args]*) → any

Note that in this version, the macroName comes first, which is convenient when it is only a string as opposed to a potentially-gigantic function. The do overloads above kept the macroName last in order to be consistent with where the function would normally be located.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants