'Allows to get **multiple** data for given ``key`` for *Hash based indexes*.
Also allows get **range** queries for *Tree based indexes* with ``start`` and ``end`` arguments.
:param index_name: Index to perform the operation
:param key: key to look for (has to be ``None`` to use range queries)
:param limit: defines limit for query
:param offset: defines offset (how many records from start it will ignore)
:param with_doc: if ``True`` data from **id** index will be included in output
:param with_storage: if ``True`` data from index storage will be included, otherwise just metadata.
:param start: ``start`` parameter for range queries
:param end: ``end`` parameter for range queries
:returns: iterator over records'

def get_many(self, index_name, key=None, limit=(-1), offset=0, with_doc=False, with_storage=True, start=None, end=None, **kwargs):

    if (index_name == 'id'):
        self.__not_opened()
        raise PreconditionsException("Can't    get    many    from    `id`")
    try:
        ind = self.indexes_names[index_name]
    except KeyError:
        self.__not_opened()
        raise IndexNotFoundException(("Index    `%s`    doesn't    exists" % index_name))
    storage = ind.storage
    if ((start is None) and (end is None)):
        gen = ind.get_many(key, limit, offset)
    else:
        gen = ind.get_between(start, end, limit, offset, **kwargs)
    while True:
        try:
            ind_data = gen.next()
        except StopIteration:
            break
        else:
            if (with_storage and ind_data[(-2)]):
                data = storage.get(*ind_data[(-3):])
            else:
                data = {}
            doc_id = ind_data[0]
            if with_doc:
                doc = self.get('id', doc_id, False)
                if data:
                    data['doc'] = doc
                else:
                    data = {'doc': doc}
            data['_id'] = doc_id
            if (key is None):
                data['key'] = ind_data[1]
            (yield data)
