关灯
护眼 字体:大
中
小
上一章
目录
下一章
10.3.1 生成初始数据
打开本地的MongoDB,分别运行项目中的generate_question.py和generate_answer.py这两个文件,在数据库中生成qa_system库,并在库中生成两个集合question和answer,如图10-10所示。
图10-10 初始数据
10.3.2 实现“查询问题列表”功能
查询问题在MongoUtil类中对应的方法为query_question。从这个方法返回的数据中可以看出,有一个answer_number 字段,即这个问题当前有多少个回答。要实现这样的返回字段,有两种办法。
1.先查询问题,再查询答案数量
这是最常想到的办法,代码如下:
代码10-1 先查询问题再查询回答
这种方法的优点是简单直接,缺点是查询次数太多。
假设有100个问题,那么就需要查询101次才能完成。这会导致网页加载数据显著降低。
1.使用$lookup同时查询问题和回答
在第8章中讲到了聚合操作的“$lookup”操作符。使用“$lookup”可以一次性查询两个集合。假设有100个问题,只需要查询1次,就可以同时获得所有的问题,以及它们各自对应的回答。
使用聚合操作配合“$lookup”的代码如下:
代码10-2 使用聚合操作的联集合查询一次获取问题和回答
在Robo 3T中的文本模式中,可以直观地看到联集合查询的运行效果,如图10-11所示。
图10-11 联集合查询的运行效果
在Python中,返回的字段中会有一个answer_list列表,这个列表里面就是所有的答案。只需要查询一下这个列表的长度,就知道这个问题有多少个回答了。
完整的query_question方法代码如下。
代码10-3 完整的查询问题代码
代码说明如下。
● 第2~7行:使用聚合操作配合“$lookup”联集合查询问题和回答。
● 第10行:使用for循环展开返回的结果,每一轮循环对应了一个问题。
● 第15行:使用“点赞”数减去“点踩”数,计算当前问题的赞同数。
● 第16行:获取answer_list这个Key对应的列表的长度,即这个问题的答案数。
修改好 query_question 方法后重启网站,可以看到目前已经能够正常显示问题列表了,如图10-1所示。
10.3.3 实现“查询回答”功能
从图10-2可以看出,进入一个问题的答案列表页以后,除看到答案外,还能够看到这个问题的描述。这说明在答案列表页面,不仅要查询答案 answer 集合,还需要查询问题 question集合。
使用聚合查询的$lookup可以提高查询的效率,对应的代码如下:
代码10-4 查询回答
其中,主要代码说明如下。
● 第2~8行:首先使用“$match”筛选出目标问题,再根据目标问题对应的ObjectId查询问题和相应的回答,并把回答存在名为“answer_list”的列表中。
● 第9行:聚合操作返回的结果是一个可迭代的对象,由于可迭代的对象的ID(ObjectId)不重复,所以这里必定只有一个元素。因此把它转化为列表再取下标为“0”的元素。
● 第10~16行:记录问题的信息。
● 第17~23行:记录每一条回答的内容。
● 第24行:把回答的列表重新存入问题信息中。
修改好query_answer方法后重启网站。在问题列表页中单击任何一个问题,则可以正常进入该问题的答案列表页面,如图10-2所示。
10.3.4 实现“提问与回答”功能
提问对应 MongoUtil 类中的方法为 insert_question,回答对应 MongoUtil 类中的方法为insert_answer,它们的代码如下:
代码10-5 保存问题与保存回答
这两个方法属于非常常规的数据插入操作。
提示:
在insert_answer方法中,参数question_id是问题对应的ObjectId的字符串形式,需要首先将question_id转化为ObjectId对象,再插入到MongoDB中。
修改好这两个方法以后,提问与回答功能恢复正常。
10.3.5 实现“点赞”与“点踩”功能
为问题“点赞”或“点踩”对应MongoUtil类中的方法为vote_for_question,为答案“点赞”和“点踩”对应的方法名为vote_for_answer。它们都使用了MongoDB的update_one方法。
1. 使用“$inc”操作符实现字段自增自减
在MongoDB 的基础部分中,update_one的用法为:
handler.update_one({'name': 'xxx'}, {'$set': {'age': 12}})
意思是查询name字段值为xxx的记录,然后把这条记录的age字段更新为12。
但是在这个项目中,“点赞”功能需要把字段vote_up自增1,“点踩”功能需要把vote_down字段自增1,而且可能多个访客会同时对一个问题“点赞”,所以“点赞”和“点踩”这两个操作都必须是原子操作,不能先查询当前问题的vote_up是多少,然后再使用update_one来设置新的值。
为了实现原子操作的字段自增,就不能使用“$set”操作符而要改成“$inc”操作符。这个的inc对应了英文单词increase(增加)。
使用格式为:
handler.update_one({'_id': 问题或答案的ObjectId}, {'$inc': {'vote_up': 1}})
实际上,自减就是在“$inc”的值对应的字典中把值设为负数。但由于本项目需要记录“点踩”的数量,所以把“点赞”和“点踩”分成两个字段来保存。因此无论是“点赞”还是“点踩”都是自增操作。
2.实现“点赞”和“点踩”
修改点赞和点踩的代码,实现它们的功能:
代码10-6 实现“点赞”与“点踩”
需要注意的是,传入进来的value可能是vote_up或者vote_down,因此把它直接作为$inc值字典的Key就可以自动实现赞或者踩。
修改完成以后重启网站,可以看到“点赞”和“点踩”功能已经恢复正常。
本章小结
本章搭建了问答网站的基本功能来巩固MongoDB的聚合操作,同时学习了update_one中的一个新的操作符“$inc”。
整个网站是基于Python的网络框架Flask来实现的,读者只需要修改your_code_here文件夹下的MongoUtil.py就可以在网站上看到运行效果。
上一章
目录
下一章