AWS-DynamoDB : 데이터 조회하기



환경

  • AWS Lambda blueprint : Node.js 6.10 & microservice-http-endpoint


사용법

  1. aws-sdk 모듈 이용 :
    • aws-sdk 모듈에 있는 DynamoDB객체(?)의 DocumentClient() 를 변수에 담아서 이 변수로 DynamoDB를 써먹는다.
  2. dynamodb-doc 모듈 이용 :
    • dynamodb-doc 모듈에 있는 DynamoDB() 를 변수에 담아서 이 변수로 DynamoDB를 써먹는다.



aws-sdk 모듈을 이용한 데이터 조회▲top


참고 : http://docs.aws.amazon.com/ko_kr/amazondynamodb/latest/gettingstartedguide/GettingStarted.NodeJs.04.html


 query() 

IndexName

DynamoDB는 두개 이상의 컬럼을 선택하려면 Secondary-index를 추가해야 한다. 

처음에 DynamoDB 테이블을 만들때 지정된 Primary Key 한 컬럼에 대해서는 이 IndexName 옵션이 필요없이 query가 가능하나,

추가로 더 뽑아내고 싶은 컬럼이 있다면 이 옵션을 써야 한다.

그러니까 RDBMS처럼 Select A, B from Table 같은 기능을 쓰고싶다면 IndexName 파라미터가 들어가줘야 한다는 거다.

테이블에 이미 많은 데이터가 있는 상태에서 Secondary-index를 추가하면 테이블 데이터를 모두 스캔하면서 인덱싱을 다시 하므로 시간이 한참 걸릴 수도 있으니 테이블 만들때부터 설계를 잘 해야한다 ㅜ


KeyConditionExpression & ExpressionAttributeValues :

user_id는 예제로 쓰는 테이블의 Primary Key이기 때문에 넣어봤다.

아래 예제의 :user_id 가 ExpressionAttributeValues에 의해 대입될 변수라고 생각하면 되겠다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
'use strict';
 
var AWS = require("aws-sdk");
var dynamo = new AWS.DynamoDB.DocumentClient();
 
exports.handler = (event, context, callback) => {
    var params = {
        TableName: "[DynamoDB 테이블명]",
        IndexName: "[DynamoDB테이블의 Secondary-index의 'Name']",    // 생략 가능
        KeyConditionExpression: "user_id = :user_id",
        ExpressionAttributeValues: {
            ":user_id": user_id
        }
    };
 
    dynamo.query(params, (err, result) => {
        if (err) callback(null, err);
        callback(null, result);
    });
}
cs



 scan() 

테이블 전체의 모든 항목을 읽고 테이블의 모든 데이터를 반환한다. 마치 SELECT * FROM TABLE 처럼..

더 알아보니 파라미터에서 ProjectionExpression과 FilterExpression 옵션으로 필터링을 할 수 있다.

그러나 필터는 테이블 전체를 스캔한 후에만 적용된다는 점에서 자칫하면 비용낭비가 될 수도 있다.

아래 코딩에 대한 결과는 사실상 RDBMS 쿼리로 따지자면 'SELECT * FROM geoseong' 이나 마찬가지 결과가 Items[] 에 출력되지만,

주석을 단 대로 'SELECT val, test, id FROM geoseong WHERE val between 1 and 100' 구문을 설정한 것과 마찬가지이다.

ProjectionExpression 

TableName의 테이블 데이터 중 이곳에 입력된 컬럼만 리턴되게 설정하는 곳이다.

= 출력 열 제어

FilterExpression 

TableName의 테이블 데이터 중 이곳에 입력된 조건에 걸린 값만 리턴되게 설정하는 곳이다.

= 출력 행 제어


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
var AWS = require("aws-sdk");
var docClient = new AWS.DynamoDB.DocumentClient();
 
exports.handler = (event, context, callback) => {
    var params = {
        TableName: "geoseong",    // 2. from geoseong
        ProjectionExpression: "#val, test, id",    // 1. select val, test, id
        FilterExpression: "#val between :start and :end"// 3. where val between :start and :end
        ExpressionAttributeNames: {
            "#val""val",
        },
        ExpressionAttributeValues: {
             ":start"1,
             ":end"100 
        }
    };
     
    docClient.scan(params, onScan);
     
    function onScan(err, data) {
        if (err)    callback(null, err);
        callback(null, data);
    }
 
};
 
/** 결과 **/
{
  "Items": [
    {
      "id""hey",
      "val"100,
      "test""test"
    },
    {
      "id""geoseong",
      "val"10,
      "test""geo"
    }
  ],
  "Count"2,
  "ScannedCount"2
}
cs




dynamodb-doc 모듈을 이용한 데이터 조회▲top


 getItem() 

Lambda의 blueprint 들 중 microservice-http-endpoint을 선택하면 나오는 템플릿에서는 이 모듈을 쓴다. 이 모듈을 권장하는것인가..

aws-sdk모듈에서 사용하는 param대로 한번 해봤는데, 안된다. 이유는 'Key' 속성이 없으면 안된단다..

KeyConditionExpression이라던가 ExpressionAttributeValues 속성은 존재하지 않는다.

그냥 테이블이름과 Key만 파라미터에 넣을 수 있는 것 같다.

