Подтвердить что ты не робот

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

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

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

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

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

4b9b3361

Ответ 1

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

Чтобы получить случайную сущность, вы можете присоединить произвольное значение с плавающей точкой от 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()

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

Ответ 2

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

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 объектов).

Ответ 3

Я думаю, что Дрю Сирс ответил выше (приложить случайный поплавок к каждому сущности при создании) имеет потенциальную проблему: у каждого предмета нет равных шансов на выбор. Например, если есть только 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() имеют последствия для производительности, если количество изображений велико.

Ответ 4

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

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)