Простая выборка данных и выборка с условием
Простая выборка данных
Простая выборка используется для поиска и вывода данных из неймспейса БД без условий. Пример:
query := db.Query("test_namespace")
query = db.new_query("test_namespace")
ResultIterator<Item> query = db.query("test_namespace", Item.class);
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"`
}
index_definitions = [
{
"name": "id",
"json_paths": ["id"],
"field_type": "int64",
'is_pk': True,
},
{
"name": "name",
"json_paths": ["author_name"],
"field_type": "string",
},
{
"name": "articles",
"json_paths": ["articles"],
"field_type": "int",
"is_array": True,
},
{
"name": "year",
"json_paths": ["year"],
"field_type": "int",
"index_type": "tree",
},
]
public static class Item {
@Reindex(name = "id", isPrimaryKey = true)
private Integer id;
@Reindex(name = "name")
private String name;
@Reindex(name = "articles")
private List<Integer> articles;
@Reindex(name = "year", type = TREE)
private Integer year;
public Item() {
}
public Item(Integer id, String name, List<Integer> articles, Integer year) {
this.id = id;
this.name = name;
this.articles = articles;
this.year = year;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Integer> getArticles() {
return articles;
}
public void setArticles(List<Integer> articles) {
this.articles = articles;
}
public Integer getYear() {
return year;
}
public void setYear(Integer year) {
this.year = year;
}
@Override
public String toString() {
return "Item{" +
"id=" + id +
", name='" + name + '\'' +
", articles=" + articles +
", year=" + year +
'}';
}
}
Тогда корректный запрос с фильтрацией полей id и name выглядит следующим образом:
query := db.Query("test_namespace").Select("ID", "author_name")
query = db.new_query("test_namespace").select_fields("id", "author_name")
ResultIterator<Item> query = db.query("test_namespace", Item.class)
.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() |
Логическое отрицание для любого следующего оператора |
| Оператор | Описание/пояснение |
|---|---|
CondType.CondEq |
Равно |
CondType.CondGt |
Больше |
CondType.CondLt |
Меньше |
CondType.CondGe |
Больше или равно |
CondType.CondLe |
Меньше или равно |
CondType.CondSet |
Проверяет значение в наборе значений, разделенных запятыми, и извлекает соответствующие строки из неймспейса. Также позволяет проверить, есть ли у массивов общие элементы |
CondType.CondAllSet |
Проверяет значение в наборе значений, разделенных запятыми, и извлекает соответствующие строки из неймспейса. Также позволяет проверить, есть ли у массивов общие элементы, при этом все элементы первого массива должны входить во второй |
CondType.CondRange |
Определяет диапазон значений, которые будут использоваться при выборке |
CondType.CondAny |
Любое значение |
CondType.CondEmpty |
Пустое значение (в том числе и массив нулевой длины) |
CondType.CondLike |
Поиск по заданному шаблону. Подробнее об операторе — в разделе Простой поиск |
CondType.CondDWithin |
Получение объектов, находящихся на определенном расстоянии от других объектов (только для геометрического типа данных) |
match() |
Запросы к полнотекстовым индексам. Подробнее — в разделе Запросы к полнотекстовым индексам |
op_not() |
Логическое отрицание для любого следующего оператора |
Условия можно комбинировать с помощью логических операторов OR и AND. Большинство условий также может быть инвертировано при помощи оператора NOT (за исключением полнотекстовых и KNN).
При этом операции с
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)
query = db.new_query("test_namespace").where("year", CondType.CondEq, 2022)
ResultIterator<Item> query = db.query("test_namespace", Item.class)
.where("year", Condition.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
}
]
}'
Пример запроса с одним отрицание (NOT). Обратите внимание, что NOT в этом случае ставится перед самим условием, а не перед оператором:
query := db.Query("test_namespace").
Not().Where("year", reindexer.EQ, 2022)
query = db.new_query("test_namespace").op_not().where("year", CondType.CondEq, 2022)
ResultIterator<Item> query = db.query("test_namespace", Item.class)
.not().where("year", Condition.EQ, 2022);
SELECT * FROM test_namespace WHERE NOT 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": [
{
"op": "NOT",
"field": "year",
"cond": "EQ",
"value": 2022
}
]
}'
Пример запроса с несколькими условиями:
query := db.Query("test_namespace").
Where("year", reindexer.EQ, 2022).
Where("price", reindexer.LE, 1000).
Not().Where("price", reindexer.SET, []int{100, 200, 300})
query = (
db.new_query("test_namespace")
.where("year", CondType.CondEq, 2022)
.where("price", CondType.CondLe, 1000)
.op_not().where("price", CondType.CondSet, [100, 200, 300])
)
ResultIterator<Item> query = db.query("test_namespace", Item.class)
.where("year", Condition.EQ, 2022)
.where("price", Condition.LE, 1000)
.not().where("price", Condition.SET, 100, 200, 300);
SELECT * FROM test_namespace WHERE year = 2022 AND price <= 1000 AND NOT price IN (100, 200, 300)
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
},
{
"op": "NOT",
"field": "price",
"cond": "LE",
"value": [100, 200, 300]
}
]
}'
Приоритет операций может быть изменён при помощи использования скобок. Механизм задания скобок зависит от конкретного 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()
query = (
db.new_query("test_namespace")
.open_bracket()
.where("year", CondType.CondEq, 2022)
.where("price", CondType.CondLe, 1000)
.close_bracket()
.op_or()
.open_bracket()
.where("year", CondType.CondEq, 2010)
.where("price", CondType.CondGt, 2000)
.close_bracket()
)
ResultIterator<Item> query = db.query("test_namespace", Item.class)
.openBracket()
.where("year", Condition.EQ, 2022)
.where("price", Condition.LE, 1000)
.closeBracket()
.or()
.openBracket()
.where("year", Condition.EQ, 2010)
.where("price", Condition.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%")
query = (
db.new_query("test_namespace")
.where("field_description", CondType.CondLike, "info%")
)
ResultIterator<Item> query = db.query("test_namespace", Item.class)
.where("field_description", Condition.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")
query = (
db.new_query("test_namespace")
.where_between_fields("price", CondType.CondGe, "recommended_price")
ResultIterator<Item> query = db.query("test_namespace", Item.class)
.whereBetweenFields("price", Condition.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": [
{
"cond": "le",
"left_expression": {
"type": "field",
"value": "recommended_price"
},
"right_expression": {
"type": "field",
"value": "price"
}
]
}'
Пример запроса для выборки из композитного индекса:
query := db.Query("test_namespace")
.WhereComposite("price+year", reindexer.EQ, []interface{}{10000,2022})
db.index_add(
"test_namespace",
{
"name": "price+year",
"field_type": "composite",
"json_paths": ["price", "year"]
}
)
query = (
db.new_query("test_namespace")
.where_composite("price+year", CondType.CondEq, (10000,2022))
ResultIterator<Item> query = db.query("test_namespace", Item.class)
.whereComposite("price+year", Condition.EQ, 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 | n | n |
{"obj": {"f": null}} |
- | n | n | n |
{"obj": {"f": null, "a": 0}} |
- | n | n | 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]}} |
- |