Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/node.js/37.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Node.js 猫鼬_Node.js_Mongodb_Mongoose - Fatal编程技术网

Node.js 猫鼬

Node.js 猫鼬,node.js,mongodb,mongoose,Node.js,Mongodb,Mongoose,我已经搜索了一段时间,没有找到任何好的答案。我有一个n-deep树,我将它存储在DB中,我想填充所有的父树,所以最后我得到了完整的树 node -parent -parent . . -parent 到目前为止,我填充到第2级,正如我提到的,我需要进入第n级 Node.find().populate('parent').exec(function (err, items) { if (!err) { Node.populate(items, {pa

我已经搜索了一段时间,没有找到任何好的答案。我有一个n-deep树,我将它存储在DB中,我想填充所有的父树,所以最后我得到了完整的树

node
 -parent
  -parent
    .
    .
    -parent
到目前为止,我填充到第2级,正如我提到的,我需要进入第n级

Node.find().populate('parent').exec(function (err, items) {
   if (!err) {
     Node.populate(items, {path: 'parent.parent'}, function (err, data) {
       return res.send(data);
     });
   } else {
     res.statusCode = code;
     return res.send(err.message);
   }
 });
只是不要:)

没有什么好办法可以做到这一点。即使你做了一些MapReduce,如果你有或者需要的话,它也会有糟糕的性能和切分问题

Mongo作为NoSQL数据库非常适合存储树文档。如果没有太多的“查找特定叶”查询,您可以存储整个树,然后使用map reduce从中获取一些特定叶。如果这不适合您,请选择两个系列:

  • 简化的树结构:
    {u id:“tree1”,树:{1:[2,{3:[4,{5:6},7]}}
    。数字只是节点的ID。这样,您将在一个查询中获得整个文档。然后,您只需提取所有ID并运行第二个查询

  • 节点:
    {u id:1,数据:“something”}
    {u id:2,数据:“something”}

  • 然后,您可以编写简单的循环函数,该函数将第一个集合中的节点ID替换为第二个集合中的数据。2查询和简单的客户端处理

    小更新:

    您可以扩展第二个集合,使其更加灵活:

    {u id:2,数据:“某物”,孩子:[3,7],父母:[1,12,13]}


    这样你就可以从任何一片叶子开始搜索。然后,使用map reduce到达树的这一部分的顶部或底部。

    另一种方法是利用
    Model.populate()
    返回一个承诺,并且您可以用另一个承诺来履行承诺

    您可以通过以下方式递归填充相关节点:

    Node.findOne({ "_id": req.params.id }, function(err, node) {
      populateParents(node).then(function(){
        // Do something with node
      });
    });
    
    populateParents
    可能如下所示:

    var Promise = require('bluebird');
    
    function populateParents(node) {
      return Node.populate(node, { path: "parent" }).then(function(node) {
        return node.parent ? populateParents(node.parent) : Promise.fulfill(node);
      });
    }
    
    这不是最有效的方法,但如果您的N很小,这将起作用。

    您现在可以(使用)

    简单的选择,没有猫鼬:

    function upsert(coll, o){ // takes object returns ids inserted
        if (o.children){
            return Promise.all(o.children.map(i=>upsert(coll,i)))
                .then(children=>Object.assign(o, {children})) // replace the objects children by their mongo ids
                .then(o=>coll.insertOne(o))
                .then(r=>r.insertedId);
        } else {
            return coll.insertOne(o)
                .then(r=>r.insertedId);
        }
    }
    
    var root = {
        name: '1',
        children: [
            {
                name: '2'
            },
            {
                name: '3',
                children: [
                    {
                        name: 'foo'
                    },
                    {
                        name: 'bar'
                    }
                ]
            }
        ]
    }
    upsert(mycoll, root)
    
    
    const populateChildren = (coll, _id) => // takes a collection and a document id and returns this document fully nested with its children
      coll.findOne({_id})
        .then(function(o){
          if (!o.children) return o;
          return Promise.all(o.children.map(i=>populateChildren(coll,i)))
            .then(children=>Object.assign(o, {children}))
        });
    
    
    const populateParents = (coll, _id) => // takes a collection and a document id and returns this document fully nested with its parents, that's more what OP wanted
      coll.findOne({_id})
        .then(function(o){
          if (!o.parent) return o;
          return populateParents(coll, o.parent))) // o.parent should be an id
            .then(parent => Object.assign(o, {parent})) // replace that id with the document
        });
    

    我尝试了@fzembow的解决方案,但它似乎从最深的路径返回了对象。在我的例子中,我需要递归地填充一个对象,然后返回完全相同的对象。我是这样做的:

    // Schema definition
    const NodeSchema = new Schema({
            name: { type: String, unique: true, required: true },
            parent: { type: Schema.Types.ObjectId, ref: 'Node' },
        });
    
    const Node =  mongoose.model('Node', NodeSchema);
    
    
    
    
    
    // method
    const Promise = require('bluebird');
    
    const recursivelyPopulatePath = (entry, path) => {
        if (entry[path]) {
            return Node.findById(entry[path])
                .then((foundPath) => {
                    return recursivelyPopulatePath(foundPath, path)
                        .then((populatedFoundPath) => {
                            entry[path] = populatedFoundPath;
                            return Promise.resolve(entry);
                        });
                });
        }
        return Promise.resolve(entry);
    };
    
    
    //sample usage
    Node.findOne({ name: 'someName' })
            .then((category) => {
                if (category) {
                    recursivelyPopulatePath(category, 'parent')
                        .then((populatedNode) => {
                            // ^^^^^^^^^^^^^^^^^ here is your object but populated recursively
                        });
                } else {
                    ...
                }
            })
    

    小心,这不是很有效。如果您需要经常或深入地运行此类查询,那么您应该重新考虑您的设计,现在使用
    mongoose4
    ,这是可以做到的。现在,您可以递归到比单个级别更深的级别

    示例

    User.findOne({ userId: userId })
        .populate({ 
            path: 'enrollments.course',
            populate: {
                path: 'playlists',
                model: 'Playlist',
                populate: {
                    path: 'videos',
                    model: 'Video'
                }
            } 
        })
        .populate('degrees')
        .exec()
    

    您可以从中找到Mongoose Deep Populate的官方文档。

    这是caub回答的更直接的方法,也是很好的解决方案。一开始我觉得有点难以理解,所以我把这个版本放在一起

    重要的是,您需要同时安装“findOne”和“find”中间件挂钩才能使此解决方案正常工作*

    const mongoose = require('mongoose');
    
    const NodeSchema = new mongoose.Schema({
        children: [mongoose.Schema.Types.ObjectId],
        name: String
    });
    
    const autoPopulateChildren = function (next) {
        this.populate('children');
        next();
    };
    
    NodeSchema
        .pre('findOne', autoPopulateChildren)
        .pre('find', autoPopulateChildren)
    
    
    const Node = mongoose.model('Node', NodeSchema)
    
    const root = new Node({ name: '1' })
    const main = new Node({ name: '3' })
    const foo = new Node({ name: 'foo' })
    
    root.children = [main]
    main.children = [foo]
    
    
    mongoose.connect('mongodb://localhost:27017/try', { useNewUrlParser: true }, async () => {
        await Node.remove({});
    
        await foo.save();
        await main.save();
        await root.save();
    
        const result = await Node.findOne({ name: '1' });
    
        console.log(result.children[0].children[0].name);
    });
    
    *此外,模型定义必须在中间件定义之后*

    const mongoose = require('mongoose');
    
    const NodeSchema = new mongoose.Schema({
        children: [mongoose.Schema.Types.ObjectId],
        name: String
    });
    
    const autoPopulateChildren = function (next) {
        this.populate('children');
        next();
    };
    
    NodeSchema
        .pre('findOne', autoPopulateChildren)
        .pre('find', autoPopulateChildren)
    
    
    const Node = mongoose.model('Node', NodeSchema)
    
    const root = new Node({ name: '1' })
    const main = new Node({ name: '3' })
    const foo = new Node({ name: 'foo' })
    
    root.children = [main]
    main.children = [foo]
    
    
    mongoose.connect('mongodb://localhost:27017/try', { useNewUrlParser: true }, async () => {
        await Node.remove({});
    
        await foo.save();
        await main.save();
        await root.save();
    
        const result = await Node.findOne({ name: '1' });
    
        console.log(result.children[0].children[0].name);
    });
    

    这可能要晚很多,但mongoose对此有一些文档:

    我认为第一个更适合你,因为你正在寻找填充父母

    使用该解决方案,您可以通过一个正则表达式查询,搜索与指定输出树匹配的所有文档

    您可以使用此架构设置文档

    树:{
    名称:String,
    路径:字符串
    }
    
    路径字段将是树中的绝对路径:

    /mens
    /mens/shoes
    /mens/shoes/boots
    /womens
    /womens/shoes
    /womens/shoes/boots
    
    例如,您可以通过一个查询搜索节点“/mens/shoes”的所有子节点:

    wait Tree.find({path://^\/mens/shoes})
    
    它将返回路径以/mens/shoes开头的所有文档:

    /mens/shoes
    /mens/shoes/boots
    

    然后,您只需要一些客户端逻辑就可以将其排列成树结构(MapReduce)

    谢谢。这不是我想要的。但无论如何还是要谢谢你。我会考虑的。Mongoose deep populate是一个更好的解决方案。你可以在我下面的答案中找到一个例子。这个方法非常有效,我一直在寻找这个解决方案。谢谢你,你应该为此获得一个奖项谢谢你,你救了我一天。你能告诉我在哪里可以找到这方面的参考资料吗?感谢您,但文档不够全面(我的意思是:)这是一个救命稻草。多谢。