Сортировка и ограничение выдачи

Reindexer может сортировать документы по возрастанию (ASC, используется по умолчанию) и убыванию (DESC). При задании условий сортировки возможно использование математических выражений. Выражения с условиями сортировки могут содержать:

  • имена полей и индексов неймспейсов;
  • числа;
  • функции rank(), abs(), ST-Distance();
  • скобки и арифметические выражения.

Сортировка по полю

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

Сортировка по полям приджойненных неймспейсов работает только для InnerJoin и только если join происходит 1 к 1.

При сортировке по композитному индексу сравнение выполняется в лексикографическом порядке по входящим в него полям.

При сортировке по неиндексному полю оно должно содержать значения одного типа, либо все значения должны быть численными (bool, int, int64, float).

Примеры:

При взаимодействии с БД через Go-коннектор для сортировки используется метод Sort().

// Сортировка по полю
query := db.Query("actors").Sort("id", false)
....
// Сортировка по вложенному полю
query = db.Query("actors").Sort("person.age", false)
....
// Сортировка по полю приджойненного неймспейса.
query = db.Query("actors").
	InnerJoin(db.Query("cities")).On("birth_place_id", reindexer.EQ, "id").
	Sort("cities.population", false)

В SQL-запросах для сортировки используется оператор ORDER BY. Сортировку можно выполнять по нескольким полям, указав их через запятую. При этом сперва выполняется сортировка по первому полю, затем среди групп полей с одинаковыми значениями выполняется сортировка по второму полю и так далее. Признак DESC указывается для каждого поля отдельно. Если сначала нужно вывести записи с требуемыми значениями столбца, а затем все остальные, используйте параметр FIELD.

   -- Сортировка по полю
   SELECT * FROM actors ORDER BY person ASC
   ....
   -- Сортировка по вложенному полю
   SELECT * FROM actors ORDER BY `person.name` ASC
   ....
   -- Сортировка по joined-полю
   SELECT * from actors INNER JOIN (select * from cities) ON birth_place_id.id ORDER BY cities.population ASC
   ....
   -- Сортировка с использованием FIELD
   SELECT * FROM media_items WHERE 'ratings.imdb' > 9.2 ORDER BY FIELD (year, 2010, 2005, 2015)
# Сортировка по полю
curl --location --request POST 'http://127.0.0.1:9088/api/v1/db/testdb/query' \
--header 'Content-Type: application/json' \
--data-raw '{
  "namespace": "actors",
  "type": "select",
  "sort": [
    {
      "field": "person",
      "desc": false
    }
  ]
}'
....
# Сортировка по вложенному полю.
curl --location --request POST 'http://127.0.0.1:9088/api/v1/db/testdb/query' \
--header 'Content-Type: application/json' \
--data-raw '{
  "namespace": "actors",
  "type": "select",
  "sort": [
    {
      "field": "nested.nested2.nested3.nested4.newField",
      "desc": false
    }
  ]
}'
....
# Сортировка с использованием FIELD
curl --location --request POST 'http://127.0.0.1:9088/api/v1/db/testdb/query' \
--header 'Content-Type: application/json' \
--data-raw '{
  "namespace": "media_items",
  "type": "select",
  "filters": [
    {
      "field": "ratings.imdb",
      "cond": "GE",
      "value": 9.2
    }
  ],
  "sort": [
    {
      "field": "year",
      "values": [
        2010,
        2005,
        2015
      ],
      "desc": false
    }
  ]
}'

Для индексов также возможно установить пользовательский порядок сортировки (подробнее — в разделе Параметры индексных полей ).

Пример:

type SortModeCustomItem struct {
	ID      int    `reindex:"id,,pk"`
	InsItem string `reindex:"item_custom,hash,collate_custom=a-zA-Z0-9"` // Для индекса задан пользовательский порядок сортировки
}

type SortModeCustomItem struct {
	ID      int    `reindex:"id,,pk"`
	InsItem string `reindex:"item_custom,hash,collate_custom=АаБбВвГгДдЕеЖжЗзИиКкЛлМмНнОоПпРрСсТтУуФфХхЦцЧчШшЩщЪъЫыЬьЭ-ЯAaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0-9ЁёЙйэ-я"` // Для индекса задан пользовательский порядок сортировки
}

Сортировка с использованием функций rank(), abs(), ST_Distance()

rank() обеспечивает сортировку по рангу соответствия результата поисковому запросу. Используется только при полнотекстовом поиске.

Примеры:

query = db.Query("actors").Where("description", reindexer.EQ, "ququ").
    Sort("rank() + id / 100", true)
SELECT * FROM actors WHERE description = 'ququ' ORDER BY 'rank() + id / 100' DESC
curl --location --request POST 'http://127.0.0.1:9088/api/v1/db/testdb/query' \
--header 'Content-Type: application/json' \
--data-raw '{
  "namespace": "actors",
  "type": "select",
  "filters": [
    {
      "field": "description",
      "cond": "EQ",
      "value": "ququ"
    }
  ],
  "sort": [
    {
      "field": "rank() + id / 100",
      "desc": true
    }
  ]
}'

Функция abs() возвращает модуль числа.

Примеры использования:

query = db.Query("actors").Where("description", reindexer.EQ, "ququ").
    Sort("-id + Abs(-value + -1.2) * -Rank()", true)
