2009-07-09 11 views
9

Estoy tratando de escribir una consulta GQL que devuelva N registros aleatorios de un tipo específico. Mi implementación actual funciona pero requiere N llamadas al almacén de datos. Me gustaría hacer una llamada al almacén de datos si es posible.Consultar N registros aleatorios en el almacén de datos de Appengine

Actualmente asigno un número aleatorio a cada tipo que coloco en el almacén de datos. Cuando consulto un registro aleatorio, genero otro número aleatorio y consultamos los registros> rand ORDER BY asc LIMIT 1.

Esto funciona, sin embargo, solo devuelve 1 registro, así que necesito hacer N consultas. ¿Alguna idea sobre cómo hacer esta consulta? Gracias.

+0

que he creado un problema para esto, puede estrella para ayudar a que te lo arreglen: https://code.google.com/p/googleappengine/issues/detail?id=9044 –

Respuesta

5

"Debajo del capó" una única llamada de búsqueda solo puede devolver un conjunto de filas consecutivas de algún índice. Esta es la razón por la que algunas consultas GQL, incluido el uso de! =, Se expanden a varias llamadas al almacén de datos.

N selecciones aleatorias uniformes independientes no son (en general) consecutivas en ningún índice.

QED.

Probablemente pueda usar memcache para almacenar las entidades y reducir el costo de tomar N de ellas. O si no le molesta que las selecciones "aleatorias" estén muy juntas en el índice, seleccione un bloque elegido al azar de (digamos) 100 en una consulta, luego elija N al azar de esas. Como tiene un campo que ya está asignado al azar, no será inmediatamente obvio para un extraño que los N elementos están relacionados. Al menos, no hasta que vean muchas muestras y observen que los ítems A y Z nunca aparecen en el mismo grupo, porque están más de 100 separados en el índice aleatorizado. Y si el rendimiento lo permite, puede volver a aleatorizar sus entidades de vez en cuando.

+0

Gracias - Realmente necesito resultados aleatorizados, así que supongo que tendré que usar múltiples llamadas al almacén de datos. Intentaré minimizar N tanto como pueda adivinar. – aloo

+0

