类知乎网站的实现_第1张图片

类知乎网站的实现_第2张图片



查询问题列表:

从这个方法返回的数据中可以看出,有一个answer_number字段, 即这个问题当前有多少个回答

1.先查询问题,再查询答案数量,这是最常想到的办法,代码如下:

        图片.png

    这种方法的优点是简单直接, 缺点是查询次数太多。假设有100 个问题,那么就需要查询101 次才能完成。这会导致网页加载数据显著降低。

2.使用$lookup 同时查询问题和回答

    在第8章中讲到了聚合操作的$lookup操作符。使用$lookup可以一次性查询两个集合。假设有100个问题,只需要查询1次,就可以同时获得所有的问题,以及它们各自对应的回答。使用聚合操作配合$lookup的代码如下:

    图片.png

    类知乎网站的实现_第3张图片

    



实现“查询回答”功能:

进入一个问题的答案列表页以后,除看到答案外,还能够看到这个问题的描述。这说明在答案列表页面,不仅要查询答案answer集合,还需要查询问题question集合。

使用聚合查询的$lookup可以提高查询的效率,对应的代码如下:

类知乎网站的实现_第4张图片

类知乎网站的实现_第5张图片

• 第2~8行:首先使用$match筛选出目标问题, 再根据目标问题对应的objectid查询问题和相应的回答, 井把回答存在名为answer_list的列表中。
• 第9行:聚合操作返回的结果是一个可法代的对象, 由于可迭代的对象的ID(Objectid)不重复,所以这里必定只有一个元素。因此把它转化为列表再取下标为0的元素。
• 第10~ 1 6 行:记录问题的信息。
• 第17 ~23 行:记录每一条回答的内容。
• 第24 行: 把回答的列表重新存入问题信息中。
修改好query_answer 方法后重启网站。在问题列表页中单击任何一个问题,则可以正常进入该问题的答案列表页面


实现“提问与回答”功能:

提问对应MongoUtil类中的方法为insert_question,回答对应MongoUtil类中的方法为insert_answer,它们的代码如下:

图片.png

类知乎网站的实现_第6张图片

这两个方法属于非常常规的数据插入操作。

在insert_answer方法中,参数question_id是问题对应的Objectld的字符串形式,需要首先将question_id转化为Objectld对象,再插入到MangoDB中


实现“点赞”与“点踩”劝能:

为问题“点赞”或“点睬” 对应MongoUtil类中的方法为vote_for_ question , 为答案“点赞”和“点踩”对应的方法名为vote_for_answer。它们都使用了MongoDB的update_one方法。

使用“$inc”操作符实现字段自增自减:

在MangoDB的基础部分中,update_one的用法为:

handler.update_one({"name":"xxx"},{"$set":{"age":12}})

意思是查询name字段值为xxx的记录,然后把这条记录的age字段更新为12

但是在这个项目中,“ 点赞”功能需要把字段vote_up 自增1,“ 点踩”功能需要把vote down字段自增l ,而且可能多个访客会同时对一个问题“点赞",所以“点赞”和“点踩”这两个操作都必须是原子操作,不能先查询当前问题的vote_ up是多少,然后再使用update_one来设置新的值。
为了实现原子操作的字段自增,就不能使用“$set”操作符而要改成“$inc”操作符。这个的inc对应了英文单词increase(增加)。

使用格式为:
    handler . update_one({"_id":问题或答案的Objectid},{"$inc":{"vote_up":1}})

实际上,自减就是在“$inc”的值对应的字典中把值设为负数。但由于本项目需要记录“点踩”的数量,所以把“点赞”和“点踩”分成两个字段来保存。因此无论是“点赞”还是“点踩”都是自增操作。

2. 实现“点赞”和“点踩”
修改点赞和点踩的代码,实现它们的功能:

类知乎网站的实现_第7张图片

需要注意的是,传入进来的value可能是vote_up或者vote_down,因此把它直接作为$inc值字典的Key就可以自动实现赞或者踩。
修改完成以后重启网站,可以看到“点赞”和“点踩”功能已经恢复正常。