Json.Decode: 提取列表项并 flatten/merge 到记录

Json.Decode: Extract list item and flatten/merge it into record

我想将一些 JSON(使用 inner_hits 的 Elasticsearch 搜索结果)解析为平面记录,但记录的某些字段应该来自列表中的项目,而列表又位于嵌套对象:

{
    "_index": "test",
    "_type": "doc",
    "_id": "AUG.02.013.1320.02630.0",
    "_score": null,
    "_routing": "1",
    "_source": {
        "child_mgrpid": "1.1",
        "child_varia": "blabla",
        "type": "child",
        "my_join_field": {
            "name": "child",
            "parent": "AUG.02.013.1320"
        },
        "@version": "1",
        "@timestamp": "2020-01-12T16:45:11.302Z",
    },
    "inner_hits": {
        "prnt": {
            "hits": {
                "total": 1,
                "max_score": null,
                "hits": [
                    {
                        "_index": "test",
                        "_type": "doc",
                        "_id": "AUG.02.013.1320",
                        "_score": null,
                        "_routing": "1",
                        "_source": {
                            "pt_archiv": "",
                            "pt_id": "AUG.02.013.1320",
                            "pt_titel": "",
                            "pt_l_id": "AUG.02.013",
                            "pt_l_name": "Johann Christoph von Freyberg-Eisenberg"
                            "pt_t_id": "AUG",
                            "pt_t_kurzform": "AUG",
                            "type": "parent",
                            "my_join_field": {
                                "name": "parent"
                            },
                            "@version": "1",
                            "@timestamp": "2020-01-12T16:45:08.470Z",
                        },
                        "sort": [
                            "AUG"
                        ]
                    }
                ]
            }
        }
    }
}

(实际上,数据字段比示例中的多,所以我不能使用 Decoder.mapX 函数。)我肯定知道 entry/parent 在inner_hits.prnt.hits.hits 列表,我想获取它的 _source 字段并将它们与 "main" 对象中的字段一起展平到记录中,其类型定义如下所示:

type alias Child =
    { grp_id : String
    , varia : String
    , pt_archive : String
    , pt_id : String
    , pt_title : String
    , pt_l_id : String
    , pt_l_name : String
    , pt_t_id : String
    , pt_t_shortLabel : String
    }

这是我目前拥有的解码器:

type alias Parent =
    { pt_archive : String
    , pt_id : String
    , pt_title : String
    , pt_l_id : String
    , pt_l_name : String
    , pt_t_id : String
    , pt_t_shortLabel : String
    }


childHitDecoder : Decoder Child
childHitDecoder =
    Json.Decode.succeed Child
        |> Json.Decode.Pipeline.requiredAt [ "_source", "child_mgrp_id" ] Json.Decode.string
        |> Json.Decode.Pipeline.requiredAt [ "_source", "child_varia" ] Json.Decode.string
        |> Json.Decode.Pipeline.custom (Json.Decode.at [ "inner_hits", "prnt", "hits", "hits" ] (firstElementDecoder parentInnerHitDecoder) )


firstElementDecoder : Json.Decode.Decoder a -> Json.Decode.Decoder a
firstElementDecoder baseDecoder =
    Json.Decode.list baseDecoder
        |> Json.Decode.map List.head
        |> Json.Decode.andThen (Maybe.map Json.Decode.succeed >> Maybe.withDefault (Json.Decode.fail "Empty list"))


parentInnerHitDecoder : Decoder Parent
parentInnerHitDecoder =
    Json.Decode.succeed Parent
        |> Json.Decode.Pipeline.requiredAt [ "_source", "archiv" ] Json.Decode.string
        |> Json.Decode.Pipeline.requiredAt [ "_source", "id" ] Json.Decode.string
        |> Json.Decode.Pipeline.requiredAt [ "_source", "titel" ] Json.Decode.string
        |> Json.Decode.Pipeline.requiredAt [ "_source", "lah_id" ] Json.Decode.string
        |> Json.Decode.Pipeline.requiredAt [ "_source", "lah_name" ] Json.Decode.string
        |> Json.Decode.Pipeline.requiredAt [ "_source", "ter_id" ] Json.Decode.string
        |> Json.Decode.Pipeline.requiredAt [ "_source", "ter_kurzform" ] Json.Decode.string

firstElementDecoder 来自 ,我想用它取消列出 inner_hits 列表项。)

使用这样的设置,我的 childHitDecoder 的最后一行给出了一个错误:

This function cannot handle the argument sent through the (|>) pipe:

482|     Json.Decode.succeed Child
483|         |> Json.Decode.Pipeline.requiredAt [ "_source", "child_mgrp_id" ] Json.Decode.string
484|         |> Json.Decode.Pipeline.requiredAt [ "_source", "child_varia" ] Json.Decode.string
485|         |> Json.Decode.Pipeline.custom (Json.Decode.at [ "inner_hits", "prnt", "hits", "hits" ] (firstElementDecoder parentInnerHitDecoder) )
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The argument is:

    Decoder
        (
        String
        -> String
        -> String
        -> String
        -> String
        -> String
        -> String
        -> Child
        )

But (|>) is piping it to a function that expects:

    Decoder (Parent -> b)

Hint: It looks like it takes too many arguments. I see 6 extra.

我已经成功地使用 Decode.Pipeline.custom 将嵌套的 JSON 对象解码为其他地方的平面榆树记录,但是,显然,将其与此处的取消​​列表结合起来超出了我的范围。

这让我困惑了几天,我也尝试过将字段束与 Decode.map 组合,但也失败了。我不太了解 Decode.andThen or Decode.lazy 看他们是否可以在这里提供帮助,如果有任何帮助,我将不胜感激。

使用上面列出的 json 对象,以下应该有效:

firstPtDecoder : String -> Json.Decode.Decoder String
firstPtDecoder field =
    Json.Decode.map (Maybe.withDefault "" << List.head) <|
        Json.Decode.at [ "inner_hits", "prnt", "hits", "hits" ] <|
            Json.Decode.list (Json.Decode.at [ "_source", field ] Json.Decode.string)


childHitDecoder : Json.Decode.Decoder Child
childHitDecoder =
    Json.Decode.succeed Child
        |> Json.Decode.Pipeline.requiredAt [ "_source", "child_mgrpid" ] D.string
        |> Json.Decode.Pipeline.requiredAt [ "_source", "child_varia" ] D.string
        |> Json.Decode.Pipeline.custom (firstPtDecoder "pt_archiv")
        |> Json.Decode.Pipeline.custom (firstPtDecoder "pt_id")
        |> Json.Decode.Pipeline.custom (firstPtDecoder "pt_titel")
        |> Json.Decode.Pipeline.custom (firstPtDecoder "pt_l_id")
        |> Json.Decode.Pipeline.custom (firstPtDecoder "pt_l_name")
        |> Json.Decode.Pipeline.custom (firstPtDecoder "pt_t_id")
        |> Json.Decode.Pipeline.custom (firstPtDecoder "pt_t_kurzform")

大部分工作由 firstPtDecoder 完成,它在 "hits" 数组中搜索 field,returns 在数组的第一个元素中搜索字符串.