Как динамически обновлять атрибут в элементе dynamodb?

#amazon-dynamodb #crud

#amazon-dynamodb #crud

Вопрос:

Я создал элемент в dynamodb с помощью Node js, элемент имеет несколько атрибутов, таких как бренд, категория, скидка, срок действия и т.д. Я использую uuid для генерации идентификаторов для каждого элемента. Теперь предположим, что я хочу обновить атрибут validity элемента, и в этом случае я в настоящее время отправляю весь объект json со значением validity, измененным на новое значение.

Это определенно не оптимально, пожалуйста, помогите мне найти оптимальное решение.

 const params = {
    TableName: process.env.PRODUCT_TABLE,
    Key: {
      id: event.pathParameters.id,
    },
    ExpressionAttributeNames: {
      '#discount': 'discount',
    },
    ExpressionAttributeValues: {
      ':brand': data.brand,
      ':category': data.category,
      ':discount': data.discount,
      ':denominations': data.denominations,
      ":validity": data.validity,
      ":redemption": data.redemption    
    },
    UpdateExpression: 'SET #discount = :discount, denominations = :denominations, brand = :brand, category = :category, validity = :validity, redemption = :redemption',
    ReturnValues: 'ALL_NEW',
  };
  

Я хочу отправить только атрибут, который я хочу обновить, с новым значением, если я хочу изменить срок действия с 6 месяцев на 8 месяцев, я должен просто отправить что-то вроде:
{
«срок действия»: «8 месяцев»
}
И он должен обновить атрибут validity элемента.
То же самое должно применяться к любому другому атрибуту элемента.

 'use strict';

const AWS = require('aws-sdk');

const dynamoDb = new AWS.DynamoDB.DocumentClient();

module.exports.update = (event, context, callback) => {
  const data = JSON.parse(event.body);

  let attr = {};
  let nameobj = {};
  let exp = 'SET #';
  let arr = Object.keys(data);
  let attrname = {};

  arr.map((key) => {attr[`:${key}`]=data[key]});

  arr.map((key) => {
    exp  = `${key} = :${key}, `
  });

  arr.map((key) => {nameobj[`#${key}`]=data[key]});

  attrname = {
    [Object.keys(nameobj)[0]] : nameobj[Object.keys(nameobj)[0]]
  }

  const params = {
    TableName: process.env.PRODUCT_TABLE,
    Key: {
      id: event.pathParameters.id,
    },
    ExpressionAttributeNames: attrname,
    ExpressionAttributeValues: attr,
    UpdateExpression: exp,
    ReturnValues: 'ALL_NEW',
  };

  // update the todo in the database
  dynamoDb.update(params, (error, result) => {
    // handle potential errors
    if (error) {
      console.error(error);
      callback(null, {
        statusCode: error.statusCode || 501,
        headers: { 'Content-Type': 'text/plain' },
        body: 'Couldn't update the card',
      });
      return;
    }

    // create a response
    const response = {
      statusCode: 200,
      body: JSON.stringify(result.Attributes),
    };
    callback(null, response);
  });
};
  

Ответ №1:

Вопреки комментариям других, это вполне возможно, используйте действие updateItem.

Документы API, не зависящие от языка

Документы по API для конкретного JavaScript

Если вы хотите динамически создавать запрос, попробуйте что-то вроде этого:

 const generateUpdateQuery = (fields) => {
    let exp = {
        UpdateExpression: 'set',
        ExpressionAttributeNames: {},
        ExpressionAttributeValues: {}
    }
    Object.entries(fields).forEach(([key, item]) => {
        exp.UpdateExpression  = ` #${key} = :${key},`;
        exp.ExpressionAttributeNames[`#${key}`] = key;
        exp.ExpressionAttributeValues[`:${key}`] = item
    })
    exp.UpdateExpression = exp.UpdateExpression.slice(0, -1);
    return exp
}

let data = {
    'field' : { 'subfield': 123 },
    'other': '456'
}

let expression = generateUpdateQuery(data)

let params = {
    // Key, Table, etc..
    ...expression
}

console.log(params)
  

Вывод:

 { 
    UpdateExpression: 'set #field = :field, #other = :other',
    ExpressionAttributeNames: {
        '#field': 'field',
        '#other': 'other'
    },
    ExpressionAttributeValues: {
        ':field': { 
            'subfield': 123
        },
        ':other': '456'
    } 
}
  

Комментарии:

1. Спасибо за это, Ричард! Я удивлен отсутствием примеров кода для использования JS Dynamo SDK

