【问题标题】:Type mismatch with projection on a []byte property类型与 []byte 属性上的投影不匹配
【发布时间】:2019-10-08 18:01:55
【问题描述】:

我有一个结构如下

  type MyEntity struct {
     PF []byte `json:"-" datastore:"_pf"`
  }

没有投影的查询工作正常。但是,当我在“_pf”字段上使用投影进行查询时,出现“类型不匹配:字符串与 []uint8”错误。我实现了 PropertyLoadSaver 并检查了“_pf”属性的 prop.Value,发现有些行返回 []byte 类型和一些返回字符串。那么,为什么投影查询失败并出现此错误,而非投影查询很好?目前我正在通过实现 PropertyLoadSaver 接口并显式检查类型并将字符串类型转换为 []byte 类型来解决此问题。

这是完整的测试用例。这是在云数据存储模拟器上复制的。为下面的 datastoreProject 变量使用适当的值。其余的都应该直接工作。您可以通过插入两个实体或其中一种实体类型来查看行为。显示的错误是

panic: datastore: cannot load field "_pf" into a "tests.MyEntity": type mismatch: string versus []uint8 [recovered]
    panic: datastore: cannot load field "_pf" into a "tests.MyEntity": type mismatch: string versus []uint8

以下是代码。

type MyEntity struct {
    PF []byte `json:"-" datastore:"_pf"`
}

func TestPackedField(t *testing.T) {
    e1 := &MyEntity{PF: []byte{83, 0, 0, 0, 93, 150, 154, 206, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 3}} // returns []byte on projection
    e2 := &MyEntity{PF: []byte{83, 0, 0, 0, 93, 120, 79, 87, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 3}}   // returns string on projection

    ctx := context.Background()

    conn, err := datastore.NewClient(ctx, datastoreProject)
    if err != nil {
        panic(err)
    }

    bkey := datastore.NameKey("Bytes", "bytearray", nil)
    if true {
        conn.Put(ctx, bkey, e1)
    }
    skey := datastore.NameKey("Bytes", "string", nil)
    if true {
        conn.Put(ctx, skey, e2)
    }

    q1 := datastore.NewQuery("Bytes").Order("-_pf").Limit(2)
    var elfull []*MyEntity
    if _, err := conn.GetAll(ctx, q1, &elfull); err != nil {
        panic(err)
    }

    q2 := datastore.NewQuery("Bytes").Project("_pf").Order("-_pf").Limit(2)
    var elprojected []*MyEntity
    if _, err := conn.GetAll(ctx, q2, &elprojected); err != nil {
        conn.Delete(ctx, bkey)
        conn.Delete(ctx, skey)
        panic(err)
    }
}

【问题讨论】:

  • 能否请您提供一些代码,您在哪里进行投影查询?您能否也举一些数据示例?什么返回[]byte,什么返回string?
  • 谢谢安德烈。用测试用例和示例数据更新了帖子。

标签: go google-cloud-datastore


【解决方案1】:

为了理解为什么这适用于普通查询而不是投影查询,您可能需要首先了解这两者之间的实际区别是什么。正如post 中提到的那样:

而针对 Cloud Datastore 的“常规”(我理解为 SELECT * ...)查询通常使用仅包含查询实体属性的排序子集的索引,以及指向完整实体的指针、投影查询针对包含查询请求的所有字段的索引运行。因此,一旦通过索引识别出与查询匹配的实体集,就不再需要获取查询的实体,因此显着的延迟增益似乎来自于此。

因此,基本上,当您进行投影查询时,不会获取您查询的实体。

在阅读official documentation 时,我发现了一些与您的问题相关的非常有趣的短语:

切片类型的字段对应于 Datastore 数组属性,但 []byte 除外,它对应于 Datastore blob。如果将非数组值加载到切片字段中,则结果将是一个具有一个元素的切片,其中包含该值。

关键字段 如果结构包含带有名称“key”标记的 *datastore.Key 字段,则在 Put 上将忽略其值。将实体读回 Go 结构时,该字段将填充用于查询实体的 *datastore.Key 值。

我从这里了解到的是,在您的情况下,字段填充了该查询的键值(字符串)。

我发现的另一个有趣的事情是,根据properties and values type documentation,类型 []byte 没有被索引。正如here 所告知的那样,无法投影未索引的属性。因此,预计的查询根本不应该适用于这个特定的用例。

【讨论】:

  • “类型 []byte 未编入索引”不正确。 godoc.org/cloud.google.com/go/datastore 表示无法索引超过 1500 个字节的字符串和字节切片。我的数据正好在这个范围内。正如我所提到的,投影正在工作并返回正确的数据,只是数据类型不匹配。将字符串更改为 []byte 然后加载到结构中可以正常工作(正确的数据)。
  • 正确。请阅读此官方数据存储文档。这不是 Go 的东西,而是 Datastore 的东西。 --cloud.google.com/appengine/docs/standard/go/datastore/… 。它显示在 []byte 的 Note 列中的那个表 - “Note indexed”。然后,如果您进一步阅读,将会出现这样的短语:“因为长字节切片和长字符串没有被索引......”。
  • 无论如何,这并不重要。我的建议是,由于投影查询不获取实际的实体值,为了使其工作,它可能会在幕后自动转换为字符串(正如我所见,您有一个字符串键)。所以这可能是您的类型不匹配的原因。
  • appengine go sdk 有 datastore.ByteString 用于最多 1500 个字节数组,并且该字段可以被索引。在 Cloud Datastore 客户端库中,没有 datastore.ByteArray。相反,使用 []byte 类型并根据长度将其存储为这些类型中的任何一种。我没有得到的是某些行以正确的类型投影,而其他行则没有。
  • 我同意你关于 datastore.ByteString 的看法。但是您使用的是未编入索引的 []byte (在 Google Cloud 官方文档中非常清楚地说明了这一点)。投影查询只能用于索引属性(在 Google Cloud 官方文档中也有非常明确的说明)。我清楚地理解您的问题,您的结果之所以如此,是因为您滥用了预计的查询。您不能在 []byte 上使用投影查询。
猜你喜欢
  • 2014-09-18
  • 2013-12-22
  • 2021-10-15
  • 2013-03-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多