私信  •  关注

Neil Lunn

Neil Lunn 最近创建的主题
Neil Lunn 最近回复了
4 年前
回复了 Neil Lunn 创建的主题 » MongoDB带条件的updateMany

你真的想要 bulkWrite() 使用两个 "updateMany" 而不是里面的陈述。聚合表达式不能用于以任何形式的update语句执行“替代选择”。

db.country.bulkWrite([
  { "updateMany": {
    "filter": { "pop": { "$lt": 20000000 } },
    "update": { "$set": { "country": "Small Country" } }
  }},
  { "updateMany": {
    "filter": { "pop": { "$gt": 20000000 } },
    "update": { "$set": { "country": "Large Country" } } 
  }}
])

SERVER-6566 对于“条件语法”,但这尚未解决。“bulk”API实际上是在提出这个请求之后引入的,实际上可以是 改编 或多或少 同样的事情。

$out 在聚合语句中,不可以选择 “更新” 美元 “更新” 现有的集合, 然而 只有当要更新的集合不同于从聚合管道收集数据时使用的任何其他集合时,才会出现这种情况。所以 使用聚合管道 更新 相同的 就像你读的一样。

bulkWrite() .

5 年前
回复了 Neil Lunn 创建的主题 » 包含子字段Mongodb的多文件Mongoose查询文档[副本]

正如原始注释所解释的,您只需要以下查询条件:

{ isActive: true, "locations.isActive": true }

这是一个基本的和条件,您不需要任何特殊的操作符来验证一个条件是否满足数组中任何地方的单个属性,这就是您所要求的全部。

由于这完全按照预期工作,我只能想给你一个完整的工作清单,作为一个基础,找出你做的不同,从而导致你得到的结果与预期不一样。

const { Schema } = mongoose = require('mongoose');

const uri = 'mongodb://localhost:27017/productions';
const opts = { useNewUrlParser: true };

mongoose.set('useFindAndModify', false);
mongoose.set('useCreateIndex', true);
mongoose.set('debug', true);

const productionSchema = new Schema({
  name: String,
  isActive: { type: Boolean, default: true },
  locations: [{
    name: String,
    isActive: { type: Boolean, default: false }
  }],
  trackno: Number
})

const Production = mongoose.model('Production', productionSchema);

const data =
[
    {
        name: 'John Smith',
        locations: [{ name: 'Boston', isActive: true}],
        isActive: true,
        trackno: 2123
    },
        {
        name: 'Moe Adam',
        locations: [{ name: 'Chicago', isActive: true}],
        isActive: true,
        trackno: 5663
    },
     {
        name: 'Henry Noel',
        locations: [{ name: 'Houston', isActive: false}],
        isActive: true,
        trackno: 4552
    },
    {
        name: 'Tim Johnson',
        locations: [{ name: 'Denver', isActive: true}],
        isActive: false,
        trackno: 6672
    }
];

const log = data => console.log(JSON.stringify(data, undefined, 2));

(async function() {

  try {
    const conn = await mongoose.connect(uri, opts);

    // clean data
    await Promise.all(
      Object.entries(conn.models).map(([k, m]) => m.deleteMany())
    );

    // set up
    await Production.insertMany(data);

    // Query
    let query = { isActive: true, "locations.isActive": true };
    let results = await Production.find(query);

    log(results);


  } catch(e) {
    console.error(e)
  } finally {
    mongoose.disconnect()
  }

})()

输出两份预期文件:

