我有一个关于gocomics评论的MongoDB。示例评论(来自 “db.comments.find()。pretty()”):
{
"_id" : ObjectId("518f14e5394594efbe18068c"),
"body" : "plan for it",
"commentid" : "2525923",
"commentor" : "Chocoloop",
"commentorid" : "769338",
"da" : "25",
"filename" : "/mnt/sshfs/gocomics/comments/100.out.bz2",
"mo" : "11",
"strip" : "luann",
"stripname" : "Luann",
"time" : "1 day ago",
"yy" : "2011"
}
这表明“Chocoloop”发表评论“为它做计划” 2011-11-25“滦”地带。纪念品是2525923,并且是独一无二的 这个评论。其他字段与此问题无关。
一个人可以在同一条带上发表多条评论。对于 例如,“Chocoloop”可能会在2011-11-25发表评论 “滦”条。后面的评论会有相同的条带,da,mo, 年,评论家领域,但更高的荣誉。
我想找到我在每个地带上发表的最新评论。这很简单:
db.comments.aggregate(
{$match: {commentor:"barrycarter"}},
{$group: {_id: {strip: "$strip", yy: "$yy", da:"$da", mo:"$mo"},
mid: {$max:"$commentid"}}}
)
以下是许多结果之一:
{
"_id" : {
"strip" : "pearlsbeforeswine",
"yy" : "2007",
"da" : "28",
"mo" : "11"
},
"mid" : "2462203"
}
这说我至少做了一个评论(也许是几个) pearlsbeforeswine strip日期2007-11-28。在我提出的评论中, 最新的一个(纪念最高的一个)已经有了2462203 (mid =“max id”)。
现在,对于每个结果,我想知道:之后有人发表了评论 我做了最后的评论?
对于上面选择的结果,这意味着:是否有任何评论 2007-11-28的pearlsbeforeswine地带,其名称超过了 2462203?
当然,我可以为这一个特例写一个查询:
db.comments.find(
{strip:"pearlsbeforeswine",yy:"2007",da:"28",mo:"11",
commentid: {$gt: "2462203"}}
).pretty()
但是如何在没有结果集的情况下对结果集中的所有结果执行此操作 为每个人创建一个单独的查询(甚至是自动化的,似乎 丑陋)。
这对MongoDB来说是一个糟糕的用例吗?我有一个类似的(不完全相同) 此查询所在的SQLite3数据库:
SELECT * FROM (SELECT strip,month,date,year,MAX(id) AS mid FROM
comments WHERE commentorid=801127 GROUP BY strip,month,date,year) AS t
JOIN comments c ON (t.strip=c.strip AND t.month=c.month AND
t.date=c.date AND t.year=c.year AND c.id > t.mid)
(其中801127是我的评论[SQLite3版本不包括 “评论员”名称字段])。
注意:我的MongoDB纪念品是字符串,而不是整数。那很糟糕,但我 不要认为这会影响这个问题。
答案 0 :(得分:2)
您可以使用聚合框架来实现,并且有多种方法可以解决此问题。最简单的一点是蛮力和长 - 它可能没有最好的表现,但我认为这是最简单的理解:
proj={"$project": {
"strip" : {"$concat" : ["$strip","-","$yy","/","$mo","/","$da"]},
"commentor" : 1,
"commentid" : 1
}
};
group={"$group": {
"_id" : "$strip",
"comms" : {
"$push" : {
"c" : "$commentor",
"i" : "$commentid"
}
},
"max" : {
"$max" : "$commentid"
}
}};
match = { "$match" : { "comms.c" : "<commentorname>" } };
unwind = { "$unwind" : "$comms" };
proj2 = { "$project" : {
"meLast" : {"$cond" : [
{"$eq" : [
"$max",
"$comms.i"
]
},
1,
0
] }
}
};
group2 = {"$group" : {
"_id" : "$_id",
"afterMe" : {
"$max" : "$meLast"
}
} };
match2 = { "$match" : { "afterMe" : 0 } };
db.comments.aggregate( proj, group, match, unwind, match, proj2, group2, match2 );
基本上,无论你采用哪种方式,你必须拥有两个{$group}
步骤,一个用于查找此特定评论员的最大值,另一个用于该条带的最大值。所以它可能是项目,组,组,展开,适当的匹配项目。希望你明白这一点。
顺便说一句,如果你有一个每个条带的唯一标识符(比如说“comicId”),那么你可以获得一个特定的人更简单评论的漫画列表,然后你就不需要聚合了。只需使用:
db.comments.distinct("comicId",{commentor:"name"})
这将显着减少需要汇总的评论数量。跟踪对话/回复的一种更简单的方法可能是让评论“回复”,但我不确定您是在跟踪线索对话还是直接评论。
答案 1 :(得分:1)
我认为这是一个很好的问题和答案,所以我决定在java中使用Spring Data和MongoDB来解决这个问题。要将Asya的答案转换为java mongodb代码,我做了以下内容:
public void commentTest() {
BasicDBObject o1 = new BasicDBObject();
o1.append("c", "$commentor");
o1.append("i", "$commentid");
Aggregation aggCount = newAggregation(
project("commentid", "commentor")
.andExpression("concat(\"$strip\",\"-\",\"$yy\",\"/\",\"$mo\",\"/\",\"$da\")").as("strip"),
group("strip").push(o1).as("comms").max("commentid").as("max"),
match(Criteria.where("comms.c").is("Simon")),
unwind("comms"),
match(Criteria.where("comms.c").is("Simon")));
logger.info(aggCount.toString());
AggregationResults<CommentTest> groupCount = mongoTemplate.aggregate(aggCount, "commenttest", CommentTest.class);
List<CommentTest> resultCount = groupCount.getMappedResults();
ObjectMapper mapper = new ObjectMapper();
try {
logger.info(mapper.writeValueAsString(resultCount));
} catch (IOException e) {
e.printStackTrace();
}
}
然后,为了让mongotemplate成功地将结果解析到CommentTest
类,我必须创建一个缩小结果的类:
Document(collection = "commenttest")
@JsonInclude(JsonInclude.Include.NON_NULL)
public class CommentTest {
private String id, body, commentid, commentor, commentorid, da, filename, mo, strip, stripname, time, yy, max;
@JsonProperty
private comms comms;
public CommentTest.comms getComms() {
return comms;
}
public void setComms(CommentTest.comms comms) {
this.comms = comms;
}
public static class comms implements Serializable {
private String c,i;
public String getC() {
return c;
}
public void setC(String c) {
this.c = c;
}
public String getI() {
return i;
}
public void setI(String i) {
this.i = i;
}
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
public String getCommentid() {
return commentid;
}
public void setCommentid(String commentid) {
this.commentid = commentid;
}
public String getCommentor() {
return commentor;
}
public void setCommentor(String commentor) {
this.commentor = commentor;
}
public String getCommentorid() {
return commentorid;
}
public void setCommentorid(String commentorid) {
this.commentorid = commentorid;
}
public String getDa() {
return da;
}
public void setDa(String da) {
this.da = da;
}
public String getFilename() {
return filename;
}
public void setFilename(String filename) {
this.filename = filename;
}
public String getMo() {
return mo;
}
public void setMo(String mo) {
this.mo = mo;
}
public String getStrip() {
return strip;
}
public void setStrip(String strip) {
this.strip = strip;
}
public String getStripname() {
return stripname;
}
public void setStripname(String stripname) {
this.stripname = stripname;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
public String getYy() {
return yy;
}
public void setYy(String yy) {
this.yy = yy;
}
public String getMax() {
return max;
}
public void setMax(String max) {
this.max = max;
}
}
然后我通过插入这4个模拟条目在mongodb中创建了一些测试数据:
{ "_id" : ObjectId("518f14e5394594efbe18068c"), "body" : "1", "commentid" : "2525923", "commentor" : "Simon", "commentorid" : "769338", "da" : "25", "filename" : "/mnt/sshfs/gocomics/comments/100.out.bz2", "mo" : "11", "strip" : "luann", "stripname" : "Luann", "time" : "1 day ago", "yy" : "2011" }
{ "_id" : ObjectId("518f14e5394594efbe18068d"), "body" : "2", "commentid" : "2525924", "commentor" : "Josh", "commentorid" : "769339", "da" : "25", "filename" : "/mnt/sshfs/gocomics/comments/100.out.bz2", "mo" : "11", "strip" : "luann", "stripname" : "Luann", "time" : "1 day ago", "yy" : "2011" }
{ "_id" : ObjectId("518f14e5394594efbe18068e"), "body" : "3", "commentid" : "2525925", "commentor" : "Peter", "commentorid" : "769340", "da" : "25", "filename" : "/mnt/sshfs/gocomics/comments/100.out.bz2", "mo" : "11", "strip" : "luann", "stripname" : "Luann", "time" : "1 day ago", "yy" : "2011" }
{ "_id" : ObjectId("518f14e5394594efbe18068f"), "body" : "old1", "commentid" : "2525905", "commentor" : "Peter", "commentorid" : "769340", "da" : "24", "filename" : "/mnt/sshfs/gocomics/comments/100.out.bz2", "mo" : "11", "strip" : "luann", "stripname" : "Luann", "time" : "1 day ago", "yy" : "2011" }
然后我运行了代码,结果如下:
[{"id":"luann-2011/11/25","max":"2525925","comms":{"c":"Simon","i":"2525923"}}]
结果可以解释为帖子luann-2011/11/25
的最大评论数(或mongo id)为2525925
,而评论的ID为2525923
。因此,在您发表评论后会有一个后来的评论,因此您需要获取该新评论。您需要以编程方式为其编写逻辑。