2008-09-20 11 views
42

Me gustaría eliminar todos los datos de un tipo específico en Google App Engine. ¿Cuál es la mejor forma de hacer esto? Escribí un script de eliminación (hack), pero dado que hay demasiados datos, se agota el tiempo de espera después de unos cientos de registros.Eliminar todos los datos de un tipo en Google App Engine

+0

es un dolor en el cuello –

Respuesta

5

El official answer de Google es que tiene que eliminar en trozos repartidos en varias solicitudes. Puede usar AJAX, meta refresh, o solicitar su URL desde un script hasta que no queden otras entidades.

9

Es de suponer que el truco era algo como esto:

# Deleting all messages older than "earliest_date" 
q = db.GqlQuery("SELECT * FROM Message WHERE create_date < :1", earliest_date) 
results = q.fetch(1000) 

while results: 
    db.delete(results) 
    results = q.fetch(1000, len(results)) 

Como usted dice, si hay datos suficientes, vas a golpear la solicitud de tiempo de espera antes de que llegue a través de todos los registros. Tendría que volver a invocar esta solicitud varias veces desde el exterior para asegurarse de que se borraron todos los datos; lo suficientemente fácil de hacer, pero apenas ideal.

La consola de administración no parece ofrecer ninguna ayuda, ya que (según mi propia experiencia con ella), parece que solo permite listar entidades de un tipo determinado y luego eliminarlas página por página. .

Al realizar pruebas, tuve que purgar mi base de datos al inicio para deshacerme de los datos existentes.

Debo deducir que Google opera bajo el principio de que el disco es barato, por lo que los datos son huérfanos (los índices de datos redundantes reemplazados), en lugar de eliminarse. Dado que actualmente hay una cantidad fija de datos disponible para cada aplicación (0.5 GB), no es de mucha ayuda para los usuarios que no son de Google App Engine.

3

Desafortunadamente, no hay forma de hacer una eliminación masiva fácilmente. Su mejor opción es escribir un script que elimine un número razonable de entradas por invocación y luego llamarlo repetidamente, por ejemplo, haciendo que su script de eliminación devuelva un redireccionamiento 302 cada vez que haya más datos para eliminar, y luego ir a buscarlo con "wget- -max-redirect = 10000 "(o algún otro gran número).

+1

Los redireccionamientos 302 no funcionarán, GAE limita el número de redireccionamientos por solicitud (y también el tiempo acumulativo que tarda la solicitud). –

+0

Tres años demasiado tarde para este comentario, pero eso no es cierto y nunca lo ha sido: App Engine no limita ninguna de esas cosas; los navegadores limitan las redirecciones totales que seguirán. App Engine no resume el tiempo empleado en varias solicitudes. –

+0

Por supuesto, mi respuesta original ahora está obsoleta, dada la existencia de mapreduce y la consola de administración del almacén de datos. –

9

Intente utilizar App Engine Console entonces usted incluso no tiene que implementar ningún código especial

+0

No hay un botón (AFAIK) para eliminar TODO un cierto tipo. – Jonny

7

He intentado db.delete (resultados) y la aplicación de consola motor, y ninguno de ellos parece estar funcionando para mí. La eliminación manual de entradas del Visor de datos (límite aumentado hasta 200) tampoco funcionó, ya que he cargado más de 10000 entradas. Terminé de escribir este guión

from google.appengine.ext import db 
from google.appengine.ext import webapp 
from google.appengine.ext.webapp.util import run_wsgi_app 
import wsgiref.handlers 
from mainPage import YourData #replace this with your data 
class CleanTable(webapp.RequestHandler): 
    def get(self, param): 
     txt = self.request.get('table') 
     q = db.GqlQuery("SELECT * FROM "+txt) 
     results = q.fetch(10) 
     self.response.headers['Content-Type'] = 'text/plain' 
     #replace yourapp and YouData your app info below. 
     self.response.out.write(""" 
      <html> 
      <meta HTTP-EQUIV="REFRESH" content="5; url=http://yourapp.appspot.com/cleanTable?table=YourData"> 
      <body>""") 

     try: 
      for i in range(10): 
       db.delete(results) 
       results = q.fetch(10, len(results)) 
       self.response.out.write("<p>10 removed</p>") 
       self.response.out.write(""" 
       </body> 
       </html>""") 

     except Exception, ints: 
      self.response.out.write(str(inst)) 

def main(): 
    application = webapp.WSGIApplication([ 
    ('/cleanTable(.*)', CleanTable), 
    ]) 

    wsgiref.handlers.CGIHandler().run(application) 

