diff --git a/source/applications.txt b/source/applications.txt index 8355eee6c23..9eeb4901ea3 100644 --- a/source/applications.txt +++ b/source/applications.txt @@ -33,6 +33,7 @@ The following documents outline basic application development topics: applications/drivers applications/optimization applications/database-references + applications/server-side-code-execution applications/gridfs core/object-id core/capped-collections diff --git a/source/applications/server-side-code-execution.txt b/source/applications/server-side-code-execution.txt new file mode 100644 index 00000000000..9a97552ac8d --- /dev/null +++ b/source/applications/server-side-code-execution.txt @@ -0,0 +1,150 @@ +========================== +Server-side Code Execution +========================== + +.. default-domain:: mongodb + +MongoDB supports server-side execution of JavaScript code using various +methods. + +.. note:: The JavaScript code execution takes a JavaScript lock. + +.. _server-side-map-reduce: + +Map-Reduce +---------- + +MongoDB performs the execution of JavaScript functions for +:doc:`/applications/map-reduce` operations on the server. Within these +JavaScript functions, you must not access the database for any reason, +including to perform reads. + +See the :method:`db.collection.mapReduce()` and the +:doc:`/applications/map-reduce` documentation for more information, +including examples of map-reduce. See :ref:`map-reduce concurrency +` section for concurrency information for +map-reduce. + +.. _server-side-eval: + +``eval`` Command +---------------- + +The :dbcommand:`eval` command, and the corresponding :program:`mongo` +shell method :method:`db.eval()`, evaluates JavaScript functions on the +database server. This command may be useful if you need to touch a lot +of data lightly since the network transfer of the data could become a +bottleneck if performing these operations on the client-side. + +.. warning:: + + By default, :dbcommand:`eval` command requires a write lock. As such + :dbcommand:`eval` will block all other read and write operations + while it runs. + +See :dbcommand:`eval` command and :method:`db.eval()` documentation for +more information, including examples. + +Running .js files via a mongo shell instance on the server +---------------------------------------------------------- + +Running a JavaScript (.js) file using a :program:`mongo` shell instance +on the server is a good technique for performing batch administrative +work. When you run :program:`mongo` shell on the server, connecting via +the localhost interface, the connection is fast with low latency. +Additionally, this technique has the advantage over the +:dbcommand:`eval` command since the command :dbcommand:`eval` blocks +all other operations. + +.. _server-side-where: + +$where Operator +--------------- + +To perform :doc:`/core/read-operations`, in addition to the standard +operators (e.g. :operator:`$gt`, :operator:`$lt`), with the +:operator:`$where` operator, you can also express the query condition +either as a string or a full JavaScript function that specifies a +SQL-like ``WHERE`` clause. However, use the standard operators whenever +possible since :operator:`$where` operations have significantly slower +performance. + +.. warning:: + + Do not write to the database within the :operator:`$where` + JavaScript function. + +See :operator:`$where` documentation for more information, including +examples. + +Storing Functions Server-side +----------------------------- + +.. note:: + + We do **not** recommend using server-side stored functions if + possible. + +There is a special system collection named ``system.js`` that can store +JavaScript functions for reuse. + +To store a function, you can use the :method:`db.collection.save()`, as +in the following example: + +.. code-block:: javascript + + db.system.js.save( + { + _id : "myAddFunction" , + value : function (x, y){ return x + y; } + } + ); + +- The ``_id`` field holds the name of the function and is unique per + database. + +- The ``value`` field holds the function definition + +Once you save a function in the ``system.js`` collection, you can use +the function from any JavaScript context (e.g. :ref:`eval +`, :ref:`$where `, +:ref:`map-reduce `). + +Consider the following example from the :program:`mongo` shell that +first saves a function named ``echoFunction`` to the ``system.js`` +collection and calls the function using :ref:`db.eval() +`: + +.. code-block:: javascript + + db.system.js.save( + { _id: "echoFunction", + value : function(x) { return x; } + } + ) + + db.eval( "echoFunction( 'test' )" ) + +See ``_ for a full example. + +.. versionadded:: 2.1 + In the :program:`mongo` shell, you can use + :method:`db.loadServerScripts()` to load all the scripts saved in + the ``system.js`` collection for the current db. Once loaded, you + can invoke the functions directly in the shell, as in the following + example: + +.. code-block:: javascript + + db.loadServerScripts(); + + echoFunction(3); + + myAddFunction(3, 5); + +Concurrency +----------- + +Refer to the individual method or operator documentation for any +concurrency information. See also the :ref:`concurrency table +`. diff --git a/source/faq/concurrency.txt b/source/faq/concurrency.txt index 35d05643f73..c2be1b50fc1 100644 --- a/source/faq/concurrency.txt +++ b/source/faq/concurrency.txt @@ -105,6 +105,8 @@ opportunity to complete. is available in memory, the read will reacquire the lock to completes the operation. +.. _faq-concurrency-operations-locks: + Which operations lock the database? ----------------------------------- diff --git a/source/reference/command/eval.txt b/source/reference/command/eval.txt index f72c036216f..03bf83d123d 100644 --- a/source/reference/command/eval.txt +++ b/source/reference/command/eval.txt @@ -2,58 +2,163 @@ eval ==== +.. Edits to this page should be carried over to the method db.eval.txt + file. + .. default-domain:: mongodb .. dbcommand:: eval - The :dbcommand:`eval` command evaluates JavaScript functions - on the database server. Consider the following (trivial) example: + The :dbcommand:`eval` command evaluates JavaScript functions on the + database server and has the following form: + + .. code-block:: none + + { + eval: , + args: [ , ... ], + nolock: + } + + The command contains the following fields: + + :field JavaScript function: + A JavaScript function to evaluate. The function may + accept no arguments, as in the following example: + + .. code-block:: javascript + + function () { + + ... + + } + + The function can also accept arguments, as in the following example: + + .. code-block:: javascript + + function (arg1, arg2) { + + ... + } + + If you specify arguments, you must include the corresponding + ``args`` field in the command. + + :field Array args: + An array of corresponding arguments to the ``function``. Omit + ``args`` if the ``function`` does not take arguments. + + :field Boolean nolock: + Optional. Specifies whether to disable the write lock. By + default, :dbcommand:`eval` takes a write lock. There are + circumstances where the :dbcommand:`eval` executes a strictly + read-only operation that does not need to block other + operations. If ``nolock`` is ``true``, the + :dbcommand:`eval` does not take a write lock. + + .. warning:: + + Do not disable the write lock if the function may modify + the contents of the database in any way. + + Consider the following example which uses :dbcommand:`eval` to + perform an increment and calculate the average on the server: .. code-block:: javascript - { eval: function() { return 3+3 } } + db.runCommand( { + eval: function(name, incAmount) { + var doc = db.myCollection.findOne( { name : name } ); + + doc = doc || { name : name , num : 0 , total : 0 , avg : 0 }; + + doc.num++; + doc.total += incAmount; + doc.avg = doc.total / doc.num; + + db.myCollection.save( doc ); + return doc; + }, + args: [ "eliot", 5 ] + } + ); - The shell also provides a helper method, so you can express the - above as follows: + The ``db`` in the function refers to the current database. + + The shell also provides a helper method :method:`db.eval()`, so you + can express the above as follows: .. code-block:: javascript - db.eval( function() { return 3+3 } ); + db.eval( function(name, incAmount) { + var doc = db.myCollection.findOne( { name : name } ); + + doc = doc || { name : name , num : 0 , total : 0 , avg : 0 }; - The shell's JavaScript interpreter evaluates functions entered - directly into the shell. If you want to use the server's - interpreter, you must run :dbcommand:`eval`. + doc.num++; + doc.total += incAmount; + doc.avg = doc.total / doc.num; + + db.myCollection.save( doc ); + return doc; + }, + "eliot", 5 ); - Be aware of following behaviors and limitations: + You cannot pass the ``nolock`` flag to the :method:`db.eval()` + in the :program:`mongo` shell. - - :dbcommand:`eval` does not work in :term:`sharded ` - environments. + If you want to use the server's interpreter, you must run + :dbcommand:`eval`. Otherwise, the :program:`mongo` shell's + JavaScript interpreter evaluates functions entered directly into the + shell. - - The ``eval`` operation take a write lock by default. This means - that writes to database aren't permitted while it's running. You - can, however, disable the lock by setting the ``nolock`` flag to - ``true``. For example: + If an error occurs, :dbcommand:`eval` throws an exception. Consider + the following invalid function that uses the variable ``x`` without + declaring it as an argument: + + .. code-block:: javascript - .. code-block:: javascript + db.runCommand( + { + eval: function() { return x + x; }, + args: [3] + } + ) + + The statement will result in the following exception: + + .. code-block:: javascript - { eval: function() { return 3+3 }, nolock: true } + { + "errno" : -3, + "errmsg" : "invoke failed: JS Error: ReferenceError: x is not defined nofile_b:1", + "ok" : 0 + } - Using :method:`db.runCommand()` helper, in the :program:`mongo` - shell, this command would resemble: + .. warning:: - .. code-block:: javascript + - The :dbcommand:`eval` operation takes a write lock by default. + This means that other read and write operations to the database + are blocked while :dbcommand:`eval` is running. You can, + however, disable the lock by setting the ``nolock`` flag to + ``true`` if the ``eval`` performs a strictly read-only + operation. - db.runCommand( { eval: function() { return 3+3 }, nolock: true } ) + - :dbcommand:`eval` also takes a JavaScript lock. - .. warning:: + - Do not use :dbcommand:`eval` for long running operations as + :dbcommand:`eval` blocks all other operations. Consider using + :doc:`other server side code execution options + `. - Do not disable the write lock if the operation may modify the - contents of the database in anyway. + - You can not use :dbcommand:`eval` with :term:`sharded + ` data. In general, you should avoid using + :dbcommand:`eval` in :term:`sharded cluster`; nevertheless, it + is possible to use :dbcommand:`eval` with non-sharded + collections and databases stored in a :term:`sharded cluster`. - You cannot pass the ``nolock`` option to :method:`db.eval()` in - the :program:`mongo` shell. + .. seealso:: - There are some circumstances where the :method:`db.eval()` - implements a strictly-read only operation that need not block - other operations when disabling the write lock may be useful. Use - this functionality with extreme caution. + :doc:`/applications/server-side-code-execution` diff --git a/source/reference/method/db.eval.txt b/source/reference/method/db.eval.txt index e61de459102..5bffb0db048 100644 --- a/source/reference/method/db.eval.txt +++ b/source/reference/method/db.eval.txt @@ -2,31 +2,113 @@ db.eval() ========= +.. Edits to this page should be carried over to the command eval.txt + file. + .. default-domain:: mongodb .. method:: db.eval(function, arguments) - :param JavaScript function: A JavaScript function. + The :method:`db.eval()` provides the ability to run JavaScript code + on the MongoDB server. It is a :program:`mongo` shell wrapper around + the :dbcommand:`eval` command. + + The method accepts the following parameters: + + :param JavaScript function: A JavaScript function. + + The function may accept no arguments, as in the following + example: + + .. code-block:: javascript + + function () { + ... + } + + The function can also accept arguments, as in the following example: + + .. code-block:: javascript + + function (arg1, arg2) { + ... + } + + :param arguments: + A list of arguments to pass to the JavaScript ``function`` if + the function accepts arguments. Omit if the ``function`` does + not take arguments. + + Consider the following example of the :method:`db.eval()` method: + + .. code-block:: javascript - :param arguments: A list of arguments to pass to the JavaScript - function. + db.eval( function(name, incAmount) { + var doc = db.myCollection.findOne( { name : name } ); - Provides the ability to run JavaScript code using the JavaScript - engine embedded in the MongoDB instance. In this environment the - value of the ``db`` variable on the server is the name of the - current database. + doc = doc || { name : name , num : 0 , total : 0 , avg : 0 }; - Unless you use :method:`db.eval()`, the :program:`mongo` shell - itself will evaluate all JavaScript entered into :program:`mongo` - shell itself. + doc.num++; + doc.total += incAmount; + doc.avg = doc.total / doc.num; + + db.myCollection.save( doc ); + return doc; + }, + "eliot", 5 ); + + - The ``db`` in the function refers to the current database. + + - ``"eliot"`` is an argument to the function and corresponds to the ``name`` field + + - ``5`` is an argument to the function and corresponds to the ``incAmount`` field + If you want to use the server's interpreter, you must run + :method:`db.eval()`. Otherwise, the :program:`mongo` shell's + JavaScript interpreter evaluates functions entered directly into the + shell. + + If an error occurs, :method:`db.eval()` throws an exception. Consider + the following invalid function that uses the variable ``x`` without + declaring it as an argument: + + .. code-block:: javascript + + db.eval( function() { return x + x; }, 3 ); + + The statement will result in the following exception: + + .. code-block:: javascript + + { + "errno" : -3, + "errmsg" : "invoke failed: JS Error: ReferenceError: x is not defined nofile_b:1", + "ok" : 0 + } + .. warning:: - Do not use :method:`db.eval()` for long running operations, as - :method:`db.eval()` blocks all other operations. Consider using - :term:`map-reduce` for similar functionality in these - situations. + - The :method:`db.eval()` operation takes a write lock by default. + This means that other read and write operations to the database + are blocked while :method:`db.eval()` is running. However, if + you are using the :dbcommand:`eval` command and not the method, + you can disable the lock by setting the ``nolock`` flag to + ``true`` if the :dbcommand:`eval` performs a strictly read-only + operation. + + - :method:`db.eval()` also takes a JavaScript lock. + + - Do not use :method:`db.eval()` for long running operations, as + :method:`db.eval()` blocks all other operations. Consider using + :doc:`other server side code execution options + `. + + - You can not use :method:`db.eval()` with :term:`sharded + ` data. In general, you should avoid using + :method:`db.eval()` in :term:`sharded cluster`; nevertheless, it + is possible to use :method:`db.eval()` with non-sharded + collections and databases stored in :term:`sharded cluster`. + + .. seealso:: - The :method:`db.eval()` method cannot operate on sharded - data. However, you may use :method:`db.eval()` with non-sharded - collections and databases stored in :term:`sharded cluster`. + :doc:`/applications/server-side-code-execution` diff --git a/source/reference/operator/where.txt b/source/reference/operator/where.txt index c92d400d49a..a64f566d041 100644 --- a/source/reference/operator/where.txt +++ b/source/reference/operator/where.txt @@ -6,23 +6,64 @@ $where .. operator:: $where - Use the :operator:`$where` operator to pass a string containing a - JavaScript expression to the query system to provide greater - flexibility with queries. Consider the following: + Use the :operator:`$where` operator to pass either a string + containing a JavaScript expression or a full JavaScript function to + the query system. The :operator:`$where` provides greater + flexibility, but requires that the database processes the JavaScript + expression or function for *each* document in the collection. In the + JavaScript expression or function, the document is referenced by + either ``this`` or ``obj`` . + + .. warning:: + + - Do not write to the database within the :operator:`$where` + JavaScript function. + + - :operator:`$where` evaluates JavaScript and cannot take + advantage of indexes. Therefore, query performance improves + when you express your query using the standard MongoDB + operators (e.g., :operator:`$gt`, :operator:`$in`). + + - In general, you should use :operator:`$where` only when you + can't express your query using another operator. If you must + use :operator:`$where`, try to include at least one other + standard query operator to filter the result set. Using + :operator:`$where` alone requires a table scan. + + Consider the following examples: .. code-block:: javascript - db.collection.find( { $where: "this.a == this.b" } ); + db.myCollection.find( { $where: "this.credits == this.debits" } ); + db.myCollection.find( { $where: "obj.credits == obj.debits" } ); - .. warning:: + db.myCollection.find( { $where: function() { return (this.credits == this.debits) } } ); + db.myCollection.find( { $where: function() { return obj.credits == obj.debits; } } ); + + Additionally, if the query consists only of the :operator:`$where` + operator, you can pass in just the JavaScript expression or + JavaScript functions, as in the following examples: + + .. code-block:: javascript + + db.myCollection.find( "this.credits == this.debits || this.credits > this.debits" ); + + db.myCollection.find( function() { return (this.credits == this.debits || this.credits > this.debits ) } ); + + You can include both the standard MongoDB operators and the + :operator:`$where` operator in your query, as in the following + examples: + + .. code-block:: javascript + + db.myCollection.find( { active: true, $where: "this.credits - this.debits < 0" } ); + db.myCollection.find( { active: true, $where: function() { return obj.credits - obj.debits < 0; } } ); + + Including the standard MongoDB operator expression provides the following performance advantages: - :operator:`$where` evaluates JavaScript and cannot take - advantage of indexes. Therefore, query performance improves - when you express your query using the standard MongoDB operators - (e.g., :operator:`$gt`, :operator:`$in`). + - The standard MongoDB operator expression will be evaluated first. + If there is no match based on the standard operator expression, no + further evaluation is required. - In general, you should use :operator:`$where` only when you - can't express your query using another operator. If you must - use :operator:`$where`, try to include at least one other - standard query operator to filter the result set. Using - :operator:`$where` alone requires a table scan. + - The standard operator expression can take advantage + of an index.