DynamoDB JavaScript SDK v2 v3: scansionare i dati delle tabelle e paginazione – 5
Fare query è alla base della gestione di un database, DynamoDB offre vari costrutti per poter interrogare le nostre tabelle..
L’operazione Query sull’Amazon DynamoDB permette di estrarre dati in base ai vari elementi ma soprattutto partizionati in base alla chiave primaria.
È necessario fornire il nome dell’attributo della chiave di partizione e un singolo valore per quell’attributo. Query
restituisce tutti gli elementi con quel valore di chiave di partizione. Facoltativamente, puoi fornire un attributo chiave di ordinamento e utilizzare un operatore di confronto per perfezionare i risultati della ricerca.
Ma prima andiamo a ricordare la struttura della tabella che avevamo creato:
ItemId
:- Chiave HASH in formato String;
- Discriminante mettendo insieme tutti gli URL di quel sito;
- ES: mischianti, github ecc.
ItemName
:- Chiave RANGE in formato String;
- Nome completo con tutti i sottodomini dell’URL;
- ES: home.mischianti.org, mischianti.org, github.com, docs.github.com ecc.
ValueNum
:- Valore numerico;
- Livello di sottodominio.
ValueStr
:- Valore stringa;
- Descrizione.
Normalmente il nome della colonna non ha un nome significativo, ma per questo test è importante solo il tipo.
Creare una tabella e inserire alcuni dati di test.
Per prima cosa dobbiamo creare un ambiente per fare qualche query, quindi creeremo la tabella e aggiungeremo alcuni dati con un semplice script.
Puoi eseguire questa operazione direttamente dalla console all’indirizzo (cambia regione con il tuo):
https://eu-west-1.console.aws.amazon.com/dynamodb/home?region=eu-west-1#tables:
Quindi fai clic su crea tabella
è necessario aggiungere il nome della tabella TestTableMischianti
, una chiave di partizione ItemId
e una chiave di ordinamento ItemName
.
SDK v2
Aggiungo di nuovo lo script di creazione tabella dell’articolo precedente.
Per eseguire questo script vai nella cartella dynamodb-examples\jsv2
e avvia node table_create.js
.
/*
* DynamoDB Script Examples
* Create table in
* DB of selected region in AWS.config.update
*
* AUTHOR: Renzo Mischianti https://mischianti.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2020 Renzo Mischianti www.mischianti.org All right reserved.
*
* You may copy, alter and reuse this code in any way you like, but please leave
* reference to www.mischianti.org in your comments if you redistribute this code.
*
*/
var AWS = require("aws-sdk");
AWS.config.update({
apiVersion: '2012-08-10',
region: "eu-west-1",
// endpoint: "http://localhost:8000",
// // accessKeyId default can be used while using the downloadable version of DynamoDB.
// // For security reasons, do not store AWS Credentials in your files. Use Amazon Cognito instead.
// accessKeyId: "9oiaf7",
// // secretAccessKey default can be used while using the downloadable version of DynamoDB.
// // For security reasons, do not store AWS Credentials in your files. Use Amazon Cognito instead.
// secretAccessKey: "yz5i9"
});
var ddb = new AWS.DynamoDB();
console.log("Start script!");
var params = {
TableName: 'TestTableMischianti',
KeySchema: [ // The type of of schema. Must start with a HASH type, with an optional second RANGE.
{ // Required HASH type attribute
AttributeName: 'ItemId',
KeyType: 'HASH',
},
{
AttributeName: 'ItemName',
KeyType: 'RANGE'
}
],
AttributeDefinitions: [ // The names and types of all primary and index key attributes only
{ // Type attribute
AttributeName: 'ItemId',
AttributeType: 'S',
},
{
AttributeName: 'ItemName',
AttributeType: 'S'
}
// ... more attributes ...
],
ProvisionedThroughput: { // required provisioned throughput for the table
ReadCapacityUnits: 1,
WriteCapacityUnits: 1,
},
};
ddb.createTable(params, function(err, data) {
if (err) {
console.log('Full error response', JSON.stringify(err,null,2)); // an error occurred
console.log('message --> ', err.message);
} else {
console.log('Full success response', JSON.stringify(data,null,2)); // successful response
}
});
console.log("End script!");
Ora un semplice script che acquisisce i dati da un file in formato json e riempie la tabella.
node preload_table.js
/*
* DynamoDB Script Examples
* Preload example table
* DB of selected region in AWS.config.update
*
* AUTHOR: Renzo Mischianti
*
* https://mischianti.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2017 Renzo Mischianti www.mischianti.org All right reserved.
*
* You may copy, alter and reuse this code in any way you like, but please leave
* reference to www.mischianti.org in your comments if you redistribute this code.
*/
var AWS = require("aws-sdk");
var fs = require('fs');
AWS.config.update({
apiVersion: '2012-08-10',
region: "eu-west-1",
// endpoint: "http://localhost:8000",
// // accessKeyId default can be used while using the downloadable version of DynamoDB.
// // For security reasons, do not store AWS Credentials in your files. Use Amazon Cognito instead.
// accessKeyId: "9oiaf7",
// // secretAccessKey default can be used while using the downloadable version of DynamoDB.
// // For security reasons, do not store AWS Credentials in your files. Use Amazon Cognito instead.
// secretAccessKey: "yz5i9"
});
var ddb = new AWS.DynamoDB();
console.log("Start script!");
var allURLs = JSON.parse(fs.readFileSync('data/urls.json', 'utf8'));
allURLs.forEach(function(item) {
var params = {
TableName: "TestTableMischianti",
Item: {
"ItemId": {S: item.ItemId},
"ItemName": {S: item.ItemName},
"ValueStr": {S: item.ValueStr},
"ValueNum": {N: item.ValueNum.toString()},
}
};
ddb.putItem(params, function(err, data) {
if (err) {
console.error("Unable add item", params.ItemName, ". Error JSON:", JSON.stringify(err, null, 2));
} else {
console.log("PutItem succeeded: ", item.ItemName);
}
});
});
il file json ha questo contenuto.
[
{
"ItemId": "mischianti",
"ItemName": "www.mischianti.org",
"ValueStr": "Main url mischianti",
"ValueNum": 1
},
{
"ItemId": "mischianti",
"ItemName": "home.mischianti.org",
"ValueStr": "Test url mischianti",
"ValueNum": 2
},
{
"ItemId": "mischianti",
"ItemName": "mqtt.mischianti.org",
"ValueStr": "MQTT url mischianti",
"ValueNum": 2
},
{
"ItemId": "mischianti",
"ItemName": "game.home.mischianti.org",
"ValueStr": "Game test url mischianti",
"ValueNum": 3
},
{
"ItemId": "mischianti",
"ItemName": "mqtt.home.mischianti.org",
"ValueStr": "MQTT test url mischianti",
"ValueNum": 3
},
{
"ItemId": "git",
"ItemName": "www.github.com",
"ValueStr": "Main url github",
"ValueNum": 1
},
{
"ItemId": "git",
"ItemName": "docs.github.com",
"ValueStr": "Docs url github",
"ValueNum": 2
}
]
SDK v3
Per SDK v3 lanceremo il relativo script.
Per eseguire questo script vai nella cartella dynamodb-examples\jsv3
e avvia node table_create_async_await.js
.
/*
* DynamoDB Script Examples v3
* Create table with DynamoDB async await
* DB of selected region in configDynamoDB
*
* AUTHOR: Renzo Mischianti
*
* https://mischianti.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2017 Renzo Mischianti www.mischianti.org All right reserved.
*
* You may copy, alter and reuse this code in any way you like, but please leave
* reference to www.mischianti.org in your comments if you redistribute this code.
*
*/
const { DynamoDBClient, CreateTableCommand } = require("@aws-sdk/client-dynamodb");
const configDynamoDB = {
version: 'latest',
region: "eu-west-1",
// endpoint: "http://localhost:8000",
// credentials: {
// // accessKeyId default can be used while using the downloadable version of DynamoDB.
// // For security reasons, do not store AWS Credentials in your files. Use Amazon Cognito instead.
// accessKeyId: "9oiaf7",
// // secretAccessKey default can be used while using the downloadable version of DynamoDB.
// // For security reasons, do not store AWS Credentials in your files. Use Amazon Cognito instead.
// secretAccessKey: "yz5i9"
//
// }
};
const dbClient = new DynamoDBClient(configDynamoDB);
(async function () {
console.log("Start script!");
var params = {
TableName: 'TestTableMischianti',
KeySchema: [ // The type of of schema. Must start with a HASH type, with an optional second RANGE.
{ // Required HASH type attribute
AttributeName: 'ItemId',
KeyType: 'HASH',
},
{
AttributeName: 'ItemName',
KeyType: 'RANGE'
}
],
AttributeDefinitions: [ // The names and types of all primary and index key attributes only
{ // Type attribute
AttributeName: 'ItemId',
AttributeType: 'S',
},
{
AttributeName: 'ItemName',
AttributeType: 'S'
}
// ... more attributes ...
],
ProvisionedThroughput: { // required provisioned throughput for the table
ReadCapacityUnits: 1,
WriteCapacityUnits: 1,
},
};
try {
const command = new CreateTableCommand(params);
const data = await dbClient.send(command);
console.log('Full success response', JSON.stringify(data,null,2)); // successful response
}catch (err) {
console.log('Full error response', JSON.stringify(err,null,2)); // an error occurred
}
console.log("End script!");
})();
e poi lo script di precaricamento per riempire la tabella.
node preload_table.js
/*
* DynamoDB Script Examples v3
* Preload example table
* DB of selected region in configDynamoDB
*
* AUTHOR: Renzo Mischianti
*
* https://mischianti.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2017 Renzo Mischianti www.mischianti.org All right reserved.
*
* You may copy, alter and reuse this code in any way you like, but please leave
* reference to www.mischianti.org in your comments if you redistribute this code.
*/
const { DynamoDBClient, PutItemCommand } = require("@aws-sdk/client-dynamodb");
var fs = require('fs');
const configDynamoDB = {
version: 'latest',
region: "eu-west-1",
// endpoint: "http://localhost:8000",
// credentials: {
// // accessKeyId default can be used while using the downloadable version of DynamoDB.
// // For security reasons, do not store AWS Credentials in your files. Use Amazon Cognito instead.
// accessKeyId: "9oiaf7",
// // secretAccessKey default can be used while using the downloadable version of DynamoDB.
// // For security reasons, do not store AWS Credentials in your files. Use Amazon Cognito instead.
// secretAccessKey: "yz5i9"
//
// }
};
const dbClient = new DynamoDBClient(configDynamoDB);
console.log("Start script!");
var allURLs = JSON.parse(fs.readFileSync('data/urls.json', 'utf8'));
allURLs.forEach(function(item) {
var params = {
TableName: "TestTableMischianti",
Item: {
"ItemId": {S: item.ItemId},
"ItemName": {S: item.ItemName},
"ValueStr": {S: item.ValueStr},
"ValueNum": {N: item.ValueNum.toString()},
}
};
const command = new PutItemCommand(params);
dbClient.send(command).then(
data => console.log("PutItem succeeded: ", item.ItemName)
).catch(
err => console.error("Unable add item", params.ItemName, ". Error JSON:", JSON.stringify(err, null, 2))
);
});
console.log("End script!");
Ora devi avere questa situazione.
Ora possiamo iniziare a fare qualche query di test.
Scan di una tabella
L’operazione Scan
in Amazon DynamoDB legge ogni elemento in una tabella o in un indice secondario. Per impostazione predefinita, restituisce tutti gli attributi dei dati per ogni elemento nella tabella o nell’indice. È possibile utilizzare il parametro ProjectionExpression
in modo che lo Scan
restituisca solo alcuni degli attributi, anziché tutti.
restituisce sempre un set di risultati. Se non vengono trovati elementi corrispondenti, il set di risultati è vuoto.Scan
Una singola richiesta Scan
può recuperare un massimo di 1 MB di dati. Facoltativamente, DynamoDB può applicare un’espressione di filtro a questi dati, restringendo i risultati prima che vengano restituiti all’utente.
Ora faremo una semplice scansione con un solo filtro per ItemId (formalmente la categoria) uguale a “mischianti
“.
var params = {
TableName: "TestTableMischianti",
ExpressionAttributeValues: {
':endpoint_category' : {S: 'mischianti'}
},
ProjectionExpression: 'ItemName, ValueStr',
FilterExpression: 'ItemId = :endpoint_category'
}
Spiegheremo meglio FilterExpression nel prossimo articolo sulla query.
SDK v2
Ora un semplice script di scansione con una condizione di base.
Per eseguire questo script vai nella cartella dynamodb-examples\jsv2
e avvia node items_scan.js
.
/*
* DynamoDB Script Examples
* Scan items with DynamoDB
* DB of selected region in AWS.config.update
*
* AUTHOR: Renzo Mischianti
*
* https://mischianti.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2017 Renzo Mischianti www.mischianti.org All right reserved.
*
* You may copy, alter and reuse this code in any way you like, but please leave
* reference to www.mischianti.org in your comments if you redistribute this code.
*/
var AWS = require("aws-sdk");
AWS.config.update({
apiVersion: '2012-08-10',
region: "eu-west-1",
// endpoint: "http://localhost:8000",
// // accessKeyId default can be used while using the downloadable version of DynamoDB.
// // For security reasons, do not store AWS Credentials in your files. Use Amazon Cognito instead.
// accessKeyId: "9oiaf7",
// // secretAccessKey default can be used while using the downloadable version of DynamoDB.
// // For security reasons, do not store AWS Credentials in your files. Use Amazon Cognito instead.
// secretAccessKey: "yz5i9"
});
var ddb = new AWS.DynamoDB();
console.log("Start script!");
var params = {
TableName: "TestTableMischianti",
ExpressionAttributeValues: {
':endpoint_category' : {S: 'mischianti'}
},
ProjectionExpression: 'ItemName, ValueStr',
FilterExpression: 'ItemId = :endpoint_category'
};
ddb.scan(params, function (err, data) {
if (err) {
console.error("Unable ti get item", JSON.stringify(params, null, 2), ". Error JSON:", JSON.stringify(err, null, 2));
} else {
console.log("Get Item succeeded:", JSON.stringify(data, null, 2));
}
});
console.log("End script!");
il risultato in console è piuttosto interessante
D:\Projects\AlexaProjects\dynamodb-management\dynamodb-examples\jsv2>node items_scan.js
Start script!
End script!
Get Item succeeded: {
"Items": [
{
"ItemName": {
"S": "game.home.mischianti.org"
},
"ValueStr": {
"S": "Game test url mischianti"
}
},
{
"ItemName": {
"S": "home.mischianti.org"
},
"ValueStr": {
"S": "Test url mischianti"
}
},
{
"ItemName": {
"S": "mqtt.home.mischianti.org"
},
"ValueStr": {
"S": "MQTT test url mischianti"
}
},
{
"ItemName": {
"S": "mqtt.mischianti.org"
},
"ValueStr": {
"S": "MQTT url mischianti"
}
},
{
"ItemName": {
"S": "www.mischianti.org"
},
"ValueStr": {
"S": "Main url mischianti"
}
}
],
"Count": 5,
"ScannedCount": 7
}
In particolare puoi controllare Count e ScannedCount, il primo è il numero di risultati, il secondo è la linea scansionata per recuperare i dati, memorizzate questi dati, quando andremo ad analizzare la query scopriamo un’importante funzionalità che incide sulle performance.
La scansione legge tutte la righe e ci dà il risultato.
SDK v3
Ora lo stesso script con v3 SDK.
Per eseguire questo script vai nella cartella dynamodb-examples\jsv3
e avvia node items_scan.js
.
/*
* DynamoDB Script Examples v3
* Scan items with DynamoDB async await
* DB of selected region in configDynamoDB
*
* AUTHOR: Renzo Mischianti
*
* https://mischianti.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2017 Renzo Mischianti www.mischianti.org All right reserved.
*
* You may copy, alter and reuse this code in any way you like, but please leave
* reference to www.mischianti.org in your comments if you redistribute this code.
*
*/
const { DynamoDBClient, ScanCommand } = require("@aws-sdk/client-dynamodb");
const configDynamoDB = {
version: 'latest',
region: "eu-west-1",
// endpoint: "http://localhost:8000",
// credentials: {
// // accessKeyId default can be used while using the downloadable version of DynamoDB.
// // For security reasons, do not store AWS Credentials in your files. Use Amazon Cognito instead.
// accessKeyId: "9oiaf7",
// // secretAccessKey default can be used while using the downloadable version of DynamoDB.
// // For security reasons, do not store AWS Credentials in your files. Use Amazon Cognito instead.
// secretAccessKey: "yz5i9"
//
// }
};
const dbClient = new DynamoDBClient(configDynamoDB);
(async function () {
console.log("Start script!");
// var params = {
// TableName: 'TestTableMischianti',
// Select: 'ALL_ATTRIBUTES', // optional (ALL_ATTRIBUTES | ALL_PROJECTED_ATTRIBUTES | SPECIFIC_ATTRIBUTES | COUNT)
// };
var params = {
TableName: "TestTableMischianti",
ExpressionAttributeValues: {
':endpoint_category' : {S: 'mischianti'}
},
ProjectionExpression: 'ItemName, ValueStr',
FilterExpression: 'ItemId = :endpoint_category',
};
try {
const command = new ScanCommand(params);
const data = await dbClient.send(command);
console.log("PutItem succeeded:", JSON.stringify(params, null, 2), JSON.stringify( data, null, 2),);
}catch (err) {
console.error("Unable add item", JSON.stringify(params, null, 2), ". Error JSON:", JSON.stringify(err, null, 2));
}
console.log("End script!");
})();
e il risultato della console
D:\Projects\AlexaProjects\dynamodb-management\dynamodb-examples\jsv3>node items_scan_async_await.js
Start script!
PutItem succeeded: {
"TableName": "TestTableMischianti",
"ExpressionAttributeValues": {
":endpoint_category": {
"S": "mischianti"
}
},
"ProjectionExpression": "ItemName, ValueStr",
"FilterExpression": "ItemId = :endpoint_category"
} {
"$metadata": {
"httpStatusCode": 200,
"httpHeaders": {
"server": "Server",
"date": "Wed, 03 Feb 2021 21:10:05 GMT",
"content-type": "application/x-amz-json-1.0",
"content-length": "457",
"connection": "keep-alive",
"x-amzn-requestid": "HBHG7J1HPVV22RS6VIK9MPB4K7VV4KQNSO5AEMVJF66Q9ASUAAJG",
"x-amz-crc32": "962662631"
},
"requestId": "HBHG7J1HPVV22RS6VIK9MPB4K7VV4KQNSO5AEMVJF66Q9ASUAAJG",
"attempts": 1,
"totalRetryDelay": 0
},
"Count": 5,
"Items": [
{
"ItemName": {
"S": "game.home.mischianti.org"
},
"ValueStr": {
"S": "Game test url mischianti"
}
},
{
"ItemName": {
"S": "home.mischianti.org"
},
"ValueStr": {
"S": "Test url mischianti"
}
},
{
"ItemName": {
"S": "mqtt.home.mischianti.org"
},
"ValueStr": {
"S": "MQTT test url mischianti"
}
},
{
"ItemName": {
"S": "mqtt.mischianti.org"
},
"ValueStr": {
"S": "MQTT url mischianti"
}
},
{
"ItemName": {
"S": "www.mischianti.org"
},
"ValueStr": {
"S": "Main url mischianti"
}
}
],
"ScannedCount": 7
}
End script!
Scan: paginazione
Ora aggiungeremo un dato aggiuntivo al parametro dello script, Limit
come puoi intuire, questo parametro limita il numero di risultati.
Quindi con questo parametro
var params = {
TableName: "TestTableMischianti",
ExpressionAttributeValues: {
':endpoint_category' : {S: 'mischianti'}
},
ProjectionExpression: 'ItemName, ValueStr',
FilterExpression: 'ItemId = :endpoint_category',
Limit: 2,
};
Ottieni questo output della console.
{
"$metadata": {
"httpStatusCode": 200,
"httpHeaders": {
"server": "Server",
"date": "Wed, 03 Feb 2021 21:16:20 GMT",
"content-type": "application/x-amz-json-1.0",
"content-length": "296",
"connection": "keep-alive",
"x-amzn-requestid": "CPKH4513E21CAL47678MMUDNA3VV4KQNSO5AEMVJF66Q9ASUAAJG",
"x-amz-crc32": "1953278109"
},
"requestId": "CPKH4513E21CAL47678MMUDNA3VV4KQNSO5AEMVJF66Q9ASUAAJG",
"attempts": 1,
"totalRetryDelay": 0
},
"Items": [
{
"ItemName": {
"S": "game.home.mischianti.org"
},
"ValueStr": {
"S": "Game test url mischianti"
}
},
{
"ItemName": {
"S": "home.mischianti.org"
},
"ValueStr": {
"S": "Test url mischianti"
}
}
],
"LastEvaluatedKey": {
"ItemName": {
"S": "home.mischianti.org"
},
"ItemId": {
"S": "mischianti"
}
},
"ScannedCount": 2,
"Count": 2
}
Come puoi leggere, 2 righe vengono scansionate e 2 risultati vengono forniti, ma c’è un’altra informazione importante LastEvaluatedKey
.
"LastEvaluatedKey": {
"ItemName": {
"S": "home.mischianti.org"
},
"ItemId": {
"S": "mischianti"
}
}
puoi usare questo parametro come punto di partenza della scansione, quindi se imposti un dato aggiuntivo ExclusiveStartKey
ai parametri con questo valore
var params = {
TableName: "TestTableMischianti",
ExpressionAttributeValues: {
':endpoint_category' : {S: 'mischianti'}
},
ProjectionExpression: 'ItemName, ValueStr',
FilterExpression: 'ItemId = :endpoint_category',
Limit: 2,
ExclusiveStartKey: {
"ItemName": {
"S": "www.mischianti.org"
},
"ItemId": {
"S": "mischianti"
}
}
};
si ottiene questo risultato
{
"$metadata": {
"httpStatusCode": 200,
"httpHeaders": {
"server": "Server",
"date": "Wed, 03 Feb 2021 21:21:07 GMT",
"content-type": "application/x-amz-json-1.0",
"content-length": "296",
"connection": "keep-alive",
"x-amzn-requestid": "CJH4EFQ4VHKQ2UL11S4BHHBR0BVV4KQNSO5AEMVJF66Q9ASUAAJG",
"x-amz-crc32": "3887146499"
},
"requestId": "CJH4EFQ4VHKQ2UL11S4BHHBR0BVV4KQNSO5AEMVJF66Q9ASUAAJG",
"attempts": 1,
"totalRetryDelay": 0
},
"Items": [
{
"ItemName": {
"S": "mqtt.home.mischianti.org"
},
"ValueStr": {
"S": "MQTT test url mischianti"
}
},
{
"ItemName": {
"S": "mqtt.mischianti.org"
},
"ValueStr": {
"S": "MQTT url mischianti"
}
}
],
"LastEvaluatedKey": {
"ItemName": {
"S": "mqtt.mischianti.org"
},
"ItemId": {
"S": "mischianti"
}
},
"Count": 2,
"ScannedCount": 2
}
quindi la scansione legge 2 righe e restituisce i 2 elementi successivi, e continua a tornare LastEvaluatedKey
finché i dati non arrivano alla fine.
Grazie
- DynamoDB JavaScript SDK v2 v3: prerequisiti ed introduzione
- DynamoDB JavaScript SDK v2 v3: gestione delle tabelle
- DynamoDB JavaScript SDK v2 v3: aggiungere elementi con DB o DocumentClient
- DynamoDB JavaScript SDK v2 v3: gestione degli elementi
- DynamoDB JavaScript SDK v2 v3: scansionare i dati delle tabelle e paginazione
- DynamoDB JavaScript SDK v2 v3: query