不允许使用$ exists进行Upsert:美元($)前缀字段对存储无效

时间:2017-07-09 22:49:41

标签: mongodb meteor mongodb-query

此Meteor服务器端(Mongo 1.1.18)尝试根据所示的选择器插入文档,但出现以下错误:

  myCol.upsert({name: 'sam', job: {$exists: false}}, {$set: {parents: ['jack', 'jacky']}});
  

MongoError:美元($)前缀字段' $存在'在' job。$ exists'对存储无效。

如何插入此选定文档?或者如果它不存在就创建它? THX

1 个答案:

答案 0 :(得分:1)

原因是因为"upsert" MongoDB正试图在新创建的对象中分配作为“值”提供的任何“查询”参数。由于您无法使用$命名属性,因此它会尝试将字段“作业”创建为{ "job": { "$exists": true } },就像您在查询参数中提供的那样。

要避免这种情况,请通过指定$setOnInsert告诉MongoDB在创建时实际使用哪些字段。至少对于普通的MongoDB集合来说就是这种情况。因此,对于“meteor”,您应该访问.rawCollection()而不是“

myCol.rawCollection().update(
  {name: 'sam', job: {$exists: false}},
  {
   $setOnInsert: { name: 'sam' },
   $set: {parents: ['jack', 'jacky']}
  },
  { upsert: true }
)

请注意,执行此操作时,这不会使用各种其他流星默认值,例如_id的值,因此您可能需要使用备用方法。

此处的替代方法是使用$where来评估针对文档的JavaScript条件,以查看"job"字段不存在。实际上,这甚至不会从流星集合实现中“转发”到“upsert”:

myCol.upsert(
  {name: 'sam', '$where': "!this.hasOwnProperty('job')" },
  {
   $set: {parents: ['jack', 'jacky']}
  }
)

为了证明这一点,我启动了一个简单的流星项目,除了添加集合并在服务器启动时执行代码之外没有其他任何变化。所以基本上在一个改变server/main.js的新项目如下:

import { Meteor } from 'meteor/meteor';
import { People } from '../imports/people.js';

Meteor.startup(() => {
  // code to run on server at startup

  People.upsert(
    { name: 'john', '$where': "!this.hasOwnProperty('job')" },
    {
      '$set': { parents: [ 'jack', 'jacky' ] }
    }
  );

  People.rawCollection().update(
    { name: 'bill', job: { '$exists': false }  },
    {
      '$setOnInsert': { name: 'bill' },
      '$set': { parents: [ 'jack', 'jacky' ] }
    },
    { 'upsert': true }
  )

});

imports/people.js

import { Mongo } from 'meteor/mongo';

export const People = new Mongo.Collection('people');

文档在集合中创建,没有出现任何错误:

{
        "_id" : "irDWFLdACjNKYGEcN",
        "name" : "john",
        "parents" : [
                "jack",
                "jacky"
        ]
}
{
        "_id" : ObjectId("596489d0902edee769372ac6"),
        "name" : "bill",
        "parents" : [
                "jack",
                "jacky"
        ]
}