使用React、Node.js、MongoDB、Socket.IO开发一个角色投票应用的学习过程(三)

前篇

使用React、Node.js、MongoDB、Socket.IO开发一个角色投票应用的学习过程(一)
使用React、Node.js、MongoDB、Socket.IO开发一个角色投票应用的学习过程(二)

原文第十三步,Express API路由

第一个路由是用来创建角色的

app.post('/api/characters',(req,res,next) => {let gender = req.body.gender;let characterName = req.body.name;let characterIdLookupUrl = 'https://api.eveonline.com/eve/CharacterId.xml.aspx?names=' + characterName;const parser = new xml2js.Parser();async.waterfall([function(callback) {request.get(characterIdLookupUrl,(err,request,xml) => {if(err) return next(err);parser.parseString(xml,(err,parsedXml) => {try {let characterId = parsedXml.eveapi.result[0].rowset[0].row[0].$.characterID;app.models.character.findOne({ characterId: characterId},(err,model) => {if(err) return next(err);if(model) {return res.status(400).send({ message: model.name + ' is alread in the database'});}callback(err,characterId);});} catch(e) {return res.status(400).send({ message: ' xml Parse Error'});}});});},function(characterId) {let characterInfoUrl = 'https://api.eveonline.com/eve/CharacterInfo.xml.aspx?characterID=' + characterId;console.log(characterInfoUrl);request.get({ url: characterInfoUrl },(err,request,xml) => {if(err) return next(err);parser.parseString(xml, (err,parsedXml) => {if (err) return res.send(err);try{let name = parsedXml.eveapi.result[0].characterName[0];let race = parsedXml.eveapi.result[0].race[0];let bloodline = parsedXml.eveapi.result[0].bloodline[0];app.models.character.create({characterId: characterId,name: name,race: race,bloodline: bloodline,gender: gender},(err,model) => {if(err) return next(err);res.send({ message: characterName + ' has been added successfully!'});});} catch (e) {res.status(404).send({ message: characterName + ' is not a registered citizen of New Eden',error: e.message });}});});}]);
});

是不是看起来和原文的基本一模一样,只不过把var 变成了let 匿名函数变成了ES6的'=>'箭头函数,虽然我用的是warterline而原文中用的是mongoose但是包括方法名基本都一样,所以我感觉waterline是在API上最接近mongoose

顺便说一下,我为什么不喜欢mongodb,仅仅是因为有一次我安装了,只往里面写了几条测试数据,按文本算最多几kb,但第二天重启机器的时候,系统提示我,我的/home分区空间不足了(双系统分区分给linux分小了本来就不大),结果一查mongodb 的data文件 有2G多,我不知道什么原因,可能是配置不对还是别的什么原因,反正,当天我就把它删除了,

完成了这个API我们就可以往数据库里添加东西了,不知道哪些用户名可以用?相当简单,反正我用的全是一名人的名字(英文名),外国人也喜欢抢注名字,嘿嘿嘿

add character ui

原文第十三步,Home组件

基本保持和原文一样,只是用lodash 替换了 underscore

一开始我看到网上介绍lodash是可以无缝替换underscore,中要修改引用就可以,但是我用的版本是4.11.2已经有很多方法不一样了,还去掉了不少方法(没有去关注underscore是不是也在最新版本中有同样的改动)

原文中:

......
import {first, without, findWhere} from 'underscore';
......var loser = first(without(this.state.characters, findWhere(this.state.characters, { characterId: winner }))).characterId;......

修改为:

......
import {first, filter} from 'lodash';
......let loser = first(filter(this.state.characters,item => item.characterId != winner )).characterId;

findWhere 在最新版本的lodash中已经不存正,我用了filter来实现相同功能。

第十四步:Express API 路由(2/2)

GET /api/characters

原文的实现方法

