Простая выборка данных и выборка с условием

Простая выборка данных

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

query := db.Query("test_namespace")
SELECT * FROM test_namespace
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"
}'

Простая выборка данных с фильтрацией полей

Используя Query.Select() в go, перечисление в SELECT-секции (SELECT field1, field2, field3 from ns) в sql и select_filter в JSON при HTTP-запросах, можно управлять тем, какие поля документов будут возвращены в результате выполнения запроса. При перечислении полей требуется указывать существующие в документах JSON-пути с учётом их регистра. Несуществующие поля или поля с неправильным регистром игнорируются. Если в этом списке нет полей, удовлетворяющих данным условиям, то фильтрация не применяется, и в результате выполнения запроса будут возвращены все поля документов.

Пример:

Пусть структура документа определена следующим образом:

type Item struct {
	ID       int64  `reindex:"id,,pk"`
	Name     string `json:"author_name" reindex:"name"`
	Articles []int  `reindex:"articles"`
	Year     int    `reindex:"year,tree"`
}

Тогда корректный запрос с фильтрацией полей id и name выглядит следующим образом:

query := db.Query("test_namespace").Select ("ID", "author_name")
SELECT ID, author_name FROM test_namespace
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",
  "select_filter": [
    "ID",
    "author_name"
  ],
  "type": "select"
}'

Выборка данных с условиями

Для выборки данных с условиям в Reindexer совместно с оператором SELECT используется ключевое слово WHERE.

Можно использовать выражения с общими операторами сравнения (в SQL-запросах) и операторами сравнения значений (при взаимодействии с БД через коннекторы):

В SQL-запросах на выборку данных можно использовать следующие операторы:

Оператор Описание/пояснение
= Равно
> Больше
< Меньше
>= Больше или равно
<= Меньше или равно
<> Не равно
IN Определяет набор значений, с которым будут сравниваться результаты при выборке
RANGE Определяет диапазон значений, которые будут использоваться при выборке
LIKE Поиск по заданному шаблону. Подробнее об операторе – в разделе Простой поиск
IS NULL Значение не задано
IS NOT NULL Задано какое-либо значение
ST_DWithin Получение объектов, находящихся на определенном расстоянии от других объектов (только для геометрического типа данных)

В запросах на выборку данных при взаимодействии с Reindexer через Go-коннектор можно использовать следующие операторы и методы:

Оператор Описание/пояснение
EQ Равно
GT Больше
LT Меньше
GE Больше или равно
LE Меньше или равно
SET Проверяет значение в наборе значений, разделенных запятыми, и извлекает соответствующие строки из неймспейса. Также позволяет проверить, есть ли у массивов общие элементы
ALLSET Проверяет значение в наборе значений, разделенных запятыми, и извлекает соответствующие строки из неймспейса. Также позволяет проверить, есть ли у массивов общие элементы, при этом все элементы первого массива должны входить во второй
RANGE Определяет диапазон значений, которые будут использоваться при выборке
ANY Любое значение
EMPTY Пустое значение (в том числе и массив нулевой длины)
LIKE Поиск по заданному шаблону. Подробнее об операторе – в разделе Простой поиск
ST_DWithin() Получение объектов, находящихся на определенном расстоянии от других объектов (только для геометрического типа данных)
Match() Запросы к полнотекстовым индексам. Подробнее — в разделе Запросы к полнотекстовым индексам
Not() Логическое отрицание для любого следующего оператора

Условия можно комбинировать с помощью логических операторов OR и AND.

При этом операции с OR имеют более высокий приоритет, чем AND. Например, условие вида WHERE a AND b OR c AND d эквивалентно WHERE a AND (b OR c) AND d.

Также Reindexer поддерживает условия с фильтрацией при помощи сравнения полей одного и того же документа.

Пример запроса с одним условием:

query := db.Query("test_namespace").
    Where("year", reindexer.EQ, 2022)
SELECT * FROM test_namespace WHERE year = 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",
  "filters": [
    {
      "field": "year",
      "cond": "EQ",
      "value": 2022
    }
  ]
}'

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

query := db.Query("test_namespace").
	Where("year", reindexer.QE, 2022).
	Where("price", reindexer.LE, 1000)
SELECT * FROM test_namespace WHERE year = 2022 AND price <= 1000
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",
  "filters": [
    {
      "field": "year",
      "cond": "EQ",
      "value": 2022
    },
    {
      "field": "price",
      "cond": "LE",
      "value": 1000
    }
  ]
}'

Приоритет операций может быть изменён при помощи использования скобок. Механизм задания скобок зависит от конкретного API:

query := db.Query("test_namespace").
	OpenBracket().
	Where("year", reindexer.EQ, 2022).
	Where("price", reindexer.LE, 1000).
	CloseBracket().
	Or().
	OpenBracket().
	Where("year", reindexer.EQ, 2010).
	Where("price", reindexer.GT, 2000).
	CloseBracket()
