我有一个数组,我希望按照特定顺序排序,如
$gte
,如果它没有返回结果,$lt
以返回结果根据sorted
顺序中的weight
,数组始终为ascending
。
这就是数组的样子:
"shipping_charges" : [
{
"region" : "region3",
"weight" : 100,
"rate" : 50
},
{
"region" : "region4",
"weight" : 100,
"rate" : 150
},
{
"region" : "region1",
"weight" : 200,
"rate" : 20
},
{
"region" : "region1",
"weight" : 500,
"rate" : 30
},
{
"region" : "region1",
"weight" : 1000,
"rate" : 40
},
]
到目前为止,我使用以下查询
db.clients.find( { storeID: "asdfasdf" ,
"shipping_charges" : {
$elemMatch : { "region" : "region1" ,
"weight" : { $gte : 199 }
}
}
}, { "shipping_charges.$" : 1 } ).pretty();
这将给我第一次出现重量为$gte 199
的记录。但是如果我用$gte 1050
尝试相同的查询,我就什么也得不到,因为没有大于weight > 1050
的平板。由于重量和区域是动态查询的,我将无法控制,但我想要的只是给定重量和区域的最佳匹配最高板块。
我打算做什么? :我的想法是一个接一个地运行两个查询,首先查找给定重量的任何$gte
,如果没有记录,请转到$lt
(但它也会返回给我至少一个,因为我的数组按重量升序排序,所以我可能需要重新排序它们才能再次运行$lt
查询。
我想从这个群体中得到什么? :
Query1 :
region : "region1", weight : 500,
I want the result as
{
"region" : "region1",
"weight" : 500,
"rate" : 30
},
Query2 :
region : "region1", weight : 1050,
I want the result as
{
"region" : "region1",
"weight" : 1000,
"rate" : 40
},
Query3:
region : "region1", weight : 50
I want the result as
{
"region" : "region1",
"weight" : 200,
"rate" : 20
},
我想重写查询,以便它首先查找高于给定权重的任何内容,如果找不到,将找到所有应该是最接近的平板的最高位置。我知道这可以通过两个单独的查询一个接一个地运行,具体取决于计数但是可以合并并将其作为单个查询运行吗?
理想的解决方案? :
有没有办法可以在一个查询中提供两个$sort
订单,以便它首先查找$gte
,然后使用$lt
根据订单中的哪一个给出结果?如果没有,我如何使用“find”(首选)或使用聚合方法解决此问题?我更喜欢在mongoDB
查询本身而不是使用完整数组,循环等的php/js
解决方案中执行此操作,但是,哪个更好?我愿意接受建议。
帮我解决这个问题
这应该是我猜的。
答案 0 :(得分:0)
更新:这与lookup_weight
匹配得最近。
我认为就我理解OP而言,他要求的是与lookup_weight
最近的数字(向上或向下)。 (又名是最小的腹肌差异)。 Mongodb有$near
运算符,但仅适用于地理空间数据。
如果您无法更改数据字段 - 那么此方法可以正常工作(没有abs
,因此我们必须使用$cond
来反转我们的$subtract
,以便我们可以比较值小于那些大于$sort
的值。
col.aggregate([{"$project":
{ "difference":
{"$cond": [ { "$gte": ["$weight", lookup_weight] },
{ "$subtract": ["$weight", lookup_weight] },
{"$subtract": [lookup_weight, "$weight"] } ] },
"weight": "$weight", "rate": "$rate", "region": "$region"} },
{"$sort": {"difference": 1}},
{"$limit": 1} ])
答案 1 :(得分:0)
我不认为你的模特是最好的,但如果没有更清楚地解释你的全部意图,那么提出其他建议并不容易。我会解释一下我认为的好方法。
我可以告诉你一种通过聚合来暴力破解的方法,但这并不是非常简单或理想。更好的想法是稍微改变你的模式,如:
{
"region" : "region1",
"minWeight" : 500,
"maxWeight": 999
"rate" : 30
},
{
"region" : "region1",
"minWeight" : 1000,
"maxWeight": 999999
"rate" : 40
}
现在有" minWeight"和" maxWeight"查询范围很容易:
db.clients.find(
{
"storeID": "asdfasdf",
"shipping_charges" : {
"$elemMatch": {
"region" : "region1" ,
"minWeight": { "$lte": 1050 },
"maxWeight": { "$gte": 1050 }
}
}
},
{ "shipping_charges.$" : 1 }
).pretty();
这很简单,很简单就是你真正想要的,因为它很快。只要确保你使用合理的" maxWeight"在您的最后一个条目中,在每个保存中一致并过滤您的查询输入,以便如果在该数字上输入任何内容,那么该组最大值将用于查询
包含范围和排序顺序的完整更正数据:
{
"storeID": "asdfasdf",
"shipping_charges" : [
{
"region" : "region1",
"minWeight": 0,
"maxWeight" : 200,
"rate" : 20
},
{
"region" : "region1",
"minWeight" : 201,
"maxWeight" : 999,
"rate" : 30
},
{
"region" : "region1",
"minWeight" : 1000,
"maxWeight" : 999999,
"rate" : 40
},
{
"region" : "region3",
"minWeight": 0,
"maxWeight" : 999999,
"rate" : 50
},
{
"region" : "region4",
"minWeight": 0,
"maxweight" : 999999,
"rate" : 150
}
]
}
确保最佳指数:
db.store.ensureIndex({ "storeID": 1, "shipping_charges.region": 1 })
这里不能使用权重,因为索引中不止一个复杂字段,这是不允许的。理想情况下,此数据不在数组中,而是在查询中所需的所有字段上的最佳索引的单独文档。
每个测试用例:
<强> Q1:强>
db.store.find(
{
"storeID": "asdfasdf",
"shipping_charges" : {
"$elemMatch": {
"region" : "region1" ,
"minWeight": { "$lte": 500 },
"maxWeight": { "$gte": 500 }
}
}
},
{ "shipping_charges.$" : 1 }
).pretty();
结果:
{
"_id" : ObjectId("54c20f6137ad0dcb15cb8787"),
"shipping_charges" : [
{
"region" : "region1",
"minWeight" : 201,
"maxWeight" : 999,
"rate" : 30
}
]
}
<强> Q2 强>:
db.store.find(
{
"storeID": "asdfasdf",
"shipping_charges" : {
"$elemMatch": {
"region" : "region1" ,
"minWeight": { "$lte": 1050 },
"maxWeight": { "$gte": 1050 }
}
}
},
{ "shipping_charges.$" : 1 }
).pretty();
结果:
{
"_id" : ObjectId("54c20f6137ad0dcb15cb8787"),
"shipping_charges" : [
{
"region" : "region1",
"minWeight" : 1000,
"maxWeight" : 999999,
"rate" : 40
}
]
}
<强> Q3 强>:
db.store.find(
{
"storeID": "asdfasdf",
"shipping_charges" : {
"$elemMatch": {
"region" : "region1" ,
"minWeight": { "$lte": 50 },
"maxWeight": { "$gte": 50 }
}
}
},
{ "shipping_charges.$" : 1 }
).pretty();
结果:
{
"_id" : ObjectId("54c210f737ad0dcb15cb8788"),
"shipping_charges" : [
{
"region" : "region1",
"minWeight" : 0,
"maxWeight" : 200,
"rate" : 20
}
]
}
仅匹配符合条件且无开销的文档。
答案 2 :(得分:0)
您无法在单个find()
语句中实现此功能,但您可以按以下方式对其进行汇总。将shipping_charges
存储为已排序的数组更有意义。
让:
var weight = 50;
var region = "region1";
Match
所有至少有一个shipping_charges
的文档
指定region
。Unwind
shipping_charges
字段。Match
那些具有相同region
的未卷绕文档
我们正在寻找。Group
一起_id
,以获取最后Shipping_charges
元素,也是最重的。这个元素我们会
使用,以防我们的搜索权重大于所有
shipping_weight
元素的个人权重。Group
将largest
元素转换为array
元素
单个元素。我们这样做是为了方便unwind
的使用
舞台后期即将开始。Project
字段res作为布尔值数组,表明我们是否
找到了匹配的shipping_charges
元素。如果所有的元素
在这个数组是假的,那么我们没有找到匹配,我们需要
将largest
元素显示为匹配。Unwind
shipping_charges
元素。Match
具有相同区域且具有适当性的那些
weight
。Group
这些元素一起获取第一个匹配元素
每个文档或每个文档的largest
元素。汇总代码:
db.collection.aggregate([
{$match:{"shipping_charges.region":region}},
{$unwind:"$shipping_charges"},
{$match:{"shipping_charges.region":region}},
{$group:{"_id":"$_id",
"largest":{$last:"$shipping_charges"},
"shipping_charges":{$push:"$shipping_charges"}}},
{$group:{"_id":"$_id",
"largest":{$push:"$largest"},
"shipping_charges":{$first:"$shipping_charges"}}},
{$project:{"largest":1,
"shipping_charges":1,
"res":{$map:{
"input":"$shipping_charges",
"as":"x",
"in":{$cond:[{$gte:["$$x.weight",weight]},
true,false]}}}}},
{$project:{"largest":1,
"shipping_charges":1,
"r":{$anyElementTrue:["$res"]}}},
{$project:{"shipping_charges":{$cond:[{$eq:["$r",true]},
"$shipping_charges","$largest"]},"r":1}},
{$unwind:"$shipping_charges"},
{$match:{$or:[{"shipping_charges.weight":{$gte:weight}},
{"r":false}]}},
{$group:{"_id":"$_id",
"shipping_charges":{$first:"$shipping_charges"}}}
])
虽然聚合涉及许多步骤,但初始match
条件可以作为减少文档流入的良好过滤机制。