Mongoose: productions.deleteMany({}, {})
Mongoose: productions.insertMany([ { isActive: true, _id: 5c7f7e9367daed19d6773e9b, name: 'John Smith', locations: [ { isActive: true, _id: 5c7f7e9367daed19d6773e9c, name: 'Boston' } ], trackno: 2123, __v: 0 }, { isActive: true, _id: 5c7f7e9367daed19d6773e9d, name: 'Moe Adam', locations: [ { isActive: true, _id: 5c7f7e9367daed19d6773e9e, name: 'Chicago' } ], trackno: 5663, __v: 0 }, { isActive: true, _id: 5c7f7e9367daed19d6773e9f, name: 'Henry Noel', locations: [ { isActive: false, _id: 5c7f7e9367daed19d6773ea0, name: 'Houston' } ], trackno: 4552, __v: 0 }, { isActive: false, _id: 5c7f7e9367daed19d6773ea1, name: 'Tim Johnson', locations: [ { isActive: true, _id: 5c7f7e9367daed19d6773ea2, name: 'Denver' } ], trackno: 6672, __v: 0 } ], {})
Mongoose: productions.find({ isActive: true, 'locations.isActive': true }, { projection: {} })
[
  {
    "isActive": true,
    "_id": "5c7f7e9367daed19d6773e9b",
    "name": "John Smith",
    "locations": [
      {
        "isActive": true,
        "_id": "5c7f7e9367daed19d6773e9c",
        "name": "Boston"
      }
    ],
    "trackno": 2123,
    "__v": 0
  },
  {
    "isActive": true,
    "_id": "5c7f7e9367daed19d6773e9d",
    "name": "Moe Adam",
    "locations": [
      {
        "isActive": true,
        "_id": "5c7f7e9367daed19d6773e9e",
        "name": "Chicago"
      }
    ],
    "trackno": 5663,
    "__v": 0
  }
]
4 年前
回复了 Neil Lunn 创建的主题 » Mongodb更新子文档字段的方法比逐个更新更好[重复]

release of MongoDB 3.6 (在MongoDB 3.5.12的开发分支中可用)现在可以在一个请求中更新多个数组元素。

它使用 filtered positional $[<identifier>] 此版本中引入的更新运算符语法:

db.collection.update(
  { "events.profile":10 },
  { "$set": { "events.$[elem].handled": 0 } },
  { "arrayFilters": [{ "elem.profile": 10 }], "multi": true }
)

这个 "arrayFilters" 传递给选项 .update() 甚至 .updateOne() , .updateMany() , .findOneAndUpdate() .bulkWrite() 方法指定要在update语句中给定的标识符上匹配的条件。任何符合给定条件的元素都将被更新。

注意到 "multi" 正如在问题的上下文中所给出的,使用它是为了“更新多个元素”,但事实并非如此。这里的用法适用于 “多个文档” 一如既往,或者现在被指定为 .updateMany() 在现代的API版本中。

注意 有点讽刺,因为这是在 .update() 和方法一样,语法通常与所有最新版本的驱动程序版本兼容。

但是这不是真的 mongo shell,因为方法是在那里实现的(“讽刺的是为了向后兼容”),所以 arrayFilters 内部方法无法识别和删除参数,该方法分析选项以提供与以前的MongoDB服务器版本和“旧版”的“向后兼容性” .update() API调用语法。

所以如果你想在 蒙哥 shell或其他“基于shell”的产品(尤其是Robo 3T),您需要从3.6版或更高版本的开发分支或生产版本获得最新版本。

另见 positional all $[] 它还更新“多个数组元素”,但不应用于指定的条件,并应用于 全部的 数组中的元素,其中该元素是所需的操作。

另见 Updating a Nested Array with MongoDB 关于这些新的位置运算符如何应用于“嵌套”数组结构,其中“数组位于其他数组中”。

重要的 -从以前版本升级的安装“可能”没有启用MongoDB功能,这也可能导致语句失败。您应该确保升级过程已完成,并包含索引升级等详细信息,然后运行

   db.adminCommand( { setFeatureCompatibilityVersion: "3.6" } )

或更高版本,适用于您安装的版本。即 "4.0" 目前适用于版本4及更高版本。这就启用了新的位置更新操作符等功能。您还可以查看:

   db.adminCommand( { getParameter: 1, featureCompatibilityVersion: 1 } )

返回当前设置

6 年前
回复了 Neil Lunn 创建的主题 » Mongodb更新子文档字段的方法比逐个更新更好[重复]

此时无法使用位置运算符更新数组中的所有项。见吉拉 http://jira.mongodb.org/browse/SERVER-1243

作为一项工作,你可以:

  • 单独更新每个项目 (事件0.handled事件1.handled …)或。。。
  • 阅读文档,进行编辑 手动保存替换 旧的(检查 "Update if Current" 如果你想确保 原子更新)
5 年前
回复了 Neil Lunn 创建的主题 » 内部父数组和内部数组的计数-MongoDB[重复]

"EndpointId" 每一个 "Uid" 在里面 "Tags" 以及 "Type" 在里面 "Sensors" 将是:

