1.操作数据库
本章开发一个命令行工具,用于与Elasticsearch进行交互。Elasticsearch是一个模式自由、支持RESTful的NoSQL数据库,它通过HTTP存储和索引JSON文档。我们的程序可以通过多种选项进行配置,并且支持高级查询功能。它还能批量插入导入文档。这是一个机遇JSON的文档数据库Elasticsearch。除了操纵JSON,还会使用一个jq的命令行工具。
1.1 Elasticsearch入门
Elasticsearch是一个分布式、面向文档的NoSQL数据库,提供丰富的查询功能,包括全文搜索、词干搜索、模式搜索。Elasticsearch还可以执行各种聚合查询,使用过滤器,执行数字比较。
当然,没有一种工具是万能的,Elasticsearch也不例外。但考虑到古藤堡项目的文件特性(包括书籍名称,作者),Elasticsearch很适合存储这些数据。我们需要先把文档存储在Elasticsearch,后面在这个基础上开发RESTful API。
Elasticsearch的集群架构提供了非常好的可扩展性和可靠性。它的分片和复制机制不但可以防止停机,还能实现并行查询。与Elasticsearch进行交互必须依靠正确的HTTP请求,后面会介绍
1.1.1 安装先决条件
Elasticsearch是使用java8开发的,所以要安装java 运行时环境。建议安装java8。在命令行执行java-version,确认java已安装。
java -version
1.1.2 安装Elasticsearch
去Elasticsearch的官网上下https://www.elastic.co/cn/downloads/elasticsearch
下载后将其解压,在命令行运行
binelasticsearch
输出内容如下
1.2 使用Commader创建命令行程序
生成一个命令行程序的框架,该命令行程序能访问Elasticsearch的某些功能。开始前,创建一个package.json文件。创建目录esclu,作为Elasticsearch交互的项目目录。
进入esclu目录,运行npm init
,
1.2.1 介绍Commader和Request模块
使用一个名为Commader的模块,它可以在Node.js中构建复杂的命令行工具。
尽管Node.js的内置HTTP模块对HTTP请求提供了基本支持,但其功能还不够强大。下面将使用更高级的Request模块简化HTTP请求和处理异步响应的工作。
Commader和Request模块可以减少代码,同时提供丰富的功能。安装这2个模块
npm install --save --save-exact commander@2.9.0 request@2.79.0
Commader模块不是npm中唯一一个可以帮助创建命令行程序的模块。例如yargs模块就具有许多与Commander相同的功能。使用yargs,你不必事先明确声明每个选项,只要在开发时所需选项和数据类型做检查。
Request模块也不是唯一一个可以简化HTTP请求的模块。另一个类似的模块是superagent,但其目标主要是兼容浏览器和Node.js。
还有一个与Request类似的模块是node-fetch,它支持Fetch API,Fetch API是用于取代XMLHttpRequest的新API,后面将使用它。
1.2.2 使用Commander创建基本的命令行程序
Commander模块可以处理各种细节:强制检查所需的参数,解析命令行选项,解析标志的缩写即别名等。
首先,需要一个名为esclu(无扩展名)的可执行文件,这样可以直接执行它。而不必显式地运行Node.js。将#!放在Node.js文件的第一行行首,它告诉unix这是一个可执行文件。我们将再次使用它,不同的是,这次将工作的javascript拆分到不同的独立文件总。首先创建一个名为esclu的文件,然后
文件内容如下
#! /usr/bin/env node
require('./index.js');
保存esclu文件,使用chmod命令赋予执行权限。
chmod +x esclu
在esclu目录下新建firstCommander.js 文件,文件如下:
#! /usr/bin/env node
const program = require('commander');
const request = require('request');
console.log(program)
program
.allowUnknownOption()
.version('0.0.1')
.usage('translator <cmd> [input]')
const url = `http://fanyi.youdao.com/openapi.do?keyfrom=toaijf&key=868480929&type=data&doctype=json&version=1.1`;
const fullUrl = (path = '') => {
program.host = 'localhost';
program.port = '9200';
program.index = 'books';
let url = `http://${program.host}:${program.port}`;
if (program.index) {
console.log('program index is' + program.index);
url += program.index + "/";
if (program.type) {
url += program.type + '/';
}
}
console.log('program index is' + program.index);
return url + path.replace(/^/*/, '');
}
program
.command('url [path]')
.description('generate the URL for the options and path (default is /)')
.action((path = '/') => console.log(fullUrl(path)));
// program
// .command('get [path]')
// .description('perform an HTTP GET request for path (default is /')
// .action((path = '/') => {
// const options = {
// url: fullUrl(path),
// json: program.json
// }
// request(options, (err, res, body) => {
// if (program.json) {
// console.log(JSON.stringify(err || body))
// } else {
// if (err) throw err;
// console.log(body);
// }
// });
// });
program
.command('create-index')
.description('crete an index')
.action((index) => {
console.log('ddd')
// if (!program.index) {
// const msg = 'No index specified! Use --index <name>';
// if (!program.json) throw Error(msg);
// console.log(JSON.stringify({ error: msg }));
// return;
// }
// console.log('crete-index');
request.put(fullUrl(), handleResponse);
})
const handleResponse = (err, res, body) => {
if (program.json) {
console.log(JSON.stringify(err || body));
} else {
if (err) throw err;
console.log(body);
}
}
program
.command('query')
.description('翻译输入')
.action((obj) => {
console.log('ee')
});
program.parse(process.argv);
if (!process.argv[2]) {
program.help();
console.log();
}
注意在文件的顶部,我们将package.json赋值给一个名为pkg的常量。Node.js的require()方法可以读取JSON文件以及使用Javascript编写的模块。
接下来设置Commander提供的program对象。在设置版本、描述和用法字符串后,我们列举了一些标志及其默认值。运用哪些选项取决于具体的需求,稍后会利用这些选项和Elasticsearch进行交互。
除此之外,我们通过调用program.parse来解析Node.js命令行选项。
最后,检查program对象的args数组是否包含除字符串以外的对象。除非用户提供的参数命中了一个命名过的命令,不然Commander会把参数作为字符串保存到program.args中。稍后我们会定义一些命令。这段代码可以确保如果用户输入我们无法识别的参数,他们也会看到和输入-h一样的结果。
保存这段代码,在esclu项目目录打开终端,运行下面脚本:
$ ./esclu
Usage: esclu [options] <command> [...]
Options:
-h, --help output usage information
-V, --version output the version number
-o --host <hostname> hostname [localhost]
-p --port <number> port number [9200]
-j --json format output as JSON
-i --index <name> which index to use
-t --type <type> default type for bulk operations
正如上面看到的,帮助已经工作了。还可以尝试version选项,确认是否和package.json中指定的值相同。
1.2.3 给你的程序添加命令
接下来要给esclu添加命令以便于Elasticsearch交互。由于Elasticsearch是个RESTful数据库,所以,与它进行交互首先要编写正确的URL。
REST是representational state transfer(表现层状态转换)的缩写。RESTful的API都是基于HTTP的,其资源只能通过URL来获取。请求资源和更改资源必须用特定HTTP方法发出HTTP请求。比如,HTTP GET方法用于检索资源,HTTP PUT用于发送要保存的资源。
在Elasticsearch中,RESTful资源属于JSON文档。每个文档都存放在一个索引中,并且设置了相应的类型。要为Elasticsearch文档构建一个URL,首先需要拼接你感兴趣的索引,然后拼接你感兴趣的对象类型。(使用斜杠分割)。要获取有关整个集群的信息,可以向根目录发出HTTP GET请求。
创建一个索引来存储创建的图书数据,要获取名为books的索引信息,请使用GET请求:
http://localhost:9200/books API
URL需要包含索引和类型信息,为此,请将fullUrl()方法添加到esclu程序中,放在require行之前。
可以用这种方式执行命令
node .firstCommander.js create-index