用于删除数据存储中数据的 while 循环

while loop for deleting data in datastore

提问人:zerowords 提问时间:10/8/2012 最后编辑:Communityzerowords 更新时间:10/29/2012 访问量:444

问:

我试图清理和修改此处答案中的代码以满足我的需要,我只想从模型中删除在 get as 中表示的日期之前的数据记录。Reservationsyy,mm,dd

如果我正确地预测了针对路由的操作,那么我的代码最多只会删除 50 (10*nlimit) 数据记录。cleanTable/2012/10/5('/cleanTable/([\d]+)/([\d]+)/([\d]+)', CleanTable)

顺便说一句,原始代码的作者(他可能不再订阅 SO)声称他完成此代码的主要技巧是“在 html 中包含重定向而不是使用 self.redirect”。

我不熟悉之类的,但我的直觉是在 for 循环变成 while 循环后添加一个 or 到它。但是我不清楚引发 StopIteration 异常是否真的会导致迭代停止,或者是否需要更多。另外,我不知道如何修改,以便html在提前退出时顺利结束。raise Exceptionraise Exceptionraise StopIteration

class CleanTable(BaseHandler):

    def get(self, yy,mm,dd):
        nlimit=5
        iyy=int(yy)
        imm=int(mm)
        idd=int(dd)
        param=date(iyy,imm,idd)
        q=Reservations.all(keys_only=True)
        q.filter("date < ", dt(iyy,imm,idd))
        results = q.fetch(nlimit)
        self.response.headers['Content-Type'] = 'text/plain'
        self.response.out.write("""
          <html>
          <meta HTTP-EQUIV="REFRESH" content="url=http://yourapp.appspot.com/cleanTable">
            <body>""")

        try:
            for i in range(10):
                db.delete(results)
                results = q.fetch(nlimit, len(results))
                for r in results:
                    logging.info("r.name: %s" % r.name)
                self.response.out.write("<p> "+str(nlimit)+" removed</p>")
                self.response.out.write("""
                </body>
              </html>""")

        except Exception, inst:
            logging.info("inst: %s" % inst)
            self.response.out.write(str(inst))
python google-app-engine 异常 while-loop

评论

0赞 Sologoub 10/8/2012
这看起来像一个非常丑陋的黑客......根据您的代码,我假设所有实体的密钥都适合您的应用内存。如果这是真的,一个更优雅、更可靠的方法是使用任务队列 - 对于每个键,使用该键启动一个任务以将其删除。然后,工作线程将加载并执行删除。如果这不起作用,请批处理该进程。如果我以后有时间,我会发布示例代码。
0赞 voscausa 10/8/2012
您可以批量删除 ( db.delete(keys) ) 任务中查询的结果实体。任务的时间限制为 10 分钟。如果任务每次运行最多删除 10000 个实体,并且要删除的实体更多,则重复该任务,直到查询结果少于 10000 个实体。您还可以使用 mapreduce: developers.google.com/appengine/docs/python/dataprocessing/...
0赞 zerowords 10/9/2012
我对这段代码的终极使用是在 cron 作业中,当我不知道有多少条记录时扔掉旧记录,所以我认为某种 while 循环是合适的。

答:

0赞 Thanos Makris 10/23/2012 #1

这不是清理模型的最佳方法。更好的方法是获取实体的所有键并创建任务队列。每个队列都会获得一批需要修改的实体的密钥。

另一种方法是创建一个 cron 作业,该作业将查询 x 个最旧的修改实体,修复它们,然后将它们存储回去。

最后,如果你的实体数量如此之多,你也可以考虑使用后端

希望这会有所帮助。

0赞 Middy 10/29/2012 #2

这是我的更新例程,它已经转换了 500.000 个实体。请务必在后端实例上运行它(您可以将 Queue 定位到后端实例)。请注意,我使用的是光标,这是您可以始终如一地遍历数据的唯一方法(永远不要使用偏移量!

Queue queue = QueueFactory.getQueue("grinderQueue");
    queue.add(TaskOptions.Builder.withPayload(new DeferredTask() { //lets generate
        private static final long serialVersionUID = 1L;
        @Override
        public void run() { 
             String cursor = null;
             boolean done = false;
             Date now = new Date(1346763868L * 1000L); // 09/04/2012 

             while(!done) {
                 DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();

                 Query query = new Query("Venue");
                 query.setFilter(new FilterPredicate("timeOfLastUpdate", Query.FilterOperator.LESS_THAN,now));
                 PreparedQuery pq = datastore.prepare(query);
                 FetchOptions fetchOptions = FetchOptions.Builder.withLimit(1000);
                 if(cursor != null)
                     fetchOptions.startCursor(Cursor.fromWebSafeString(cursor));

                 QueryResultList<Entity> results = pq.asQueryResultList(fetchOptions);              


                 List<Entity> updates = new ArrayList<Entity>();
                 List<Entity> oldVenueUpdates = new ArrayList<Entity>();
                 int tuples = 0;
                 for(Entity en : results) {
                    tuples++;
                    try { 
                        if(en.getProperty(Venue.VENUE_KEY) == null)
                            continue;
                        Entity newVenue = new Entity("CPVenue",(String)en.getProperty(Venue.VENUE_KEY));                
                        newVenue.setPropertiesFrom(en);
                        newVenue.removeProperty("timeOfLastVenueScoreCalculation");
                        newVenue.removeProperty("actionsSinceLastVenueScoreCalculation");
                        newVenue.removeProperty("venueImageUrl");
                        newVenue.removeProperty("foursquareId");

                        newVenue.setProperty("geoCell", GeoCellCalculator.calcCellId(Double.valueOf((String)en.getProperty("lng")), Double.valueOf((String)en.getProperty("lat")),8)); 
                        newVenue.setProperty(Venue.TIME_SINCE_LAST_UPDATE, new Date());
                        updates.add(newVenue);

                        Venue v = new Venue(newVenue);

                        //Set timestamp on Venue
                        en.setProperty("timeOfLastUpdate", now);
                        oldVenueUpdates.add(en);

                    }catch(Exception e) {
                        logger.log(Level.WARNING,"",e);             
                    }
                 }
                done = tuples == 0;
                tuples = 0;
                if(results.getCursor() != null)
                    cursor = results.getCursor().toWebSafeString();
                else
                    done = true;

                System.out.println("Venue Conversion LOOP updates.. " + updates.size() + " cursor " + cursor);
                datastore.put(updates);
                datastore.put(oldVenueUpdates);
             }
             System.out.println("Venue Conversion DONE");
        }}));

评论

0赞 zerowords 10/30/2012
我不知道在后端实例上运行它意味着什么。我假设使用的语言是 Java,但我的应用程序是 gae 上的 python。我还能使用你们的代码吗?
0赞 Middy 11/1/2012
好吧,您需要将其转换为python。但它都是 API 调用,所以应该很容易。您可以使用目标(只是一个名称)定义一个任务 QUEUE,该名称应该是后端实例,您也需要配置该实例
0赞 zerowords 11/2/2012
我必须学习才能做到这一点,这可能需要一些时间。我会在文档中查找.我会尽量记得回复你。backend instance