LogiCOMPOSERでFUSIONを試す！（準備編）

# LogiCOMPOSERの最新版がやって来た！
今回、insightsoftware社の協力のもと、最新版のCOMPOSERを試用できるチャンスを得ましたので、BI目線的に昨今の「ノーコード・ローコード」を意識して、Equalumで即時同期処理で手元のSingleStore上にDX/利活用目的で自由に利用できる、いろいろな可能性を持ったテーブルを、「COMPOSERの可視化環境内でJOIN」して、それらのテーブルに含まれる情報相互の組み合わせで創出される、新たなデータ駆動型DX的洞察・知見をサポートできる活用を検証してみたいと思います。

# FUSIONという機能について
COMPOSERには、源流であるZoomdata時代よりFUSIONというインメモリ系のテーブル統合の機能が存在しています。初期の頃はSPARKを組み合わせての処理を行い、途中からSPARKの限界を超える目的で、独自の実装によるインメモリ系内部処理zエンジンを開発して現場に投入した実績があります。

今回は最新版という事なので、独自実装の最新型zエンジンを使う事になりますが、初期を知る人間として・・・特にGUI方面がどのように進化しているか（前述のノーコード・ローコード目線）を中心に試用確認してみたいと思います。

# まずはFUSIONするテーブル群を生成するツールを作る
今回の試用環境が、リソースの関係で大きなテーブルを振り回す事が出来ませんので、とりあえず手頃なサイズの複数テーブルをFUSIONして、ノーコードで柔軟に即効性の高いJOIN系作業＆可視化が可能なのか？に注力して作業を進める事にします。

今回のコードは、毎度恒例のNDA（ノン・ダメ出し・アグリーメント）ベースという事で、今まで実験・検証で使ってきた過去作品をベースに、サクッ！と作ってしまいます・・・・（汗）

~~~Python
##################################################
#
# LogiCOMPOSERのFUSION動作検証
#
##################################################
import pymysql.cursors
import datetime
import random
from faker import Faker
#############################################
#
# グローバル変数の定義
#
#############################################
#
Demo_Host = "ZZZ.ZZZ.ZZZ.ZZZ" # MySQLのIPアドレス
Demo_Port = 3306              # MySQLの接続ポート
Demo_User = "ZZZZZ"           # MySQLの接続アカウント
Demo_Pass = "ZZZZZ"           # MySQLの接続パスワード
Demo_DB   = "ZZZZZ"           # 使用するデータベース名
Demo_Char = "utf8mb4"         # 使用する文字コード
#
Table_Name1 = "Fusion_Table1" # メインのテーブル（基礎情報をコードで記録）
Table_Name2 = "Fusion_Table2" # 商材区分コードのテーブル
Table_Name3 = "Fusion_Table3" # 商品コードのテーブル
Table_Name4 = "Fusion_Table4" # 県コードと地域名コードのテーブル
#
# DB処理で使う情報
MY_Init   = "DROP TABLE IF EXISTS "
MY_Create = "CREATE TABLE IF NOT EXISTS "
#
MY_DC0 = "id INT AUTO_INCREMENT, ts DATETIME(6) DEFAULT CURRENT_TIMESTAMP(6), "
MY_DC1 = "Category_ID VARCHAR(5), Product_ID VARCHAR(5), Units INT, Prefecture_ID VARCHAR(5), "
MY_DC2 = "PRIMARY KEY(id, ts)"
#
# ルックアップ型のテーブル情報
Category_List  = ["酒類","家電","書籍","DVD/CD","雑貨"]
Product_Name0  = ["日本酒","バーボン","ビール","芋焼酎","赤ワイン","白ワイン","スコッチ","ブランデー","泡盛","テキーラ"]
Product_Price0 = [1980, 2500, 490, 2000, 3000, 2500, 3500, 5000, 1980, 2000]
Product_Name1  = ["テレビ","洗濯機","ラジオ","ステレオ","電子レンジ","パソコン","電池","エアコン","乾燥機","掃除機"]
Product_Price1 = [49800, 39800, 2980, 88000, 29800, 64800, 198, 64800, 35800, 24800]
Product_Name2  = ["週刊誌","歴史","写真集","漫画","参考書","フィクション","経済","自己啓発","月刊誌","新刊"]
Product_Price2 = [280, 1500, 2500, 570, 1480, 1400, 1800, 1540, 980, 1980]  
Product_Name3  = ["洋楽","演歌","Jポップ","洋画","アイドル","クラッシック","邦画","連続ドラマ","企画","アニメ"]
Product_Price3 = [1980, 2200, 2500, 3500, 2980, 1980, 3800, 2690, 1980, 2400]
Product_Name4  = ["洗剤","電球","贈答品","医薬部外品","ペットフード","乾電池","文房具","男性用品","女性用品","季節用品"]
Product_Price4 = [498, 198, 1980, 398, 980, 248, 398, 2980, 3580, 1980]
# 都道府県名情報
Pref_Name      = ["北海道","青森県","岩手県","宮城県","秋田県","山形県","福島県",
                  "茨城県","栃木県","群馬県","埼玉県","千葉県","東京都","神奈川県",
                  "新潟県","富山県","石川県","福井県","山梨県","長野県","岐阜県","静岡県","愛知県",
                  "三重県","滋賀県","京都府","大阪府","兵庫県","奈良県","和歌山県",
                  "鳥取県","島根県","岡山県","広島県","山口県",
                  "徳島県","香川県","愛媛県","高知県",
                  "福岡県","佐賀県","長崎県","熊本県","大分県","宮崎県","鹿児島県","沖縄県"]
