Запросы для обновления и удаления данных

Запросы для обновления (UPDATE) и удаления (DELETE) применяются в Reindexer для выполнения следующих операций:

  • изменение содержимого полей записи или группы записей,
  • добавление новых полей,
  • удаление неиндексных полей,
  • удаление записей.

UPDATE-запросы для изменения и добавления полей записей

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

db.Query("nsName").Where("field", reindexer.EQ, 40).Set("field1", value1).Set("field2", value2) ... Set("fieldN", valueN).Update()

Кроме того, в одном запросе можно объединить несколько выражений Set... разных типов, например: Set(...).SetExpression(...).SetObject(...)...

UPDATE nsName
SET field1 = value1, field2 = value2, ..
WHERE condition;
curl --location --request PUT 'http://127.0.0.1:9088/api/v1/db/testdb/query' \
--header 'Content-Type: application/json' \
--data-raw '{
  "namespace": "items",
  "filters": [
    {
      "Field": "id",
      "Cond": "EQ",
      "Value": [
        2
      ]
    }
  ],
  "update_fields": [
    {
      "name": "info",
      "type": "value",
      "is_array": false,
      "values": [
        "new info"
      ]
    }
  ]
}'

Изменение типа значения поля при обновлении записи

В Reindexer механизмы обновления полей имеют следующие особенности:

  • При изменении неиндексного поля замена текущего значения на значение другого типа полностью заменяет содержимое поля, устанавливается значение другого типа.

    Пример:

    Записываем в поле значение типа int:

    UPDATE nsName SET newField = 3 WHERE id = 5
    

    Обновляем поле, записываем в него значение типа string:

    UPDATE nsName SET newField = 'String' WHERE id = 5
    

    Запрос выполнится успешно.

  • Значения индексных полей можно преобразовать только в смежный типы. Например, int в числовую строку вида ‘123456’ и обратно, либо bool  в строку вида ’true’ и обратно.

    Пример:

    Обновляем индексное поле с типом значения int, записываем в него числовую строку:

    UPDATE nsName SET newField = '123' WHERE id = 5
    

    Запрос выполнится успешно.

    Обновляем индексное поле с типом значения int, записываем в него строку:

    UPDATE nsName SET newField = 'Text' WHERE id = 5
    

    Получим ошибку “Can`t convert Text to number”.

  • Изменение значения индексного поля-массива на null возвращает его к значению по умолчанию.

Добавление новых полей к записям в неймспейсах

С помощью UPDATE-запросов возможно добавление новых полей к существующим записям в неймспейсе. Можно добавить поле для одной или нескольких записей.

Пример:

db.Query("items").Where("id", reindexer.GE, 100).Set("newField", "Brand new!").Update()
UPDATE items SET newField = 'Brand new!' WHERE id > 100
curl --location --request PUT 'http://127.0.0.1:9088/api/v1/db/testdb/query' \
--header 'Content-Type: application/json' \
--data-raw '{
  "namespace": "items",
  "filters": [
    {
      "Field": "id",
      "Cond": "GE",
      "Value": [
        100
      ]
    }
  ],
  "update_fields": [
    {
      "name": "newField",
      "type": "value",
      "is_array": false,
      "values": [
        "Brand new!"
      ]
    }
  ]
}'

Добавление вложенных полей с несколькими уровнями вложенности

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

Пример:

db.Query("items").Where("id", reindexer.GE, 100).Set("nested.nested2.nested3.nested4.newField", "new nested field!").Update()

Код из этого примера добавляет в неймспейс items поле nested с вложенными объектами nested2, nested3, nested4. В nested4 добавляется поле newField с содержимым new nested field!.

UPDATE items SET nested.nested2.nested3.nested4.newField = 'new nested field!' WHERE id > 100

Код из этого примера добавляет в неймспейс items поле nested с вложенными объектами nested2, nested3, nested4. В nested4 добавляется поле newField с содержимым new nested field!.

Для добавления вложенных полей при обновлении записей по HTTP значение поля “update_fields” -> “type” в теле запроса должно быть object.

curl --location --request PUT 'http://127.0.0.1:9088/api/v1/db/testdb/query' \
--header 'Content-Type: application/json' \
--data-raw '{
  "namespace": "items",
  "filters": [
    {
      "Field": "id",
      "Cond": "GE",
      "Value": [
        100
      ]
    }
  ],
  "update_fields": [
    {
      "name": "nested",
      "type": "object",
      "is_array": false,
      "values": [
        {
          "nested2": {
            "nested3": {
              "nested4": {
                "newField": "new nested field!"
              }
            }
          }
        }
      ]
    }
  ]
}'

Код из этого примера добавляет в неймспейс full поле nested с вложенными объектами nested2, nested3, nested4. В nested4 добавляется поле newField с содержимым new nested field!.

Арифметические выражения в UPDATE-запросах

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

  • + (сложение),
  • - (вычитание),
  • / (деление),
  • * (умножение),
  • ().

Пример запроса на обновление данных в Reindexer с использованием арифметических выражений:

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

db.Query("items").Where("field5", reindexer.EQ, 1).SetExpression("field", "field1+field2").Update()
UPDATE items SET field1 = field2+field3-(field4+5)/2 WHERE field5 = 1

Для добавления арифметических выражений при обновлении поля по HTTP значение поля “update_fields” -> “type” в теле запроса должно быть expression.

curl --location --request PUT 'http://127.0.0.1:9088/api/v1/db/testdb/query' \
--header 'Content-Type: application/json' \
--data-raw '{
  "namespace": "items",
  "filters": [
    {
      "Field": "id",
      "Cond": "EQ",
      "Value": [
        1
      ]
    }
  ],
  "update_fields": [
    {
      "name": "field",
      "type": "expression",
      "is_array": false,
      "values": [
        "field1 + field2"
      ]
    }
  ]
}'

Также в UPDATE-запросах можно использовать такие функции, как now(), sec(), serial().

Пример:

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

db.Query("items").Where("id", reindexer.EQ, 3).SetExpression("field", "field2*now()").Update()
UPDATE items SET field1 = field2*NOW() WHERE id = 3

Для добавления арифметических выражений при обновлении поля по HTTP значение поля “update_fields” -> “type” в теле запроса должно быть expression.

curl --location --request PUT 'http://127.0.0.1:9088/api/v1/db/testdb/query' \
--header 'Content-Type: application/json' \
--data-raw '{
  "namespace": "items",
  "filters": [
    {
      "Field": "id",
      "Cond": "EQ",
      "Value": [
        1
      ]
    }
  ],
  "update_fields": [
    {
      "name": "field",
      "type": "expression",
      "is_array": false,
      "values": [
        "field1 * now()"
      ]
    }
  ]
}'

Обновление полей-массивов

В Reindexer реализован механизм обновления, который позволяет изменять определенный элемент существующего массива или заменять поле-массив целиком.

Примеры:

Изменение элемента массива:

db.Query("items").Where("id", reindexer.EQ, 11).Set("arrayField[0]", 3).Update()
UPDATE items SET arrayField[0] = 3 WHERE id = 1

Для изменения элемента массива по HTTP значение поля “update_fields” -> “type” в теле запроса должно быть value.

curl --location --request PUT 'http://127.0.0.1:9088/api/v1/db/testdb/query' \
--header 'Content-Type: application/json' \
--data-raw '{
  "namespace": "items",
  "filters": [
    {
      "Field": "id",
      "Cond": "EQ",
      "Value": [
        11
      ]
    }
  ],
  "update_fields": [
    {
      "name": "arrayField[0]",
      "type": "value",
      "is_array": false,
      "values": [
        3
      ]
    }
  ]
}'

Обновление всего массива:

db.Query("items").Where("id", reindexer.EQ, 11).Set("arrayField", []int{999, 1999, 2999}).Update()
UPDATE items SET arrayField = [999, 1999, 2999] WHERE id = 1
curl --location --request PUT 'http://127.0.0.1:9088/api/v1/db/testdb/query' \
--header 'Content-Type: application/json' \
--data-raw '{
  "namespace": "items",
  "filters": [
    {
      "Field": "id",
      "Cond": "EQ",
      "Value": [
        11
      ]
    }
  ],
  "update_fields": [
    {
      "name": "arrayField",
      "type": "value",
      "is_array": true,
      "values": [
        999,
        1999,
        2999
      ]
    }
  ]
}'

Добавление элементов в начало массива:

Для добавления элементов в начало массива через Go используйте метод SetExpression().

db.Query("items").Where("id", reindexer.EQ, 11).SetExpression("arrayField", "[999, 1999, 2999]||arrayField").Update()
UPDATE items SET arrayField = [999, 1999, 2999] || arrayField WHERE id = 1

Для добавления элементов в начало массива по HTTP значение поля “update_fields” -> “type” в теле запроса должно быть expression.

curl --location --request PUT 'http://127.0.0.1:9088/api/v1/db/testdb/query' \
--header 'Content-Type: application/json' \
--data-raw '{
  "namespace": "items",
  "filters": [
    {
      "Field": "id",
      "Cond": "EQ",
      "Value": [
        11
      ]
    }
  ],
  "update_fields": [
    {
      "name": "arrayField",
      "type": "expression",
      "is_array": false,
      "values": [
        "[999, 1999, 2999]||arrayField"
      ]
    }
  ]
}'

Добавление элементов в конец массива:

Для добавления элементов в конец массива через Go используйте метод SetExpression().

db.Query("items").Where("id", reindexer.EQ, 11).SetExpression("arrayField", "arrayField || [999, 1999, 2999]").Update()
UPDATE items SET arrayField = arrayField || [999, 1999, 2999] WHERE id = 1

Для добавления элементов в конец массива по HTTP значение поля “update_fields” -> “type” в теле запроса должно быть expression.

curl --location --request PUT 'http://127.0.0.1:9088/api/v1/db/testdb/query' \
--header 'Content-Type: application/json' \
--data-raw '{
  "namespace": "items",
  "filters": [
    {
      "Field": "id",
      "Cond": "EQ",
      "Value": [
        11
      ]
    }
  ],
  "update_fields": [
    {
      "name": "arrayField",
      "type": "expression",
      "is_array": false,
      "values": [
        "arrayField||[999, 1999, 2999]"
      ]
    }
  ]
}'

Обновление массивов объектов

Reindexer также позволяет обновлять элементы массивов объектов.

Примеры:

Обновление элемента массива объектов:

Для обновления элемента массива объектов через Go используйте метод SetObject().

db.Query("items").Where("id", reindexer.EQ, 1).SetObject("objectsArray[0]", updatedValue).Update()
UPDATE items SET objectsArray[0] = {"Id":0,"Description":"Updated!"} WHERE id = 1
curl --location --request PUT 'http://127.0.0.1:9088/api/v1/db/testdb/query' \
--header 'Content-Type: application/json' \
--data-raw '{
  "namespace": "items",
  "filters": [
    {
      "Field": "id",
      "Cond": "EQ",
      "Value": [
        1
      ]
    }
  ],
  "update_fields": [
    {
      "name": "objectsArray[0]",
      "type": "object",
      "is_array": false,
      "values": [
        {
          "Id": 0,
          "Description": "Updated"
        }
      ]
    }
  ]
}'

Обновление индексного Array-поля, находящегося внутри массива объектов:

Пример ниже рассмотрен для неймспейса с именем ns, в котором есть индекс с параметрами: name="nested",json_paths=["nested.field1"] и is_array=true.

ВАЖНО. Для фильтрации (выборки массива объектов) при выполнении таких запросов используйте имя индекса (в примере — nested). А для изменения значений вложенных полей — их jsonpath (в примере — nested[1].field1).

Исходная запись:

{
    "id":1, 
    "nested":[{"field1": 1, "field2": 2},
              {"field1": 3, "field2": 4},
              {"field1": 5, "field2": 6}]
}

Запись после обновления индексного Array-поля, находящегося внутри массива объектов:

{
    "id":1, 
    "nested":[{"field1": 1, "field2": 2},
              {"field1": 123, "field2": 4},
              {"field1": 5, "field2": 6}]
}

Для обновления индексного Array-поля, находящееся внутри массива объектов, через Go используйте метод Set().

db.Query("test").Where("nested", reindexer.EQ, 1).Set("nested[1].field1", 123).Update()
UPDATE ns SET nested[1].field1 = 123 WHERE nested = 1
curl --location --request PUT 'http://127.0.0.1:9088/api/v1/db/testdb/query' \
--header 'Content-Type: application/json' \
--data-raw '{
  "namespace": "ns",
  "filters": [
    {
      "Field": "nested",
      "Cond": "EQ",
      "Value": [
        1
      ]
    }
  ],
  "update_fields": [
    {
      "name": "nested[1].field1",
      "values": [
        123
      ]
    }
  ]
}'

Обновление индексного поля, находящегося внутри массива объектов, с заменой типа поля (массив->скаляр и наоборот):

Reindexer позволяет обновлять индексные поля, находящееся внутри массива объектов, и одновременно с этим менять их тип.

Такое обновление доступно только для смены типа поля со скалярного на массив и наоборот.

Пример ниже рассмотрен для неймспейса с именем ns, в котором есть индекс с параметрами: name="nested_field",json_paths=["nested.field1"] и is_array=true.

Исходная запись:

{
    "id":1, 
    "nested":[{"field1": [1,2]},{"field1": 3 }] // Тип поля "field1" — массив
}

Запись после обновления и изменения типа индексного поля, находящегося внутри массива объектов, с массива на скалярный:

{
    "id":1, 
    "nested":[{"field1": 123},{"field1": 3 }] // Тип поля "field1" — изменен на скалярный
}

Для обновления индексного Array-поля, находящееся внутри массива объектов, через Go используйте метод Set().

db.Query("test").Where("nested_field", reindexer.EQ, 1).Set("nested[0].field1", 123).Update()
UPDATE ns SET nested[0].field1 = 123 WHERE nested_field = 1
curl --location --request PUT 'http://127.0.0.1:9088/api/v1/db/testdb/query' \
--header 'Content-Type: application/json' \
--data-raw '{
  "namespace": "ns",
  "filters": [
    {
      "Field": "nested_field",
      "Cond": "EQ",
      "Value": [
        1
      ]
    }
  ],
  "update_fields": [
    {
      "name": "nested[0].field1",
      "values": [
        123
      ]
    }
  ]
}'

Изменение и добавление полей объектов

Reindexer позволяет обновлять и добавлять поля объектов. Объект может быть задан как struct, map или в виде байтового массива (то есть JSON-версии представления объекта).

Пример:

type ClientData struct {
  Name          string `reindex:"name" json:"name"`
  Age           int    `reindex:"age" json:"age"`
  Address       int    `reindex:"year" json:"year"`
  Occupation    string `reindex:"occupation" json:"occupation"`
  TaxYear       int    `reindex:tax_year json:"tax_year"`
  TaxConsultant string `reindex:tax_consultant json:"tax_consultant"`
}
type Client struct {
  ID      int         `reindex:"id" json:"id"`
  Data    ClientData  `reindex:"client_data" json:"client_data"`
  ...
}
clientData := updateClientData(clientId)
db.Query("clients").Where("id", reindexer.EQ, 100).SetObject("client_data", clientData).Update()

При передаче объекта в SetObject в виде map требуется использовать map с ключами типа string (например, map[string]interface{}).

UPDATE-запросы для удаления полей записей

В Reindexer используется следующий синтаксис UPDATE-запросов для удаления полей:

UPDATE nsName
DROP field1, field2, ..
WHERE condition;

С помощью таких запросов можно удалить только неиндексные поля или индексные sparse-поля.

Примеры:

db.Query("items").Where("id", reindexer.EQ, 1).Drop("fieldToDrop1").Drop("fieldToDrop2").Update()
UPDATE items DROP fieldToDrop1, fieldToDrop2 WHERE id = 1
curl --location --request PUT 'http://127.0.0.1:9088/api/v1/db/testdb/query' \
--header 'Content-Type: application/json' \
--data-raw '{
  "namespace": "items",
  "filters": [
    {
      "Field": "id",
      "Cond": "EQ",
      "Value": [
        1
      ]
    }
  ],
  "drop_fields": [
    "fieldToDrop1",
    "fieldToDrop2"
  ]
}'

Удаление элемента массива по индексу

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

db.Query("items").Where("id", reindexer.EQ, 1).Drop("arrayField[2]").Update()
UPDATE items DROP arrayField[2] WHERE id = 11
curl --location --request PUT 'http://127.0.0.1:9088/api/v1/db/testdb/query' \
--header 'Content-Type: application/json' \
--data-raw '{
    "namespace": "items",
    "filters": [
        {
            "Field": "id",
            "Cond": "EQ",
            "Value": [
                1
            ]
        }
    ],
    "drop_fields" : [ 
        "arrayField[2]"
    ]
}'

Удаление элементов массива по значению

Для удаления элементов массива по значению используются функции:

  • array_remove(). Удаляет из массива все найденные вхождения элементов, переданных в качестве аргументов.
  • array_remove_once(). Удаляет из массива только первые найденные вхождения элементов, переданных в качестве аргументов. Удаление происходит по числу вхождений: если какое-то значение задано несколько раз, то и из целевого массива будет удалено столько же его копий.

Функции могут принимать в качестве аргумента одно или несколько значений. В обоих случаях значения указываются в квадратных скобках.

Пример удаления всех найденных вхождений элементов, переданных в качестве аргументов, с помощью array_remove:

В Go array_remove используется через метод SetExpression().

db.Query("items").Where("id", reindexer.EQ, 1).SetExpression("arrayField", "array_remove(arrayField, [5,6,7,8])").Update()
UPDATE items SET arrayField = array_remove(arrayField, [5,6,7,8]) WHERE id = 1
curl --location --request PUT 'http://127.0.0.1:9098/api/v1/db/mydb/query' \
--header 'Content-Type: application/json' \
--data '{
  "namespace": "items",
  "filters": [
    {
      "Field": "id",
      "Cond": "EQ",
      "Value": [
        1
      ]
    }
  ],
  "update_fields": [
    {
      "name": "arrayField",
      "type": "expression",
      "is_array": false,
      "values": [
        "array_remove(arrayField, [5,6,7,8])"
      ]
    }
  ]
}'

Пример удаления из массива только первых найденных вхождений элементов, переданных в качестве аргументов, помощью array_remove_once:

В Go array_remove_once используется через метод SetExpression().

db.Query("items").Where("id", reindexer.EQ, 1).SetExpression("arrayField", "array_remove_once(arrayField, [5,5,5,6])").Update()
UPDATE items SET arrayField = array_remove_once(arrayField, [5,5,5,6]) WHERE id = 1
curl --location --request PUT 'http://127.0.0.1:9098/api/v1/db/mydb/query' \
--header 'Content-Type: application/json' \
--data '{
  "namespace": "items",
  "filters": [
    {
      "Field": "id",
      "Cond": "EQ",
      "Value": [
        1
      ]
    }
  ],
  "update_fields": [
    {
      "name": "arrayField",
      "type": "expression",
      "is_array": false,
      "values": [
        "array_remove_once(arrayField, [5,5,5,6])"
      ]
    }
  ]
}'

Использование array_remove и array_remove_once можно комбинировать с добавлением элементов в начало или конец массива:

db.Query("items").Where("id", reindexer.EQ, 1).SetExpression("arrayField", "array_remove(arrayField, [5,6,7,8]) || [1,2,3]").Update()
UPDATE items SET arrayField = array_remove(arrayField, [5,6,7,8]) || [1,2,3] WHERE id = 1
curl --location --request PUT 'http://127.0.0.1:9098/api/v1/db/mydb/query' \
--header 'Content-Type: application/json' \
--data '{
  "namespace": "items",
  "filters": [
    {
      "Field": "id",
      "Cond": "EQ",
      "Value": [
        1
      ]
    }
  ],
  "update_fields": [
    {
      "name": "arrayField",
      "type": "expression",
      "is_array": false,
      "values": [
        "array_remove(arrayField, [5,6,7,8]) || [1,2,3]"
      ]
    }
  ]
}'

В качестве второго параметра в array_remove и array_remove_once может быть использован не только набор значений, но и имя поля, содержащего массив:

db.Query("items").Where("id", reindexer.EQ, 1).SetExpression("arrayField", "array_remove(arrayField, arrayField2)").Update()
UPDATE items SET arrayField = array_remove(arrayField, arrayField2) WHERE id = 1
curl --location --request PUT 'http://127.0.0.1:9098/api/v1/db/mydb/query' \
--header 'Content-Type: application/json' \
--data '{
  "namespace": "items",
  "filters": [
    {
      "Field": "id",
      "Cond": "EQ",
      "Value": [
        1
      ]
    }
  ],
  "update_fields": [
    {
      "name": "arrayField",
      "type": "expression",
      "is_array": false,
      "values": [
        "array_remove(arrayField, arrayField2)"
      ]
    }
  ]
}'

Указание набора значений или имени поля, содержащего массив, в качестве набора значений для удаления из другого можно комбинировать с добавлением элементов в начало или конец массива:

db.Query("items").Where("id", reindexer.EQ, 1).SetExpression("arrayField", "[3] || array_remove(arrayField, arrayField2) || arrayField3 || array_remove(arrayField, [8,1]) || [2,4]").Update()
UPDATE items SET arrayField = [3] || array_remove(arrayField, arrayField2) || arrayField3 || array_remove(arrayField, [8,1]) || [2,4] WHERE id = 1
curl --location --request PUT 'http://127.0.0.1:9098/api/v1/db/mydb/query' \
--header 'Content-Type: application/json' \
--data '{
  "namespace": "items",
  "filters": [
    {
      "Field": "id",
      "Cond": "EQ",
      "Value": [
        1
      ]
    }
  ],
  "update_fields": [
    {
      "name": "arrayField",
      "type": "expression",
      "is_array": false,
      "values": [
        "[3] || array_remove(arrayField, arrayField2) || arrayField3 || array_remove(arrayField, [8,1]) || [2,4]"
      ]
    }
  ]
}'

array_remove и array_remove_once можно использовать для инициализации массива отфильтрованными значениями из другого поля:

db.Query("items").Where("id", reindexer.EQ, 1).SetExpression("arrayField", "array_remove(arrayField2, arrayField3)").Update()
db.Query("items").Where("id", reindexer.EQ, 1).SetExpression("arrayField", "array_remove([1,2,3], arrayField2)").Update()
UPDATE items SET arrayField = array_remove(arrayField2, arrayField3) WHERE id = 1
UPDATE items SET arrayField = array_remove([1,2,3], arrayField2) WHERE id = 1
curl --location --request PUT 'http://127.0.0.1:9098/api/v1/db/mydb/query' \
--header 'Content-Type: application/json' \
--data '{
  "namespace": "items",
  "filters": [
    {
      "Field": "id",
      "Cond": "EQ",
      "Value": [
        1
      ]
    }
  ],
  "update_fields": [
    {
      "name": "arrayField",
      "type": "expression",
      "is_array": false,
      "values": [
        "array_remove(arrayField2, arrayField3)"
      ]
    }
  ]
}'
curl --location --request PUT 'http://127.0.0.1:9098/api/v1/db/mydb/query' \
--header 'Content-Type: application/json' \
--data '{
  "namespace": "items",
  "filters": [
    {
      "Field": "id",
      "Cond": "EQ",
      "Value": [
        1
      ]
    }
  ],
  "update_fields": [
    {
      "name": "arrayField",
      "type": "expression",
      "is_array": false,
      "values": [
        "array_remove([1,2,3], arrayField2)"
      ]
    }
  ]
}'

DELETE-запросы

DELETE-запросы в Reindexer используются для удаления одной или нескольких записей из неймспейса. Пример:

db.Query("items").Where("id", reindexer.EQ, 11).Delete()
DELETE FROM items WHERE id = 11
curl --location --request DELETE 'http://127.0.0.1:9088/api/v1/db/testdb/query' \
--header 'Content-Type: application/json' \
--data-raw '{
  "namespace": "items",
  "filters": [
    {
      "Field": "id",
      "Cond": "EQ",
      "Value": [
        1
      ]
    }
  ]
}'