db.collection.aggregate([
  { "$unwind": "$Tags" },
  { "$unwind": "$Tags.Sensors" },
  { "$group": {
    "_id": {
      "EndpointId": "$EndpointId",
      "Uid": "$Tags.Uid",
      "Type": "$Tags.Sensors.Type"
    },
  }},
  { "$group": {
    "_id": {
      "EndpointId": "$_id.EndpointId",
      "Uid": "$_id.Uid",
    },
    "count": { "$sum": 1 }
  }},
  { "$group": {
    "_id": "$_id.EndpointId",
    "tagCount": { "$sum": 1 },
    "sensorCount": { "$sum": "$count" }
  }}
])

或者对C

    var results = collection.AsQueryable()
      .SelectMany(p => p.Tags, (p, tag) => new
        {
          EndpointId = p.EndpointId,
          Uid = tag.Uid,
          Sensors = tag.Sensors
        }
      )
      .SelectMany(p => p.Sensors, (p, sensor) => new
        {
          EndpointId = p.EndpointId,
          Uid = p.Uid,
          Type = sensor.Type
        }
      )
      .GroupBy(p => new { EndpointId = p.EndpointId, Uid = p.Uid, Type = p.Type })
      .GroupBy(p => new { EndpointId = p.Key.EndpointId, Uid = p.Key.Uid },
        (k, s) => new { Key = k, count = s.Count() }
      )
      .GroupBy(p => p.Key.EndpointId,
        (k, s) => new
        {
          EndpointId = k,
          tagCount = s.Count(),
          sensorCount = s.Sum(x => x.count)
        }
      );

哪些输出:

{
  "EndpointId" : "89799bcc-e86f-4c8a-b340-8b5ed53caf83",
  "tagCount" : 4,
  "sensorCount" : 16
}

尽管考虑到所呈现的文档对于 “UID” 无论如何 $reduce 文件中的金额:

db.collection.aggregate([
  { "$group": {
    "_id": "$EndpointId",
    "tags": {
      "$sum": {
        "$size": { "$setUnion": ["$Tags.Uid",[]] }
      }
    },
    "sensors": {
      "$sum": {
        "$sum": {
          "$map": {
            "input": { "$setUnion": ["$Tags.Uid",[]] },
            "as": "tag",
            "in": {
              "$size": {
                "$reduce": {
                  "input": {
                    "$filter": {
                      "input": {
                        "$map": {
                          "input": "$Tags",
                          "in": {
                            "Uid": "$$this.Uid",
                            "Type": "$$this.Sensors.Type"
                          }
                        }
                      },
                      "cond": { "$eq": [ "$$this.Uid", "$$tag" ] }
                    }
                  },
                  "initialValue": [],
                  "in": { "$setUnion": [ "$$value", "$$this.Type" ] }
                }
              }
            }
          }
        }
      }
    }
  }}
])

但是,该语句并不能很好地映射到linq,因此需要使用 BsonDocument 接口为语句生成bson。当然,同样的 “UID” 值“did”实际上出现在集合中的多个文档中,然后 $unwind 语句是必要的,以便从数组项中跨文档将这些语句“组合”在一起。


原件

你可以通过获取 $size 数组的。对于外部数组,这只是应用于文档中数组的字段路径,对于需要处理的内部数组项 $map 为了处理每一个 “标签” 元素,然后获取 美元大小 属于 “传感器” $sum 要减少到总计数的结果数组。

每份文件应:

db.collection.aggregate([
  { "$project": {
    "tags": { "$size": "$Tags" },
    "sensors": {
      "$sum": {
        "$map": {
          "input": "$Tags",
           "in": { "$size": "$$this.Sensors" }
        }
      }
    }
  }}
])

在C代码中分配给类的位置如下:

collection.AsQueryable()
  .Select(p => new
    {
      tags = p.Tags.Count(),
      sensors = p.Tags.Select(x => x.Sensors.Count()).Sum()
    }
  );

如果他们回来了:

{ "tags" : 3, "sensors" : 13 }
{ "tags" : 2, "sensors" : 8 }

你想去哪里 $group 结果,例如在整个集合中,您将执行以下操作:

