システムデザイン

S3 Select を利用したCSV検索

*本記事は旧TechblogからCOLORSに統合した記事です。

我々 m-filedでは 未経験者が様々な 次世代技術にも対応できる人間を育成する為に、
ST&Company IT Training Centerを2019年に立ち上げました。
その中での取り組みの一つとして、Cloudエンジニアの育成を積極的におこなっております。

ST&Company IT Training Centerでは、以下のようなカリキュラムで研修をおこなっています。
今回は、その研修の一つとしておこなっているサーバーレス開発の研修内容をご説明します。

1. 未経験エンジニア Cloud研修内容

Cloudエンジニア育成研修はAWS環境構築を主とした内容となっています。

Cloudエンジニア育成研修はAWS環境構築を主とした内容となり、
IT業界経験者とIT業界未経験者とにカリキュラムを分けて、
個人の学習スピードに合わせたカリキュラムを構築しています。
主なカリキュラムの内容は以下の通りになります。

<<IT業界 未経験者向け>>

<<IT業界 経験者向け>>

ST&Company IT Training Center 、または上記のカリキュラムに関心を持たれた方は、
以下のリンクからアクセスしてみて下さい。

1.2. サーバーレス アプリケーション

サーバーレスアプリケーションとしては、API-GateWay、Lambda関数、DynamoDBなどを利用したアプリ開発を行ってもらっています。
今回、サーバーレスアプリケーションの研修内容の一つをご紹介致します。

①クライアントからS3にホスティングされたHTMLファイルにアクセス

②S3配置されたHTMLファイルをクライアントに返却、クライアント側で検索値を入力

③HTMLファイルから、AjaxでAPI-GateWayを呼び出す

④API-GateWayから登録されたLambda関数を呼び出し、pythonで記載された処理を実施

⑤LambdaからS3に対してSQL文(今回はアメリカの州名をLike検索)を発行

⑥CSVの内容をJSONで画面上に返却、リストで画面上に表示

2. 実装

それでは、いよいよ実装に入っていきましょう。
実装の流れは以下の通りとなります。

2.1 S3のWebホスティング設定

2.2 Lambda関数の作成とAPI GateWayの作成

2.3 HTML + JavaScriptの実装

<<2.1 S3のWebホスティング設定>>

Amazon S3とは、 Amazon Simple Storage Service の略で、 オブジェクトストレージサーバーになります。
安価で耐久性が高く、容量無制限のファイル置き場になります。

Webホスティングとは、HTML、CSS、JavaScript、画像等の静的コンテンツをホストするWEBサイトとして、設定する事ができます。


それでは設定方法を紹介します。
S3の画面からバケットを作成するを押下して、任意のバケットを作成して下さい。

バケットを作成したら、プロパティタブからStatic website hostingを押下します。

Static website hostingを押下後、「このバケットを使用してウェブサイトをホストする」を押下して下さい。
表示されているエンドポイントが、アクセスする為のURLになります。

インデックスドキュメントには任意の値を入力して下さい。
上記の設定が完了したら保存ボタンを押下。
これでエンドポイントのURLでS3にアクセスする事が出来ます。

<<2.2 Lambda関数の作成とテスト>>

AWS Lambdaとは サーバーを起動する事なくコードが実行できるコンピューティングサービスです。
Lambdaは必要に応じてコードを実行して、実行した分だけ料金が発生します。

その為、EC2などの常設のサーバーとは異なり、常に稼働する必要がない為、 費用を極端に抑える事が出来ます。


それではいよいよLambda関数の作成となります。
AWS Lambda画面から「関数の作成」を押下して下さい。

基本的な情報に以下の値を入力して下さい。
・関数名 : 任意の値

・ランタイム : python 3系 今回は3.8を選択しました。

Lambda Designer画面に移りましたら、まずトリガーの設定を行います。

トリガーの追加押下後、API-GateWayの設定を行います。

<<2.2.2 API-GateWayの設定>>

Amazon API GateWayとは、API を作成、公開、保守、モニタリング、および保護するための AWS サービスです。
API 開発者は、AWS または他のウェブサービス、AWS クラウドに保存されているデータにアクセスする API を作成できます。
それでは設定方法を紹介します。
トリガーの追加画面で以下の設定を行います。

・トリガー : API GateWay
・API  : 新規APIの作成
・テンプレートを選択 : REST
・セキュリティ : オープン

上記の設定が完了したら、画面下の「追加」ボタンを押下して下さい。
API GateWayの作成が完了したら、Lambda関数のコード部分を作成します。
コードは以下のものになります。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
 
import boto3
import json
import urllib
import ast
 
s3 = boto3.client( 's3', 'ap-northeast-1' )
 
