'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):

    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):
            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']]