/*** GET /api/characters* Returns 2 random characters of the same gender that have not been voted yet.*/
app.get('/api/characters', function(req, res, next) {var choices = ['Female', 'Male'];var randomGender = _.sample(choices);Character.find({ random: { $near: [Math.random(), 0] } }).where('voted', false).where('gender', randomGender).limit(2).exec(function(err, characters) {if (err) return next(err);if (characters.length === 2) {return res.send(characters);}var oppositeGender = _.first(_.without(choices, randomGender));Character.find({ random: { $near: [Math.random(), 0] } }).where('voted', false).where('gender', oppositeGender).limit(2).exec(function(err, characters) {if (err) return next(err);if (characters.length === 2) {return res.send(characters);}Character.update({}, { $set: { voted: false } }, { multi: true }, function(err) {if (err) return next(err);res.send([]);});});});
});

可以看到原文中用{ random: { $near: [Math.random(), 0] } }做为查询条件从而在数据库里取出两条随机的记录返回给页面进行PK,前文说过random的类型在mysql没有类似的,所以我把这个字段删除了。本来mysql,可以用order by rand() 之类的方法但是,waterlinesort(order by rand())不被支持,所以我是把所有符合条件的记录取出来,能过lodashsampleSize方法从所有记录中获取两天随机记录。

