了解virtual属性

virtual是虚拟属性的意思,即原来Schema定义里是不存该属性,后来通过virtual方法赋予的属性。可以这样理解,Schema定义的属性是要保存到数据库里,而virtual属性基于已有属性做的二次定义,这样可以提供更好的语义上的处理。

  1. 模型属性 = Schema定义的属性 + virtual属性

下面举几个例子

实例1

需求是根据手机号和邀请码来判断该用户是否是有效的,继而完成之后的操作步骤。而数据库只有手机号和邀请码二个字段,那么我们需要怎么做呢?最简单的办法是把状态存在数据库里,加一个字段的事儿。这种方法很好,但对于一个代码洁癖爱好者来说是无法忍受的,明明手机号和邀请码已经可以表达了业务含义,为啥还要多加一个字段呢?

更好的做法就是使用virtual属性,既增加属性,又没有数据库冗余

  1. UserSchema.virtual('is_valid').get(function(){
  2. if(this.phone_number == undefined | this.invite_code == undefined){
  3. return false;
  4. }
  5. return this.invite_code.length >= 2 && this.phone_number > 10000000
  6. });

看一下具体测试,主要针对2种情况

  • 当同时有invite_code和phone_number
  • 当只有phone_number时
  1. import test from 'ava';
  2. // 1、引入`mongoose connect`
  3. require('../connect');
  4. // 2、引入`User` Model
  5. const User = require('./user');
  6. // 3、定义`user` Entity
  7. const user_with_invite_code = new User({
  8. username: 'i5ting',
  9. password: '0123456789',
  10. invite_code : 'xxxx',
  11. phone_number: 18611112222
  12. });
  13. const user_with_no_invite_code = new User({
  14. username: 'i5ting',
  15. password: '0123456789',
  16. phone_number: 18611112222
  17. });
  18. test('#is_valid() === true if user_with_invite_code', t => {
  19. user_with_invite_code.save((err, u) => {
  20. t.true(u.is_valid)
  21. })
  22. });
  23. test('#is_valid() === false if user_with_no_invite_code', t => {
  24. user_with_no_invite_code.save((err, u) => {
  25. t.false(user.is_valid)
  26. })
  27. });

执行

  1. $ cd db && ava -v virtual/test.js
  2. 数据库连接成功
  3. #is_valid() === true if user_with_invite_code
  4. #is_valid() === false if user_with_no_invite_code
  5. 2 tests passed

实例2

我们给模型增加了一个字段叫address,即用户的地址。一般是保存省、市、区(县)、详细,假设他们是通过-作为分隔符存的,但是有的时候保存的数据未必如此,从address字段里,我们如果要获得任意省或市要怎么做呢?

把表拆了,重写?那太不划算了,好的做法是使用virtual属性对字段进行二次加工、转换、格式化等等操作。

  1. function _sp (str) {
  2. return str.replace(/\n/g,'').trim().split('-');
  3. }
  4. UserSchema.virtual('province').get(function () {
  5. var array = _sp(this.address);
  6. return array[0];
  7. });
  8. UserSchema.virtual('city').get(function () {
  9. var array = _sp(this.address);
  10. return array[1];
  11. });
  12. UserSchema.virtual('county').get(function () {
  13. var array = _sp(this.address);
  14. return array[2];
  15. });
  16. UserSchema.virtual('detail_address').get(function () {
  17. var array = _sp(this.address);
  18. return array[2];
  19. });

这样就可以在不变表结构的情况下,增加province,city,county,detail_address,这是非常便捷的,无入侵的。

测试代码

  1. import test from 'ava';
  2. // 1、引入`mongoose connect`
  3. require('../connect');
  4. // 2、引入`User` Model
  5. const User = require('../user/virtual/user2');
  6. // 3、定义`user` Entity
  7. const user = new User({
  8. username: 'i5ting',
  9. password: '0123456789',
  10. address : '天津-天津-东丽区-空港商务园'
  11. });
  12. test.before.cb(t => {
  13. User.remove({}, (err, result) => {
  14. t.end()
  15. })
  16. })
  17. test('#province city county detail_address', t => {
  18. user.save((err, u) => {
  19. t.is(u.province, '天津')
  20. t.is(u.city, '天津')
  21. t.is(u.county, '东丽区')
  22. t.is(u.detail_address, '空港商务园')
  23. })
  24. });

执行

  1. $ cd db && ava -v virtual/address.js
  2. 数据库连接成功
  3. #province city county detail_address
  4. 1 test passed