Mongoose 唯一如果不为空并且如果状态

Mongoose unique if not null and if state

我有一个这样的唯一索引

    code: {
        type: String,
        index: {
            unique: true,
            partialFilterExpression: {
                code: { $type: 'string' }
            }
        },
        default: null
    },
    state: { type: Number, default: 0 },

但是state为2时(已存档)我想保留代码,但应该可以重用代码,所以state为2时不能唯一。 我有什么方法可以做到这一点吗?

使用索引无法做到这一点。即使您对 codestate 使用复合索引,仍然会有

的情况

新建文档

{
  code: 'abc',
  state: 0
}

归档文件

{
  code: 'abc',
  state: 2

}

现在,尽管您拥有相同的代码,但您将无法存档新文档或取消存档已存档的文档。

你可以这样做

const checkCode = await this.Model.findOne({code:'abc', active:0})
if(checkCode){
  throw new Error('Code has to be unique')
}
else{
 .....do something
}

这是可能的,尽管它是通过此处记录的变通方法实现的 https://jira.mongodb.org/browse/SERVER-25023

在 MongoDB 4.7 中,您将能够对同一字段应用不同的索引选项,但现在您可以添加一个不存在的字段来分隔两个索引。

这是一个使用变通方法的示例。

(async () => {
  const ItemSchema = mongoose.Schema({
    code: {
      type: String,
      default: null
    },
    state: {
      type: Number,
      default: 0,
    },
  });

  // Define a unique index for active items
  ItemSchema.index({code: 1}, {
    name: 'code_1_unique',
    partialFilterExpression: {
      $and: [
        {code: {$type: 'string'}},
        {state: {$eq: 0}}
      ]
    },
    unique: true
  })

  // Defined a non-unique index for non-active items
  ItemSchema.index({code: 1, nonExistantField: 1}, {
    name: 'code_1_nonunique',
    partialFilterExpression: {
      $and: [
        {code: {$type: 'string'}},
        {state: {$eq: 2}}
      ]
    },
  })

  const Item = mongoose.model('Item', ItemSchema)

  await mongoose.connect('mongodb://localhost:27017/so-unique-compound-indexes')
  
  // Drop the collection for test to run correctly
  await Item.deleteMany({})

  // Successfully create an item
  console.log('\nCreating a unique item')
  const itemA = await Item.create({code: 'abc'});


  // Throws error when trying to create with the same code
  await Item.create({code: 'abc'})
    .catch(err => {console.log('\nThrowing a duplicate error when creating with the same code')})


  // Change the active code
  console.log('\nChanging item state to 2')
  itemA.state = 2; 
  await itemA.save();


  // Successfully created a new doc with sama code
  await Item.create({code: 'abc'})
    .then(() => console.log('\nSuccessfully created a new doc with sama code'))
    .catch(() => console.log('\nThrowing a duplicate error'));
  

  // Throws error when trying to create with the same code 
  Item.create({code: 'abc'})
  .catch(err => {console.log('\nThrowing a duplicate error when creating with the same code again')})
})();