El truco era incluir redirigir en html en lugar de utilizar self.redirect. Estoy listo para esperar toda la noche para deshacerme de todos los datos en mi mesa. Con suerte, el equipo de GAE hará que sea más fácil dejar caer tablas en el futuro.

+0

Quiero adaptar su solución para eliminar todos los artículos de un tipo, con una 'fecha' anterior a cierta 'Fecha'. He encontrado algunos errores tipográficos en su solución, pero me preocupan otros errores tipográficos presentes b/c. No entiendo totalmente el código. Por ejemplo, 'inst' vs' ints', ¿cómo se establece el primer valor 'txt' ?, ¿cuál es el propósito de' 5'? Entonces, si ha identificado otros errores tipográficos, por favor mencionarlos. Mi pregunta principal es que parece que borras 100 o menos registros; ¿se puede cambiar el 'for' por un' while db.delete (results): 'añadiendo un' break' al final de la cláusula 'except', o eso rompe el código? – zerowords

27

Actualmente estoy borrando las entidades por su clave, y parece ser más rápido.

from google.appengine.ext import db 

class bulkdelete(webapp.RequestHandler): 
    def get(self): 
     self.response.headers['Content-Type'] = 'text/plain' 
     try: 
      while True: 
       q = db.GqlQuery("SELECT __key__ FROM MyModel") 
       assert q.count() 
       db.delete(q.fetch(200)) 
       time.sleep(0.5) 
     except Exception, e: 
      self.response.out.write(repr(e)+'\n') 
      pass 

de la terminal, corro rizo -N http: // ...

+0

Para mí, devuelve "Se produjo un error del servidor. Póngase en contacto con el administrador". en lugar de excepción, pero funciona :) –

+1

El código anterior rompe el ciclo lanzando un AssertionError y atrapándolo. ¡Eso es bastante feo! Debería romperse solo si q.count() es 0 – boxed

4

Un consejo. Sugiero que conozca el remote_api para este tipo de usos (eliminación masiva, modificación, etc.). Pero, incluso con la API remota, el tamaño del lote puede limitarse a unos pocos cientos a la vez.

+0

Wow ... eso es útil, y yo ni siquiera había oído hablar de él. Eso es lo que obtengo para trabajar principalmente con la API de Java que no tiene nada similar a remote_api. – mcherm

10

Si fuera una persona paranoica, diría que Google App Engine (GAE) no nos ha facilitado la eliminación de datos si queremos.Voy a omitir el debate sobre los tamaños de índice y cómo se traducen los 6 GB de datos a 35 GB de almacenamiento (que se facturarán). Esa es otra historia, pero tienen formas de evitarlo: limitar el número de propiedades para crear índices (índices generados automáticamente), etcétera.

La razón por la que decidí escribir esta publicación es porque necesito "nuclear" todas mis clases en un arenero. Lo leí y finalmente se me ocurrió este código:

package com.intillium.formshnuker; 

import java.io.IOException; 
import java.util.ArrayList; 

import javax.servlet.http.HttpServlet; 
import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpServletResponse; 

import com.google.appengine.api.datastore.Key; 
import com.google.appengine.api.datastore.Query; 
import com.google.appengine.api.datastore.Entity; 
import com.google.appengine.api.datastore.FetchOptions; 
import com.google.appengine.api.datastore.DatastoreService; 
import com.google.appengine.api.datastore.DatastoreServiceFactory; 

import com.google.appengine.api.labs.taskqueue.QueueFactory; 
import com.google.appengine.api.labs.taskqueue.TaskOptions.Method; 

import static com.google.appengine.api.labs.taskqueue.TaskOptions.Builder.url; 