#　地域名参照用辞書
Area_Dict = {'北海道':'北海道','青森県':'東北','岩手県':'東北','宮城県':'東北','秋田県':'東北','山形県':'東北','福島県':'東北',
             '茨城県':'関東','栃木県':'関東','群馬県':'関東','埼玉県':'関東','千葉県':'関東','東京都':'関東','神奈川県':'関東',
             '新潟県':'中部','富山県':'中部','石川県':'中部','福井県':'中部','山梨県':'中部','長野県':'中部','岐阜県':'中部','静岡県':'中部','愛知県':'中部',
             '三重県':'近畿','滋賀県':'近畿','京都府':'近畿','大阪府':'近畿','兵庫県':'近畿','奈良県':'近畿','和歌山県':'近畿',
             '鳥取県':'中国','島根県':'中国','岡山県':'中国','広島県':'中国','山口県':'中国',
             '徳島県':'四国','香川県':'四国','愛媛県':'四国','高知県':'四国',
             '福岡県':'九州・沖縄','佐賀県':'九州・沖縄','長崎県':'九州・沖縄','熊本県':'九州・沖縄','大分県':'九州・沖縄','宮崎県':'九州・沖縄','鹿児島県':'九州・沖縄','沖縄県':'九州・沖縄'}
#
MY_LU0 = "ts DATETIME(6) DEFAULT CURRENT_TIMESTAMP(6), "
#
MY_LU1 = "Category_ID VARCHAR(5), Category_Name VARCHAR(20), "
MY_LU2 = "PRIMARY KEY (Category_ID, ts)"
#
MY_LU3 = "Product_ID VARCHAR(5), Product_Name VARCHAR(20), Product_Price INT, "
MY_LU4 = "PRIMARY KEY(Product_ID, ts)"
#
MY_LU5 = "Prefecture_ID VARCHAR(5), Prefecture_Name VARCHAR(10), Aria_Name VARCHAR(10), "
MY_LU6 = "PRIMARY KEY(Prefecture_ID, ts)"
#
#　Fakerの初期化
#
fakegen = Faker('ja_JP') 
Faker.seed(fakegen.random_digit())
#
# データベース初期化のメイン処理
#
def Table_Init(db, Table_Name, SQL_Data):

    with db.cursor() as cursor:
        cursor.execute( MY_Init + Table_Name )
        db.commit()
        cursor.execute( MY_Create + Table_Name + " (" + SQL_Data + ")" )
        db.commit()
#
# 商材系統名のルックアップ作成
#
def LU1_Make(db):

    ID_Name = "CAT"
    ID = 1

    with db.cursor() as cursor:
        for Category in Category_List:
            C_ID = ID_Name + str(ID).zfill(2) # ゼロ埋めで５けたのID文字列を作成
            cursor.execute("INSERT INTO " + Table_Name2 + "(Category_ID, Category_Name) VALUES ('" + C_ID + "','" + Category + "')")
            db.commit()
            ID = ID + 1

    return(ID)
