雪花中的嵌套变体更新和删除

时间:2021-03-05 17:06:38

标签: snowflake-cloud-data-platform variant

目前正在将变更数据捕获事件从 MongoDB 流式传输到雪花中,希望将它们应用于已经存在的原始数据。

假设我有一张这样的桌子:

+---------------------+-----------------+-----------+
|         key         |      value      | document  |
+---------------------+-----------------+-----------+
| foo.bar             | "changed value" | <variant> |
| foo.stuff.anArray.1 | 1000            | <variant> |
| ...                 | ...             | ...       |
+---------------------+-----------------+-----------+

其中变体包含非常严重的嵌套 JSON 示例:

{
    "foo": {
        "bar": "Some info",
        "baz": "Other info",
        "stuff": {
            "anArray": [1, 2, 3],
            "things": "More nested info"
        }
    }
}

我想使用 OBJECT_DELETEOBJECT_INSERT 函数来更新雪花中的这个嵌套变体数据。

尝试制作 js UDF,但不支持 eval()

其他方法,例如编写执行 key.split(".") 的 UDF,然后递归遍历结构并更新字段似乎需要很长时间,并且在某些情况下会以 JavaScript out of memory error: UDF thread memory limit exceeded 失败。

正在寻找更有效的方法来解决这个问题。

2 个答案:

答案 0 :(得分:1)

我遇到了 similar problem 并使用通用 UDF 来解决它。下面是一个 UDF 实现示例,它可以满足您的需求:

create or replace function edit_nested_entity("variant_object" variant, "path" string, "value" string)
returns variant
language javascript
as
$$
// https://stackoverflow.com/questions/6491463/accessing-nested-javascript-objects-and-arrays-by-string-path?page=1&tab=votes#tab-top
    Object.byString = function(o, s) {
        s = s.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
        s = s.replace(/^\./, '');           // strip a leading dot
        var a = s.split('.');
        for (var i = 0, n = a.length; i < n; ++i) {
            var k = a[i];
            if (k in o) {
                o = o[k];
            } else {
                return;
            }
        }
        return o;
   }
   // get the entity base
   nested_entity = Object.byString(variant_object, path)
   // update the value
   nested_entity = value
   return variant_object;
$$;

现在您需要运行以下 SQL 命令来实现您的需要:

UPDATE t1
SET document = edit_nested_entity(document, key, value) 

您可能会对此 UDF 进行一些微调以使其更通用(或为不同的数据类型使用不同的 UDF),但这会起作用。

答案 1 :(得分:0)

有一种使用 OBJECT_INSERT 的方法,但它并不漂亮。不幸的是,我没有看到在单个 OBJECT_INSERT 中指定嵌套键的方法。 所以:

create or replace table test2 (document variant);
insert into test2 select object_construct('foo',object_construct('bar','Some info', 'baz', 'Other info','stuff', object_construct('anArray', array_construct(1, 2, 3), 'things', 'More nested info')));
select * from test2;

我明白了:

{
      "foo": {
                "bar": "Some info",
                "baz": "Other info",
                "stuff": {
                          "anArray": [1,2,3],
                          "things": "More nested info"
                }
      }

}

现在,我想用“已更改信息”更新 foo.bar,这样我就可以了(记住将标志设置为 TRUE,这样您就可以获得更新而不是插入):

update test2 set document = OBJECT_INSERT(document, 'foo', OBJECT_INSERT(document:foo::VARIANT, 'bar', 'Changed value', TRUE), TRUE) WHERE document:foo.bar::VARCHAR = 'Some info';

我回来了:

{
          "foo": {
                    "bar": "Changed value",
                    "baz": "Other info",
                    "stuff": {
                              "anArray": [1,2,3],
                              "things": "More nested info"
                    }
          }
}

您也可以使用前面提到的 Javascript UDF here