db.collection.aggregate([
  /* The shell would use $match for "query" conditions */
  //{ "$match": { "EndpointId": "89799bcc-e86f-4c8a-b340-8b5ed53caf83" } },
  { "$group": {
    "_id": null,
    "tags": { "$sum": { "$size": "$Tags" } },
    "sensors": {
      "$sum": {
        "$sum": {
          "$map": {
            "input": "$Tags",
             "in": { "$size": "$$this.Sensors" }
          }
        }
      }
    }
  }}
])

对于您的C代码,如前所示:

collection.AsQueryable()
  .GroupBy(p => "", (k,s) => new
    {
      tags = s.Sum(p => p.Tags.Count()),
      sensors = s.Sum(p => p.Tags.Select(x => x.Sensors.Count()).Sum())
    }
  );

如果他们回来了:

{ "tags" : 5, "sensors" : 21 }

为了 "EndpointId ,则只需使用该字段作为分组键,而不是 null 0 当它被C驱动程序映射应用时:

collection.AsQueryable()
  /* Use the Where if you want a query to match only those documents */
  //.Where(p => p.EndpointId == "89799bcc-e86f-4c8a-b340-8b5ed53caf83")            
  .GroupBy(p => p.EndpointId, (k,s) => new
    {
      tags = s.Sum(p => p.Tags.Count()),
      sensors = s.Sum(p => p.Tags.Select(x => x.Sensors.Count()).Sum())
    }
  );

当然,这是您提供给我们的两份文件样本的总和:

{“标签”:5,“传感器”:21}

所以这些都是非常简单的结果,一旦习惯了语法,就可以简单地执行管道。

我建议你熟悉一下 Aggregation Operators 从核心文档中,当然还有 "LINQ Cheat Sheet" 的表达式及其使用映射。

也见将军 LINQ Reference 在C驱动程序参考中,了解如何将其映射到MongoDB的聚合框架的其他示例。

6 年前
回复了 Neil Lunn 创建的主题 » 从MongoDB(v3.4)文档中仅获取子数组[副本]

在最简单的意义上,这只是遵循了 "dot notation" 如MongoDB所用。无论内部数组成员在哪个数组成员中,只要它匹配一个值,它都可以工作:

db.mycollection.find({
    "someArray.someNestedArray.name": "value"
})

对于“单个字段”值,对于匹配要使用的多个字段,这是很好的 $elemMatch :

db.mycollection.find({
    "someArray": { 
        "$elemMatch": {
            "name": "name1",
            "someNestedArray": {
                "$elemMatch": {
                    "name": "value",
                    "otherField": 1
                }
            }
        }
    }
})

它与文档匹配,该文档将包含与值匹配的“路径”处的字段。如果您打算“匹配并过滤”结果,因此只返回匹配的元素,那么位置运算符投影不可能这样做, as quoted :

嵌套数组

位置$运算符不能用于遍历多个数组的查询,例如遍历嵌套在其他数组中的数组的查询,因为$占位符的替换是单个值

现代MongoDB

我们可以通过申请 $filter $map 在这里。这个 美元地图 是真正需要的,因为“内部”数组可以由于“过滤”而改变,“外部”数组当然与“内部”被剥夺所有元素时的条件不匹配。

再次遵循在每个数组中实际要匹配多个属性的示例:

db.mycollection.aggregate([
  { "$match": {
    "someArray": {
      "$elemMatch": {
         "name": "name1",
         "someNestedArray": {
           "$elemMatch": {
             "name": "value",
             "otherField": 1
           }
         }
       }
    }
  }},
  { "$addFields": {
    "someArray": {
      "$filter": {
        "input": {
          "$map": {
            "input": "$someArray",
            "as": "sa",
            "in": {
              "name": "$$sa.name",
              "someNestedArray": {
                "$filter": {
                  "input": "$$sa.someNestedArray",
                  "as": "sn",
                  "cond": {
                    "$and": [
                      { "$eq": [ "$$sn.name", "value" ] },
                      { "$eq": [ "$$sn.otherField", 1 ] }
                    ]
                  }
                }
              }             
            }
          },
        },
        "as": "sa",
        "cond": {
          "$and": [
            { "$eq": [ "$$sa.name", "name1" ] },
            { "$gt": [ { "$size": "$$sa.someNestedArray" }, 0 ] }
          ]
        }
      }
    }
  }}
])

