如何存储不同类型的实体

时间:2015-08-01 14:34:09

标签: mongodb

我试图找到存储不同类型实体的最佳方法。

我有一个通用实体Person和两个类型的人PersonType1PersonType2,它从Person实体继承其字段。

我需要将此人数据存储到mongo db。

创建单个集合或集合来绘制每种类型是否更好?

1 个答案:

答案 0 :(得分:2)

你在这里基本上讨论的是Polymorphism的一般持久性,其中类与不同属性中的基类不同。

这通常由数据持久性的常见实现中的鉴别器模式处理,因此通常在相同的集合中,特别是在面向文档的数据库中,它可以很好地处理不同的属性。

所以一般来说,在MongoDB下,存储在单个集合中的概念是为了这些"继承"对象基本上是个好主意。在同一个集合中拥有共享公共属性或其他相关数据的东西是有好处的,因为有必要进行演示。

不是认可,但可以通过.discriminator()库可用的mongoose构造函数演示一个简单的示例。因此,作为一个好处的例子,它(适合我)适合快速演示:

var util = require('util'),
    async = require('async'),
    mongoose = require('mongoose'),
    Schema = mongoose.Schema;

mongoose.connect('mongodb://localhost/school');

// Util function for base type
function AbstractPersonSchema() {
  Schema.apply(this,arguments);

  this.add({
    name: String,
    age: Number,
    sex: { type: String, enum: [ 'Male', 'Female' ] }
  });

}

util.inherits( AbstractPersonSchema, Schema );


// Schema definitions
var personSchema = new AbstractPersonSchema();

var studentSchema = new AbstractPersonSchema({
  courses: [String]
});

var teacherSchema = new AbstractPersonSchema({
  department: String,
  teaches: [String]
});


// Model assignments
var Person    = mongoose.model( 'Person', personSchema ),
    Student   = Person.discriminator( 'Student', studentSchema ),
    Teacher   = Person.discriminator( 'Teacher', teacherSchema );

var normalPeople = [
  { "name": "Bill",   "age": 48, "sex": "Male"   },
  { "name": "Sarah",  "age": 24, "sex": "Female" }
];

var students = [
  {
    "name": "Ted",
    "age": 21,
    "sex": "Male",
    "courses": ["Math","Science","English"]
  },
  {
    "name": "Julie",
    "age": 22,
    "sex": "Female",
    "courses": ["Art","History","English"]
  }
];

var teachers = [
  {
    "name": "Harry",
    "age": 35,
    "sex": "Male",
    "department": "Maths",
    "teaches": ["Math","Science","English"]
  },
  {
    "name": "Sally",
    "age": 32,
    "sex": "Female",
    "department": "History",
    "teaches": ["English","History"]
  }
];