마치 SELECT * FROM TABLE WHERE USER_ID = 'geoseong' 같다.

근데 getItem()은 너무 기본적인기능밖에 없는 것 같다.. 이런 기능을 왜 Lambda의 기본 템플릿으로 해놨지? ㅋ (내가 무식한게 죄다.. getItem말고도 여러가지 조회 방법이 있는 것을 알았다.)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
'use strict';
 
const doc = require('dynamodb-doc');
const dynamo = new doc.DynamoDB();
 
exports.handler = (event, context, callback) => {
    
    const params = {
        TableName: Table.USER_INFO,
        Key: {
            user_id: 'geoseong'
        }
    };
 
    dynamo.getItem(params, (err, result) => {
        if(err) callback(null, err);
        callback(null, result);
    });
 
};
cs




 getBatchItem() 

다수의 조건으로 검색 할 수 있는 메소드이다.

여러번의 삽질 끝에 구현에 성공한 코드를 공유한다.

Node.js의 dynamodb라이브러리가 한가지가 있는 것이 아니고, 그에 따라 지원되고 되지않고 하는 것들이 머릿속을 너무 복잡하게 했다 ㅠㅠ

특히 내가 npmjs의 공식문서를 보고 따라도 해 보았는데 잘 되지가 않았다 ㅜ (https://www.npmjs.com/package/dynamodb-doc)

그러던 중에 'dynamodb-doc'의 소스를 보니 'aws-sdk' 를 활용하고 있다는 것을 알게 된 이후부터 getBatchItem()을 파면서부터 열심히 파고 나온 결과물이다 ㅎㅎ


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
'use strict';
 
const doc = require('dynamodb-doc');
const dynamo = new doc.DynamoDB();
 
exports.handler = (event, context, callback) => {
    console.log('event!', JSON.stringify(eventnull2));
    
    const Table = {
        'MemberInfo''MemberInfo',
        'CategoryInfo''CategoryInfo'
    }
 
    var tableMember = Table.MemberInfo;
    var tableCategory = Table.CategoryInfo;
 
    var paramsR = {
        "RequestItems": {},
        "ReturnConsumedCapacity""NONE"
    };
    paramsR.RequestItems[Table.MemberInfo] = {
        "ConsistentRead"true,
        "AttributesToGet": ["categories"],
        "Keys": [
            {
                "id"event.id,
                "pw"event.pw
            }
        ],
    }
    paramsR.RequestItems[Table.CategoryInfo] = {
        "AttributesToGet": ["id""categoryName""pages"],
        "Keys": [ { "id"'category-geoseong-1' },
            { "id"'category-geoseong-2' },
            { "id"'category-geoseong-22' }]
    }
    console.log('[[paramsR]]', JSON.stringify(paramsR, null2));
    dynamo.batchGetItem(paramsR, function(err1, data1) {
        if (err1){
            console.log('error shit...', err1); // an error occurred
            return callback(err1, null);
        }else{
            console.log('batchGetItem data', JSON.stringify(data1, null2));
            callback(null, data1)
        }
    });
};
cs


* dynamoDB의 테이블 구조

MemberInfo

1
2
3
4
5
6
7
8
9
{
  "categories": [
    "category-geoseong-22",
    "category-geoseong-1",
    "category-geoseong-2"
  ],
  "id""geoseong",
  "pw""1234"
}
cs

CategoryInfo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
  "categoryName""ehlsid",
  "id""category-geoseong-22",
  "pages": []
},
{
  "categoryName""ayy",
  "id""category-geoseong-1",
  "pages": []
},
{
  "categoryName""왜그랬어",
  "id""category-geoseong-2",
  "pages": []
}
cs


* CloudWatch에서 확인한 콘솔로그와 결과

파라미터

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
[[paramsR]]
{
    "RequestItems": {
        "MemberInfo": {
            "ConsistentRead"true,
            "AttributesToGet": [
                "categories"
            ],
            "Keys": [
                {
                    "id""geoseong",
                    "pw""1234"
                }
            ]
        },
        "CategoryInfo": {
            "AttributesToGet": [
                "id",
                "categoryName",
                "pages"
            ],
            "Keys": [
                {
                    "id""category-geoseong-1"
                },
                {
                    "id""category-geoseong-2"
                },
                {
                    "id""category-geoseong-22"
                }
            ]
        }
    },
    "ReturnConsumedCapacity""NONE"
}
cs


결과

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
batchGetItem data
{
    "Responses": {
        "MemberInfo": [
            {
                "categories": [
                    "category-geoseong-22",
                    "category-geoseong-1",
                    "category-geoseong-2"
                ]
            }
        ],
        "CategoryInfo": [
            {
                "id""category-geoseong-22",
                "categoryName""ehlsid",
                "pages": []
            },
            {
                "id""category-geoseong-1",
                "categoryName""ayy",
                "pages": []
            },
            {
                "id""category-geoseong-2",
                "categoryName""왜그랬어",
                "pages": []
            }
        ]
    },
    "UnprocessedKeys": {}
}
cs





Secondary-index ▲top


테이블 만들때의 Second Index 추가화면


테이블 만들어진 상황에서 Secondary-Index 추가화면




+ Recent posts