我的数据如下:
{max:3.4.6, min: 1.10.2}
{max:10.9.12, min:6.90.1}
数字3.5.6
存储为string
,0.10.0
应大于0.9.0
。我想给另一个数字cur
,并在cur之间返回所有结果。
find all document where min < cur < max
我可以定义一个能识别1.10.2 < 2.1.3 < 3.4.6
的比较函数吗?并用它来做查询?
答案 0 :(得分:3)
还有一个可以使用聚合框架的解决方案,但它需要当前版本的mongoDB,因为它使用$split
和$strLenBytes
运算符。我在版本3.3.10上创建了它。
这个想法基本上是基于使用前导零填充你的版本部分直到固定大小(在我的例子中,每个部分可以从000到999但你可以根据需要调整它)并对文档字段做同样的事情min
和max
,以便人们可以比较字符串
所以对于像这样的样本数据
/* 1 */
{
"_id" : ObjectId("57ac0c264ae6fbd5fb5b6e97"),
"max" : "3.4.6",
"min" : "1.10.2"
}
/* 2 */
{
"_id" : ObjectId("57ac0c264ae6fbd5fb5b6e98"),
"max" : "10.9.12",
"min" : "6.90.1"
}
/* 3 */
{
"_id" : ObjectId("57ac0c264ae6fbd5fb5b6e99"),
"max" : "3.5.9",
"min" : "0.5.7"
}
/* 4 */
{
"_id" : ObjectId("57ac0c264ae6fbd5fb5b6e9a"),
"max" : "2.0.0",
"min" : "1.5.0"
}
管道
var cur = "1.11.1";
var curParts = cur.split('.');
var curPadded = ("00" + curParts[0]).slice(-3) + "." +
("00" + curParts[1]).slice(-3) + "." +
("00" + curParts[2]).slice(-3);
db.getCollection('minmax').aggregate([
{
$project: {
min: 1, max: 1,
maxTmp: {
$let: {
vars: {
maxParts: { $split: ["$max", "."] }
},
in: {
major: { $arrayElemAt: ["$$maxParts", 0] },
majorLen: { $strLenBytes: { $arrayElemAt: ["$$maxParts", 0] } },
minor: { $arrayElemAt: ["$$maxParts", 1] },
minorLen: { $strLenBytes: { $arrayElemAt: ["$$maxParts", 1] } },
patch: { $arrayElemAt: ["$$maxParts", 2] },
patchLen: { $strLenBytes: { $arrayElemAt: ["$$maxParts", 2] } }
}
}
},
minTmp: {
$let: {
vars: {
minParts: { $split: ["$min", "."] }
},
in: {
major: { $arrayElemAt: ["$$minParts", 0] },
majorLen: { $strLenBytes: { $arrayElemAt: ["$$minParts", 0] } },
minor: { $arrayElemAt: ["$$minParts", 1] },
minorLen: { $strLenBytes: { $arrayElemAt: ["$$minParts", 1] } },
patch: { $arrayElemAt: ["$$minParts", 2] },
patchLen: { $strLenBytes: { $arrayElemAt: ["$$minParts", 2] } }
}
}
}
}
},
{
$project: {
min: 1, max: 1,
maxMajor: { $substr: [{ $concat: ["_00", "$maxTmp.major"] }, "$maxTmp.majorLen", 3] },
maxMinor: { $substr: [{ $concat: ["_00", "$maxTmp.minor"] }, "$maxTmp.minorLen", 3] },
maxPatch: { $substr: [{ $concat: ["_00", "$maxTmp.patch"] }, "$maxTmp.patchLen", 3] },
minMajor: { $substr: [{ $concat: ["_00", "$minTmp.major"] }, "$minTmp.majorLen", 3] },
minMinor: { $substr: [{ $concat: ["_00", "$minTmp.minor"] }, "$minTmp.minorLen", 3] },
minPatch: { $substr: [{ $concat: ["_00", "$minTmp.patch"] }, "$minTmp.patchLen", 3] },
}
},
{
$project: {
min: 1, max: 1,
maxPadded: { $concat: ["$maxMajor", ".", "$maxMinor", ".", "$maxPatch"] },
minPadded: { $concat: ["$minMajor", ".", "$minMinor", ".", "$minPatch"] }
}
},
{
$match: {
maxPadded: { $gt: curPadded },
minPadded: { $lt: curPadded }
}
},
{
$project: {
min: 1,
max: 1
}
}
])
将产生输出
/* 1 */
{
"_id" : ObjectId("57ac0c264ae6fbd5fb5b6e97"),
"max" : "3.4.6",
"min" : "1.10.2"
}
/* 2 */
{
"_id" : ObjectId("57ac0c264ae6fbd5fb5b6e99"),
"max" : "3.5.9",
"min" : "0.5.7"
}
/* 3 */
{
"_id" : ObjectId("57ac0c264ae6fbd5fb5b6e9a"),
"max" : "2.0.0",
"min" : "1.5.0"
}
解释
第一阶段将字符串的各个部分拆分为&#39;。&#39;并确定这些字符串的长度,以便可以将下一阶段的字符串修剪为固定大小。
$project: {
min: 1, max: 1,
maxTmp: {
$let: {
vars: {
maxParts: { $split: ["$max", "."] }
},
in: {
major: { $arrayElemAt: ["$$maxParts", 0] },
majorLen: { $strLenBytes: { $arrayElemAt: ["$$maxParts", 0] } },
minor: { $arrayElemAt: ["$$maxParts", 1] },
minorLen: { $strLenBytes: { $arrayElemAt: ["$$maxParts", 1] } },
patch: { $arrayElemAt: ["$$maxParts", 2] },
patchLen: { $strLenBytes: { $arrayElemAt: ["$$maxParts", 2] } }
}
}
},
minTmp: ... // same idea
}
管道中的文档现在看起来像这样
/* 1 */
{
"_id" : ObjectId("57ac0c264ae6fbd5fb5b6e97"),
"max" : "3.4.6",
"min" : "1.10.2",
"maxTmp" : {
"major" : "3",
"majorLen" : 1,
"minor" : "4",
"minorLen" : 1,
"patch" : "6",
"patchLen" : 1
}
}
... // others
如前所述,现在必须将各个部分填充并修剪成固定尺寸
$project: {
min: 1, max: 1,
maxMajor: { $substr: [{ $concat: ["_00", "$maxTmp.major"] }, "$maxTmp.majorLen", 3] },
maxMinor: { $substr: [{ $concat: ["_00", "$maxTmp.minor"] }, "$maxTmp.minorLen", 3] },
maxPatch: { $substr: [{ $concat: ["_00", "$maxTmp.patch"] }, "$maxTmp.patchLen", 3] },
minMajor: ... // same idea
}
以便文档现在看起来像这样
/* 1 */
{
"_id" : ObjectId("57ac0c264ae6fbd5fb5b6e97"),
"max" : "3.4.6",
"min" : "1.10.2",
"maxMajor" : "003",
"maxMinor" : "004",
"maxPatch" : "006",
"minMajor" : "001",
"minMinor" : "010",
"minPatch" : "002"
}
... // others
在将它们与过滤器匹配之前,必须通过
连接它们$project: {
min: 1, max: 1,
maxPadded: { $concat: ["$maxMajor", ".", "$maxMinor", ".", "$maxPatch"] },
minPadded: ... // same idea
}
产生
/* 1 */
{
"_id" : ObjectId("57ac0c264ae6fbd5fb5b6e97"),
"max" : "3.4.6",
"min" : "1.10.2",
"maxPadded" : "003.004.006",
"minPadded" : "001.010.002"
}
... // others
最后两个阶段然后进行实际匹配并将文档重新整形为原始状态
$match: {
maxPadded: { $gt: curPadded },
minPadded: { $lt: curPadded }
},
$project: {
min: 1,
max: 1
}
最后注意事项:通过压缩&#39;管道可能会更短。将$project
阶段合二为一,但我想这很难跟进,因此我将$project
阶段分成几个阶段。
答案 1 :(得分:1)
您可以在查询语句中使用 $ where 查询运算符, 它接受一个功能:
db.test.find({
$where: function(){
var cur = '7.1.3';
return compare(this.max, cur) > 0 &&compare(this.min, cur) < 0 ;
function compare(base, cur) {
var baseArr = base.split('.');
var curArr = cur.split('.');
//Compare from head to tail
for(var i in baseArr) {
if(i <= baseArr.length - 1) {
if(parseInt(baseArr[i]) < parseInt(cur[i])) return -1;
else if(parseInt(baseArr[i]) > parseInt(cur[i])) return 1;
else {
continue;
}
}
else {
return 0;
}
}
}
}
});
但是这种方式不能使用索引,请注意性能