Golang实现未知数量的数据存储过滤器(需要一个函数来追加过滤器)

huangapple go评论73阅读模式
英文:

Golang implementation of unknown number of datastore filters (need function to append filters)

问题

我正在开发一个Web应用程序,用户可以选择不同的选项,比如应用价格、星级数量、更新时间等等。

问题是,使用Datastore只能进行一次不等式过滤,所以我不能这样说:

query = datastore.NewQuery("iOSApp").Filter("StarRatings >=", stars).Filter("MinimumAge >=", age).Filter("Price >=", price)

相反,我添加了几个布尔/整数实体,所以如果我想说星级评分大于3(最大为5),我可以这样说:

query = datastore.NewQuery("iOSApp").Filter("Stars2Up =", false).Filter("Stars3Up =", true)

这个解决方案似乎很好(我相信这是一个好的/快速的解决方案,尽管我可能错了)

现在我遇到的问题是执行此查询的函数必须执行很多检查...下面的代码只检查了StarRating过滤器,但我还想过滤年龄、价格、下载量、更新时间等等。

var query *datastore.Query
switch stars {
case 1:
    query = datastore.NewQuery("AppStoreApp").Filter("Stars1Up =", true)
case 2:
    query = datastore.NewQuery("AppStoreApp").Filter("Stars1Up =", false).Filter("Stars2Up =", true)
case 3:
    query = datastore.NewQuery("AppStoreApp").Filter("Stars2Up =", false).Filter("Stars3Up =", true)
case 4:
    query = datastore.NewQuery("AppStoreApp").Filter("Stars3Up =", false).Filter("Stars4Up =", true)
case 5:
    query = datastore.NewQuery("AppStoreApp").Filter("Stars3Up =", false).Filter("Stars4Up =", true)
default:
    log.Println("error...")
}
var app []iOSApp
if _, err := db.client.GetAll(ctx, query, &app); err != nil {
    log.Fatalf("err: ", err)
}
return &app, nil

如果我尝试使用if/else或switch语句来完成这个任务,我很快就会得到成千上万个不同的if/else/switch情况...所以我在考虑是否可以添加一个函数,该函数接受查询和属性作为输入,然后作为输出返回包含新过滤器的查询。

例如:

// 查询之前
query = datastore.NewQuery("AppStoreApp").Filter("Stars1Up =", false).Filter("Stars2Up =", true)

query, err = appendFilter(query, updatetime) // 在这个例子中,updatetime = 6个月内更新

// 查询之后
query = datastore.NewQuery("AppStoreApp").Filter("Stars1Up =", false).Filter("Stars2Up =", true).Filter("UpdatedWithin6Months =", true).Filter("UpdatedWithin1Year =", false)

我非常感谢关于如何实现这个appendFilter函数的帮助。

英文:

I'm working on a web app where users can select different options, such as app price, number of stars, update time, etc.

The problem is that with Datastore then it's only possible to make 1 inequality filter, so I cannot say:

query = datastore.NewQuery("iOSApp").Filter("StarRatings >=", stars).Filter("MinimumAge >=", age).Filter("Price >=", price)

Instead, I added several bool/int entities, so that if I want to say StarRatings is greater than 3 (maximum is 5), then I can say:

query = datastore.NewQuery("iOSApp").Filter("Stars2Up =", false).Filter("Stars3Up =", true)