@SuppressWarnings("serial") 
public class FormsnukerServlet extends HttpServlet { 

public void doGet(final HttpServletRequest request, final HttpServletResponse response) throws IOException { 

    response.setContentType("text/plain"); 

    final String kind = request.getParameter("kind"); 
    final String passcode = request.getParameter("passcode"); 

    if (kind == null) { 
    throw new NullPointerException(); 
    } 

    if (passcode == null) { 
    throw new NullPointerException(); 
    } 

    if (!passcode.equals("LONGSECRETCODE")) { 
    response.getWriter().println("BAD PASSCODE!"); 
    return; 
    } 

    System.err.println("*** deleting entities form " + kind); 

    final long start = System.currentTimeMillis(); 

    int deleted_count = 0; 
    boolean is_finished = false; 

    final DatastoreService dss = DatastoreServiceFactory.getDatastoreService(); 

    while (System.currentTimeMillis() - start < 16384) { 

    final Query query = new Query(kind); 

    query.setKeysOnly(); 

    final ArrayList<Key> keys = new ArrayList<Key>(); 

    for (final Entity entity: dss.prepare(query).asIterable(FetchOptions.Builder.withLimit(128))) { 
    keys.add(entity.getKey()); 
    } 

    keys.trimToSize(); 

    if (keys.size() == 0) { 
    is_finished = true; 
    break; 
    } 

    while (System.currentTimeMillis() - start < 16384) { 

    try { 

    dss.delete(keys); 

    deleted_count += keys.size(); 

    break; 

    } catch (Throwable ignore) { 

    continue; 

    } 

    } 

    } 

    System.err.println("*** deleted " + deleted_count + " entities form " + kind); 

    if (is_finished) { 

    System.err.println("*** deletion job for " + kind + " is completed."); 

    } else { 

    final int taskcount; 

    final String tcs = request.getParameter("taskcount"); 

    if (tcs == null) { 
    taskcount = 0; 
    } else { 
    taskcount = Integer.parseInt(tcs) + 1; 
    } 

    QueueFactory.getDefaultQueue().add(
    url("/formsnuker?kind=" + kind + "&passcode=LONGSECRETCODE&taskcount=" + taskcount).method(Method.GET)); 

    System.err.println("*** deletion task # " + taskcount + " for " + kind + " is queued."); 

    } 

    response.getWriter().println("OK"); 

} 

} 

Tengo más de 6 millones de registros. Eso es mucho. No tengo idea de cuál será el costo de borrar los registros (quizás sea más económico no eliminarlos). Otra alternativa sería solicitar una eliminación para toda la aplicación (sandbox). Pero eso no es realista en la mayoría de los casos.

Decidí ir con grupos más pequeños de registros (en consulta fácil). Sé que podría ir a 500 entidades, pero luego comencé a recibir tasas muy altas de fallas (función de borrar de nuevo).

Mi pedido al equipo de GAE: por favor agregue una función para eliminar todas las entidades de un tipo en una sola transacción.

+0

+1 Esto funcionó para mí :) – Asaph

0

Puede usar las colas de tareas para eliminar fragmentos de, por ejemplo, 100 objetos. Eliminar objetos en GAE muestra cuán limitadas son las capacidades de administración en GAE. Tienes que trabajar con lotes en 1000 entidades o menos. Puede usar la herramienta de carga masiva que funciona con CSV, pero la documentación no cubre Java. Estoy usando GAE Java y mi estrategia para eliminar implica tener 2 servlets, uno para eliminar realmente y otro para cargar las colas de tareas. Cuando quiero hacer una eliminación, ejecuto el servlet de carga de la cola, carga las colas y luego GAE va a trabajar ejecutando todas las tareas en la cola.

Cómo hacerlo: Cree un servlet que elimine una pequeña cantidad de objetos. Agregue el servlet a sus colas de tareas. Ve a casa o al trabajo en otra cosa;) Controlar el almacén de datos de vez en cuando ...

que tienen un almacén de datos con cerca de 5.000 objetos que purgar todas las semanas y se tarda alrededor de 6 horas para limpiar, así que ejecutar la tarea el viernes por la noche. Uso la misma técnica para cargar de forma masiva mis datos, que son aproximadamente 5000 objetos, con aproximadamente una docena de propiedades.

1

con Django, url configuración:

url(r'^Model/bdelete/$', v.bulk_delete_models, {'model':'ModelKind'}), 

vista Configuración

def bulk_delete_models(request, model): 
    import time 
    limit = request.GET['limit'] or 200 
    start = time.clock() 
    set = db.GqlQuery("SELECT __key__ FROM %s" % model).fetch(int(limit)) 
    count = len(set) 
    db.delete(set) 
    return HttpResponse("Deleted %s %s in %s" % (count,model,(time.clock() - start))) 

A continuación, ejecute en PowerShell:

$client = new-object System.Net.WebClient 
$client.DownloadString("http://your-app.com/Model/bdelete/?limit=400") 
0

Esto funcionó para mí:

class ClearHandler(webapp.RequestHandler): 
    def get(self): 
     self.response.headers['Content-Type'] = 'text/plain' 
     q = db.GqlQuery("SELECT * FROM SomeModel") 
     self.response.out.write("deleting...") 
     db.delete(q) 
5

La manera más rápida y eficiente de manejar la eliminación masiva en Datastore es mediante el uso del nuevo mapper API anunciado en el último Google I/O.

Si su idioma de elección es Python, solo tiene que registrar su asignador en un mapreduce.yaml archivo y definir una función como esta:

from mapreduce import operation as op 
def process(entity): 
yield op.db.Delete(entity) 

En Java usted debe echar un vistazo a this article que sugiere una función como esta:

