首页

10.3项目开发过程

关灯 护眼    字体:

上一章 目录 下一章




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就可以在网站上看到运行效果。




上一章 目录 下一章