因此在“外部”数组上 滤器 实际上是看 $size 在“内部”数组本身被“过滤”之后,所以当整个内部数组实际上与noting匹配时,可以拒绝这些结果。

旧版MongoDB

为了只“投影”匹配的元素,您需要 .aggregate() 方法:

db.mycollection.aggregate([
    // Match possible documents
    { "$match": {
        "someArray.someNestedArray.name": "value"
    }},

    // Unwind each array
    { "$unwind": "$someArray" },
    { "$unwind": "$someArray.someNestedArray" },

    // Filter just the matching elements
    { "$match": {
        "someArray.someNestedArray.name": "value"
    }},

    // Group to inner array
    { "$group": {
        "_id": { 
            "_id": "$_id", 
            "name": "$someArray.name"
        },
        "someKey": { "$first": "$someKey" },
        "someNestedArray": { "$push": "$someArray.someNestedArray" }
    }},

    // Group to outer array
    { "$group": {
        "_id": "$_id._id",
        "someKey": { "$first": "$someKey" },
        "someArray": { "$push": {
            "name": "$_id.name",
            "someNestedArray": "$someNestedArray"
        }}
    }} 
])

它允许您为文档中的一个或多个结果“筛选”嵌套数组中的匹配项。

5 年前
回复了 Neil Lunn 创建的主题 » 更新用户配置文件时,某些信息会从db、mongodb中删除。

这是按设计的。

你实际上包括一个“完整的对象” location 在你的论点中 $set ,还有这个 简单地 覆盖 那个物体。这就是MongoDB的工作原理。

MongoDB工作方式的另一部分是当您打算 部分 使用“嵌套路径”更新文档,需要将该对象结构处理为 "Dot Notation" 形式。

事实上,这里有一个我很久以前收集到的东西的基本版本:

var data = {
    email: "bill@example.com",
    name: "Bill S Preston",
    photo: "",
    genres: "Music, Lyrics",
    musicLinks: "",
    location: {
      type: "Point",
      coordinates: [
        0,
        0
      ],
      address: "somewhere"
    }
};

以及一个简单的函数:

function dotNotate(obj,target,prefix) {
  target = target || {},
  prefix = prefix || "";

  Object.keys(obj).forEach(function(key) {
    if ( Array.isArray(obj[key] )) {
      return target[prefix + key] = obj[key];
    } else if ( typeof(obj[key]) === "object" ) {
      dotNotate(obj[key],target,prefix + key + ".");
    } else {
      return target[prefix + key] = obj[key];
    }
  });

  return target;
}

运行该函数会产生:

var updates = dotNotate(data);

{
        "email" : "bill@example.com",
        "name" : "Bill S Preston",
        "photo" : "",
        "genres" : "Music, Lyrics",
        "musicLinks" : "",
        "location.type" : "Point",
        "location.coordinates" : [
                0,
                0
        ],
        "location.address" : "somewhere"
}

当然了。 { $set: updates } 只会 部分更新 未覆盖的显式指定的字段。

6 年前
回复了 Neil Lunn 创建的主题 » MongoDB如何筛选在其他集合中找到的记录

你的编辑基本上有答案。简单地 $match 如果数组为空:

db.getCollection('collA').aggregate([
    { "$lookup": {
      "from": "collB",
      "localField": "_id",
      "foreignField": "_id",
      "as": "collB"
    }},
   { "$match": { "collB.0": { "$exists": false } } }
])

这个 $exists 数组索引的测试 0 在查询中询问“这是一个包含项的数组”是最有效的方法。

10 年前
回复了 Neil Lunn 创建的主题 » 如何在页面上显示MongoDB查询结果?

所以这看起来非常像是您已经在一个现有的数据库中创建了集合,现在您正试图用Mongoose模型访问这些集合。

问题是Mongoose使用了一些您可能不知道的默认值,所以您在shell中显示的示例与Mongoose默认情况下所做的不同。

因此,您可以重命名集合以匹配Mongoose在默认情况下的期望值,或者更改Mongoose的操作以匹配现有名称。在后一种情况下,您直接定义模型名称,如下所示:

mongoose.model( "Todo", toDoSchema, "Todo" );

所以第三个论点 method 实际上指定要用于集合的显式名称。如果不这样做,默认规则下的假定名称将是“todos”。

使用任何一种方法使它们匹配。