@Override 
public void map(Key key, Entity value, Context context) { 
    log.info("Adding key to deletion pool: " + key); 
    DatastoreMutationPool mutationPool = this.getAppEngineContext(context) 
      .getMutationPool(); 
    mutationPool.delete(value.getKey()); 
} 
+2

este es el enfoque que uso, aunque requiere mucha CPU (no CPU de almacén de datos, CPU general). –

0

Gracias a todos ustedes, obtuve lo que necesito. : D
Esto puede ser útil si tiene muchos modelos de db para eliminar, puede enviarlo a su terminal. Y también, puede administrar la lista de eliminación en DB_MODEL_LIST usted mismo.
db_1 Delete:

python bulkdel.py 10 DB_1 

Eliminar todo DB:

python bulkdel.py 11 

Aquí está el archivo bulkdel.py:

import sys, os 

URL = 'http://localhost:8080' 
DB_MODEL_LIST = ['DB_1', 'DB_2', 'DB_3'] 

# Delete Model 
if sys.argv[1] == '10' : 
    command = 'curl %s/clear_db?model=%s' % (URL, sys.argv[2]) 
    os.system(command) 

# Delete All DB Models 
if sys.argv[1] == '11' : 
    for model in DB_MODEL_LIST : 
     command = 'curl %s/clear_db?model=%s' % (URL, model) 
     os.system(command) 

Y aquí es la versión modificada del código de Alexandre Fiori.

from google.appengine.ext import db 
class DBDelete(webapp.RequestHandler): 
    def get(self): 
     self.response.headers['Content-Type'] = 'text/plain' 
     db_model = self.request.get('model') 
     sql = 'SELECT __key__ FROM %s' % db_model 

     try: 
      while True: 
       q = db.GqlQuery(sql) 
       assert q.count() 
       db.delete(q.fetch(200)) 
       time.sleep(0.5) 
     except Exception, e: 
      self.response.out.write(repr(e)+'\n') 
      pass 

Y, por supuesto, se debe asignar el enlace para modelar en un archivo (como main.py en GAE),;)
En el caso de algunos tipos como yo lo necesitan en detalle, aquí es parte del principal .py:

from google.appengine.ext import webapp 
import utility # DBDelete was defined in utility.py 
application = webapp.WSGIApplication([('/clear_db',utility.DBDelete),('/',views.MainPage)],debug = True) 
22

ahora puede utilizar el almacén de datos de administración para que: https://developers.google.com/appengine/docs/adminconsole/datastoreadmin#Deleting_Entities_in_Bulk

+2

Esta debería ser la respuesta aceptada ahora ... – Lipis

+0

Una publicación de blog que le advierte sobre las ineficiencias de Datastore Admin: http://marram.posterous.com/google-app-engines-datastore-admin-is-terribl –

+0

arriba del enlace muerto, pero puedo agregar que la eliminación masiva de entidades no funciona para mí en este momento (realiza las mociones y da un mensaje eliminado con éxito, pero todas las entidades permanecen). –

1

Sí se puede: Ir al almacén de datos de administración, y luego seleccione el tipo de Entitiy que desee eliminar y haga clic en eliminar. ¡Mapreduce se encargará de eliminar!

-2

en JavaScript, lo siguiente será eliminar todas las entradas de la página:

document.getElementById("allkeys").checked=true; 
checkAllEntities(); 
document.getElementById("delete_button").setAttribute("onclick",""); 
document.getElementById("delete_button").click(); 

dado que está en el administrador de páginas (.../_ ah/admin) con las entidades que desea eliminar .

+0

No, creo que esto elimina todas las entradas. Este es un código de JavaScript que puede ejecutar en la consola de su navegador para enviar las consultas http correctas a las páginas de administración de appengine. – Herbert

+0

La respuesta de Herbert es una forma sugerida de automatizar marginalmente el navegador para marcar las casillas y hacer clic en el botón Eliminar. Si uno fuera a configurar un proyecto de automatización de navegador con Selenium o similar, esta sería una solución legítima pero dolorosamente lenta para eliminar todas las entidades a través de la pantalla de administración, ya que solo elimina como máximo 20 entidades a la vez. –

+0

Lo que dice lalala ... lambda. – Herbert

0

en un CD dev server, una lata al directorio de su aplicación a continuación, ejecutar de esta manera:

dev_appserver.py --clear_datastore=yes . 

Si lo hace, se iniciará la aplicación y borrar el almacén de datos. Si ya tiene otra instancia ejecutándose, la aplicación no podrá unirse a la IP necesaria y, por lo tanto, no podrá iniciarse ... y borrar su almacén de datos.