def lambda_handler(event, context):
    # API Gatewayから渡されたパラメータのBody部分を取得
    param = urllib.parse.parse_qs(event['body'])
    query = ''
    
    # ユーザー名とパスワードをパラメータから取得
    if len(param) == 0:
        query = "SELECT * FROM S3Object s"
    else :
        stateName = param['state-name'][0].upper()
        # SQL文のLike検索を作成
        query = "SELECT * FROM S3Object s WHERE s.name like '%" + stateName  + "%'"
    
    response_s3select = s3.select_object_content(
        Bucket                  = 's3-bucket',
        Key                     = 'S3select.csv',
        ExpressionType          = 'SQL',
        Expression              = query,
        InputSerialization      = {
        'CompressionType': 'NONE',
        'CSV'   : {
                'FileHeaderInfo'        : 'Use',
                'RecordDelimiter'       : '\n',
                'FieldDelimiter'        : ','
                }
        },
        OutputSerialization = {
        'JSON'    : {
        'RecordDelimiter' : ','
                }
        }
        )
 
    for event in response_s3select[ 'Payload' ]:
        if 'Records' in event:
                records = event[ 'Records' ][ 'Payload' ].decode( 'utf-8' )
    
    # 戻り値を返す
    body = {
        'result' : 1
    }
    result = {
        'statusCode' : 200,
        'headers' : {
            # クロスドメインの問題を回避する為に、'access-control-allow-origin'をヘッダーに設定
            'access-control-allow-origin' : '*',
            'content-type' : 'application/json'
        },
        # JSONに変換しクライアントへ返却
        # astを使って一度 dictionaryに変換してからJSON.dumpsを使用
        'body' : json.dumps(ast.literal_eval(records))
    }
        
    return result

テストを行い、データの取得が行われている事を確認しましょう。

新しいイベントの作成を選択し、 下記の値をテンプレートに設定します。

{
  "body": "state-name=ala"
}

これは、実際にHTMLに”ala”と入力された場合を想定しています。
上記の値を設定後、テストボタンを押下して下さい。

エラーが発生しない事を確認しましょう。

またExecution Resultを確認してみましょう。
下記のように、”ALA”が含まれる州の名前が検索結果に出力されていれば、 無事 Like検索が出来た事になります。

{
    "name": "ALABAMA",
    "abbr": "AL",
    "population": "4858979",
    "capital": "Montgomeryr"
},
{
    "name": "ALASKA",
    "abbr": "AK",
    "population": "738432",
    "capital": "Juneaur"
}

<<2.3 HTML + JavaScriptの実装>>

HTMLを作成し、S3にアップロードし、いよいよ完成です。

//1)対象のAPI-GateWayを設定の部分を対象のAPI-GateWayのURL
に変更し、S3にアップロードして下さい。
API GateWayのURLは、Lambda関数のAPI GateWay欄の apiエンドポイントに記載されています。

・HTML + JavaScript

<!DOCTYPE html>
  <html lang="ja">
    <head>
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
        <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <script>
      var myfunc = function () {
 
        $('#obj').empty();
        //1)対象のAPI-GateWayを設定の部分を対象のAPI-GateWayのURL
        var apiurl='https://xxx.xxx.amazonaws.com/default/S3select';
        var form = $('#myform');
        var formdata = form.serialize();
 
        $.ajax({
          type : 'POST',
          url:apiurl,
          data:formdata,
          dataType :'json',
          success: function(result, dataType){
            $('#obj').append('<table border="1" id="tobj" class="tobj">');
            $('#obj').css({'padding-top':'10px'});
            $('#obj').css({'padding-bottom':'30px'});
            $('#tobj').append('<thead>');
            $('#tobj').append('<tr >');
            $('#tobj').append('<th>No</th><th>Name</th><th>Population</th><th>Capital</th>');
            $('#tobj').append('</tr>');
            $('#tobj').append('</thead>');
            $('#tobj').append('<tbody>');
            $.each(result, function(index, val) {
              var indexnum = parseInt(index) + parseInt(1)
              $('#tobj').append('<tr><td> ' + indexnum  + '</td><td>' + result[index].name + '</td><td style="text-align:right;">' + separate(result[index].population) + '</td><td>' + result[index].capital + '</td></tr>');
            });
            $('#tobj').append('</tbody>');
            $('#obj').append('</table>');
          }
        });
      }
    </script>
    <script>
      function separate(num){
        return String(num).replace( /(\d)(?=(\d\d\d)+(?!\d))/g, '$1,');
      }
    </script>
    </head>
    <body>
      <div class="main">
        <p class="sign" align="center">United States List</p>
        <form class="form1" id="myform">
          <input class="un " type="text" required="required" align="center" placeholder="state name" name="state-name" >
          <div class="button_wrapper">
            <input class="submit" type="botton" onclick='myfunc()' value="Search">
          </div>
        </form>
        <div class="obj" id="obj" text-align="center" align="center">
        </div>
      </div>
    </body>
  </html>

※今回、CSSに関しては記載を割愛しております。

それでは、実際の動作確認をしてみましょう。
テストと同じように、”ala”と入力した後、「search」ボタンを押下します。

無事、HTMLにも出力されました。
なお、今回は何も検索されなかった場合の制御は行っておりません、あしからず。

まとめ

今回は以上となります。これで簡単にサーバーレスプログラミングが出来たのではないでしょうか?

m-fieldでは、 ST&Company IT Training Center のみならず、m-fieldメンバーとなって頂ける方を常時募集しております。
もし、この記事を見てm-fieldに関心を持たれた方は、以下のリンクからエントリーしてみて下さい。
募集お待ちしております。