SELECT * FROM test_namespace WHERE (year = 2022 AND price <= 1000) OR (year = 2010 AND price > 2000)
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",
  "filters": [
    {
      "filters": [
        {
          "field": "year",
          "cond": "EQ",
          "value": 2022
        },
        {
          "field": "price",
          "cond": "LE",
          "value": 1000
        }
      ]
    },
    {
      "op": "OR",
      "filters": [
        {
          "field": "year",
          "cond": "EQ",
          "value": 2010
        },
        {
          "field": "price",
          "cond": "GT",
          "value": 2000
        }
      ]
    }
  ]
}'

Пример запроса с использованием условного оператора LIKE:

query := db.Query("test_namespace").
	Where("field_description", reindexer.LIKE, "info%")
SELECT * FROM test_namespace WHERE field_description LIKE 'info%'
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",
  "filters": [
    {
      "field": "name",
      "cond": "LIKE",
      "value": "info%"
    }
  ]
}'

LIKE использует метод полного сканирования поля и его применение ведет к увеличению нагрузки на сервер. Для полнотекстового поиска с высокой скоростью рекомендуется использовать полнотекстовые индексы.

Пример запроса с фильтрацией при помощи сравнения полей одного и того же документа:

В Go-коннекторе для запросов с фильтрацией при помощи сравнения полей одного и того документа используется метод WhereBetweenFields().

query := db.Query("test_namespace").
	WhereBetweenFields("price", reindexer.GE, "recommended_price")

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

SELECT * FROM test_namespace WHERE "recommended_price" > "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",
  "filters": [
    {
      "first_field": "recommended_price",
      "cond": "LE",
      "second_field": "price"
    }
  ]
}'

Пример запроса для выборки из композитного индекса:

query := db.Query("test_namespace").WhereComposite("price+year", reindexer.EQ,[]interface{}{10000,2022})

Имя композитного индекса необходимо передавать в кавычках, а требуемые значения — в фигурных скобках.

SELECT * FROM test_namespace WHERE "price+year" = {10000, 2022}

Особенности поиска по условию IS NULL

  • Фильтрация по IS NULL допустима только для sparse-индексов, массивов, векторных индексов и неиндексных полей;
  • Условие f = NULL будет заменено планировщиком на f IS NULL;
  • Условие f IN (1, 2, 3, NULL) будет заменено планировщиком на f IN (1, 2, 3) OR f IS NULL;
  • Условие f ALLSET (1, 2, 3, NULL) будет заменено планировщиком на f ALLSET (1, 2, 3) AND f IS NULL;
  • Для остальных условий c аргументом NULL (например f > NULL) будет возвращена ошибка.

Результаты запросов по условию IS NULL в зависимости от значения поля в документе:

  • + - документ будет в выдаче;
  • - - документа не будет в выдаче;
  • n - невозможно создать такой документ.

Поле без вложенности. В индексе задан json_paths: ["f"] или json_paths: ["obj"] в зависимости от контекста:

Документ Неиндексное Массив Sparse Sparse массив
{} + + + +
{"f": null} + + + +
{"f": []} + + + +
{"f": [null]} + - + +
{"f": [1, null]} + - + +
{"f": 0} - - - -
{"f": [0]} - - - -
{"obj": {}} - n - -
{"obj": {"f": null}} - n - -
{"obj": {"f": null, "a": 0}} - n - -

Поле без вложенности. В индексе задан json_paths: ["f1", "f2"] или json_paths: ["obj1", "obj2"] в зависимости от контекста:

Документы Массив с несколькими json путями
{}, {} +
{"f1": 1}, {} -
{"f1": null}, {"f2": null} +
{"f1": 1}, {"f2": null} -
{"f1": []}, {"f2": []} +
{"f1": [1]}, {"f2": []} -
{"f1": [null]}, {"f2": [null]} -
{"f1": [1, null]}, {"f2": [1, null]} -
{"f1": 0}, {"f2": 0} -
{"f1": [0]}, {"f2": [0]} -
{"obj1": {}}, {"obj2": {}} n
{"obj1": {"f": null}}, {"obj2": {"f": null}} n
{"obj1": {"f": null, "a": 0}}, {"obj2": {"f": null, "a": 0}} n

Поле со вложенностью. В индексе задан json_paths: ["obj.f"]:

Документ Массив Sparse Sparse массив
{"obj": {}} + + +
{"obj": {"f": null}} + + +
{"obj": {"f": null, "a": 0}} + + +
{"obj": {"f": [null]}} - n +
{"obj": {"f": [1, null]}} - n +

Поле со вложенностью. В индексе задан json_paths: ["obj1.f", "obj2.f"]:

Документы Массив с несколькими json путями
{"obj1": {}}, {"obj2": {}} +
{"obj1": {"f": 1}}, {"obj2": {}} -
{"obj1": {"f": null}}, {"obj2": {"f": null}} +
{"obj1": {"f": 1}}, {"obj2": {"f": null}} -
{"obj1": {"f": null, "a": 0}}, {"obj2": {"f": null, "a": 0}} +
{"obj1": {"f": 1, "a": 0}}, {"obj2": {"f": null, "a": 0}} -
{"obj1": {"f": [null]}}, {"obj2": {"f": [null]}} -
{"obj1": {"f": [1, null]}}, {"obj2": {"f": [1, null]}} -