SELECT * FROM actors WHERE description = 'ququ' ORDER BY '-id + Abs(-value + -1.2) * -Rank()' DESC
curl --location --request POST 'http://127.0.0.1:9088/api/v1/db/testdb/query' \
--header 'Content-Type: application/json' \
--data-raw '{
  "namespace": "actors",
  "type": "select",
  "filters": [
    {
      "field": "description",
      "cond": "EQ",
      "value": "ququ"
    }
  ],
  "sort": [
    {
      "field": "-id + Abs(-value + -1.2) * -Rank()",
      "desc": true
    }
  ]
}'

ST_Distance() используется для вычисления расстояния между двумя точками. Точками могут быть rtree индексы в основном или приджойненных неймспейсах либо фиксированные точки в формате ST_GeomFromText("point(1 -3)").

Пример:

query = db.Query("actors").
    Join(db.Query("cities")).On("birth_place_id", reindexer.EQ, "id").
    Sort("ST_Distance(cities.center, ST_GeomFromText(\"point(1 -3)\"))", false).
    Sort("ST_Distance(location, cities.center)", false)
SELECT * FROM actors ORDER BY 'ST_Distance(location, ST_GeomFromText(\"point(1 -3)\"))' ASC ORDER BY 'ST_Distance(location, cities.center)' ASC
curl --location --request POST 'http://127.0.0.1:9088/api/v1/db/testdb/query' \
--header 'Content-Type: application/json' \
--data-raw '{
  "namespace": "actors",
  "type": "select",
  "sort": [
    {
      "field": "ST_Distance(location, ST_GeomFromText(\"point(1 -3)\"))",
      "desc": false
    },
    {
      "field": "ST_Distance(location, cities.center)",
      "desc": false
    }
  ]
}'

Сортировка в соответствии с арифметическим выражением

Для установки правила сортировки могут использоваться арифметические выражения, включающие операции сложения (+), вычитания (-), умножения (*), деления (/) и скобки (()). Арифметические выражения в SQL-запросах указываются в одинарных кавычках. Параметры и математические операторы в них должны быть разделены пробелом.

Поля, используемые в выражении, должны содержать числовые значения (bool, int, int64 или float) либо строку, в которой записано число (ведущие и конечные пробелы игнорируются).

Примеры:

query = db.Query("actors").Sort("person.age / -10 + price / 1000 * (id - 5)", true)
SELECT * FROM actors WHERE description = 'ququ' ORDER BY 'rank() + id / 100' DESC
curl --location --request POST 'http://127.0.0.1:9088/api/v1/db/testdb/query' \
--header 'Content-Type: application/json' \
--data-raw '{
  "namespace": "actors",
  "type": "select",
  "filters": [
    {
      "field": "description",
      "cond": "EQ",
      "value": "ququ"
    }
  ],
  "sort": [
    {
      "field": "rank() + id / 100",
      "desc": true
    }
  ]
}'

Форсированная сортировка

Вы можете указать конкретные значения, которые при сортировке будут выводиться первыми в результатах (при их наличии в неймспейсе).

Пример:

query := db.Query("items").Sort("id", false, "100", "11", "10")
SELECT * FROM test_namespace ORDER BY FIELD(id, '100', '10', '11')
curl --location --request POST 'http://127.0.0.1:9088/api/v1/db/testdb/query' \
--header 'Content-Type: application/json' \
--data-raw '{
  "namespace": "test_namespace",
  "type": "select",
  "sort": [
    {
      "field": "id",
      "values": [
        "100",
        "10",
        "11"
      ]
    }
  ]
}'

Форсированная сортировка применяется только для первого поля в ORDER BY. Запросы с несколькими полями в ORDER BY вида SELECT * FROM test_namespace ORDER BY FIELD(id, '100', '10', '11'), FIELD(location_id, 200001, 600001, 400001) и SELECT * FROM test_namespace ORDER BY id, FIELD(location_id, 200001, 600001, 400001) не поддерживаются. Пример корректного запроса с форсированной сортировкой: SELECT * FROM test_namespace ORDER BY FIELD(id, '100', '10', '11'), location_id.

Ограничение выдачи (LIMIT и OFFSET)

Для ограничения выдачи используется параметр LIMIT. Для вывода элементов, начиная с определенной позиции используется параметр OFFSET.

Пример:

query := db.Query("test_table").
		Where("year", reindexer.QE, 2022).
        Limit(10)
....
query := db.Query("test_table").
		Where("year", reindexer.QE, 2022).
        Limit(10).
		Offset(5).
		Sort("price", false)
SELECT * FROM test_table WHERE year=2022 LIMIT 10
....
SELECT * FROM test_table WHERE year=2022 LIMIT 10 OFFSET 5 ORDER BY price
curl --location --request POST 'http://127.0.0.1:9088/api/v1/db/testdb/query' \
--header 'Content-Type: application/json' \
--data-raw '{
  "namespace": "test_namespace",
  "type": "select",
  "limit": 10,
  "filters": [
    {
      "field": "year",
      "cond": "EQ",
      "value": [
        2022
      ]
    }
  ]
}'
....
curl --location --request POST 'http://127.0.0.1:9088/api/v1/db/testdb/query' \
--header 'Content-Type: application/json' \
--data-raw '{
  "namespace": "test_namespace",
  "type": "select",
  "limit": 10,
  "offset": 5,
  "filters": [
    {
      "field": "year",
      "cond": "EQ",
      "value": [
        2022
      ]
    }
  ],
  "sort": [
    {
      "field": "price",
      "desc": false
    }
  ]
}'