从集合中删除时观察游标时可能出现的错误 [英] Possible bug when observing a cursor, when deleting from collection

查看:29
本文介绍了从集合中删除时观察游标时可能出现的错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遇到了一个有趣的可能错误,但在这种情况下,它可能是由于我在观察集合时缺少用于删除文档的函数所致.或者我滥用了observe...这很可能是这种情况!

以下是重现我遇到的问题的示例代码.

在撰写本文时我正在使用 devel 分支,所以我不确定这是否适用于 0.3.5

observebug.html

<title>observebug</title><身体>{{>主要的}}<模板名称="main"><h1>显示 Meteor wrt 观察中可能存在的错误的示例</h1><div><p>尝试删除笔记.您会注意到它似乎没有被删除.但是,在服务器上,确实发生了删除.刷新页面以查看删除确实发生了.</p><h2>注意事项:</h2><ul>{{#每个笔记}}{{>note_row}}{{/每个}}

<模板名称="note_row"><li>{{title}} <button name="delete">delete</button></li>

观察bug.js

//客户端如果(流星.is_client){Notes = new Meteor.Collection("notes_collection");Meteor.autosubscribe(函数(){Meteor.subscribe("notes_subscription");});Template.main.notes = function () {返回 Notes.find();};Template.note_row.events = {点击按钮[name='delete']":函数(evt){Meteor.call("deleteNote", this._id, function (error, result) {如果(!错误){console.log("笔记删除成功.");} 别的 {console.log("删除笔记时出错.");}});}};}//服务器如果(流星.is_server){Notes = new Meteor.Collection("notes_collection");流星.方法({deleteNote":函数(note_id){尝试 {Notes.remove(note_id);返回真;}赶上(e){返回假;}}});Meteor.publish("notes_subscription", function () {var notes = Notes.find({}, {sort: {title: 1}});var self = this;//我们在这里所做的基本上是制作一个精确的副本//笔记集合.一个可能的用例是//要实时对复制的集合进行实际更改,//比如在不影响原有字段的情况下添加新字段//收藏.var upsertHandler = 函数(注意,idx){note.some_new_field = 100;self.set("notes_collection", note._id, note);self.flush();};var handle = notes.observe({添加:upsertHandler,改变:upsertHandler,删除:功能(注意,idx){//据我所知,unset 不会删除文档,//只有文档的属性.我不认为有//一种处理实际删除整个文档的方法?self.unset("notes_collection", note._id);self.flush();}});self.onStop(function () {句柄停止();self.flush();});});//添加示例注释流星.启动(函数(){if (Notes.find().count() === 0) {Notes.insert({title: "Note #1"});Notes.insert({title: "Note #2"});Notes.insert({title: "Note #3"});Notes.insert({title: "Note #4"});Notes.insert({title: "Note #5"});Notes.insert({title: "Note #6"});}});}

你会看到什么

当您启动此应用程序时,您会看到 6 个示例注释",每个示例都带有一个删除按钮.我建议你打开你的控制台,这样你就可以看到 console.logs.单击任何笔记上的删除按钮.注释确实会被删除,但这不会反映回客户端.

我怀疑问题在于我如何利用 observe 创建集合的副本(然后我可以在不影响原始集合的情况下对其进行操作).我觉得我需要一个删除整个文档的函数,而不仅仅是一些属性(unset).

http://observebug.meteor.com/

解决方案

所以我已经弄清楚了.我确实需要使用 observe,并且在这方面没有 Meteor 错误.只是对我试图完成的事情所涉及的内容缺乏了解.幸运的是,我在 Meteor 代码本身中找到了一个很好的起点,并编写了 _publishCursor 函数的调整版本,我称之为 publishModifiedCursor.

以下是调整后的项目模板和代码:

观察.html

<title>观察</title><身体>{{>主要的}}<模板名称="main"><h1>尝试发布集合的修改副本的示例</h1><div><h2>注意事项:</h2><ul>{{#每个笔记}}{{>note_row}}{{/每个}}<p><button name="add">添加备注</button></p>

<模板名称="note_row"><li><strong>原标题:</strong><em>{{title}}</em><br/><strong>修改后的标题:</strong><em>{{__modified_title}}</em><br/><button name="delete">delete</button>

observe.js

//客户端如果(流星.is_client){ModifiedNotes = new Meteor.Collection("modified_notes_collection");Meteor.autosubscribe(函数(){Meteor.subscribe("modified_notes_subscription");});Template.main.notes = function () {返回 ModifiedNotes.find();};模板.main.events = {点击按钮[name='add']":函数(evt){Meteor.call("addNote", function (error, result) {如果(!错误){console.log("注释添加成功.");} 别的 {console.log("添加注释时出错.");}});}};Template.note_row.events = {点击按钮[name='delete']":函数(evt){Meteor.call("deleteNote", this._id, function (error, result) {如果(!错误){console.log("笔记删除成功.");} 别的 {console.log("删除笔记时出错.");}});}};}//服务器如果(流星.is_server){Notes = new Meteor.Collection("notes_collection");流星.方法({添加注释":函数(){尝试 {Notes.insert({title: "Note #" + (Notes.find().count() + 1)});返回真;}赶上(e){返回假;}},deleteNote":函数(note_id){尝试 {Notes.remove(note_id);返回真;}赶上(e){返回假;}}});Meteor.publish("modified_notes_subscription", function () {//拉起原来的notes_collectionvar notes = Notes.find({}, {sort: {title: 1}});//发布一个几乎相同的集合,并进行修改this.publishModifiedCursor(notes, "modified_notes_collection", function (note) {note.__modified_title = getTitleModifiedByServer(note.title);退货单;});});var getTitleModifiedByServer = 函数(标题){返回标题 + "!!!";};//添加示例注释流星.启动(函数(){if (Notes.find().count() === 0) {Notes.insert({title: "Note #1"});Notes.insert({title: "Note #2"});Notes.insert({title: "Note #3"});Notes.insert({title: "Note #4"});Notes.insert({title: "Note #5"});Notes.insert({title: "Note #6"});}});_.extend(Meteor._LivedataSubscription.prototype, {publishModifiedCursor:函数(光标,名称,map_callback){var self = this;var 集合 = 名称 ||cursor.collection_name;varobserve_handle = cursor.observe({添加:函数(对象){obj = map_callback.call(self, obj);self.set(collection, obj._id, obj);self.flush();},改变:函数(obj,old_idx,old_obj){变量集 = {};obj = map_callback.call(self, obj);_.each(obj, 函数 (v, k) {如果 (!_.isEqual(v, old_obj[k])) {设置[k] = v;}});self.set(collection, obj._id, set);var dead_keys = _.difference(_.keys(old_obj), _.keys(obj));self.unset(collection, obj._id, dead_keys);self.flush();},删除:函数(old_obj,old_idx){old_obj = map_callback.call(self, old_obj);self.unset(collection, old_obj._id, _.keys(old_obj));self.flush();}});self.complete();self.flush();self.onStop(_.bind(observe_handle.stop,observe_handle));}});}

http://observebug.meteor.com/ 现场观看.最终的效果并不理想,因为它只是一个涉及添加/删除笔记的测试......但是嘿,现在它可以工作了!

希望这能在未来帮助其他人.

I've run into an interesting possible bug, but in this case it may be caused by the lack of a function for me to use to delete a document when observing a collection. Or I am misusing observe... which could very well be the case!

Here is sample code that will reproduce the issue I'm having.

I'm using the devel branch as of this writing, so I'm not sure if this works in 0.3.5

observebug.html

<head>
    <title>observebug</title>
</head>

<body>
    {{> main}}
</body>

<template name="main">
    <h1>Example showing possible bug in Meteor wrt observe</h1>
    <div>
        <p>Try to delete a note. You will notice that it doesn't appear to get deleted. However, on the server, the delete did occur. Refresh the page to see that the delete did in fact occur.</p>
        <h2>Notes:</h2>
        <ul>
            {{#each notes}}
                {{> note_row}}
            {{/each}}
        </ul>
    </div>
</template>

<template name="note_row">
    <li>{{title}} <button name="delete">delete</button></li>
</template>

observebug.js

// CLIENT

if (Meteor.is_client) {
    Notes = new Meteor.Collection("notes_collection");

    Meteor.autosubscribe(function () {
        Meteor.subscribe("notes_subscription");
    });

    Template.main.notes = function () {
        return Notes.find();
    };

    Template.note_row.events = {
        "click button[name='delete']": function (evt) {
            Meteor.call("deleteNote", this._id, function (error, result) {
                if (!error) {
                    console.log("Note deletion successful.");
                } else {
                    console.log("Error when deleting note.");
                }
            });
        }
    };
}

// SERVER

if (Meteor.is_server) {
    Notes = new Meteor.Collection("notes_collection");

    Meteor.methods({
        "deleteNote": function (note_id) {
            try {
                Notes.remove(note_id);
                return true;
            } catch (e) {
                return false;
            }
        }
    });

    Meteor.publish("notes_subscription", function () {
        var notes = Notes.find({}, {sort: {title: 1}});
        var self = this;

        // What we're doing here is basically making an exact duplicate
        // of the notes collection. A possible use-case for this would be
        // to actually make changes to the copied collection on the fly,
        // such as adding new fields without affecting the original
        // collection.
        var upsertHandler = function (note, idx) {
            note.some_new_field = 100;
            self.set("notes_collection", note._id, note);
            self.flush();
        };

        var handle = notes.observe({
            added: upsertHandler,
            changed: upsertHandler,
            removed: function (note, idx) {
                // As far as I can tell, unset won't remove the document,
                // only attributes of the document. I don't think there's
                // a method to handle actually removing a whole document?
                self.unset("notes_collection", note._id);
                self.flush();
            }
        });

        self.onStop(function () {
            handle.stop();
            self.flush();
        });
    });

    // Add example notes
    Meteor.startup(function () {
        if (Notes.find().count() === 0) {
            Notes.insert({title: "Note #1"});
            Notes.insert({title: "Note #2"});
            Notes.insert({title: "Note #3"});
            Notes.insert({title: "Note #4"});
            Notes.insert({title: "Note #5"});
            Notes.insert({title: "Note #6"});
        }
    });
}

What you'll be seeing

When you start this application up, you'll see 6 example "notes", each with a delete button. I suggest having your console open so you can see the console.logs. Click the delete button on any note. The note does get deleted, however this doesn't get reflected back up to the client.

I suspect the issue lies in how I'm leveraging observe to create a copy of the collection (which I can then manipulate without affecting the original collection). I feel like I need a function that deletes a whole document, not just some attributes (unset).

EDIT: See the example in action over at http://observebug.meteor.com/

解决方案

So I've got it figured out. I did indeed need to use observe, and there is no Meteor bug on that end. Just a lack of understanding of what was involved in what I was trying to accomplish. Luckily I found a great starting point within the Meteor code itself and wrote an adjusted version of the _publishCursor function, which I've called publishModifiedCursor.

Here are the adjusted project templates and code:

observe.html

<head>
    <title>observe</title>
</head>

<body>
    {{> main}}
</body>

<template name="main">
    <h1>Example in trying to publish a modified copy of a collection</h1>
    <div>
        <h2>Notes:</h2>
        <ul>
            {{#each notes}}
                {{> note_row}}
            {{/each}}
        </ul>
        <p><button name="add">add note</button></p>
    </div>
</template>

<template name="note_row">
    <li>
        <strong>Original title:</strong> <em>{{title}}</em><br />
        <strong>Modified title:</strong> <em>{{__modified_title}}</em><br />
        <button name="delete">delete</button>
    </li>
</template>

observe.js

// CLIENT

if (Meteor.is_client) {
    ModifiedNotes = new Meteor.Collection("modified_notes_collection");

    Meteor.autosubscribe(function () {
        Meteor.subscribe("modified_notes_subscription");
    });

    Template.main.notes = function () {
        return ModifiedNotes.find();
    };

    Template.main.events = {
        "click button[name='add']": function (evt) {
            Meteor.call("addNote", function (error, result) {
                if (!error) {
                    console.log("Note addition successful.");
                } else {
                    console.log("Error when adding note.");
                }
            });
        }
    };

    Template.note_row.events = {
        "click button[name='delete']": function (evt) {
            Meteor.call("deleteNote", this._id, function (error, result) {
                if (!error) {
                    console.log("Note deletion successful.");
                } else {
                    console.log("Error when deleting note.");
                }
            });
        }
    };
}

// SERVER

if (Meteor.is_server) {
    Notes = new Meteor.Collection("notes_collection");

    Meteor.methods({
        "addNote": function () {
            try {
                Notes.insert({title: "Note #" + (Notes.find().count() + 1)});
                return true;
            } catch (e) {
                return false;
            }
        },
        "deleteNote": function (note_id) {
            try {
                Notes.remove(note_id);
                return true;
            } catch (e) {
                return false;
            }
        }
    });

    Meteor.publish("modified_notes_subscription", function () {
        // Pull up the original notes_collection
        var notes = Notes.find({}, {sort: {title: 1}});

        // Publish a near identical collection, with modifications
        this.publishModifiedCursor(notes, "modified_notes_collection", function (note) {
            note.__modified_title = getTitleModifiedByServer(note.title);
            return note;
        });
    });

    var getTitleModifiedByServer = function (title) {
        return title + "!!!";
    };

    // Add example notes
    Meteor.startup(function () {
        if (Notes.find().count() === 0) {
            Notes.insert({title: "Note #1"});
            Notes.insert({title: "Note #2"});
            Notes.insert({title: "Note #3"});
            Notes.insert({title: "Note #4"});
            Notes.insert({title: "Note #5"});
            Notes.insert({title: "Note #6"});
        }
    });

    _.extend(Meteor._LivedataSubscription.prototype, {
        publishModifiedCursor: function (cursor, name, map_callback) {
            var self = this;
            var collection = name || cursor.collection_name;

            var observe_handle = cursor.observe({
                added: function (obj) {
                    obj = map_callback.call(self, obj);
                    self.set(collection, obj._id, obj);
                    self.flush();
                },
                changed: function (obj, old_idx, old_obj) {
                    var set = {};
                    obj = map_callback.call(self, obj);
                    _.each(obj, function (v, k) {
                        if (!_.isEqual(v, old_obj[k])) {
                            set[k] = v;
                        }
                    });
                    self.set(collection, obj._id, set);
                    var dead_keys = _.difference(_.keys(old_obj), _.keys(obj));
                    self.unset(collection, obj._id, dead_keys);
                    self.flush();
                },
                removed: function (old_obj, old_idx) {
                    old_obj = map_callback.call(self, old_obj);
                    self.unset(collection, old_obj._id, _.keys(old_obj));
                    self.flush();
                }
            });

            self.complete();
            self.flush();

            self.onStop(_.bind(observe_handle.stop, observe_handle));
        }
    });
}

See it live over at http://observebug.meteor.com/. The final effect is underwhelming, since it's just a test involving adding/removing notes... but hey now it works!

Hope this helps someone else out in the future.

这篇关于从集合中删除时观察游标时可能出现的错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
其他开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