#
# 商品名のルックアップ作成
#           
def LU2_Make(db, Product_List, Price_List, ID):

    ID_Name = "PRO"
    Count   = 0

    with db.cursor() as cursor:
        for Product in Product_List:
            P_ID    = ID_Name + str(ID).zfill(2) # ゼロ埋めで５けたのID文字列を作成
            Price   = str(Price_List[Count])
            cursor.execute("INSERT INTO " + Table_Name3 + "(Product_ID, Product_Name, Product_Price) VALUES ('" + P_ID + "','" + Product + "','" + Price + "')")
            db.commit()
            Count = Count + 1
            ID = ID + 1
    
    return(ID)
#
# 都道府県・地域名のルックアップ作成
#
def LU3_Make(db):

    ID_Name = "PRF"
    ID = 1

    with db.cursor() as cursor:
        for Pref in Pref_Name:
            A_ID   = ID_Name + str(ID).zfill(2) # ゼロ埋めで５けたのID文字列を作成
            A_Name = Area_Dict.get(Pref) # 県名から地域名を選択
            cursor.execute("INSERT INTO " + Table_Name4 + "(Prefecture_ID, Prefecture_Name, Aria_Name) VALUES ('" + A_ID + "','" + Pref + "','" + A_Name + "')")
            db.commit()
            ID = ID + 1

    return(ID)
#
# なんちゃって販売実績テーブルを作成
#
def Data_Gen():
    
    # 各IDの頭文字を設定
    CAT_ID = "CAT"
    PRO_ID = "PRO"
    PRF_ID = "PRF"
    
    # ランダムに書き込む商材の種類(０ー４）
    Category_ID = fakegen.random_digit()
    if Category_ID > 4: Category_ID = Category_ID - 5 # 仕様範囲に収める
    C_ID = CAT_ID + str(Category_ID + 1).zfill(2) # ゼロ埋めで５けたのID文字列を作成
    
    # ランダムに書き込む商品ID（０ー９）
    Product_ID = Category_ID * 10 + fakegen.random_digit()
    P_ID = PRO_ID + str(Product_ID + 1).zfill(2) # ゼロ埋めで５けたのID文字列を作成

    # カラム情報の設定
    if Category_ID   == 0: # 酒類
        Units = fakegen.random_digit() + 1
    elif Category_ID == 1: # 家電
        Units = 1
    elif Category_ID == 2: # 書籍
        Units = fakegen.random_digit() + 1
        if Units > 3: Units = 3
    elif Category_ID == 3: # DVD/CD
        Units = fakegen.random_digit() + 1
        if Units > 2: Units = 2
    else: # 雑貨
        Units = fakegen.random_digit() + 1
        if Units >4: Units = 4

    # 1-47の範囲でランダムな番号を生成し県名コードを作成する
    Pre_No = random.randint(1, 47)
    A_ID = PRF_ID + str(Pre_No).zfill(2) # ゼロ埋めで５けたのID文字列を作成

    # SQLデータを生成して文字列化して返す
    return(C_ID + "','" + P_ID + "','" + str(Units) + "','" + A_ID)
