我有一个与此类似的架构。
{id: Number,
line_items: [{
id: String,
quantity: Number,
review_request_sent :Boolean
}],
total_price: String,
name: String,
order_number: Number
}
我想要找到的是所有没有为数组中的所有项目设置字段review_request_sent的元素。
示例
{
id: 1,
line_items: [
{
id: 43,
review_request_sent: true
}
]
},
{
id: 2,
line_items: [
{
id: 1,
review_request_sent: false
},
{
id: 39
},
]
},
{
id: 3,
line_items: [
{
id: 23,
review_request_sent: true
},
{
id: 85,
review_request_sent: true
},
{
id: 12,
review_request_sent: false
}
]
}
我希望得到一个查询/查找的帮助,它只返回第二个文档,因为它的数组中的所有项目都没有review_request_sent字段。
答案 0 :(得分:19)
您基本上需要$elemMatch
和$exists
运算符,因为这将检查每个元素以查看条件“field not exists”对于任何元素是否为真:
Model.find({
"line_items": {
"$elemMatch": { "review_request_sent": { "$exists": false } }
}
},function(err,docs) {
});
仅返回第二个文档,因为该字段不存在于其中一个数组子文档中:
{
"id" : 2,
"line_items" : [
{
"id" : 1,
"review_request_sent" : false
},
{
"id" : 39
}
]
}
请注意,这与此表格“不同”:
Model.find({
"line_items.review_request_sent": { "$exists": false }
},function(err,docs) {
})
在那里询问“全部”数组元素是否包含此字段,当文档至少有一个包含该字段的元素时,这不是真的。因此$eleMatch
使条件针对“每个”数组元素进行测试,从而得到正确的响应。
如果你想更新这个数据,以便发现任何不包含这个字段的数组元素然后接收到值为false
的字段(大概),那么你甚至可以写一个这样的语句:
Model.aggregate(
[
{ "$match": {
"line_items": {
"$elemMatch": { "review_request_sent": { "$exists": false } }
}
}},
{ "$project": {
"line_items": {
"$setDifference": [
{"$map": {
"input": "$line_items",
"as": "item",
"in": {
"$cond": [
{ "$eq": [
{ "$ifNull": [ "$$item.review_request_sent", null ] },
null
]},
"$$item.id",
false
]
}
}},
[false]
]
}
}}
],
function(err,docs) {
if (err) throw err;
async.each(
docs,
function(doc,callback) {
async.each(
doc.line_items,
function(item,callback) {
Model.update(
{ "_id": doc._id, "line_items.id": item },
{ "$set": { "line_items.$.review_request_sent": false } },
callback
);
},
callback
);
},
function(err) {
if (err) throw err;
// done
}
);
}
);
.aggregate()
结果不仅匹配文档,而且过滤掉不存在字段的数组中的内容,以便只返回该特定子文档的“id”。
然后,循环.update()
语句匹配每个文档中每个找到的数组元素,并在匹配的位置添加缺少的字段和值。
通过这种方式,您将在每个文档的所有子文档中都显示该字段。
如果你想做这样的事情,那么改变你的模式以确保该字段始终存在也是明智的:
{id: Number,
line_items: [{
id: String,
quantity: Number,
review_request_sent: { type: Boolean, default: false }
}],
total_price: String,
name: String,
order_number: Number
}
因此,下次在代码中向数组中添加新项时,如果没有另外明确设置,则元素将始终以其默认值存在。这样做可能是个好主意,并且可以在您经常需要的其他字段上设置required
,例如“id”。
答案 1 :(得分:2)
您可以在不使用package main
import (
"crypto/rand"
"encoding/base64"
)
// GenerateRandomBytes returns securely generated random bytes.
// It will return an error if the system's secure random
// number generator fails to function correctly, in which
// case the caller should not continue.
func GenerateRandomBytes(n int) ([]byte, error) {
b := make([]byte, n)
_, err := rand.Read(b)
// Note that err == nil only if we read len(b) bytes.
if err != nil {
return nil, err
}
return b, nil
}
// GenerateRandomString returns a URL-safe, base64 encoded
// securely generated random string.
func GenerateRandomString(s int) (string, error) {
b, err := GenerateRandomBytes(s)
return base64.URLEncoding.EncodeToString(b), err
}
func main() {
// Example: this will give us a 44 byte, base64 encoded output
token, err := GenerateRandomString(32)
if err != nil {
// Serve an appropriately vague error to the
// user, but log the details internally.
}
}
和$map
$redact 的情况下找到另一种方式:
$unwind
在上面的查询db.collectionName.aggregate({
"$match": {
"line_items.review_request_sent": {
"$exists": true
}
}
}, {
"$redact": {
"$cond": {
"if": {
"$eq": [{
"$ifNull": ["$review_request_sent", "null"]
}, "null"]
},
"then": "$$DESCEND",
"else": "$$PRUNE"
}
}
}, {
"$match": {
"line_items": {
"$size": 1
}
}
}).pretty()
中,检查match
$ifNull中是否存在review_request_sent
用于检查redact
是否存在,如果不存在则已更换为review_request_sent
和"null"
以检查$eq
这将返回
"null"
在最后一场比赛之后,只检查那些{ "_id" : ObjectId("55e6ca322c33bb07ff2c163e"), "id" : 1, "line_items" : [ ] }
{ "_id" : ObjectId("55e6ca322c33bb07ff2c163f"), "id" : 2, "line_items" : [ { "id" : 39 } ] }
{ "_id" : ObjectId("55e6ca322c33bb07ff2c1640"), "id" : 3, "line_items" : [ ] }
数组包含值$size到1
答案 2 :(得分:0)
let rankers = [
{
"_id": "5e4d4a13e0eb7b0733f27a18",
"totalMarks": 400,
"correct": 78,
"incorrect": 22,
"obtainMarks": 290,
"attemptCount": 100,
"timeTaken": 0,
"rank": 0,
"userRank": 1
},
{
"_id": "5e4d4a13e0eb7b0733f27a18",
"totalMarks": 400,
"correct": 77,
"incorrect": 21,
"obtainMarks": 287,
"attemptCount": 98,
"rank": 0,
"userRank": 2
},
{
"_id": "5e4d4a13e0eb7b0733f27a18",
"totalMarks": 400,
"correct": 76,
"incorrect": 21,
"obtainMarks": 283,
"attemptCount": 97,
"timeTaken": 0,
"userRank": 3
}
]
在rankers集合中,rank和timeTaken密钥在某些文档中丢失,所以请
Results.aggregate([
{
$project: {
"totalMarks": 1,
"correct": "$correct",
"incorrect": "$incorrect",
"obtainMarks": "$obtainMarks",
"attemptCount": "$attemptCount",
"timeTaken": {
$cond: {
if: { "$eq": [{ "$type": "$timeTaken" }, "missing"] },
then: 0,
else: "$timeTaken"
}
},
"rank": {
$cond: {
if: { "$eq": [{ "$type": "$rank" }, "missing"] },
then: 0,
else: "$rank"
}
}
}
}])