Perform a map/reduce operation on this collection.

        If `full_response` is ``False`` (default) returns a
        :class:`~pymongo.collection.Collection` instance containing
        the results of the operation. Otherwise, returns the full
        response from the server to the `map reduce command`_.

        :Parameters:
          - `map`: map function (as a JavaScript string)
          - `reduce`: reduce function (as a JavaScript string)
          - `out`: output collection name or `out object` (dict). See
            the `map reduce command`_ documentation for available options.
            Note: `out` options are order sensitive. :class:`~bson.son.SON`
            can be used to specify multiple options.
            e.g. SON([('replace', <collection name>), ('db', <database name>)])
          - `full_response` (optional): if ``True``, return full response to
            this command - otherwise just return the result collection
          - `**kwargs` (optional): additional arguments to the
            `map reduce command`_ may be passed as keyword arguments to this
            helper method, e.g.::

            >>> db.test.map_reduce(map, reduce, "myresults", limit=2)

        .. note:: The :meth:`map_reduce` method does **not** obey the
           :attr:`read_preference` of this :class:`Collection`. To run
           mapReduce on a secondary use the :meth:`inline_map_reduce` method
           instead.

        .. note:: The :attr:`~pymongo.collection.Collection.write_concern` of
           this collection is automatically applied to this operation (if the
           output is not inline) when using MongoDB >= 3.4.

        .. versionchanged:: 3.4
           Apply this collection's write concern automatically to this operation
           when connected to MongoDB >= 3.4.

        .. seealso:: :doc:`/examples/aggregation`

        .. versionchanged:: 3.4
           Added the `collation` option.
        .. versionchanged:: 2.2
           Removed deprecated arguments: merge_output and reduce_output

        .. _map reduce command: http://docs.mongodb.org/manual/reference/command/mapReduce/

        .. mongodoc:: mapreduce

def map_reduce(self, map, reduce, out, full_response=False, **kwargs):
        """Perform a map/reduce operation on this collection.

        If `full_response` is ``False`` (default) returns a
        :class:`~pymongo.collection.Collection` instance containing
        the results of the operation. Otherwise, returns the full
        response from the server to the `map reduce command`_.

        :Parameters:
          - `map`: map function (as a JavaScript string)
          - `reduce`: reduce function (as a JavaScript string)
          - `out`: output collection name or `out object` (dict). See
            the `map reduce command`_ documentation for available options.
            Note: `out` options are order sensitive. :class:`~bson.son.SON`
            can be used to specify multiple options.
            e.g. SON([('replace', <collection name>), ('db', <database name>)])
          - `full_response` (optional): if ``True``, return full response to
            this command - otherwise just return the result collection
          - `**kwargs` (optional): additional arguments to the
            `map reduce command`_ may be passed as keyword arguments to this
            helper method, e.g.::

            >>> db.test.map_reduce(map, reduce, "myresults", limit=2)

        .. note:: The :meth:`map_reduce` method does **not** obey the
           :attr:`read_preference` of this :class:`Collection`. To run
           mapReduce on a secondary use the :meth:`inline_map_reduce` method
           instead.

        .. note:: The :attr:`~pymongo.collection.Collection.write_concern` of
           this collection is automatically applied to this operation (if the
           output is not inline) when using MongoDB >= 3.4.

        .. versionchanged:: 3.4
           Apply this collection's write concern automatically to this operation
           when connected to MongoDB >= 3.4.

        .. seealso:: :doc:`/examples/aggregation`

        .. versionchanged:: 3.4
           Added the `collation` option.
        .. versionchanged:: 2.2
           Removed deprecated arguments: merge_output and reduce_output

        .. _map reduce command: http://docs.mongodb.org/manual/reference/command/mapReduce/

        .. mongodoc:: mapreduce

        """
        if not isinstance(out, (string_type, collections.Mapping)):
            raise TypeError("'out' must be an instance of "
                            "%s or a mapping" % (string_type.__name__,))

        cmd = SON([("mapreduce", self.__name),
                   ("map", map),
                   ("reduce", reduce),
                   ("out", out)])
        collation = validate_collation_or_none(kwargs.pop('collation', None))
        cmd.update(kwargs)

        inline = 'inline' in cmd['out']
        with self._socket_for_primary_reads() as (sock_info, slave_ok):
            if (sock_info.max_wire_version >= 5 and self.write_concern and
                    not inline):
                cmd['writeConcern'] = self.write_concern.document
            cmd.update(kwargs)
            if (sock_info.max_wire_version >= 4 and 'readConcern' not in cmd and
                    inline):
                # No need to parse 'writeConcernError' here, since the command
                # is an inline map reduce.
                response = self._command(
                    sock_info, cmd, slave_ok, ReadPreference.PRIMARY,
                    read_concern=self.read_concern,
                    collation=collation)
            else:
                response = self._command(
                    sock_info, cmd, slave_ok, ReadPreference.PRIMARY,
                    parse_write_concern_error=not inline,
                    collation=collation)

        if full_response or not response.get('result'):
            return response
        elif isinstance(response['result'], dict):
            dbase = response['result']['db']
            coll = response['result']['collection']
            return self.__database.client[dbase][coll]
        else:
            return self.__database[response["result"]]