2. Нет проблем. Я мог бы обновить этот ответ позже, поскольку я думаю, что у меня где-то есть немного лучшая версия. Вы правы, примеров не так много, но документы довольно приличные, если вы знаете, что ищете. Я уже некоторое время использую Dynamo и извлек большинство важных уроков методом проб и ошибок. Тем не менее, теперь это моя база данных, если только нет веских оснований для использования чего-то другого. Они возлагают большую ответственность на разработчика, что поначалу сложно, но это также означает, что вы можете в значительной степени освоить весь API в скором времени и сохранить всю свою логику на одном языке.

3. Отлично, я как раз собирался написать это сам, спасибо за сэкономленное время!

Ответ №2:

Использование Javascript SDK V3:

Импорт из правильного пакета:

 import { DynamoDBClient PutItemCommandInput, UpdateItemCommandInput, UpdateItemCommand } from '@aws-sdk/client-dynamodb';
  

Функция для динамического выполнения частичных обновлений элемента:
(приведенный ниже код typescript можно легко преобразовать в Javascript, просто удалите типы!)

 function updateItem(id: string, item: any) {
  const dbClient = new DynamoDBClient({region: 'your-region-here });

  let exp = 'set ';
  let attNames: any = { };
  let attVal: any = { };
  for(const attribute in item) {
    const valKey = `:${attribute}`;
    attNames[`#${attribute}`] = attribute;
    exp  = `#${attribute} = ${valKey}, `;     
    const val = item[attribute]; 
    attVal[valKey] = { [getDynamoType(val)]: val };
  }

  exp = exp.substring(0, exp.length - 2);

  const params: UpdateItemCommandInput = {
    TableName: 'your-table-name-here',
    Key: { id: { S: id } },
    UpdateExpression: exp,
    ExpressionAttributeValues: attVal,
    ExpressionAttributeNames: attNames,
    ReturnValues: 'ALL_NEW',
  };

  try {
    console.debug('writing to db: ', params);
    const command = new UpdateItemCommand(params);
    const res = await dbClient.send(command);
    console.debug('db res: ', res);
    return true;
  } catch (err) {
    console.error('error writing to dynamoDB: ', err);
    return false;
  }
}
  

И использовать его (мы также можем выполнять частичные обновления):

 updateItem('some-unique-id', { name: 'some-attributes' });
  

Ответ №3:

Что я сделал, так это создал helper class .

  • Вот простая функция: добавьте все атрибуты и значения, которые входят в, если значение равно нулю или не определено, его не будет в выражении.

  • Я рекомендую создать helper class с typescript и добавить дополнительные функции и другие вещи, такие как генератор expressionAttributeValues , expressionAttributeNames … , Надеюсь, это поможет.

 function updateExpression(attributes, values) {
  const expression = attributes.reduce((res, attribute, index) => {

    if (values[index]) {
      res  = ` #${attribute}=:${attribute},`;
    }

    return res;
  }, "SET ");

  return expression.slice(0, expression.length - 1)
}

console.log(
  updateExpression(["id", "age", "power"], ["e8a8da9a-fab0-55ba-bae3-6392e1ebf624", 28, undefined])
);  

Ответ №4:

Вы можете использовать код и сгенерировать объект params на основе предоставленного вами объекта. Это просто объект JavaScript, вы проходите по элементам, чтобы выражение обновления содержало только те поля, которые вы предоставили. На самом деле это не вопрос DynamoDB, поскольку это скорее общий вопрос JS-кодирования.

Комментарии:

1. Со стороны JS это может быть сделано очевидно, но я подумал, предоставляет ли dynamodb какое-то выражение обновления, чтобы сделать то же самое.

2. Хорошо, тогда короткий ответ — нет. 🙂 Но, честно говоря, это также имеет место в SQL, только укажите параметры, которые вы хотите использовать. В целом, хотя в DynamoDB гораздо меньше удобных функций, я годами работал с SQL Server и Oracle, и особенно в последнем есть много приятных дополнительных функций.

3. Я пытался это сделать, я опубликовал свой код выше. Можете ли вы проверить и сказать мне, что я здесь делаю не так.

4. Я думаю, проблема в отсутствии #. Вам нужно добавить # перед именами всех полей. В итоге Exp должен выглядеть примерно так #k1 = :v1, #k2 = :v2

5. Фрагмент кода вверху взят из рабочей версии, в которой я использовал только символ ‘#’ перед первой переменной.

Ответ №5:

Вы можете использовать updateItem; чтобы ознакомиться с запросами DynamoDB, я бы предложил вам DynamoDB NoSQL workbench:https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/workbench.settingup.html

Он может генерировать фрагменты для вас на основе ваших запросов.

Запрос скриншота DynamoDB NoSQL workbench

Комментарии:

1. Я думаю, что это тот же ответ, что и у Ричарда?

2. нет, потому что он не говорит вам использовать DynamoDB workbench, который может выполнить тяжелую работу за вас?