esto no es verdad. tanto [operaciones por lotes] (https://developers.google.com/appengine/docs/python/datastore/entities?hl=es#Batch_Operations) como ['IN'] (https://developers.google.com/ appengine/docs/python/datastore/queries # Property_Filters) el operador de consulta puede devolver entidades no consecutivas. – ryan

+0

@ryan: lo mismo con '! ='. Tanto eso como 'IN' se implementan como un número limitado de subconsultas. Las operaciones por lotes no son realmente relevantes para la pregunta, pero sí, es cierto que ciertas operaciones actúan en entidades que no son contiguas en ningún índice. Simplemente no búsquedas. –

3

Parece que el único método es almacenar el valor entero aleatorio en la propiedad especial de cada entidad y consultar sobre eso. Esto se puede hacer de manera completamente automática si solo agrega una propiedad inicializada automáticamente.

Desafortunadamente esto requerirá el procesamiento de todas las entidades una vez si su almacén de datos ya está lleno de.

Es raro, lo sé.

+0

Creo que este es un gran enfoque, y se ajusta al modelo NoSQL de hacer el trabajo de escribir en lugar de leer. Por supuesto, esto no sería del todo aleatorio: si siempre obtenía N entradas secuenciales en ese momento, un usuario ocasionalmente vería los mismos registros uno al lado del otro. Pero eso podría ser lo suficientemente aleatorio para el OP. (También puede crear varias (incluso cientos) propiedades con diferentes números aleatorios y rotar de qué índice extrae.) – npdoty

4

¿Qué tipo de concesiones estás buscando? Si está dispuesto a aguantar un pequeño golpe de rendimiento al insertar estas entidades, puede crear una solución para obtener N de ellas muy rápidamente.

Esto es lo que hay que hacer:

Al insertar sus entidades, especifique la clave. Desea dar las claves a sus entidades en orden, comenzando con 1 y subiendo desde allí. (Esto requerirá un poco de esfuerzo, ya que el motor de la aplicación no tiene autoincrement(), por lo que deberá realizar un seguimiento de la última identificación que usó en otra entidad, vamos a llamarlo IdGenerator)

Ahora cuando lo necesite N entidades aleatorias, genera N números aleatorios entre 1 y cualquiera que sea la última identificación que hayas generado (tu IdGenerator lo sabrá). A continuación, puede hacer un lote por clave usando las teclas N, que solo requerirá un viaje al almacén de datos, y será más rápido que una consulta también, ya que las claves obtenidas son generalmente más rápidas que las consultas, AFAIK.

Este método requiere tratar con algunos detalles molestos:

  1. Su IdGenerator podría convertirse en un cuello de botella cuando se inserta un montón de estos elementos sobre la marcha (más de uno por segundo), lo que requeriría algún tipo de implementación de IdGenerator fragmentado.Si todos estos datos están precargados, o no son de alto volumen, lo tienes fácil.
  2. Es posible que descubra que un Id ya no tiene una entidad asociada, porque lo eliminó o porque put() falló en alguna parte. Si esto sucediera, tendrías que tomar otra entidad aleatoria. (Si desea conseguir la suposición y reducir las probabilidades de que esto se podría hacer esta identificación a disposición del IdGenerator reutilizar para "rellenar los huecos")

Así que la pregunta se reduce a la rapidez con que necesita éstos N elementos con respecto a la frecuencia con la que los agregará y eliminará, y si una pequeña complejidad adicional vale la pena para aumentar el rendimiento.

+1

Puede implementar esto más o menos usando la numeración incorporada de App Engine para ID: si conoce la ID máxima, puedes elegir algunos uniformemente al azar. Algunos no existirán, así que vuelva a intentarlos con nuevos identificadores aleatorios, y así sucesivamente. Si su espacio ID es denso, esto funcionará bien. –

+0

dulce. No sabía que podíamos confiar en la numeración incorporada para comenzar en 1 y subir de 1 en 1 desde allí. –

+0

No se puede, pero se asignará en bloques, y siempre que los bloques se utilicen _mostly_, los intentos deben ser lo suficientemente pequeños como para ser manejables. –

0

Acabo de tener el mismo problema. Decidí no asignar ID a mis entradas ya existentes en el almacén de datos e hice esto, ya que ya tenía la cuenta total de un contador fragmentado.

Esto selecciona las entradas de "conteo" de las entradas de "totalcount", ordenadas por clave.

# 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) 

Mira que esto para mí es algo complejo, y todavía no estoy conviced que no tiene errores off-by-one o off-by-x.

Y tenga en cuenta que si el recuento está cerca del total, esto puede ser muy costoso. Y tenga en cuenta que en millones de filas no es posible hacerlo dentro de los límites de tiempo de appengine.

1

Acepto la respuesta de Steve, no hay forma de recuperar N filas al azar en una consulta.

Sin embargo, incluso el método de recuperación de una sola entidad no suele funcionar de forma tal que la probabilidad de que los resultados devueltos se distribuyan de manera uniforme. La probabilidad de devolver una entidad dada depende de la brecha de su número asignado aleatoriamente y el siguiente número aleatorio más alto. P.ej. si se han asignado números aleatorios 1, 2 y 10 (y ninguno de los números 3 a 9), el algoritmo devolverá "2" 8 veces más a menudo que "1".

He solucionado esto de una manera un poco más costosa. Si alguien está interesado, me complace compartir

-1

Si entiendo correctamente, necesita recuperar N instancia aleatoria.

Es fácil. Solo consulta con solo las teclas. Y haga random.choice N veces en la lista resultado de claves. A continuación, obtenga resultados buscando claves.

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. 
+0

¿Y si tiene un millón de entidades en su almacén de datos? Cargue todas las claves del almacén de datos: parece malo ... – aloo

+0

@aloo si tiene tantas instancias, puede hacer un seguimiento del número total de ellas en el almacén de datos y en la Memcache, luego simplemente haga 'random.choice' en el rango de número entre 0 y el número total. Y después de iterar en las claves con índice, que generó. O simplemente usa límite y desplazamiento. –

Cuestiones relacionadas