async.series(
  [
    // Example cleanup
    function(callback) {
      Person.remove({},callback);
    },

    function(callback) {
      async.parallel(
        [
          function(callback) {
            async.each(normalPeople,function(person,callback) {
              Person.create(person,callback);
            },callback);
          },
          function(callback) {
            async.each(students,function(student,callback) {
              Student.create(student,callback);
            },callback);
          },
          function(callback) {
            async.each(teachers,function(teacher,callback) {
              Teacher.create(teacher,callback);
            },callback);
          }
        ],
        callback
      );
    },

    function(callback) {
      console.log("Teachers per subject");
      Teacher.aggregate(
        [
          { "$unwind": "$teaches" },
          { "$group": {
            "_id": "$teaches",
            "count": { "$sum": 1 }
          }}
        ],
        function(err,result) {
          if (err) callback(err);
          console.log(result);
          callback();
        }
      );
    },

    function(callback) {
      console.log("Students and teachers in subject");
      Person.aggregate(
        [
          { "$match": { "__t": { "$in": ["Teacher","Student"] } } },
          { "$project": {
            "name": 1,
            "__t": 1,
            "subject": {
              "$cond": [
                { "$eq": [ "$__t", "Teacher" ] },
                "$teaches",
                "$courses"
              ]
            }
          }},
          { "$unwind": "$subject" },
          { "$group": {
            "_id": "$subject",
            "teachers": {
              "$addToSet": {
                "$cond": [
                  { "$eq": [ "$__t", "Teacher" ] },
                  "$name",
                  false
                ]
              }
            },
            "students": {
              "$addToSet": {
                "$cond": [
                  { "$eq": [ "$__t", "Student" ] },
                  "$name",
                  false
                ]
              }
            }
          }},
          { "$project": {
            "teachers": { "$setDifference": [ "$teachers", [false] ] },
            "students": { "$setDifference": [ "$students", [false] ] }
          }}
        ],
        function(err,results) {
          if (err) callback(err);
          console.log(results);
          callback();
        }
      );
    },

    function(callback) {
      console.log("Average age of students");
      Student.aggregate(
        [
          { "$group": {
            "_id": null,
            "average_age": { "$avg": "$age" }
          }}
        ],
        function(err,results) {
          if (err) throw err;
          console.log(results);
          callback();
        }
      )
    },

    function(callback) {
      console.log("Average age of normal people");
      Person.aggregate(
        [
          { "$match": { "__t": { "$exists": false } } },
          { "$group": {
            "_id": null,
            "average_age": { "$avg": "$age" }
          }}
        ],
        function(err,results) {
          if (err) throw err;
          console.log(results);
          callback();
        }
      );
    }
  ],
  function(err) {
    if (err) throw err;
    mongoose.disconnect();
  }
);

产生输出:

Teachers per subject
[ { _id: 'History', count: 1 },
  { _id: 'English', count: 2 },
  { _id: 'Science', count: 1 },
  { _id: 'Math', count: 1 } ]
Students and teachers in subject
[ { _id: 'History', teachers: [ 'Sally' ], students: [ 'Julie' ] },
  { _id: 'Art', teachers: [], students: [ 'Julie' ] },
  { _id: 'English',
    teachers: [ 'Sally', 'Harry' ],
    students: [ 'Julie', 'Ted' ] },
  { _id: 'Science', teachers: [ 'Harry' ], students: [ 'Ted' ] },
  { _id: 'Math', teachers: [ 'Harry' ], students: [ 'Ted' ] } ]
Average age of students
[ { _id: null, average_age: 21.5 } ]
Average age of normal people
[ { _id: null, average_age: 36 } ]

使用鉴别器模式时要特别注意的是有几个"模型"那里的定义是针对“人”,“学生”等#39;和老师'。这些中的每一个都是从它自己的属性中实例化的,继承自基础' Person'定义

在构建和存储时,这里采用的方法是所有项目基本上都存储在同一个物理集合中("人员和#34;复数形式),但都具有不同的属性,并且有一个定义根据标准集合分配.discriminator()方法的编码。

这实际上是为每个继承的"类"添加了一个字段。代表"模型"在这里输入:

{ "name": "Ted", "__t": "Student" }

现在任务落到了库实现中,以便读取"鉴别器"值和正确分配定义的"类/模型"从数据库中读取信息。有了这些数据,这就可以让库正确地将数据作为类型化对象强制转换为目标类。

实现也可以从示例也在此处显示的内容中受益。请注意使用“人物”,“学生”和“教师”中的每一个使用不同的查询。模特在这里。无论教师在哪里'或者'学生'模型被称为,图书馆可以自动地"过滤掉与其鉴别器类型不匹配的结果。这对于查询操作特别适用于他们希望操作的对象的类/模型类型非常有用,因此无需在不需要的情况下使用嘈杂类型检查来模糊代码。

另一方面,基类/模型仍可用于在类型鉴别器值上编码的特定检查。这允许您通常检查所有继承的类型,或者在广义上处理单一继承路径所需的信息子集。

因此,在需要这种相关性的情况下,绝对有意义地存储在同一个集合中并利用鉴别器模式。只有当你根本不打算进行这样的分析时,将存储分成单独的集合才有意义。

请记住,这是MongoDB,而不是关系数据库。如果你想使用数据"所有在一起"然后你设计保持它#34;所有在一起"因为你无法执行联接,而你需要围绕这个概念进行设计。