在Yandex.Metrica中,用戶使用JSON作為訪問參數。為了處理這些JSON,實現了一些函數。(盡管在大多數情況下,JSON是預先進行額外處理的,並將結果值放在單獨的列中。)所有的這些函數都進行了盡可能的假設。以使函數能夠盡快的完成工作。
我們對JSON格式做了如下假設:
字段名稱(函數的參數)必須使常量。
字段名稱必須使用規范的編碼。例如:visitParamHas('{"abc":"def"}', 'abc') = 1,但是 visitParamHas('{"\\u0061\\u0062\\u0063":"def"}', 'abc') = 0
函數可以隨意的在多層嵌套結構下查找字段。如果存在多個匹配字段,則返回第一個匹配字段。
JSON除字符串文本外不存在空格字符。
--1.visitParamHas(參數,名稱)
檢查是否存在«name»名稱的字段
--2.visitParamExtractUInt(參數,名稱)
將名為«name»的字段的值解析成UInt64。如果這是一個字符串字段,函數將嘗試從字符串的開頭解析一個數字。如果該字段不存在,或無法從它中解析到數字,則返回0。
--3.visitParamExtractInt(參數,名稱)
與visitParamExtractUInt相同,但返回Int64。
--4.visitParamExtractFloat(參數,名稱)
與visitParamExtractUInt相同,但返回Float64。
--5.visitParamExtractBool(參數,名稱)
解析true/false值。其結果是UInt8類型的。
--6.visitParamExtractRaw(參數,名稱)
返回字段的值,包含空格符。
示例:
visitParamExtractRaw('{"abc":"\\n\\u0000"}', 'abc') = '"\\n\\u0000"'
visitParamExtractRaw('{"abc":{"def":[1,2,3]}}', 'abc') = '{"def":[1,2,3]}'
visitParamExtractString(參數,名稱)
使用雙引號解析字符串。這個值沒有進行轉義。如果轉義失敗,它將返回一個空白字符串。
示例:
visitParamExtractString('{"abc":"\\n\\u0000"}', 'abc') = '\n\0'
visitParamExtractString('{"abc":"\\u263a"}', 'abc') = '☺'
visitParamExtractString('{"abc":"\\u263"}', 'abc') = ''
visitParamExtractString('{"abc":"hello}', 'abc') = ''
目前不支持\uXXXX\uYYYY這些字符編碼,這些編碼不在基本多文種平面中(它們被轉化為CESU-8而不是UTF-8)。
以下函數基於simdjson,專為更復雜的JSON解析要求而設計。但上述假設2仍然適用。
--7.JSONHas(json[, indices_or_keys]…)
如果JSON中存在該值,則返回1。
如果該值不存在,則返回0。
示例:
select JSONHas('{"a": "hello", "b": [-100, 200.0, 300]}', 'b') = 1 select JSONHas('{"a": "hello", "b": [-100, 200.0, 300]}', 'b', 4) = 0
indices_or_keys可以是零個或多個參數的列表,每個參數可以是字符串或整數。
String = 按成員名稱訪問JSON對象成員。
正整數 = 從頭開始訪問第n個成員/成員名稱。
負整數 = 從末尾訪問第n個成員/成員名稱。
您可以使用整數來訪問JSON數組和JSON對象。
例如:
select JSONExtractKey('{"a": "hello", "b": [-100, 200.0, 300]}', 1) = 'a' select JSONExtractKey('{"a": "hello", "b": [-100, 200.0, 300]}', 2) = 'b' select JSONExtractKey('{"a": "hello", "b": [-100, 200.0, 300]}', -1) = 'b' select JSONExtractKey('{"a": "hello", "b": [-100, 200.0, 300]}', -2) = 'a' select JSONExtractString('{"a": "hello", "b": [-100, 200.0, 300]}', 1) = 'hello'
--8.JSONLength(json[, indices_or_keys]…)
返回JSON數組或JSON對象的長度。
如果該值不存在或類型錯誤,將返回0。
示例:
select JSONLength('{"a": "hello", "b": [-100, 200.0, 300]}', 'b') = 3 select JSONLength('{"a": "hello", "b": [-100, 200.0, 300]}') = 2
--9.JSONType(json[, indices_or_keys]…)
返回JSON值的類型。
如果該值不存在,將返回Null。
示例:
select JSONType('{"a": "hello", "b": [-100, 200.0, 300]}') = 'Object' select JSONType('{"a": "hello", "b": [-100, 200.0, 300]}', 'a') = 'String' select JSONType('{"a": "hello", "b": [-100, 200.0, 300]}', 'b') = 'Array'
--10.JSONExtractUInt(json[, indices_or_keys]…)
JSONExtractInt(json[, indices_or_keys]…)
JSONExtractFloat(json[, indices_or_keys]…)
JSONExtractBool(json[, indices_or_keys]…)
解析JSON並提取值。這些函數類似於visitParam*函數。
如果該值不存在或類型錯誤,將返回0。
示例:
select JSONExtractInt('{"a": "hello", "b": [-100, 200.0, 300]}', 'b', 1) = -100 select JSONExtractFloat('{"a": "hello", "b": [-100, 200.0, 300]}', 'b', 2) = 200.0 select JSONExtractUInt('{"a": "hello", "b": [-100, 200.0, 300]}', 'b', -1) = 300
--11.JSONExtractString(json[, indices_or_keys]…)
解析JSON並提取字符串。此函數類似於visitParamExtractString函數。
如果該值不存在或類型錯誤,則返回空字符串。
該值未轉義。如果unescaping失敗,則返回一個空字符串。
示例:
select JSONExtractString('{"a": "hello", "b": [-100, 200.0, 300]}', 'a') = 'hello' select JSONExtractString('{"abc":"\\n\\u0000"}', 'abc') = '\n\0' select JSONExtractString('{"abc":"\\u263a"}', 'abc') = '☺' select JSONExtractString('{"abc":"\\u263"}', 'abc') = '' select JSONExtractString('{"abc":"hello}', 'abc') = ''
--12.JSONExtract(json[, indices_or_keys…], Return_type)
解析JSON並提取給定ClickHouse數據類型的值。
這是以前的JSONExtract<type>函數的變體。 這意味着JSONExtract(…, ‘String’)返回與JSONExtractString()返回完全相同。JSONExtract(…, ‘Float64’)返回於JSONExtractFloat()`返回完全相同。
示例:
SELECT JSONExtract('{"a": "hello", "b": [-100, 200.0, 300]}', 'Tuple(String, Array(Float64))') = ('hello',[-100,200,300]) SELECT JSONExtract('{"a": "hello", "b": [-100, 200.0, 300]}', 'Tuple(b Array(Float64), a String)') = ([-100,200,300],'hello') SELECT JSONExtract('{"a": "hello", "b": [-100, 200.0, 300]}', 'b', 'Array(Nullable(Int8))') = [-100, NULL, NULL] SELECT JSONExtract('{"a": "hello", "b": [-100, 200.0, 300]}', 'b', 4, 'Nullable(Int64)') = NULL SELECT JSONExtract('{"passed": true}', 'passed', 'UInt8') = 1 SELECT JSONExtract('{"day": "Thursday"}', 'day', 'Enum8(\'Sunday\' = 0, \'Monday\' = 1, \'Tuesday\' = 2, \'Wednesday\' = 3, \'Thursday\' = 4, \'Friday\' = 5, \'Saturday\' = 6)') = 'Thursday' SELECT JSONExtract('{"day": 5}', 'day', 'Enum8(\'Sunday\' = 0, \'Monday\' = 1, \'Tuesday\' = 2, \'Wednesday\' = 3, \'Thursday\' = 4, \'Friday\' = 5, \'Saturday\' = 6)') = 'Friday'
--13.JSONExtractKeysAndValues(json[, indices_or_keys…], Value_type)
從JSON中解析鍵值對,其中值是給定的ClickHouse數據類型。
示例:
SELECT JSONExtractKeysAndValues('{"x": {"a": 5, "b": 7, "c": 11}}', 'x', 'Int8') = [('a',5),('b',7),('c',11)];
--14JSONExtractRaw(json[, indices_or_keys]…)
返回JSON的部分。
如果部件不存在或類型錯誤,將返回空字符串。
示例:
select JSONExtractRaw('{"a": "hello", "b": [-100, 200.0, 300]}', 'b') = '[-100, 200.0, 300]'
示例應用查詢:
WITH '[{"name":"天台","tall":100,"model":"M779011"},{"name":"樓頂","tall":90,"model":"M669011"}]' AS new, 'S123' AS num SELECT new, num ┌─new────────────────────────────────────────────────────────────────────────────────────────┬─num──┐ │ [{"name":"天台","tall":100,"model":"M779011"},{"name":"樓頂","tall":90,"model":"M669011"}] │ S123 │ └────────────────────────────────────────────────────────────────────────────────────────────┴──────┘ SELECT visitParamExtractBool('{"name":true}', 'name') AS bool, visitParamExtractInt('{"name":123}', 'name') AS int, visitParamExtractFloat('{"name":0.1}', 'name') AS float, visitParamExtractString('{"name":"你好"}', 'name') AS str, visitParamExtractRaw('{"name":"你好"}', 'name') AS raw ┌─bool─┬─int─┬─float─┬─str──┬─raw────┐ │ 1 │ 123 │ 0.1 │ 你好 │ "你好" │ └──────┴─────┴───────┴──────┴────────┘ --使用JSONExtractArrayRaw()函數,將字符串轉化為json數組: SELECT visitParamExtractString(json, 'name') AS name, visitParamExtractInt(json, 'tall') AS tall, visitParamExtractString(json, 'model') AS model, num FROM ( WITH '[{"name":"天台","tall":100,"model":"M779011"}, {"name":"樓頂","tall":90,"model":"M669011"}, {"name":"秀兒","tall":80,"model":"M559011"}]' AS new, 'S123' AS num SELECT new, num, JSONExtractArrayRaw(new) AS arr, arrayJoin(arr) AS json ) ┌─name─┬─tall─┬─model───┬─num──┐ │ 天台 │ 100 │ M779011 │ S123 │ │ 樓頂 │ 90 │ M669011 │ S123 │ │ 秀兒 │ 80 │ M559011 │ S123 │ └──────┴──────┴─────────┴──────┘ --可以使用字符截取: WITH '[{"name":"天台","tall":100,"model":"M779011"}, {"name":"樓頂","tall":90,"model":"M669011"}, {"name":"秀兒","tall":80,"model":"M559011"}]' AS new, replaceAll(replaceAll(new, '[', ''), ']', '') AS out, concat(arrayJoin(splitByString('},', out)), '}') AS json, 'S123' AS num SELECT visitParamExtractString(json, 'name') AS name, visitParamExtractInt(json, 'tall') AS tall, visitParamExtractString(json, 'model') AS model, num ┌─name─┬─tall─┬─model───┬─num──┐ │ 天台 │ 100 │ M779011 │ S123 │ │ 樓頂 │ 90 │ M669011 │ S123 │ │ 秀兒 │ 80 │ M559011 │ S123 │ └──────┴──────┴─────────┴──────┘