app.get('/api/characters', (req,res,next) => {let choice = ['Female', 'Male'];let randomGender = _.sample(choice);//原文中是通过nearby字段来实现随机取值,waterline没有实现mysql order by rand()返回随机记录,所以返回所有结果,用lodash来处理app.models.character.find().where({'voted': false}).exec((err,characters) => {if(err) return next(err);//用lodash来取两个随机值let randomCharacters = _.sampleSize(_.filter(characters,{'gender': randomGender}),2); if(randomCharacters.length === 2){//console.log(randomCharacters);return res.send(randomCharacters);}//换个性别再试试let oppsiteGender = _.first(_.without(choice, randomGender));let oppsiteCharacters = _.sampleSize(_.filter(characters,{'gender': oppsiteGender}),2); if(oppsiteCharacters === 2) {return res.send(oppsiteCharacters);}//没有符合条件的character,就更新voted字段,开始新一轮PKapp.models.character.update({},{'voted': false}).exec((err,characters) => {if(err) return next(err);return res.send([]);});});});

在数据量大的情况下,这个的方法性能上肯定会有问题,好在我们只是学习过程,数据量也不大。将就用一下,能实现相同的功能就可以了。

GET /api/characters/search

这个API之前还有两个API,和原文基本一样,所做的修改只是用了ES6的语法,就不浪费篇幅了,可以去我的github看

这一个也只是一点mongoosewaterline的一点点小区别
原文中mongoose的模糊查找是用正则来做的,mysql好像也可以,但是warterline中没有找到相关方法(它的文档太简陋了)
所以原文中

app.get('/api/characters/search', function(req, res, next) {var characterName = new RegExp(req.query.name, 'i');Character.findOne({ name: characterName }, function(err, character) {......

我改成了

app.get('/api/characters/search', (req,res,next) => {app.models.character.findOne({name:{'contains':req.query.name}}, (err,character) => {.....

通过contains来查找,其实就是like %sometext%的方法来实现
下面还有两个方法修改的地方也大同小异,就不仔细讲了,看代码吧

GET /api/stats

这个是原文最后一个路由了,
原文中用了一串的函数来获取各种统计信息,原作者也讲了可以优化,哪我们就把它优化一下吧

app.get('/api/stats', (req,res,next) => {let asyncTask = [];let countColumn = [{},{race: 'Amarr'},{race: 'Caldari'},{race: 'Gallente'},{race: 'Minmatar'},{gender: 'Male'},{gender: 'Female'}];countColumn.forEach(column => {asyncTask.push( callback => {app.models.character.count(column,(err,count) => {callback(err,count);});})});asyncTask.push(callback =>{app.models.character.find().sum('wins').then(results => {callback(null,results[0].wins);});} );asyncTask.push(callback => {app.models.character.find().sort('wins desc').limit(100).select('race').exec((err,characters) => {if(err) return next(err);let raceCount = _.countBy(characters,character => character.race);console.log(raceCount);let max = _.max(_.values(raceCount));console.log(max);let inverted = _.invert(raceCount);let topRace = inverted[max];let topCount = raceCount[topRace];callback(err,{race: topRace, count: topCount});});});asyncTask.push(callback => {app.models.character.find().sort('wins desc').limit(100).select('bloodline').exec((err,characters) => {if(err) return next(err);let bloodlineCount = _.countBy(characters,character => character.bloodline);let max = _.max(_.values(bloodlineCount));let inverted = _.invert(bloodlineCount);let topBloodline = inverted[max];let topCount = bloodlineCount[topBloodline];callback(err,{bloodline: topBloodline, count: topCount});});});async.parallel(asyncTask,(err,results) => {if(err) return next(err);res.send({totalCount: results[0],amarrCount: results[1],caldariCount: results[2],gallenteCount: results[3],minmatarCount: results[4],maleCount: results[5],femaleCount: results[6],totalVotes: results[7],leadingRace: results[8],leadingBloodline:results[9]});}) 
});

我把要统计数据的字段放入一个数组countColumn通过forEach把push到asyncTask,最后两个统计方法不一样的函数,单独push,最后用async.parallel方法执行并获得结果。

underscore的max方法可以从{a:1,b:6,d:2,e:3}返回最大值,但是lodash新版中的不行,只能通过_.max(_.values(bloodlineCount))这样的方式返回最大值。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/398383.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

匿名方法,lambad表达式,匿名类

其实lambad表达式就是“函数”或者说是“方法”写法的一个进化,越来越简化而已,如数学方法里的f(X)。 匿名方法:顾名思义,匿名方法就是没有名称的方法,但是有定义参数。 匿名方法最明显的好处就是可以降低另写一个方法…

Python3.1-标准库之Numpy

这系列用来介绍Python的标准库的支持Numpy部分。资料来自http://wiki.scipy.org/Tentative_NumPy_Tutorial,页面有许多链接,这里是直接翻译,所以会无法链接。可以大致看完该博文,再去看英文版。 1、先决条件 想要运行numpy&#x…

poxtfix+dovecot+saslauthd+courier-authlib +mysql + extmail 完整虚拟邮箱系统部署

所需软件:平台,RHEL5.8postfix-2.9.15 dovecot系统自带courier-authlib-0.62.4高与0.64本平台可能无法使用mysql 5.7 含:mysql-5.7.12-1.el5.x86_64.rpm mysql-community-client-5.7.12-1.el5.x86_64.rpmmysql-community-common-5.7.12-1.el5…

C++笔记------模版

模版-----是为了让代码更加通用&#xff0c;使代码不受数据类型的影响。减少代码冗余。模版将数据类型当作一个参数进行传递。包括函数模版和类模板。 函数模版&#xff1a; //定义一个比较大小的函数模版 template<typename Type> // 也可以写成 template <class Ty…

黑马Android全套视频无加密完整版

课程描述&#xff1a;java基础到javaweb开发&#xff0c;从基础入门到实战。安卓基础到实战。实体班近5个月的课程实录&#xff01;&#xff01;&#xff01;课程表&#xff1a;xml&dom_sax_dom4j编程tomcat与web程序结构与Http协议与HttpUrlConnectionjava web之servletja…

target runtime apache v7.0 not defined 的解决方法

在工作台目录下找到自己操作的项目的文件夹 /.settings/org.eclipse.wst.common.project.facet.core.xml 删掉 <runtime name"Apache Tomcat v7.0"/> 参考&#xff1a;https://jingyan.baidu.com/album/ce09321b479d102bfe858f66.html?picindex1转载于:https…

强大的CSS3动画库animate.css

今天要给大家介绍一款强大的CSS3动画库animate.css&#xff0c;animate.css定义了大概50多种动画形式&#xff0c;包括淡入淡出&#xff0c;文字飞入、左右摇摆动画等等。使用animate.css也非常简单&#xff0c;你可以给页面上的任意元素&#xff0c;特别是文字添加各种神奇的动…

有关系统环境变量的设置问题

不知道各位朋友有没有想过这样用的问题&#xff0c;我们在使用windows使用在cmd下运行Ping、Netstat等之类的命令时&#xff0c;为什么可以使用呢&#xff1f; 这些后面牵涉到哪些东西呢&#xff1f; 我们今天就来介绍下有关这方面的知识。我们现在以win8作为测试对象&#xff…

ThinkPHP系统流程

流程图&#xff1a; 参考网址&#xff1a;http://www.thinkphp.cn/topic/35803.html 1 用户URL请求2 调用应用入口文件&#xff08;通常是网站的index.php&#xff09;3 载入框架入口文件&#xff08;ThinkPHP.php&#xff09;4 记录初始运行时间和内存开销5 系统常量判断及定义…

@HTML

Html.TextBoxFor(model > model.ProCategoryNo, new { maxlength "30", readonly true })转载于:https://www.cnblogs.com/dekevin/p/4371794.html

linux下安装配置jdk(解压版)

在linux下登录oracle官网&#xff0c;下载解压版jdk 传送门系统默认下载到“下载”目录中创建要将该文件解压的文件夹&#xff1a;其中 -p 参数代表递归创建文件夹&#xff08;可以创建多级目录&#xff09; 进入到下载目录&#xff0c;将下载好的jdk解压到指定目录配置环境…

curl 学习

查看网站所用的web服务器信息&#xff0c;curl -I url #curl -I www.baidu.com转载于:https://www.cnblogs.com/amusic/p/5489378.html

Struts2的Action配置的各项默认值

1 如果没有为action指定class&#xff0c;默认是ActionSupport 2 如果没有为action指定method&#xff0c;默认执行action中的execute()方法 3 如果没有指定result的name属性&#xff0c;默认值为success 转载于:https://www.cnblogs.com/feifeicui/p/8728546.html

谈谈Angular关于$watch,$apply 以及 $digest的工作原理

这篇文章主要是面向那些刚开始学AngularJs和想要了解数据绑定&#xff08;data-binding&#xff09;是怎么工作的&#xff0c; 如果你已经熟悉如何使用angularjs了&#xff0c;我强烈建议你不用阅读了。 angularjs使用者想要知道data-binding是如何工作的&#xff0c;就会遇到很…

Tachyon更名为 Alluxio,并发布1.0版本

详细参考 http://www.alluxio.org/releases/alluxio-1-0-0-release.html http://geek.csdn.net/news/detail/57243 http://www.alluxio.org/ Alluxio介绍 Alluxio 1.0版本&#xff0c;作为世界上首款以内存为中心的虚拟分布式存储系统&#xff0c;它能够统一数据访问并成为连接…

[leetcode] N-Queens II

N-Queens II Follow up for N-Queens problem. Now, instead outputting board configurations, return the total number of distinct solutions. 1 class Solution2 {3 private:4 // 试探算法从最右边的列开始。5 void test(long row, long ld, long rd)6 {7 if (row …

php设计模式-工厂设计模式

概念&#xff1a; 工厂设计模式提供获取某个对象的新实例的一个接口&#xff0c;同时使调用代码避免确定实际实例化基类步骤。 很多高级模式都是依赖于工厂模式。 转载于:https://www.cnblogs.com/sjhsszl/p/8729217.html

一幅长文细学MongoDB(四)——索引

4 索引 文章目录4 索引4.1 概述4.2 索引类型4.3 索引创建4.3 删除索引4.4 查看索引执行计划4.5 涵盖的查询4.1 概述 说明&#xff1a;索引支持在MongoDB中高效地查询。如果没有索引&#xff0c;MongoDB必须执行全集合扫描&#xff0c;即扫描集合中的每个文档&#xff0c;以选择…

【转】Android图片加载神器之Fresco-加载图片基础[详细图解Fresco的使用]

Fresco简单的使用—SimpleDraweeView 百学须先立志—学前须知&#xff1a; 在我们平时加载图片(不管是下载还是加载本地图片…..)的时候&#xff0c;我们经常会遇到这样一个需求&#xff0c;那就是当图片正在加载时应该呈现正在加载时的图像&#xff0c;当图片加载失败时应该呈…

对象映射工具AutoMapper介绍

AutoMapper是用来解决对象之间映射转换的类库。对于我们开发人员来说&#xff0c;写对象之间互相转换的代码是一件极其浪费生命的事情&#xff0c;AutoMapper能够帮助我们节省不少时间。 一. AutoMapper解决了什么问题? 要问AutoMapper解决了什么问题&#xff1f; 难道不是对象…