Spec-Zone.ru › RethinkDB javascript

Команда ReQL: group

Синтаксис команды

sequence.group(field | function..., [{index: <indexname>, multi: false}]) → grouped_stream
r.group(sequence, field | function..., [{index: <indexname>, multi: false}]) → grouped_stream

Описание

Принимает поток и разбивает его на несколько групп на основе указанных полей или функций.

Флаг multi позволяет одному документу быть назначенным в несколько групп, аналогично поведению мультииндексов. Когда multi равен true, а значение группировки — массив, документы будут помещены в каждую группу, соответствующую элементам массива. Если массив пуст, строка будет пропущена.

Предположим, что таблица games имеет следующие данные:

[
    {id: 2, player: "Bob", points: 15, type: "ranked"},
    {id: 5, player: "Alice", points: 7, type: "free"},
    {id: 11, player: "Bob", points: 10, type: "free"},
    {id: 12, player: "Alice", points: 2, type: "free"}
]

Пример: Группировка игр по игроку.

> r.table('games').group('player').run(conn, callback)

// Result passed to callback
[
    {
        group: "Alice",
        reduction: [
            {id: 5, player: "Alice", points: 7, type: "free"},
            {id: 12, player: "Alice", points: 2, type: "free"}
        ]
    },
    {
        group: "Bob",
        reduction: [
            {id: 2, player: "Bob", points: 15, type: "ranked"},
            {id: 11, player: "Bob", points: 10, type: "free"}
        ]
    }
]

Команды, присоединённые после group, будут вызваны для каждого из этих сгруппированных подпотоков, создавая сгруппированные данные.

Пример: Какая игра у каждого игрока лучшая?

> r.table('games').group('player').max('points').run(conn, callback)

// Result passed to callback
[
    {
        group: "Alice",
        reduction: {id: 5, player: "Alice", points: 7, type: "free"}
    },
    {
        group: "Bob",
        reduction: {id: 2, player: "Bob", points: 15, type: "ranked"}
    }
]

Команды, присоединённые к сгруппированным данным, будут работать с каждым элементом группы, создавая более сгруппированные данные.

Пример: Какое максимальное количество очков набрал каждый игрок?

> r.table('games').group('player').max('points')('points').run(conn, callback)

// Result passed to callback
[
    {
        group: "Alice",
        reduction: 7
    },
    {
        group: "Bob",
        reduction: 15
    }
]

Вы также можете сгруппировать по нескольким полям.

Пример: Какое максимальное количество очков набрал каждый игрок для каждого типа игры?

> r.table('games').group('player', 'type').max('points')('points').run(conn, callback)

// Result passed to callback
[
    {
        group: ["Alice", "free"],
        reduction: 7
    }
    {
        group: ["Bob", "free"],
        reduction: 10,
    },
    {
        group: ["Bob", "ranked"],
        reduction: 15,
    }
]

Вы также можете сгруппировать по функции.

Пример: Какое максимальное количество очков набрал каждый игрок для каждого типа игры?

> r.table('games')
    .group(function(game) {
        return game.pluck('player', 'type')
    }).max('points')('points').run(conn, callback)

// Result passed to callback
[
    {
        group: {"player": "Alice", "type": "free"},
        reduction: 7
    },
    {
        group: {"player": "Bob", "type": "free"},
        reduction: 10
    },
    {
        group: {"player": "Bob", "type": "ranked"},
        reduction: 15
    }
]

Используя функцию, вы также можете сгруппировать по дате в поле ReQL дата.

Пример: Сколько матчей было сыграно в этом году по месяцам?

> r.table('matches').group(
      [r.row('date').year(), r.row('date').month()]
  ).count().run(conn, callback)

// Result passed to callback
[
    {
        group: [2014, 2],
        reduction: 2
    },
    {
        group: [2014, 3],
        reduction: 2
    },
    {
        group: [2014, 4],
        reduction: 1
    },
    {
        group: [2014, 5],
        reduction: 3
    }
]

Вы также можете сгруппировать по индексу (первичному или вторичному).

Пример: Какое максимальное количество очков набрано для каждого типа игры?

> r.table('games').group({index:'type'}).max('points')('points').run(conn, callback)

// Result passed to callback
[
    {
        group: "free",
        reduction: 10
    },
    {
        group: "ranked",
        reduction: 15
    }
]

Группировка по значению с multi

Предположим, что таблица games2 имеет следующие данные:

[
    { id: 1, matches: {'a': [1, 2, 3], 'b': [4, 5, 6]} },
    { id: 2, matches: {'b': [100], 'c': [7, 8, 9]} },
    { id: 3, matches: {'a': [10, 20], 'c': [70, 80]} }
]

Используя опцию multi, мы можем сгруппировать данные по матчу A, B или C.

r.table('games2').group(r.row('matches').keys(), {multi: true}).run(conn, callback);
// Result passed to callback
[
    {
        group: "a",
        reduction: [ <id 1>, <id 3> ]
    },
    {
        group: "b",
        reduction: [ <id 1>, <id 2> ]
    },
    {
        group: "c",
        reduction: [ <id 2>, <id 3> ]
    }
]

(Полный набор результатов сокращен на рисунке; <id 1>, <id 2> и <id 3> будут полными документами, соответствующими этим ключам.)

Пример: Используйте map и sum для получения общей суммы очков для каждого матча.

