Spec-Zone.ru › RethinkDB java

Команда ReQL: group

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

sequence.group([field | function...,]) → grouped_stream
r.group(sequence, [field | function...,]) → grouped_stream

Описание

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

Доступны два варианта через optArg: index может быть именем индекса для группировки (вместо поля). Флаг multi, булевский (по умолчанию false), позволяет назначать отдельные документы в несколько групп, подобно поведению мульти-индексов. Когда multi является true, а значение группировки — массив, документы будут помещены в каждую группу, соответствующую элементам массива. Если массив пустой, строка будет проигнорирована.

Данные, возвращаемые group, будут List<GroupedResult>:

public class GroupedResult<G,V> {
    public final G group;
    public final List<V> values;

    public GroupedResult(G group, List<V> values){
        this.group = group;
        this.values = values;
    }
}

Предположим, что таблица 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);

Для отображения возвращаемых данных мы снова воспользуемся JSON-представлением, с group и values в качестве полей, соответствующих элементам в каждой GroupedResult.

[
    {
        "group": "Alice",
        "values": [
            {"id": 5, "player": "Alice", "points": 7, "type": "free"},
            {"id": 12, "player": "Alice", "points": 2, "type": "free"}
        ]
    },
    {
        "group": "Bob",
        "values": [
            {"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);
[
    {
        "group": "Alice",
        "values": {"id": 5, "player": "Alice", "points": 7, "type": "free"}
    },
    {
        "group": "Bob",
        "values": {"id": 2, "player": "Bob", "points": 15, "type": "ranked"}
    }
]

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

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

r.table("games").group("player").max("points").g("points").run(conn);
[
    {
        "group": "Alice",
        "values": 7
    },
    {
        "group": "Bob",
        "values": 15
    }
]

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

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

r.table("games").group("player", "type").max("points").g("points").run(conn);
[
    {
        "group": ["Alice", "free"],
        "values": 7
    }
    {
        "group": ["Bob", "free"],
        "values": 10,
    },
    {
        "group": ["Bob", "ranked"],
        "values": 15,
    }
]

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

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

r.table("games").group(
    game -> game.pluck("player", "type")
).max("points").g("points").run(conn);
[
    {
        "group": {"player": "Alice", "type": "free"},
        "values": 7
    },
    {
        "group": {"player": "Bob", "type": "free"},
        "values": 10
    },
    {
        "group": {"player": "Bob", "type": "ranked"},
        "values": 15
    }
]

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

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

r.table("matches").group(
    match -> r.array(match.g("date").year(), match.g("date").month())
).count().run(conn);
[
    {
        "group": [2014, 2],
        "values": 2
    },
    {
        "group": [2014, 3],
        "values": 2
    },
    {
        "group": [2014, 4],
        "values": 1
    },
    {
        "group": [2014, 5],
        "values": 3
    }
]

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

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

r.table("games").group().optArg("index", "type")
 .max("points").g("points").run(conn);
[
    {
        "group": "free",
        "values": 10
    },
    {
        "group": "ranked",
        "values": 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(
    row -> row.g("matches").keys()
).optArg("multi", true).run(conn);
[
    {
        "group": "a",
        "values": [ <id 1>, <id 3> ]
    },
    {
        "group": "b",
        "values": [ <id 1>, <id 2> ]
    },
    {
        "group": "c",
        "values": [ <id 2>, <id 3> ]
    }
]

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

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

r.table("games2").group(
    row -> row.g("matches").keys()
).optArg("multi", true).ungroup().map(
    doc -> r.hashMap("match", doc.g("group")).with(
        "total", doc.g("reduction").sum(
            set -> set.g("matches").bracket(doc.g("group")).sum()
        )
    )
).run(conn);
[
    { "match": "a", "total": 36 },
    { "match": "b", "total": 115 },
    { "match": "c", "total": 174 }
]

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

Разгруппировка

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

Формат массива, возвращаемого ungroup, совпадает с форматом по умолчанию для сгруппированных данных в JavaScript-драйвере и Data Explorer.

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

r.table('games').group('player').max('points')['points'].ungroup().run(conn);
[
    {
        "group": "Alice",
        "reduction": 7
    },
    {
        "group": "Bob",
        "reduction": 15
    }
]

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

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

r.table("games").group("player").max("points").g("points").ungroup()
 .orderBy(r.desc("reduction")).run(conn);
[
    {
        "group": "Bob",
        "reduction": 15
    },
    {
        "group": "Alice",
        "reduction": 7
    }
]

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

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

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

r.table("games").group("player").avg("points").run(conn).optArg("group_format", "raw");
{
    "$reql_type$": "GROUPED_DATA",
    "data": [
        ["Alice", 4.5],
        ["Bob", 12.5]
    ]
}

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

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

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

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

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

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

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

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

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

Дополнительные примеры

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

r.table("games").filter(
    game -> game.g("type").eq("free")
).group("player").max("points").g("points").run(conn);
[
    {
        "group": "Alice",
        "values": 7
    },
    {
        "group": "Bob",
        "values": 10
    }
]

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

r.table("games").group(
    "name", game -> game.g("points").mod(2)
).max("points").g("points").run(conn);
[
    {
        "group": ["Alice", 1],
        "values": 7
    },
    {
        "group": ["Bob", 0],
        "values": 10
    },
    {
        "group": ["Bob", 1],
        "values": 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/java/group/

Spec-Zone.ru

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