This solution seems to be working great (and I believe it's a good/fast solution, although I may be wrong)

The problem I have now is that the function that performs this query have to perform so many checks.. the code below is only checking the StarRating filter, but I also want to filter age, price, downloads, update time, etc.

var query *datastore.Query
switch stars {
case 1:
	query = datastore.NewQuery("AppStoreApp").Filter("Stars1Up =", true)
case 2:
	query = datastore.NewQuery("AppStoreApp").Filter("Stars1Up =", false).Filter("Stars2Up =", true)
case 3:
	query = datastore.NewQuery("AppStoreApp").Filter("Stars2Up =", false).Filter("Stars3Up =", true)
case 4:
	query = datastore.NewQuery("AppStoreApp").Filter("Stars3Up =", false).Filter("Stars4Up =", true)
case 5:
	query = datastore.NewQuery("AppStoreApp").Filter("Stars3Up =", false).Filter("Stars4Up =", true)
default:
	log.Println("error...")
}
var app []iOSApp
if _, err := db.client.GetAll(ctx, query, &app); err != nil {
	log.Fatalf("err: ", err)
}
return &app, nil

If I try to do this using if/else or switch cases then I will quickly end up with a thousand different if/else/switch scenarios.. so I was thinking that I could add a function that took the query and the property as input, and then as output it would return the query containing it's new filter..

For example

// query before
query = datastore.NewQuery("AppStoreApp").Filter("Stars1Up =", false).Filter("Stars2Up =", true)

query, err = appendFilter(query, updatetime) // in this case updatetime = within 6 months as an example

// query after
query = datastore.NewQuery("AppStoreApp").Filter("Stars1Up =", false).Filter("Stars2Up =", true).Filter("UpdatedWithin6Months =", true).Filter("UpdatedWithin1Year =", false)

I would greatly appreciate help with how this appendFilter function could be implemented

答案1

得分: 2

Query.Filter() 方法返回一个派生查询,该查询将包含调用它的查询的所有过滤器,以及您在参数中指定的过滤器。因此,如果您计划分多个步骤构建最终查询,您需要存储其返回值。

所以你的 appendFilter() 是不必要的,你可以这样做:

q := datastore.NewQuery("AppStoreApp")

// 添加一个过滤器:
q = q.Filter("filter1", value1)

// 添加另一个过滤器:
q = q.Filter("filter2", value2)

为了优化你的 "stars" 过滤器,可以这样做:

func applyStarFilter(q *datastore.Query, minStars int) *Query {
    if minStars > 1 {
        q = q.Filter(fmt.Sprintf("Stars%dUp =", minStars-1), false)
    }
    return q.Filter(fmt.Sprintf("Stars%dUp =", minStars), true)
}

利用具有枚举值的字段

你的 UpdatedWithinXXYY 字段与 stars 非常相似:具有固定的枚举值,并且要按其进行过滤,您需要设置 2 个过滤字段(一个为 true,一个为 false)。

如果您提前枚举这些值,可以自动设置这两个字段。例如:

func applyUpdatedWithin(q *datastore.Query, updateIdx int) *Query {
    q = q.Filter(updatedValues[updateIdx], true)
    if updateIdx+1 < len(updatedValues) {
        q = q.Filter(updatedValues[updateIdx+1], false)
    }
    return q
}

var updatedValues = []string{
    "UpdatedWithin1Day =",
    "UpdatedWithin1Week =",
    "UpdatedWithin1Month =",
    "UpdatedWithin1Year =",
}

所以一个应用最低 3 颗星和 UpdatedWithin6Months 的示例:

q := datastore.NewQuery("AppStoreApp")
q = applyStarFilter(q, 3)
q = applyUpdatedWithin(q, 2)

与您的情况相关,为了规划您的 Datastore 索引,我强烈建议阅读这篇文章:

Index Selection and Advanced Search

英文:

The Query.Filter() method returns a derivative query that will contain all filters of the query it was called on, plus the one you specify in its arguments. So if you plan to build the final query in multiple steps, you need to store its return value.

So your appendFilter() is unnecessary, you can just do:

q := datastore.NewQuery(&quot;AppStoreApp&quot;)

// Append a filter:
q = q.Filter(&quot;filter1&quot;, value1)

// Append another filter:
q = q.Filter(&quot;filter2&quot;, value2)

To optimize your "stars" filter, it may be done like this:

func applyStarFilter(q *datastore.Query, minStars int) *Query {
	if minStars &gt; 1 {
		q = q.Filter(fmt.Sprintf(&quot;Stars%dUp =&quot;, minStars-1), false)
	}
	return q.Filter(fmt.Sprintf(&quot;Stars%dUp =&quot;, minStars), true)
}

Taking advantage of fields having enumerated values

Your UpdatedWithinXXYY fields are very similar to stars: have fixed, enumerated values, and to filter by it, you need to set 2 filter fields (one true and one false).

If you enumerate these in advance, you can automate setting those 2 fields. For example:

func applyUpdatedWithin(q *datastore.Query, updateIdx int) *Query {
	q = q.Filter(updatedValues[updateIdx], true)
	if updateIdx+1 &lt; len(updatedValues) {
		q = q.Filter(updatedValues[updateIdx+1], false)
	}
	return q
}

var updatedValues = []string{
	&quot;UpdatedWithin1Day =&quot;,
	&quot;UpdatedWithin1Week =&quot;,
	&quot;UpdatedWithin1Month =&quot;,
	&quot;UpdatedWithin1Year =&quot;,
}

So an example to apply min 3 stars and UpdatedWithin6Months:

q := datastore.NewQuery(&quot;AppStoreApp&quot;)
q = applyStarFilter(q, 3)
q = applyUpdatedWithin(q, 2)

Relevant to your case, for planning your Datastore indices, I highly recommend to read this article:

Index Selection and Advanced Search

huangapple
  • 本文由 发表于 2017年2月8日 21:14:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/42114198.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定