简体   繁体   English

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

[英]How to do an SQL style search query in google cloud datastore?

Google Cloud datastore does not allow to make SQL style search queries like Google Cloud数据存储区不允许进行 SQL 样式的搜索查询,例如

SELECT * FROM Person WHERE Name LIKE "Rob*"

that would return Rob, Robert, Roberto, Roberta, Roby and so on.那将返回Rob, Robert, Roberto, Roberta, Roby等等。

GCP datastore only allows to filter on fields using operators: >, >=, <, <= with rules like: GCP数据存储区仅允许使用运算符过滤字段: >, >=, <, <= ,规则如下:

  • any upper case letter is smaller than any lower case letter任何大写字母都小于任何小写字母
  • a < b < c < ... < z

With these rules, the query有了这些规则,查询

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

would not only return all Person whose name starts with Rob , but also all Person whose name is greater than Rob (Ruben, Sarah, Zoe) and all Person whose names starts with a lower case letter.不仅会返回名称以Rob开头的所有 Person ,还会返回名称大于Rob (Ruben、Sarah、Zoe)的所有 Person 以及名称以小写字母开头的所有 Person 。

The present post is a hack that I found in Go to emulate a SQL style search query.这篇文章是我在 Go 中找到的一个模拟 SQL 样式搜索查询的技巧。

Specifically for prefix matches you can use multiple inequality filters on the same property.特别是对于前缀匹配,您可以在同一属性上使用多个不等式过滤器。 Ie from https://cloud.google.com/datastore/docs/concepts/queries "If a query has multiple inequality filters on a given property, an entity will match the query only if at least one of its individual values for the property satisfies all of the filters."即来自https://cloud.google.com/datastore/docs/concepts/queries “如果一个查询在给定的属性上有多个不等式过滤器,则实体将匹配该查询,前提是该实体至少有一个单独的值满足所有过滤器。”

The example on that page is SELECT * FROM Task WHERE tag > 'learn' AND tag < 'math' .该页面上的示例是SELECT * FROM Task WHERE tag > 'learn' AND tag < 'math' Or for your case query := datastore.NewQuery("Person").Filter("Name >= ", "Rob").Filter("Name <= Rob~").Order("Name")或者对于您的案例query := datastore.NewQuery("Person").Filter("Name >= ", "Rob").Filter("Name <= Rob~").Order("Name")

The following solution addresses the issue programmatically.以下解决方案以编程方式解决了该问题。 It is coded in go but i believe it could be easily adapted to any language.它是用go编码的,但我相信它可以很容易地适应任何语言。

I'll first produce the whole snippet and then I shall break it down.我将首先生成整个片段,然后将其分解。

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 - Lower case assumption 1 - 小写假设

In order for this code to work we first need to make a very strong assumption that all names are in lower case .为了让这段代码能够工作,我们首先需要做出一个非常强的假设,即所有的名字都是小写的 If, for some reason, you cannot make this assumption, you can still partially use this code, but it will be less efficient.如果由于某种原因,您不能做出这个假设,您仍然可以部分使用此代码,但效率会降低。

2 - Query the datastore 2 - 查询数据存储

The first part of the code is dedicated to fetching the datastore for Persons whose name is matching the desired pattern.代码的第一部分专用于获取名称与所需模式匹配的人员的数据存储。

    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)
    }

We make sure we use strings.ToLower(name) to make sure we won't fetch ALL Person s.我们确保使用strings.ToLower(name)来确保我们不会获取所有Person Ideally name should be also trimmed, but this is usually done in the front-end, so we ommitted this here.理想情况下name也应该被修剪,但这通常是在前端完成的,所以我们在这里省略了。

Once again, this is base on the assumption that all Name s are in lower case.再一次,这是基于所有Name都是小写的假设。 If you cannot assume this, you can always use如果你不能假设这一点,你可以随时使用

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

You'll just simply get an initial list with ( possibly a lot ) more Person s.您只需简单地获得一个包含(可能很多)更多Person的初始列表。

Finally, we order our fetched list with .Order("Name") as this is going to be the base point of the second part of the code.最后,我们使用.Order("Name")对获取的列表进行.Order("Name")因为这将成为代码第二部分的基点。

3 - Filter on Names 3 - 名称过滤

Up to here it was a simple GetAll piece of code.到这里为止,它是一段简单的GetAll代码。 We still need to insert the keys into the Person structures.我们仍然需要将键插入到Person结构中。 We need to find a way to optimize that.我们需要找到一种方法来优化它。 For this we can rest on the fact that persons and keys list are in exact length and order.为此,我们可以依赖于personskeys列表的长度和顺序准确的事实。 So the upcoming for loop starts exactly like a regular insert Key into structure bit.因此,即将到来的for循环与常规将 Key 插入结构位完全一样。

    for i, p := range persons {
        p.ID = keys[i].ID

The next bit is where the optimization is: since we know that the Person s are ordered by Name we are certain that, as soon as strings.Contains(p.Name, strings.ToLower(name)) is not true we have selected all Person s whose Name matches our criteria, that is, as soon as p.Name doesn't start with Rob anymore but with Roc or Rod or anything lexicographically greater than this.下一点是优化的地方:因为我们知道Person是按Name排序的,所以我们确定,只要strings.Contains(p.Name, strings.ToLower(name))不为真,我们就选择了所有Person s 的Name符合我们的标准,也就是说,只要p.Name不再以Rob开头,而是以RocRod或任何字典序大于此的p.Name开头。

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

We can then escape our loop using a break instruction, hopefuly having only parsed the first few elements of persons list that matche our criteria.然后我们可以使用break指令来逃避我们的循环,希望只解析了符合我们标准的persons列表的前几个元素。 These fall into the else statement:这些属于else语句:

        } else {
            filteredPersons = append(filteredPersons, p)
        }

4 - Filter on Names without the lower case assumption 4 - 过滤没有小写假设的名称

As I said earlier, if you cannot assume all names are in lower case, you can still use this code, but it will not be optimized, because you will mandatorily have to scan the full persons list returned by the query.正如我之前所说,如果你不能假设所有的名字都是小写的,你仍然可以使用这个代码,但它不会被优化,因为你必须扫描查询返回的完整persons列表。

The code should look liske this代码应该看起来像这样

    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)
        }
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM