Eliminar todos los datos de un tipo en Google App Engine


Me gustaría borrar todos los datos de un tipo específico en Google App Engine. ¿Qué es el la mejor manera de hacer esto? Escribí un script de eliminación (hack), pero ya que hay tantos datos es se acabó el tiempo después de unos cientos de discos.

Author: user8548, 2008-09-20

18 answers

La respuesta oficial de Google es que tienes que eliminar en trozos repartidos en múltiples solicitudes. Puede usar AJAX, meta refresh , o solicitar su URL desde un script hasta que no queden entidades.

 6
Author: Alexander Kojevnikov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-23 02:44:10

Actualmente estoy eliminando 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

Desde el terminal, corro curl-N http://...

 27
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-06-21 11:41:24

Ahora puede usar el Administrador del almacén de datos para eso: https://developers.google.com/appengine/docs/adminconsole/datastoreadmin#Deleting_Entities_in_Bulk

 23
Author: Pieter Herroelen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-09-24 07:04:49

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 saltarme la discusión sobre los tamaños de los índices y cómo traducen 6 GB de datos a 35 GB de almacenamiento (que se facturan). Esa es otra historia, pero tienen formas de evitar eso: limitar el número de propiedades para crear índices (índices generados automáticamente), etc.

La razón por la que decidí escribir este post es que necesito "bombardear" a todos mis tipos en una caja de arena. Leí sobre ello 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 eliminar los registros (tal vez 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 easy query). Sé que podría ir por 500 entidades, pero luego empecé recibiendo tasas muy altas de falla(función de eliminación re).

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

 10
Author: babakm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-12-10 17:41:07

Intenta usar App Engine Console entonces ni siquiera tienes que implementar ningún código especial

 9
Author: Sam,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-11-14 23:58:35

Presumiblemente tu hackeo fue 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 suficientes datos, usted va a golpear el tiempo de espera de la solicitud 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 todos los datos se borraron; bastante fácil de hacer, pero difícilmente ideal.

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

Al probar, he tenido que purgar mi base de datos en el inicio para deshacerse de los datos existentes.

Yo inferiría de esto que Google opera bajo el principio de que el disco es barato, y por lo tanto los datos son típicamente huérfanos (índices a datos redundantes reemplazados), en lugar de eliminados. Dado que hay una cantidad fija de datos disponibles para cada aplicación en este momento (0.5 GB), eso no es mucha ayuda para los usuarios que no son de Google App Engine.

 9
Author: Jason Etheridge,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-04-17 09:49:33

He probado db.delete (results) y App Engine Console, y ninguno de ellos parece estar funcionando para mí. Eliminar manualmente las entradas del Visor de datos (límite aumentado hasta 200) tampoco funcionó ya que he subido más de 10000 entradas. Terminé escribiendo este script

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 redireccionamiento en html en lugar de usar self.redirigir. Estoy listo para esperar toda la noche para deshacerse de todos los datos en mi tabla. Con suerte, el equipo de GAE hará que sea más fácil colocar tablas en el futuro.

 7
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-11-27 06:00:44

La forma más rápida y eficiente de manejar la eliminación masiva en el almacén de datos es mediante el uso de la nueva API mapper anunciada en la última E/S de Google.

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

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

En Java deberías echar un vistazo a este artículo 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());
}
 5
Author: systempuntoout,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-09-08 20:49:35

Un consejo. Le 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 se puede limitar a unos pocos cientos a la vez.

 4
Author: RyanW,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-09-09 15:47:34

Desafortunadamente, no hay manera de hacer fácilmente una eliminación masiva. 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 delete devuelva una redirección 302 cada vez que haya más datos que eliminar, y luego obtenerlo con "wget max max-redirección=10000" (o algún otro gran número).

 3
Author: Nick Johnson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-20 19:03:04

Con django, url de configuración:

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

Vista de 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)))

Luego ejecute en powershell:

$client = new-object System.Net.WebClient
$client.DownloadString("http://your-app.com/Model/bdelete/?limit=400")
 1
Author: Janusz Skonieczny,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-03-24 21:19:21

Si estás usando Java / JPA puedes hacer algo como esto:

    em = EntityManagerFactoryUtils.getTransactionalEntityManager(entityManagerFactory)
    Query q = em.createQuery("delete from Table t");
    int number = q.executeUpdate();

La información de Java / JDO se puede encontrar aquí: http://code.google.com/appengine/docs/java/datastore/queriesandindexes.html#Delete_By_Query

 1
Author: bebeastie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-01-05 03:12:37

Sí puedes: Vaya a Administrador del almacén de datos y, a continuación, seleccione el tipo de entidad que desea eliminar y haga clic en Eliminar. Mapreduce se encargará de borrar!

 1
Author: David,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-12-09 11:42:36

En un servidor de desarrollo , uno puede cd al directorio de su aplicación y luego ejecutarlo de la siguiente manera:

dev_appserver.py --clear_datastore=yes .

Al hacerlo, se iniciará la aplicación y se borrará el almacén de datos. Si ya tiene otra instancia en ejecución, la aplicación no podrá vincularse a la IP necesaria y, por lo tanto, no se iniciará...y para limpiar su almacén de datos.

 1
Author: hamx0r,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-11-27 20:11:34

Puede usar las colas de tareas para eliminar fragmentos de, por ejemplo, 100 objetos. La eliminación de 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 utilizar la herramienta bulkloader que funciona con CSV, pero la documentación no cubre Java. Estoy usando GAE Java y mi estrategia para las eliminaciones implica tener 2 servlets, uno para hacer la eliminación real y otro para cargar las colas de tareas. Cuando quiero hacer una eliminación, corro la cola cargando servlet, carga las colas y luego GAE va a trabajar ejecutando todas las tareas en la cola.

Cómo hacerlo: Cree un servlet que elimine un pequeño número de objetos. Agregue el servlet a sus colas de tareas. Ir a casa o trabajar en otra cosa ;) Compruebe el almacén de datos de vez en cuando ...

Tengo un almacén de datos con alrededor de 5000 objetos que purgo cada semana y tarda aproximadamente 6 horas en limpiarse, así que corro la tarea el viernes por la noche. Utilizo la misma técnica para cargar mis datos a granel que son unos 5000 objetos, con una docena de propiedades.

 0
Author: Iggy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-03-18 15:07:46

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)
 0
Author: Timothy Jordan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-07-31 01:41:22

Gracias a todos, tengo lo que necesito. : D
Esto puede ser útil si tiene muchos modelos de base de datos para eliminar, puede enviarlo en su terminal. Y también, puede administrar la lista de eliminación en DB_MODEL_LIST usted mismo.
Suprímase DB_1:

python bulkdel.py 10 DB_1

Suprímase todo DB:

python bulkdel.py 11

Aquí está el bulkdel.py archivo:

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í está 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, debe asignar el enlace al modelo en un archivo (como main.py in GAE), ;)
En caso de que algunos chicos como yo lo necesiten en detalle, aquí es parte de main.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)
 0
Author: Kjuly,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-09-03 07:27:45

En javascript, lo siguiente eliminará todas las entradas para on page:

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

Dado que estás en la página de administración (.../_ah/admin) con las entidades que desea eliminar.

 -2
Author: Herbert,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-11-29 13:06:19