查询AppEngine上的数据存储N个随机记录
-
12-09-2019 - |
题
我想写一个GQL查询,返回一个特定种类的N个随机记录。我目前的执行工作,但需要N调用到数据存储。我想如果可能的话,使之1调用数据存储。
目前,我分配一个随机数,以每样,我投入的数据存储。当我查询的随机记录我生成另一个随机数和用于记录查询>兰特ORDER BY ASC LIMIT 1。
这个作品,但是,它只所以我需要做N次查询返回1个记录。如何使这一个查询任何想法?感谢。
解决方案
“引擎盖下”的单个搜索查询呼叫只能从一些索引返回一组连续的行的。这就是为什么有些GQL查询,包括任何使用!=,扩展到多个数据存储的电话。
N个独立的均匀随机选择是不(通常)在任何索引连续的。
QED。
您很可能使用内存缓存来存储实体,并减少他们抓N个的成本。或者,如果你不介意的“随机”选接近,因此在索引中,在一个查询中选择(比如说)100中随机选择的块,然后选择ñ从这些随机的。既然你有一个已经随机的字段,它不会立即对一个局外人的N项相关明显。至少,直到他们看到了很多样品,并注意项目A和Z永远不会出现在同一组,因为他们在随机指数是100多个分开。如果性能允许,可以随时再随机化的实体的时间。
其他提示
如何取舍的你找谁?如果你愿意忍受一个小的性能打在插入这些实体,你可以创建一个解决方案,让他们N个速度非常快。
下面是你需要做的:
当您插入实体,指定密钥。你想给钥匙给您实体顺序,从1开始,并从那里往上走。 (这将需要一些努力,因为App Engine不具有自动增量(),所以你需要让你在其他一些实体所用的最后一个ID的轨迹,让我们把它称为IdGenerator)
现在,当您需要N个随机实体,生成N 1,无论您最后生成的ID是(你IdGenerator会知道这一点)之间的随机数。然后,您可以做一个批处理通过按键使用N键,这将只需要一次旅行到数据存储,并且会比查询速度更快,以及得到的,因为Key获取通常比查询速度更快,据我所知。
此方法确实需要处理几个烦人的细节:
- 您IdGenerator如果您在飞行(几比第二多),这将需要某种碎片化IdGenerator实现插入大量的这些项目可能会成为瓶颈。如果所有这些数据是预装,或者不是大批量的,你必须很容易。
- 您可能会发现某些标识实际上并没有与它了相关联的实体,因为你删除它,或者因为看跌期权()某处失败。如果发生这种情况你就必须抓住另一个随机实体。 (如果你想获得幻想和减少这种可能性,你可以让这个ID提供给IdGenerator重用,以“在孔中填入”) 醇>
因此,问题归结到你的速度有多快需要这些N项VS多久你将被添加和删除他们,一点点额外的复杂性是否值得的性能提升。
看起来像的唯一方法是通过存储在每个实体的特殊属性的随机整数值和查询上。这可相当自动完成,如果你只需要添加一个自动初始化属性。
不幸的是,这将要求所有实体一次的处理,如果你的数据存储已经填写。
这很奇怪,我知道了。
我同意从史蒂夫答案,有在一个查询以检索N个随机的行没有这样的方法。
然而,检索一个单一实体,即使该方法不平时工作,使得所述返回的结果的prbability均匀分布。返回给定实体的概率取决于它的Gap的随机分配序号,下一个更高的随机数。例如。如果随机数1,2,和10已被分配(以及没有数字3-9),该算法将返回“2” 8倍更经常大于“1”。
我曾经在一个稍微expensice方式固定于此。如果有人有兴趣,我很乐意分享
我刚刚有同样的问题。我决定不分配ID给我的数据存储已经存在的条目,这样做,因为我已经从分片计数器TOTALCOUNT。
此选择来自 “TOTALCOUNT” 条目,排序的键强>
“计数” 条目 # select $count from the complete set
numberlist = random.sample(range(0,totalcount),count)
numberlist.sort()
pagesize=1000
#initbuckets
buckets = [ [] for i in xrange(int(max(numberlist)/pagesize)+1) ]
for k in numberlist:
thisb = int(k/pagesize)
buckets[thisb].append(k-(thisb*pagesize))
logging.debug("Numbers: %s. Buckets %s",numberlist,buckets)
#page through results.
result = []
baseq = db.Query(MyEntries,keys_only=True).order("__key__")
for b,l in enumerate(buckets):
if len(l) > 0:
result += [ wq.fetch(limit=1,offset=e)[0] for e in l ]
if b < len(buckets)-1: # not the last bucket
lastkey = wq.fetch(1,pagesize-1)[0]
wq = baseq.filter("__key__ >",lastkey)
要注意的是这对我来说是有点复杂,我还没有说服了,我不具备的off-by-一个或关闭用-X的错误。
和要注意,如果计数是接近TOTALCOUNT这可能是非常昂贵的。 和提防的几百万行,可能是不可能的AppEngine上的时间范围内做的。
如果我理解正确的话,你需要找回N个随机实例。
这很容易。只是不要只用键查询。而这样做的 random.choice 的上键列表结果N倍。然后通过提取的键上得到的结果。
keys = MyModel.all(keys_only=True)
n = 5 # 5 random instance
all_keys = list(keys)
result_keys = []
for _ in range(0,n)
key = random.choice(all_keys)
all_keys.remove(key)
result_keys.append(key)
# result_keys now contain 5 random keys.