r.table('games2').group(r.row('matches').keys(), {multi: true}).ungroup().map(
    function (doc) {
        return { match: doc('group'), total: doc('reduction').sum(
            function (set) {
                return set('matches')(doc('group')).sum();
            }
        )};
    }
).run(conn, callback);
// Result passed to callback
[
    { match: "a", total: 36 },
    { match: "b", total: 115 },
    { match: "c", total: 174 }
]

Внутренняя sum суммирует баллы по матчу в каждом документе; внешняя sum суммирует эти результаты для получения общей суммы по всем документам.

Рассредоточение

Если вы хотите обработать все группы, а не каждую группу (например, если вы хотите отсортировать группы по результату их сокращения), вы можете использовать ungroup для преобразования сгруппированного потока или сгруппированных данных в массив объектов, представляющих группы.

Пример: Рассредоточение сгруппированных данных.

> r.table('games').group('player').max('points')('points').ungroup().run(conn, callback)

// Result passed to callback
[
    {
        group: "Alice",
        reduction: 7
    },
    {
        group: "Bob",
        reduction: 15
    }
]

Рассредоточение полезно, например, для сортировки сгруппированных данных или для вставки сгруппированных данных в таблицу.

Пример: Какое максимальное количество очков набрал каждый игрок, начиная с тех, у кого больше всего очков?

> r.table('games')
   .group('player').max('points')('points')
   .ungroup().orderBy(r.desc('reduction')).run(conn, callback)

// Result passed to callback
[
    {
        group: "Bob",
        reduction: 15
    },
    {
        group: "Alice",
        reduction: 7
    }
]

Детали реализации

При возвращении сгруппированных данных клиенту они преобразуются в специфичный для клиента родной тип. (Аналогично делается с временем.) В JavaScript сгруппированные данные преобразуются в Array. Если вы хотите получить сырой псевдотип с сервера, вы можете указать groupFormat: 'raw' в качестве необязательного аргумента к run:

Пример: Получение сырого псевдотипа GROUPED_DATA.

> r.table('games').group('player').avg('points').run(conn, {groupFormat:'raw'}, callback)

// Result passed to callback
{
    $reql_type$: "GROUPED_DATA",
    data: [
        ["Alice", 4.5],
        ["Bob", 12.5]
    ]
}

Если флаг group_format не был передан, будет возвращено:

[
    {
        group: "Alice":
        reduction: 4.5
    },
    {
        group: "Bob"
        reduction: 12.5
    }
]

Вы также можете использовать команду ungroup (см. выше), которая преобразует сгруппированные данные в массив объектов на сервере.

Детали производительности

Если вы выполняете запрос, возвращающий сгруппированный поток, он будет автоматически преобразован в сгруппированные данные перед отправкой вам (в настоящее время нет эффективного способа передавать группы из RethinkDB). Эти сгруппированные данные ограничены размером массива, по умолчанию 100 000 элементов (см. run для получения информации о том, как использовать опцию arrayLimit для изменения этого значения).

В целом, операции над сгруппированными потоками будут эффективно распределены, а операции над сгруппированными данными — нет. Вы можете определить, с чем вы работаете, добавив typeOf в конец вашего запроса. Ниже приведены эффективные и неэффективные примеры.

Пример: Эффективная операция.

// r.table('games').group('player').typeOf().run(conn, callback)
// Returns "GROUPED_STREAM"
r.table('games').group('player').min('points').run(conn, callback) // EFFICIENT

Пример: Неэффективная операция.

// r.table('games').group('player').orderBy('score').typeOf().run(conn, callback)
// Returns "GROUPED_DATA"
r.table('games').group('player').orderBy('score').nth(0).run(conn, callback) // INEFFICIENT

Что означает неэффективность здесь? При работе со сгруппированными данными вместо сгруппированного потока все данные должны быть доступны на узле, обрабатывающем запрос. Это означает, что операция будет использовать ресурсы только одного сервера и потребует памяти, пропорциональной размеру обрабатываемых сгруппированных данных. (В случае с orderBy в неэффективном примере это означает память, пропорциональную размеру таблицы.) Ограничение на размер массива также применяется к сгруппированным данным, поэтому пример orderBy потерпит неудачу для таблиц с более чем 100 000 записей без изменения опции arrayLimit на run.

Ещё примеры

Пример: Какое максимальное количество очков набрал каждый игрок в свободных играх?

> r.table('games').filter( r.row('type').eq('free'))
    .group('player').max('points')('points')
    .run(conn, callback)

// Result passed to callback
[
    {
        group: "Alice":
        reduction: 7
    },
    {
        group: "Bob",
        reduction: 10
    }
]

Пример: Каковы наибольшие чётное и нечётное значение очков каждого игрока?

r.table('games')
    .group('name', function(game) {
        return game('points').mod(2)
    }).max('points')('points').run(conn, callback)

// Result passed to callback
[
    {
        group: ["Alice", 1]
        reduction: 7,
    },
    {
        group: ["Bob", 0],
        reduction: 10
    },
    {
        group: ["Bob", 1],
        reduction: 15
    }
]

Связанные команды

  • ungroup
  • map
  • reduce
  • count
  • sum
  • avg
  • min
  • max

© RethinkDB contributors
Licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License.
https://rethinkdb.com/api/javascript/group/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API