为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
鸿蒙传说 2022-05-18 16:56:08
谷歌云数据存储不允许进行 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 风格的搜索查询。
查看完整描述

2 回答

?
慕妹3242003

TA贡献1824条经验 获得超6个赞

特别是对于前缀匹配,您可以在同一属性上使用多个不等式过滤器。即来自https://cloud.google.com/datastore/docs/concepts/queries “如果查询在给定属性上有多个不等式过滤器,则实体将匹配该查询,前提是该属性至少有一个单独的值满足所有过滤条件。”

该页面上的示例是SELECT * FROM Task WHERE tag > 'learn' AND tag < 'math'. 或者对于你的情况query := datastore.NewQuery("Person").Filter("Name >= ", "Rob").Filter("Name <= Rob~").Order("Name")


查看完整回答
反对 回复 2022-05-18
?
慕盖茨4494581

TA贡献1850条经验 获得超11个赞

以下解决方案以编程方式解决了该问题。它是编码的,go但我相信它可以很容易地适应任何语言。


我将首先制作整个片段,然后将其分解。


func (store *PersonStore) Search(name string) ([]Person, error) {

    context := context.Background()

    persons := make([]*Person, 0)

    query := datastore.NewQuery("Person").Filter("Name >= ", strings.ToLower(name)).Order("Name")


    keys, err := store.client.GetAll(context, query, &persons)

    if err != nil {

        return nil, fmt.Errorf("unable to search for persons w/ names containing %s - %v", name, err)

    }


    filteredPersons := make([]*Perons, 0)

    for i, p := range persons {

        p.ID = keys[i].ID

        if !strings.Contains(p.Name, strings.ToLower(name)) {

            break

        } else {

            filteredPersons = append(filteredPersons, p)

        }

    }

}

1 - 小写假设

为了让这段代码工作,我们首先需要做出一个非常强的假设,即所有名称都是小写的。如果由于某种原因,您无法做出此假设,您仍然可以部分使用此代码,但效率会降低。


2 - 查询数据存储

代码的第一部分专门用于获取名称与所需模式匹配的 Persons 的数据存储区。


    context := context.Background()

    persons := make([]*Person, 0)

    query := datastore.NewQuery("Person").Filter("Name >= ", strings.ToLower(name)).Order("Name")


    keys, err := store.client.GetAll(context, query, &persons)

    if err != nil {

        return nil, fmt.Errorf("unable to search for persons w/ names containing %s - %v", name, err)

    }

我们确保我们使用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 插入结构位一样。


    for i, p := range persons {

        p.ID = keys[i].ID


下一点是优化的地方:因为我们知道Persons 是由 排序的,所以Name我们确定,只要strings.Contains(p.Name, strings.ToLower(name))不是真的,我们就选择了所有符合我们标准Person的 s Name,也就是说,只要p.Name不是以Rob不再有,但有Roc或Rod或任何在字典上大于此的东西。


        if !strings.Contains(p.Name, strings.ToLower(name)) {

            break

然后我们可以使用break指令来逃避我们的循环,希望只解析persons列表中符合我们标准的前几个元素。这些属于else声明:


        } else {

            filteredPersons = append(filteredPersons, p)

        }

4 - 在没有小写假设的情况下过滤名称

正如我之前所说,如果你不能假设所有的名字都是小写的,你仍然可以使用这个代码,但它不会被优化,因为你必须扫描persons查询返回的完整列表。


代码应该看起来像这样


    filteredPersons := make([]*Perons, 0)

    for i, p := range persons {

        p.ID = keys[i].ID

        if strings.Contains(p.Name, strings.ToLower(name)) {

            filteredPersons = append(filteredPersons, p)

        }

    }


查看完整回答
反对 回复 2022-05-18
  • 2 回答
  • 0 关注
  • 119 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号