9
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Metadataをたまには使ってみる

Last updated at Posted at 2021-12-07

先日の勉強会にて取り上げた題材をもとに再構成したものです。
Metadataを積極的に使うべきとは思いませんが、全く知らないというのは勿体ない。

#Metadataとは
ごく簡単に言えば、それぞれの値の隠し情報がMetadataです。
M言語仕様によれば、どの値にもMetadataの場所があるとされています。(中身が入っているとは限らない)
まずはどう使われているか見てみましょう。

##システム上の利用例
こんな具合でパラメータを作ったとします。
パラメータ設定.png
それを詳細エディタで開くと、コードはこんな具合になっております。
パラメータの詳細エディタ.png

上図のmeta以降のrecord部分――[]で括られている部分――がMetadataです。ここにはパラメータの各種設定が記述されていて、こことPower Queryエディタのパラメータ用のUIは連動しているようです。
他にも、#sharedにある関数の説明書や、ファイルから得たbinaryにもMetadataが入っていることが分かっています。

#Metadataを記述する
こうしたMetadataはユーザが自由に設定できますし、読み取ることができます。
システム側でしか使えないもの、ではないです。
定義する際には、先のシステムが生成したコードのように、metaの後に入れたい中身をrecordとして記述します。

//定義したいとき
song1 ="mela!" meta [artist ="緑黄色社会",cm="パルティ"],
//読み取りたいとき
Song1Artist = Value.Metadata(song1)[artist] //"緑黄色社会"

song1自体はただのtextですが、そこからMetadataが取り出せていることが分かると思います。
※スクショの関係でrecordで記述していますが、recordでなくても使えます。

定義例.png

##Metadataは引き継がれない
ある値(Value1としましょう)にMetadataを入れた後に、Value1の表の値に何らかの処理をした場合、処理後の値にはMetadataは引き継がれません。
こんな具合です。

x   = "元旦" meta [date=#date(2021,1,1)],//Metadataを定義
test = Value.Metadata(x)[date],//#date(2021,1,1)
y    = x & "★",//xを改変してyに入れる
z    = Value.Metadata(y) =[] //Metadataが空っぽということ

image.png

##Metadataが残る時
構造化された値(table、record、list)は値の中にも値がある、ということになります。
なので、構造化された値の中の特定の値にMetadata入れていた場合、構造化された値の他の箇所をいじってもMetadataは生き残る、ということになります。

tableでの例(クリックするとコードを展開)
let
    //「データの入力」で作ったテーブル
    Source = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText("i45WMjRSitUBUsYQygRCmSrFxgIA", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [列1 = _t]),
    AddMetadata = Table.TransformColumns(Source,{{"列1",each _ meta [msg = _&"日"],type text}}),
    列1をいじらない処理 = Table.AddColumn(AddMetadata, "カスタム", each null),
    列1 = Value.Metadata( 列1をいじらない処理{0}[列1] )
in
    列1
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/370992/5df51974-31b8-74d7-7489-ecb252d99a57.png)

#番外編:Metadataにクエリの途中のステップを入れる
クエリの完成時に、ステップの途中経過をMetadataに入れておくことで、別クエリから取り出すということも可能です。
例えば、このようにクエリを書いておくと。

クエリ2
let
    //「データの入力」で作ったテーブル
    Source = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText("i45WMjRSitUBUsYQygRCmSrFxgIA", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [列1 = _t]),
    型変換 = Table.TransformColumnTypes(Source,{{"列1", Int64.Type}}),
    処理1 = Table.TransformColumns(型変換, {{"列1", each _ * 100, type number}}),
    処理2とMetadata入れ = Table.FromColumns(List.Repeat({処理1[列1]},5)) meta [base = 処理1[列1]]
in
    処理2とMetadata入れ

image.png

こんな具合で参照できます。
image.png

#注意点
クエリの実行速度という面では下記のような問題があることに触れておきます。

  • Metadataを定義する際、Metadataに入れる値が評価されてしまう。
  • ある値Value1のMetadataだけを呼び出すときでも、Value1自体は評価されてしまう。

##診断で確かめる
Power BI Desktopの診断機能で実証してみましょう。
例えば、こんなクエリを作ります。Sourceの中身が評価されると、診断結果にその旨が書き込まれるようにしています。
Diagnostics.Trace関数については @PowerBIxyz さんの記事が詳しいです。
https://qiita.com/PowerBIxyz/items/e1f5abad9257adc7c517

診断実験1
let
    fx_ForTrace =(arg as any)=>
        Diagnostics.Trace(
                        TraceLevel.Information,
                        Text.Format("#{0}を計算した",{arg}),
                        arg
        ),
    Source = [
                a = fx_ForTrace("a"),
                b = fx_ForTrace("b"),
                c =[test= fx_ForTrace("c")]
    ],
    dummy1 =0,dummy2 =2,dummy3=4,dummy6=10,
    Custom1 = dummy1
in
    Custom1

最後のステップを右クリックして「診断」を実行します。
image.png

僕のPCですと3秒程度で診断結果のクエリが作成されます。
Diagnostics.Trace関数で仕込んだ結果は、このクエリのAdditional Info列(左から13列目)に出力されますので、展開してやります。この例ではMessageフィールドがないので、Sourceの中身は全く評価されなかったということです。
Custom1を評価するうえで、Sourceは関係ないですから、この診断結果は想定通りということになります。
image.png

では、診断実験1クエリの最後のステップCustom1に、MetadataとしてSourceを入れてやるとどうなるか。

Custom1 = dummy1 meta Source

ステップ診断結果は下記の通り。なんと、a,bどころかrecordに括っているcすらも評価されてしまうのです。
これは特徴のある動きです。
ただ単に最終出力をSourceとした場合は、cは評価されないからです。
a,bはプレビューが出るので評価されるが、cはプレビューされないため。
image.png

これを回避するには、Metadataに入れる値を関数にしまう、というのがスマートなやり方だと思います。(@PowerBIxyz さん、ありがとうございます)
例えば、診断実験1クエリの最後のステップCustom1をこのように書き換えてやれば、Sourceは評価されなくなります。

Custom1 = dummy1 meta [Temp=each Source]

※その他、二重のrecordやlistに括っても評価を免れますが、もはや取り出しが面倒なので割愛しました。

#参考
Metadata - PowerQuery M | Microsoft Docs

Metadataを操作する関数
https://docs.microsoft.com/en-us/powerquery-m/value-functions#__toc360789761

#テスト環境
Power BI Desktop:2.93.981.0 64-bit (2021年5月)
Excel:MS365のExcel バージョン2105(Power Query 2.93.422.0 64 ビット)
※2010アドイン版から既にあったと思います。

9
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?