英文:
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("AppStoreApp")
// Append a filter:
q = q.Filter("filter1", value1)
// Append another filter:
q = q.Filter("filter2", value2)
To optimize your "stars" filter, it may be done like this:
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)
}
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 < len(updatedValues) {
q = q.Filter(updatedValues[updateIdx+1], false)
}
return q
}
var updatedValues = []string{
"UpdatedWithin1Day =",
"UpdatedWithin1Week =",
"UpdatedWithin1Month =",
"UpdatedWithin1Year =",
}
So an example to apply min 3 stars and UpdatedWithin6Months
:
q := datastore.NewQuery("AppStoreApp")
q = applyStarFilter(q, 3)
q = applyUpdatedWithin(q, 2)
Relevant to your case, for planning your Datastore indices, I highly recommend to read this article:
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论