【问题标题】:How do I pass through values from an Elasticsearch document in Go?如何在 Go 中传递 Elasticsearch 文档中的值?
【发布时间】:2021-06-15 23:00:04
【问题描述】:

我正在尝试用 Go 重写我们系统的某些部分。它们目前是用 Python 编写的。我想要服务的一些数据存在于 Elasticsearch 中。

在我们的用户中,我们有一些标准字段,但也允许人们创建一些特定于其环境的自定义字段。例如,我们有一个产品对象,它有一些常见的字段,如 nameprice,但我们让某人创建一个像 discount_pricerequires_freight 这样的字段来同意他们的用例。

在 Python 中,这很容易适应。读入 JSON,我们选择的 JSON 解析器会进行一些合理的类型推断,然后我们可以在处理完数据后返回。

在 Go 中,我们想要处理来自 Elasticsearch JSON 响应的任何数据都必须映射到一个类型。或者至少这是我的理解。例如:

import (
    "encoding/json"
)

...

type Product struct {
    Name              string   `json:"name"`
    Price             string   `json:"price"`
}

以下是数据可能外观的简化示例。我已经在我想通过的非标准字段的名称前加上custom

{
    "id": "ABC123",
    "name": "Great Product",
    "price": 10.99,
    "custom_alternate_names": ["Great Product"],
    "custom_sellers": [{
        "id": "ABC123",
        "name": "Great Product LLC",
        "emails": ["great@stuff.com"]
    }]
}

对于路由实际需要处理或操作自定义字段的地方的特殊情况是可以的。但在大多数情况下,通过 JSON 数据保持不变就可以了,因此我们没有为了安全而强加任何类型映射的事实并没有添加任何东西。

有没有办法设置struct 的接口(?)可以作为任何未映射字段的传递?或者在返回数据之前获取未解析的 JSON 数据并与映射对象重新组合的方法?

【问题讨论】:

  • 您可以将json.RawMessage 用于您只想写入/读取并且不需要操作和/或验证的数据。对于您确实需要操作的自定义字段,您可以使用映射或切片,并且映射/切片的元素类型可以是具有 3 个成员 fieldName、fieldType、fieldValue 的结构。
  • 这很有帮助。然后我将如何将包罗万象添加到结构中?抱歉,这是一个愚蠢的问题——我对 Go 还很陌生。
  • 取决于 json,自定义字段是否与非自定义字段处于同一级别(嵌套),例如{"name":"...","price":"...","custom_field1":"...","custom_field2":"..."}?或者它们是否组合在一起并嵌套在某个预定义字段下,即{"name":"...","price":"...","custom":{"field1":"...","field2":"..."}}?还是更复杂?如果可以,请通过添加您要处理的 json 示例来更新问题。
  • 嵌套是不均匀的,但我认为它只能深入两层。我将挖掘一个可共享的示例并更新问题。
  • 在问题中添加了我认为非常有代表性的例子。

标签: json go elasticsearch


【解决方案1】:

你可以这样做

package main

import (
    "encoding/json"
    "log"
)

type Product struct {
    //embed product to be able to pull the properties without
    //requiring a nested object in the json
    KnownFields
    OtherStuff json.RawMessage
}

//Custom unmarshaller
func (p *Product) UnmarshalJSON(b []byte) error {
    var k KnownFields
    //unmarshal the known fields
   err := json.Unmarshal(b, &k)
    if err != nil {
        return err
    }
    p.KnownFields = k
    //You can use json.RawMessage or map[string]interface
    p.OtherStuff = json.RawMessage(b)
    return nil
}

type KnownFields struct {
    Name  string      `json:"name"`
    Price json.Number `json:"price"`
}

const JSON = `
{
    "id": "ABC123",
    "name": "Great Product",
    "price": 10.99,
    "custom_alternate_names": ["Great Product"],
    "custom_sellers": [{
        "id": "ABC123",
        "name": "Great Product LLC",
        "emails": ["great@stuff.com"]
    }]
}`

func main() {
    var p Product
    err := json.Unmarshal([]byte(JSON), &p)
    if err != nil {
        log.Panic(err)
    }
    log.Printf("%v", p)
}

如果您要对 Product 进行变异和编组,则必须实现自定义编组器,并且您还需要使用 map[string]interface{} 作为 OtherStuff 以避免已知字段的重复条目

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-10-21
    • 2012-06-02
    • 2023-03-17
    • 2021-07-19
    • 2017-04-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多