Как получить что-то случайное в хранилище данных (AppEngine)?

В настоящее время я использую что-то вроде этого:

images = Image.all() count = images.count() random_numb = random.randrange(1, count) image = Image.get_by_id(random_numb) 

Но оказывается, что идентификаторы в хранилище данных AppEngine не начинаются с 1. У меня есть два изображения в хранилище данных, а их идентификаторы – 6001 и 7001.

Есть ли лучший способ получить случайные изображения?

Распространяется хранилище данных, поэтому идентификаторы не являются последовательными: два узла хранилища данных должны иметь возможность генерировать идентификатор одновременно, не вызывая конфликта.

Чтобы получить случайную сущность, вы можете присоединить произвольное значение float между 0 и 1 для каждого объекта при создании. Затем, чтобы запросить, сделайте что-то вроде этого:

 rand_num = random.random() entity = MyModel.all().order('rand_num').filter('rand_num >=', rand_num).get() if entity is None: entity = MyModel.all().order('rand_num').get() 

Редактировать: Обновленный сквозной случай по предложению Ника.

Другое решение (если вы не хотите добавлять дополнительное свойство). Храните набор ключей в памяти.

 import random # Get all the keys, not the Entities q = ItemUser.all(keys_only=True).filter('is_active =', True) item_keys = q.fetch(2000) # Get a random set of those keys, in this case 20 random_keys = random.sample(item_keys, 20) # Get those 20 Entities items = db.get(random_keys) 

Вышеприведенный код иллюстрирует базовый метод получения только ключей, а затем создание случайного набора, с помощью которого можно выполнить пакетный get. Вы можете сохранить этот набор ключей в памяти, добавить к нему при создании новых ItemUser Entities, а затем использовать метод, который возвращает n случайных объектов. Для управления ключами memcached вам придется реализовать некоторые накладные расходы. Мне нравится это решение лучше, если вы часто выполняете запрос для случайных элементов (я предполагаю, что использование пакета get для n объектов более эффективно, чем запрос для n объектов).

Я думаю, что ответ Дрю Сирса выше (привязка случайного поплавка к каждому сущности при создании) имеет потенциальную проблему: у каждого предмета нет равных шансов на выбор. Например, если есть только 2 объекта, а один получает rand_num 0,2499, а другой получает 0,25, то 0,25 будет собрано почти все время. Это может иметь или не иметь значения для вашей заявки. Вы можете исправить это, изменив rand_num объекта каждый раз, когда он будет выбран, но это означает, что каждое чтение также требует записи.

И ответ Пика всегда будет выбирать первый ключ.

Вот лучшее решение общего назначения, которое я мог бы придумать:

 num_images = Image.all().count() offset = random.randrange(0, num_images) image = Image.all().fetch(1, offset)[0] 

Никаких дополнительных свойств не требуется, но недостатком является то, что count () и fetch () имеют последствия для производительности, если количество изображений велико.

Другой (менее эффективный) метод, который не требует установки:

 query = MyModel.all(keys_only=True) # query.filter("...") selected_key = None n = 0 for key in query: if random.randint(0,n)==0: selected_key = key n += 1 # just in case the query is empty if selected_key is None: entry = None else: entry = MyModel.get(selected_key)