1

我有一个 MongoDB 的 gocomics 评论。示例评论(来自“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“Luann”条上发表了“plan for it”的评论。commentid 是 2525923,并且是该评论所独有的。其他字段与此问题无关。

一个人可以在同一条上发表多条评论。例如,“Chocoloop”可能在 2011 年 11 月 25 日的“Luann”条上发表了稍后的评论。后面的评论将具有相同的条带、da、mo、yr、commentor 字段,但commentid 更高。

我想找到我对每个条带所做的最新评论。这很简单:

 
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" 
                } 

这表示我在 2007 年 11 月 28 日对 pearlsbeforeswine 条至少发表了一条评论(可能是几条评论)。在我发表的评论中,最新的评论(评论 ID 最高的评论)的评论 ID 为 2462203(mid = "max id")。

现在,对于每个结果,我想知道:在我发表最后一条评论后,是否有人发表了评论?

对于上面选定的结果,这意味着:对于日期为 2007-11-28 且评论 ID 超过 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 是我的 commentorid [SQLite3 版本不包括“commentor”名称字段])。

注意:我的 MongoDB 注释是字符串,而不是整数。这很糟糕,但我认为它不会影响这个问题。

4

2 回答 2

1

您可以使用聚合框架来做到这一点,并且有多种方法可以做到这一点。最简单的有点蛮力和长 - 它可能没有最好的性能,但我认为它最容易理解:

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},您必须在管道中执行两个步骤,一个是为该特定评论者找到最大评论 ID,另一个是为该条找到超过最大评论 ID。因此,它可以是项目、组、组、展开、项目,并根据需要进行匹配。希望你能明白。

顺便说一句,如果您有每个条带的唯一标识符(例如“comicId”),那么您可以更简单地获取特定人评论的漫画列表,然后您就不需要聚合,您可以使用:

db.comments.distinct("comicId",{commentor:"name"})

这将大大减少需要汇总的评论数量。跟踪对话/回复的一种更简单的方法可能是让评论具有“回复中”,但我不确定您是在跟踪线程对话还是直接评论。

于 2013-05-12T20:15:02.347 回答
0

我认为这是一个很好的问题和答案,所以我决定在 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"}}]

结果可以解释为 postluann-2011/11/25具有最大评论数(或 mongo id),2525925而您的评论的 id 为2525923. 因此,在您发表评论后会有稍后的评论,因此您需要获取该新评论。您将需要以编程方式为其编写逻辑。

于 2016-06-04T19:36:38.960 回答