#
# メインの処理
#    
try:
   
    print("------------------------------------------------")
    print("********* 検証データの初期化・作成処理 *********")
    print(datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S") + " : " + "処理開始")
    print("------------------------------------------------")

    # データベースと接続
    db = pymysql.connect(host = Demo_Host, port = Demo_Port, user = Demo_User, password = Demo_Pass, db = Demo_DB, charset = Demo_Char, cursorclass = pymysql.cursors.DictCursor)
    print(datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S") + " : " + "検証に使用するデータベースと接続")

    # 検証に使うテーブルの初期化作成
    Table_Init(db, Table_Name1, MY_DC0 + MY_DC1 + MY_DC2) # メインのテーブル
    Table_Init(db, Table_Name2, MY_LU0 + MY_LU1 + MY_LU2) # 商材区分のテーブル
    Table_Init(db, Table_Name3, MY_LU0 + MY_LU3 + MY_LU4) # 商品のテーブル
    Table_Init(db, Table_Name4, MY_LU0 + MY_LU5 + MY_LU6) # 都道府県・地域名のテーブル
    print(datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S") + " : " + "検証に使用するテーブルを初期化")

    # ルックアップテーブル1作成
    ID_Cat = LU1_Make(db) # 商材区分
    print(datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S") + " : " + str(ID_Cat-1) + "個の商材区分表を作成")

    # ルックアップテーブル2作成
    ID = 1
    ID = LU2_Make(db, Product_Name0, Product_Price0, ID) # 酒類
    ID = LU2_Make(db, Product_Name1, Product_Price1, ID) # 家電
    ID = LU2_Make(db, Product_Name2, Product_Price2, ID) # 書籍
    ID = LU2_Make(db, Product_Name3, Product_Price3, ID) # DVD/CD
    ID = LU2_Make(db, Product_Name4, Product_Price4, ID) # 雑貨
    print(datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S") + " : " + str(ID-1) + "個の商材をマスターに登録")

    # ルックアップテーブル３作成
    ID_Pre = LU3_Make(db) # 都道府県・地域
    print(datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S") + " : " + str(ID_Pre-1) + "個の都道府県をマスターに登録")

    # ループカウンターの初期化
    Loop_Counter = 0

    # メインの販売テーブルを作成
    with db.cursor() as cursor:

        # データの生成
        while Loop_Counter < 50:

            # 処理に使用するSQLを作成
            SQL = "INSERT INTO " + Table_Name1 + "(Category_ID, Product_ID, Units, Prefecture_ID) VALUES ('" + Data_Gen() + "')"

            # データベースへの書き込み
            cursor.execute(SQL)    
            db.commit()

            Loop_Counter = Loop_Counter + 1

    print(datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S") + " : " + str(Loop_Counter) + "個の検証データを作成")
#
# 割り込み発生時の処理
#   
except KeyboardInterrupt:

    print("------------------------------------------------")
    print("!!!!!!!!!!!!!!!!!! 割り込み処理 !!!!!!!!!!!!!!!!!!")
    print(datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S") + " : " + "割り込み発生")
    print("------------------------------------------------") 
#
# 処理の終了処理
#  
finally:

    # データベースを切り離す
    db.close()   

    print("------------------------------------------------")
    print("********* 検証データの初期化・作成処理 *********")
    print(datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S") + " : " + "処理終了")
    print("------------------------------------------------")     
#######################################################################################################

~~~

最後の部分にあるループ処理でメインのテーブルに出力するデータを作成していますが、今回は取り急ぎ５０個ということで設定してあります。

# 出力されたデータを確認してみる・・・
とりあえず、今回のツールを動かしてMySQL上に作成された検証用のテーブルを見てみます。

### 基本のテーブル
全ての情報を参照コードで生成した基準のテーブルになります。
![スクリーンショット 2023-02-01 12.26.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197589/b9900fba-6068-bc62-37cd-97827148b314.png)

### 連携するテーブル
商材区分コードに併せて実データを供給するテーブルになります。
![スクリーンショット 2023-02-01 12.26.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197589/82dd3d43-7d2e-794c-51ee-f79abce57e8c.png)

商品コードから商品名と単価を供給するテーブルになります。
![スクリーンショット 2023-02-01 12.27.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197589/18e6b501-1c67-a4d3-c9c0-4b6c8eccf351.png)

都道府県コードから都道府県名と地域名を供給するテーブルになります。
![スクリーンショット 2023-02-01 12.27.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197589/bfefebba-6e54-1c58-53bd-17c970a9930f.png)

無事に想定通りのデータ・テーブルが出来ている様なので、次回は実際にCOMPOSER上でこれらをFUSION機能でJOIN操作してみたいと思います。

# 今回のまとめ
今回は、最新版のLogiCOMPOSERを試用できるチャンスを得られましたので、BI環境側でJOIN処理を行い（逆に言えば、BI側で組み合わせを調整できる）、その結果を可視化する為の「基本データ生成ツール」を作成し、実際にMySQLと連携させて動作を確認しました。

次回は、これらの検証テーブルを実際にFUSION機能でJOINさせて、その統合テーブルに対して可視化ダッシュボードを作成してみたいと思います。

# 謝辞
本検証は、insightsoftware社の全面バックアップにより実施しています。この貴重な機会を提供して頂いた同社に対して感謝の意を表すると共に、本内容とinsightsoftwareの公式ホームページで公開されている内容等が異なる場合は、insightsoftwareの情報が優先する事をご了解ください。

[**LogiCOMPOSERでFUSIONを試す！（検証編）**](https://qiita.com/wadaken1961/items/98a69401ad70918367fb)はこちら