我想在MongoDB中询问一些与findAndModify相关的信息。 据我所知,查询是“按文档隔离”。
这意味着如果我像这样运行2个findAndModify:
{a:1},{set:{status:"processing", engine:1}}
{a:1},{set:{status:"processing", engine:2}}
并且此查询可能会影响2.000个文档,因为有2个查询(2引擎),然后可能某个文档将具有“engine:1”和另一个“engine:2”。
我认为findAndModify不会隔离“第一个查询”。 为了隔离第一个查询,我需要使用$ isolated。
一切都写了我写的东西吗?
想法是编写一个接近引擎。 用户拥有1000-2000-3000个用户,或数百万用户。
1 - 从最近点“lng,lat”开始排序 2 - 在NodeJS中,我做了一些我不能在MongoDB中进行的计算 3 - 现在我将用户分组为“UserGroup”,我写了一个批量更新
当我有2000-3000个用户时,这个过程(从1到3)需要时间。 所以我想要并行使用多线程。
并行线程意味着并行查询。 这可能是一个问题,因为Query3可以占用Query1的一些用户。 如果发生这种情况,那么在第(2)点,我没有最接近的用户,但最近的“对于此查询”,因为可能另一个查询占用了其余的用户。这可能会造成纽约的一些用户与洛杉矶的用户分组。
我有一个这样的集合:
{location:[lng,lat], name:"1",gender:"m", status:'undone'}
{location:[lng,lat], name:"2",gender:"m", status:'undone'}
{location:[lng,lat], name:"3",gender:"f", status:'undone'}
{location:[lng,lat], name:"4",gender:"f", status:'done'}
我应该做的是通过最近的分组创建“组”用户。每组有1个男性+ 1个女性。在上面的例子中,我期望只有一个组(user1 + user3),因为有男性+女性并且彼此非常接近(用户2也是男性,但远离用户3和用户) -4也是女性,但状态为“已完成”,因此已经处理完毕。
现在创建了组(仅1组),因此2个用户被标记为“已完成”,而另一个用户2被标记为“撤消”以供将来操作。
我希望能够非常快速地管理1000-2000-3000个用户。
更新3:来自社区 好的,现在。我可以试着总结一下你的情况。根据您的数据,您希望根据彼此的接近程度将男性和女性条目“配对”在一起。大概你不想做所有可能的匹配,只是设置一般的“推荐”列表,让每个用户按最近的位置说10。现在我不得不愚蠢地看不到这方面的全部方向,但这总结了基本的初始问题陈述。处理每个用户,找到他们的“配对”,一旦配对就将它们标记为“已完成”,并通过组合完成将其排除在其他配对之外?
答案 0 :(得分:2)
这是一个非常重要的问题,无法轻易解决。
首先,迭代方法(无可否认是我的第一个方法)可能会导致错误的结果。
鉴于我们有以下文件
{
_id: "A",
gender: "m",
location: { longitude: 0, latitude: 1 }
}
{
_id: "B",
gender: "f",
location: { longitude: 0, latitude: 3 }
}
{
_id: "C",
gender: "m",
location: { longitude: 0, latitude: 4 }
}
{
_id: "D",
gender: "f",
location: { longitude: 0, latitude: 9 }
}
通过迭代方法,我们现在将以“A”开始并计算最接近的女性,当然将是“B”,距离为2.然而,事实上,男性和女性之间的距离最近女性将是1(从“B”到“C”的距离)。但即使我们发现这一点,也会留下另一场比赛“A”和“D”,距离为8,在我们之前的解决方案中,“A”的距离只有2到“B”
所以我们需要决定走哪条路
var users = db.collection.find(yourQueryToFindThe1000users);
// We can safely use an unordered op here,
// which has greater performance.
// Since we use the "done" array do keep track of
// the processed members, there is no drawback.
var pairs = db.pairs.initializeUnorderedBulkOp();
var done = new Array();
users.forEach(
function(currentUser){
if( done.indexOf(currentUser._id) == -1 ) { return; }
var genderToLookFor = ( currentUser.gender === "m" ) ? "f" : "m";
// using the $near operator,
// the returned documents automatically are sorted from nearest
// to farest, and since findAndModify returns only one document
// we get the closest matching partner.
var nearPartner = db.collection.findAndModify(
query: {
status: "undone",
gender: genderToLookFor,
$near: {
$geometry: {
type: "Point" ,
coordinates: currentUser.location
}
}
},
update: { $set: { "status":"done" } },
fields: { _id: 1}
);
// Obviously, the current use already is processed.
// However, we store it for simplifying the process of
// setting the processed users to done.
done.push(currentUser._id, nearPartner._id);
// We have a pair, so we store it in a bulk operation
pairs.insert({
_id:{
a: currentUser._id,
b: nearPartner._id
}
});
}
)
// Write the found pairs
pairs.execute();
// Mark all that are unmarked by now as done
db.collection.update(
{
_id: { $in: done },
status: "undone"
},
{
$set: { status: "done" }
},
{ multi: true }
)
这将是理想的解决方案,但解决起来非常复杂。我们需要一个性别的所有成员,计算与其他性别的所有成员的所有距离,并迭代所有可能的匹配集。在我们的示例中,它非常简单,因为对于任何给定的性别,只有4种组合。考虑两次,这可能至少是旅行商问题的一个变种(MTSP?)。如果我说得对,那么组合的数量应该是
所有n> 2的,其中n是可能的对数。
因此
对于n = 10 ,
和令人惊讶的
对于n = 25 ,
那是7.755千万亿(长尺度)或7.755 septillion(短尺度)。 虽然有解决此类问题的方法,但世界纪录在25,000个节点的范围内,使用大量硬件和非常棘手的算法。我认为,出于所有实际目的,可以排除这种“解决方案”。
为了防止人们可能与他们之间不可接受的距离匹配的问题,并根据您的使用情况,您可能希望根据他们与公共地标(他们将要见面的地方)的距离来匹配人员,例如下一个更大的城市。)
对于我们的例子,假设我们的城市在[0,2]和[0,7]。因此,城市之间的距离(5)必须是我们可接受的比赛范围。所以我们对每个城市进行查询
db.collection.find({
$near: {
$geometry: {
type: "Point" ,
coordinates: [ 2 , 0 ]
},
$maxDistance: 5
}, status: "done"
})
并天真地迭代结果。由于“A”和“B”将是结果集中的第一个,因此它们将匹配并完成。这里“C”运气不好,因为没有女孩留给他。但是,当我们对第二个城市进行相同的查询时,他获得了第二次机会。好吧,他的旅行时间有点长,但是嘿,他和“D”约会了!
要找到相应的距离,请选择一组固定的城市(城镇,大都市区,无论您的比例是多少),按位置排序,并将每个城市半径设置为与其近邻的两个距离中较大的一个。这样,您就可以获得重叠区域。因此,即使在一个地方找不到匹配,也可以在其他地方找到。
Iirc,谷歌地图允许它根据他们的大小抓住一个国家的城市。一种更简单的方法是让人们选择他们各自的城市。