如何在谷歌云数据存储中进行 SQL 风格的搜索查询?

谷歌云数据存储不允许进行 SQL 风格的搜索查询,如

SELECT * FROM Person WHERE Name LIKE "Rob*"

那会返回Rob, Robert, Roberto, Roberta, Roby等等。

GCP数据存储区仅允许使用运算符过滤字段:>, >=, <, <=使用以下规则:

  • 任何大写字母都小于任何小写字母

  • a < b < c < ... < z

有了这些规则,查询

query := datastore.NewQuery("Person").Filter("Name >= ", "Rob").Order("Name")

不仅会返回名称以 开头的所有 Person,Rob还会返回名称大于Rob(Ruben, Sarah, Zoe) 的所有 Person 以及名称以小写字母开头的所有 Person。

这篇文章是我在 Go 中找到的一个 hack,用于模拟 SQL 风格的搜索查询。


鸿蒙传说
浏览 120回答 2
2回答

慕妹3242003

特别是对于前缀匹配,您可以在同一属性上使用多个不等式过滤器。即来自https://cloud.google.com/datastore/docs/concepts/queries&nbsp;“如果查询在给定属性上有多个不等式过滤器,则实体将匹配该查询,前提是该属性至少有一个单独的值满足所有过滤条件。”该页面上的示例是SELECT * FROM Task WHERE tag > 'learn' AND tag < 'math'.&nbsp;或者对于你的情况query := datastore.NewQuery("Person").Filter("Name >= ", "Rob").Filter("Name <= Rob~").Order("Name")

慕盖茨4494581

以下解决方案以编程方式解决了该问题。它是编码的,go但我相信它可以很容易地适应任何语言。我将首先制作整个片段,然后将其分解。func (store *PersonStore) Search(name string) ([]Person, error) {&nbsp; &nbsp; context := context.Background()&nbsp; &nbsp; persons := make([]*Person, 0)&nbsp; &nbsp; query := datastore.NewQuery("Person").Filter("Name >= ", strings.ToLower(name)).Order("Name")&nbsp; &nbsp; keys, err := store.client.GetAll(context, query, &persons)&nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; return nil, fmt.Errorf("unable to search for persons w/ names containing %s - %v", name, err)&nbsp; &nbsp; }&nbsp; &nbsp; filteredPersons := make([]*Perons, 0)&nbsp; &nbsp; for i, p := range persons {&nbsp; &nbsp; &nbsp; &nbsp; p.ID = keys[i].ID&nbsp; &nbsp; &nbsp; &nbsp; if !strings.Contains(p.Name, strings.ToLower(name)) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break&nbsp; &nbsp; &nbsp; &nbsp; } else {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; filteredPersons = append(filteredPersons, p)&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }}1 - 小写假设为了让这段代码工作,我们首先需要做出一个非常强的假设,即所有名称都是小写的。如果由于某种原因,您无法做出此假设,您仍然可以部分使用此代码,但效率会降低。2 - 查询数据存储代码的第一部分专门用于获取名称与所需模式匹配的 Persons 的数据存储区。&nbsp; &nbsp; context := context.Background()&nbsp; &nbsp; persons := make([]*Person, 0)&nbsp; &nbsp; query := datastore.NewQuery("Person").Filter("Name >= ", strings.ToLower(name)).Order("Name")&nbsp; &nbsp; keys, err := store.client.GetAll(context, query, &persons)&nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; return nil, fmt.Errorf("unable to search for persons w/ names containing %s - %v", name, err)&nbsp; &nbsp; }我们确保我们使用strings.ToLower(name)来确保我们不会获取ALL Person。理想情况下name也应该修剪,但这通常是在前端完成的,所以我们在这里省略了。Name再一次,这是基于所有s 都是小写的假设。如果你不能假设这一点,你可以随时使用query := datastore.NewQuery("Person").Filter("Name >= ", name).Order("Name")你只会得到一个带有(可能很多)更多Person的初始列表。最后,我们对获取的列表进行排序,.Order("Name")因为这将成为代码第二部分的基点。3 - 过滤名称到目前为止,它只是一段简单GetAll的代码。我们仍然需要将键插入到Person结构中。我们需要找到一种方法来优化它。为此,我们可以基于这样一个事实,即persons和keyslist 的长度和顺序是准确的。因此,即将到来的for循环开始就像一个常规的将 Key 插入结构位一样。&nbsp; &nbsp; for i, p := range persons {&nbsp; &nbsp; &nbsp; &nbsp; p.ID = keys[i].ID下一点是优化的地方:因为我们知道Persons 是由 排序的,所以Name我们确定,只要strings.Contains(p.Name, strings.ToLower(name))不是真的,我们就选择了所有符合我们标准Person的 s Name,也就是说,只要p.Name不是以Rob不再有,但有Roc或Rod或任何在字典上大于此的东西。&nbsp; &nbsp; &nbsp; &nbsp; if !strings.Contains(p.Name, strings.ToLower(name)) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break然后我们可以使用break指令来逃避我们的循环,希望只解析persons列表中符合我们标准的前几个元素。这些属于else声明:&nbsp; &nbsp; &nbsp; &nbsp; } else {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; filteredPersons = append(filteredPersons, p)&nbsp; &nbsp; &nbsp; &nbsp; }4 - 在没有小写假设的情况下过滤名称正如我之前所说,如果你不能假设所有的名字都是小写的,你仍然可以使用这个代码,但它不会被优化,因为你必须扫描persons查询返回的完整列表。代码应该看起来像这样&nbsp; &nbsp; filteredPersons := make([]*Perons, 0)&nbsp; &nbsp; for i, p := range persons {&nbsp; &nbsp; &nbsp; &nbsp; p.ID = keys[i].ID&nbsp; &nbsp; &nbsp; &nbsp; if strings.Contains(p.Name, strings.ToLower(name)) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; filteredPersons = append(filteredPersons, p)&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go