志科旅行サービスシステム
序文
通过简单的学习了elasticsearch基础,想做一个简单的、类似于百度京东等搜索项目巩固学习知识。
ログスタッシュは使いやすい
下载使用logstash:
Logstash は、分散した多様なログを簡単に収集し、カスタマイズした処理を実行して、サーバーやファイルなどの指定した場所に転送できる、軽量のログ収集および処理フレームワークです。Logstash はデータベースと直接関連付けられ、インデックスを自動的に更新します。データベース内のデータに基づいています。
https://www.elastic.co/cn/downloads/past-releases/logstash-7-7-0
1.控制台采集数据,控制台输出数据:
logstash -e " intput {stdin {}} output {stdout {}}"
2.控制台采集数据,控制台输出数据(json)
bin/logstash -e "input { stdin { } } output { stdout {codec => json} }"
3.把命令放到文件中使用:
创建demo.conf,添加: intput {stdin {}} output {stdout {}}
启动 bin/logstash -f demo.conf
4.将mysql数据同步到es:
a. mysql ドライバーをダウンロードします
b. インストール ディレクトリに、新しい構成ファイル demo2.conf を作成し、次の内容を追加します
input {
# 多张表的同步只需要设置多个jdbc的模块就行了
jdbc {
# mysql 数据库链接
jdbc_connection_string => "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC"
# 用户名和密码
jdbc_user => "root"
jdbc_password => "986836"
# 驱动
jdbc_driver_library => "G:/Download/logstash-7.7.0/data/mysql-connector-java-8.0.27.jar"
# 驱动类名
jdbc_driver_class => "com.mysql.cj.jdbc.Driver"
#是否分页,及分页大小
jdbc_paging_enabled => "true"
jdbc_page_size => "100"
#直接执行sql语句
statement =>"select * from book"
# 执行的sql 文件路径+名称
# statement_filepath => "..../test.sql"
#设置监听间隔 各字段含义(由左至右)分、时、天、月、年,全部为*默认含义为每分钟都更新
schedule => "10 * * * *"
# 索引类型
#type => "jdbc"
}
}
output {
elasticsearch {
#es的ip和端口
hosts => ["http://hadoop:9200"]
#ES索引名称(自己定义的)
index => "books"
#文档类型
document_type => "_doc"
#设置数据的id为数据库中的字段
document_id => "%{isbn}"
}
stdout {
codec => json_lines
}
}
実行: logstash -f …/data/demo2.conf
1. プロジェクト要件の概要
このシステムは、ユーザーのニーズに応え、機能が充実しており、操作も簡単で使いやすいです。主な機能は、近くの/コスト効率の高いグルメ、ホテル、レジャー、エンターテイメント、フットマッサージ、KTVなどを推奨できるシステムの実装です。 、ユーザーの場所/評価を通じて、魅力や美しさ、クラブやその他の食べたり飲んだり楽しんだりできる場所がユーザーに推奨されます。また、推奨される方法は、近接性の原則に基づいて計算されるだけでなく、システム上の加盟店評価によっても計算されます。さまざまなユーザーのニーズに基づいて、さまざまなカテゴリのプロジェクトを推奨します。このシステムはコードを最適化するだけでなく、データ処理の速度も向上させるため、目的のユーザー エクスペリエンスに合わせて開発中のユーザーの快適性が向上します。
開発効率とビルドレベルの問題を考慮すると、システムのパフォーマンスの長所と短所は、システムの応答性だけでなく、ユーザーの視覚的なエクスペリエンスや使用方法に基づいて表現できます。このシステムはコードを最適化するだけでなく、データ処理の速度も向上させるため、目的のユーザー エクスペリエンスに合わせて開発中のユーザーの快適性が向上します。
ユーザーはシステム内の検索機能を通じて希望のカテゴリー項目を検索することができ、検索機能ではユーザーの実際の距離と店舗までの距離をLBSに基づく評価項目として厳密に計算し、距離、評価、低価格を利用して現在検索している加盟店を評価することができます。 . 検索機能を並べ替えたり、絞り込んだりできます。
ユーザーは販売者を表示するときに、販売者のストアにコメントおよび評価して、ストアの推奨事項の精度を向上させることができます。
ユーザーのコメントや評価情報、ユーザーのクリック行動を通じて、オフラインおよびリアルタイムの分析も行われます。
2. 技術的な実現可能性
フロントエンドとバックエンドの分離は、インターネット プロジェクト開発の業界標準手法となっています。このシステムも、フロントエンドとバックエンドの分離 vue+springboot アーキテクチャを採用します。このシステムを実装するには、ここでは Java 言語を選択します。 -エンドテクニカルサポート、vue+elementUI+echarts など。フロントエンドアーキテクチャサポート、Python クローラー、ケトル、flume+kafka はデータ収集サポートとして使用され、mysql、mongdb はデータストレージサポートとして使用され、elasticsearch は完全な実装に使用されます。 - テキスト検索など Java 言語をベースとしたいくつかのミドルウェアは、ビッグデータ開発のサポートとして使用されます。
インターネットの世界では、市場要因のあるソフトウェアには爆発的な力があり、ソフトウェアのアクセス数やユーザー数が爆発的に増加する場合、ソフトウェアのコードが堅牢かどうか、ピークを抑えるミドルウェアが使用されているかどうかが非常に重要です。本プロジェクトの設計では、キャッシュ層フレームワークとしてredisをフル活用し、データベースへのアクセス回数を削減するほか、データ重複排除効果や地理的位置計算などの機能を実現します。大量の DEMO データや実際のユーザー データがある場合、ログ分析方法として Hadoop (mapreduce) + Spark + scala が使用されます。これには、ページ訪問数、HTTP リクエスト数、独立した IP 訪問の数、さまざまなページの訪問と更新、数量など。Kafka は、トラフィックのピークカットやメッセージ キューの非同期およびリアルタイム送信にも使用されます。
推奨アルゴリズムでは、マーチャントのユーザー評価の計算に als アルゴリズムが使用されます。式は、共分散 cov(X に等しい 2 つの連続変数 (X, Y) のピアソン相関係数 P(x, y) として定義されます、Y) をそれぞれの標準偏差 (σX,σY) の積で割ります。係数の値は常に -1.0 から 1.0 の間です。0 に近い変数は無相関と呼ばれ、1 または -1 に近い変数は強い相関と呼ばれます。通常、変数の相関の強さは次の値の範囲で判断されます。 相関係数 0.8-1.0 非常に強い相関 0.6-0.8 強い相関 0.4-0.6 中程度の相関 0.2-0.4 弱い相関 0.0-0.2 非常に弱い相関または相関がない場合は、echarts を使用してください。データ結果を視覚化し、最終的に Springboot を通じてさまざまなフレームワークを統合してユーザーに表示します。
3. es+hbase は検索エンジンを構築します
インデックス ライブラリ パラメータの指定
次に、インデックス ライブラリの設定とマッピング パラメータを手動で指定します。
{
"settings":{
"number_of_shards":5, #分片数
"number_of_replicas":1 #副本数
},
"mappings":{
"dynamic":"strict",
"_source":{"excludes":["content"]},
"properties":{
"title":{"type":"text","analyzer":"ik_max_word"},
"point":{"type":"keyword"},
"price":{"type":"double"},
"level":{"type":"keyword"},
"address":{"type":"text","analyzer":"ik_max_word"}
"describe":{"type":"text","analyzer":"ik_max_word"},
"content":{"type":"text","analyzer":"ik_max_word"}
}
}
}
説明:
動的パラメータには 4 つのオプション値があります:
1. true はデフォルトで、動的マッピングが有効であることを示します
2. false は、未定義のフィールドが無視されることを示します
3. strict は、不明なフィールドが見つかったときに例外がスローされることを示します。
4. runtime は、不明なフィールドが検出されたことを示します。フィールドを使用するときは、それを runtime フィールドとして扱います。runtime フィールドは ES7.11 バージョンで追加されました。runtime フィールドにはインデックスが付けられませんが、runtime フィールドの内容はインデックス付けできます。 _source から取得されるため、ランタイムは既知のパブリック フィールドに適しており、未知の拡張フィールドのシナリオと互換性が必要です。
データをクロールして hbase に保存し、es インデックスを構築します。
package com.niit.data_manager.utils;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.util.Bytes;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Stream;
/**
* @author brett
* @date 2022-05-08 20:25:43
*
* Jsoup爬取旅游数据
*/
public class mockData {
private static final String[] arr = {
//todo 用alt+光标 选中同时编辑多行
"https://gs.ctrip.com/html5/you/place/1.html",
"https://gs.ctrip.com/html5/you/place/2.html",
"https://gs.ctrip.com/html5/you/place/39.html",
"https://gs.ctrip.com/html5/you/place/38.html",
"https://gs.ctrip.com/html5/you/place/21.html",
"https://gs.ctrip.com/html5/you/place/32.html",
"https://gs.ctrip.com/html5/you/place/61.html",
"https://gs.ctrip.com/html5/you/place/100001.html",
"https://gs.ctrip.com/html5/you/place/100007.html",
"https://gs.ctrip.com/html5/you/place/100008.html",
"https://gs.ctrip.com/html5/you/place/100003.html",
"https://gs.ctrip.com/html5/you/place/100009.html",
"https://gs.ctrip.com/html5/you/place/100076.html",
"https://gs.ctrip.com/html5/you/place/100039.html",
"https://gs.ctrip.com/html5/you/place/100058.html",
"https://gs.ctrip.com/html5/you/place/100053.html",
"https://gs.ctrip.com/html5/you/place/100064.html",
"https://gs.ctrip.com/html5/you/place/100062.html",
"https://gs.ctrip.com/html5/you/place/100065.html",
"https://gs.ctrip.com/html5/you/place/100066.html",
"https://gs.ctrip.com/html5/you/place/100068.html",
"https://gs.ctrip.com/html5/you/place/100056.html",
"https://gs.ctrip.com/html5/you/place/100055.html",
"https://gs.ctrip.com/html5/you/place/100054.html",
"https://gs.ctrip.com/html5/you/place/100051.html",
"https://gs.ctrip.com/html5/you/place/100059.html",
"https://gs.ctrip.com/html5/you/place/100038.html",
"https://gs.ctrip.com/html5/you/place/100052.html",
"https://gs.ctrip.com/html5/you/place/100060.html",
"https://gs.ctrip.com/html5/you/place/100067.html",
"https://gs.ctrip.com/html5/you/place/100063.html",
"https://gs.ctrip.com/html5/you/place/100031.html",
"https://gs.ctrip.com/html5/you/place/100032.html",
"https://gs.ctrip.com/html5/you/place/19.html",
"https://gs.ctrip.com/html5/you/place/14.html",
"https://gs.ctrip.com/html5/you/place/5.html",
"https://gs.ctrip.com/html5/you/place/508.html",
"https://gs.ctrip.com/html5/you/place/23.html"
};
//todo 百度百科爬取数据
public static Scenery searchScenery(Scenery scenery, String key) throws IOException {
ArrayList<String> contentList = new ArrayList<>();
ArrayList<String> imgList = new ArrayList<>();
String url = "https://baike.baidu.com/item/";
Document document = Jsoup.connect(url + key).get(); // 获取页面文档
String describe = document.getElementsByClass("para").eq(0).text();
scenery.setDescribe(describe);
Stream<Element> para = document
.getElementsByClass("para")
.stream().limit(6); // 获取前6条数据
para.forEach(element -> contentList.add(element.text()));
scenery.setContent(contentList);
Stream<Element> img = document.getElementsByClass("lazy-img").stream().limit(6);
img.forEach(element -> imgList.add(element.attr("data-src")));
scenery.setImgs(imgList);
return scenery;
}
//todo 设置es索引并将数据内容保存到hbase中
public static void main(String[] args) throws Exception {
HBaseUtil.createTable("scenery", "info"); //先创建数据表
String url = "https://m.ctrip.com/webapp/you/gspoi/sight/";
for (String s : arr) {
String s2 = s.split("/")[6];
List<String> linkList = new ArrayList<>();
if (s2.length() <= 10) {
linkList.add(s2);
} else {
Document document = Jsoup.connect(s).get();
Elements sightList = document.getElementsByClass("js_list_view");
Document parse = Jsoup.parse(sightList.toString());
Elements js_toJump = parse.getElementsByClass("js_toJump");
js_toJump.forEach(e -> {
String link = e.getElementsByIndexEquals(0).attr("href");
if (link.contains("html")) {
linkList.add(link.split("/")[6]);
}
});
}
for (String s3 : linkList) {
List<Put> sceneryList = new ArrayList<>();
Document document = Jsoup.connect(url + s3).get();
Object[] poiNames = document.getElementsByClass("poiName").stream().limit(5).toArray();
Object[] distanceFields = document.getElementsByClass("distanceField").stream().limit(5).toArray();
Object[] poiLevel = document.getElementsByClass("poiLevel").stream().limit(5).toArray();
Object[] commentScoreNum = document.getElementsByClass("commentScoreNum").stream().limit(5).toArray();
Object[] priceFont = document.getElementsByClass("priceFont").stream().limit(5).toArray();
System.out.println("name:" + poiNames.length + " 地址:" + distanceFields.length + " 等级:" + poiLevel.length + " 评分:" + commentScoreNum.length + " 价格:" + priceFont.length);
if (poiLevel.length == 5 && priceFont.length == 5 && distanceFields.length==5 && commentScoreNum.length==5) {
for (int i = 0; i < 5; i++) {
HashMap<String, String> indexMap = new HashMap<>();
String title = ((Element) poiNames[i]).getElementsByTag("a").attr("title");
String addr = ((Element) distanceFields[i]).text();
String level = ((Element) poiLevel[i]).text();
Double score = new Double(((Element) commentScoreNum[i]).text());
Double price = new Double(((Element) priceFont[i]).text());
Scenery scenery = new Scenery();
scenery.setName(title);
scenery.setAddress(addr);
scenery.setLevel(level);
scenery.setScore(score);
scenery.setTicketPrice(price);
scenery.setPoint(BaiDuApiUtil.getLatAndLngByAddress(title));
Scenery s02 = searchScenery(scenery, title);
// 设置数据在elasticsearch中的索引
indexMap.put("title", s02.getName());
indexMap.put("point", s02.getPoint());
indexMap.put("price", s02.getTicketPrice().toString());
indexMap.put("level", s02.getLevel());
indexMap.put("score", s02.getScore().toString());
indexMap.put("address", s02.getAddress());
indexMap.put("describe", s02.getDescribe());
//向索引库中添加
ESUtil.addIndex("scenery", s02.getName(), indexMap);
Put put = new Put(Bytes.toBytes(s02.getName()));
// 将数据入库到hbase
put.addColumn(Bytes.toBytes("info"), Bytes.toBytes("img"), Bytes.toBytes(s02.getImgs().toString()));
put.addColumn(Bytes.toBytes("info"), Bytes.toBytes("content"), Bytes.toBytes(s02.getContent().toString()));
sceneryList.add(put);
}
HBaseUtil.put2HBaseList("scenery", sceneryList);
try {
Thread.sleep(600);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
図に示すように、hbase はデータを正常に保存します。
インデックスの作成が成功しました。
シーナリー シーナリー エンティティ クラス
package com.example.ssm.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* @author brett
* @date 2022-05-09 15:21:23
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Scenery {
private StringBuilder title; // 景点名
private String point; //经纬度
private Double ticketPrice; //票价
private String level; //景区等级
private Double score; //评分
private String address; //地址
private StringBuilder describe; // 描述
private String firstImg; //封面图
private List<String> content; //景点简介
private List<String> imgs; // 景点图片
}
地名から対応する経度と緯度を検索する
package com.niit.data_manager.utils;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
/**
* @author brett
* @date 2022-05-15 20:29:39
*/
public class BaiDuApiUtil {
private static final String AK = "q4WRaidEQFHCqNC7Izof4GxWe8zAEbDj";
public static String getLatAndLngByAddress(String addr) {
String address = "";
String result = "";
try {
address = java.net.URLEncoder.encode(addr, "UTF-8");
} catch (UnsupportedEncodingException e1) {
e1.printStackTrace();
}
String url = "https://api.map.baidu.com/geocoding/v3/?address=" + address +
"&output=json&ak="+AK+"&callback=showLocation ";
URL myURL = null;
URLConnection httpsConn;
//进行转码
try {
myURL = new URL(url);
} catch (MalformedURLException ignored) {
ignored.printStackTrace();
}
try {
assert myURL != null;
httpsConn = myURL.openConnection();
if (httpsConn != null) {
InputStreamReader insr = new InputStreamReader(
httpsConn.getInputStream(), StandardCharsets.UTF_8);
BufferedReader br = new BufferedReader(insr);
String data = br.readLine();
String[] split = data.replace("{", ":").replace("}", "::").split("::");
if (split.length >=3){
result = split[2];
}
insr.close();
}
} catch (IOException ignored) {
ignored.printStackTrace();
}
return result;
}
}
HBaseUtil ツール クラス
package com.example.ssm.utils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CellUtil;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.util.Bytes;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class HBaseUtil {
private static Connection connection;
//初始化创建连接
static {
try{
connection = getConnection();
}catch(IOException e){
e.printStackTrace();
}
}
//todo 创建hbase连接
public static Connection getConnection() throws IOException {
Configuration conf = HBaseConfiguration.create();
conf.set("hbase.zookeeper.quorum","192.168.111.137");
conf.set("hbase.zookeeper.property.clientPort","2181");
return ConnectionFactory.createConnection(conf);
}
//TODO 创建hbase表
public static void addTable(String tableName,String... cfs) throws IOException {
//获取admin
Admin admin = connection.getAdmin();
//创建列族builder
List<ColumnFamilyDescriptor> list = new ArrayList<>();
for (String cf:cfs){
ColumnFamilyDescriptor columnFamilyDescriptor = ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes(cf)).build();
list.add(columnFamilyDescriptor);
}
//创建TableBuilder
TableDescriptor tabBuilder = TableDescriptorBuilder.newBuilder(TableName.valueOf(Bytes.toBytes(tableName)))
.setColumnFamilies(list).build();
admin.createTable(tabBuilder);
admin.close();
}
//todo 向hbase中添加一条数据
public static void addData(String tabName,String rowKey,String cfName,String column,String value) throws IOException {
Table table = connection.getTable(TableName.valueOf(Bytes.toBytes(tabName)));
Put put = new Put(Bytes.toBytes(rowKey));
put.addColumn(Bytes.toBytes(cfName),Bytes.toBytes(column),Bytes.toBytes(value));
table.put(put);
table.close();
}
//todo 向hbase中添加一组数据
public static void addListData(String tabName,List<Put> list) throws IOException {
Table table = connection.getTable(TableName.valueOf(tabName));
table.put(list);
table.close();
}
//todo 根据rowKey获取hbase中的数据
public static Map<String,String> getDataByRowkey(String tabName,String rowkey) throws IOException {
Table table = connection.getTable(TableName.valueOf(Bytes.toBytes(tabName)));
Get get = new Get(Bytes.toBytes(rowkey));
Result result = table.get(get);
HashMap<String, String> map = new HashMap<>();
map.put("title",rowkey);
System.out.println("title:"+rowkey);
for (Cell c:result.listCells()){
//获取列信息
byte[] column = CellUtil.cloneQualifier(c);
//获取值
byte[] value = CellUtil.cloneValue(c);
map.put(new String(column),new String(value));
}
return map;
}
}
ESUtil ツール クラス
package com.example.ssm.utils;
import com.example.ssm.domain.Scenery;
import org.apache.http.HttpHost;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author brett
* @date 2022-05-09 18:49:34
*
* es工具类
*/
public class ESUtil {
private ESUtil(){
};
private static RestHighLevelClient client;
private static String host = "192.168.111.137";
private static int port = 9200;
static {
//获取es连接
RestClientBuilder restBuilder = RestClient.builder(new HttpHost(host, port, "http"));
client = new RestHighLevelClient(restBuilder);
}
//获取es连接
public RestHighLevelClient getClient(){
return client;
}
// 建立索引
public static void addIndex(String index, String id, Map<String,String> map) throws IOException {
IndexRequest indexRequest = new IndexRequest(index);
indexRequest.id(id);
indexRequest.source(map);
// 执行
client.index(indexRequest, RequestOptions.DEFAULT);
}
//todo 全文检索功能
public static Map<String,Object> search(String key,String index,int start,int row) throws IOException {
if (start<=1){
start=1;
}
SearchRequest searchRequest = new SearchRequest();
// 指定索引库
searchRequest.indices(index);
//指定searchType
searchRequest.searchType(SearchType.QUERY_THEN_FETCH);
//组装查询条件
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//如果传递了搜索参数,则拼接查询条件
// if (StringUtils.isNotBlank(key)){ //StringUtils.isNotBlank()判断参数是否为空
// searchSourceBuilder.query(QueryBuilders.multiMatchQuery(key,"title"));
// }
//多条件查询
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
MatchQueryBuilder q1 = QueryBuilders.matchQuery("title", key);
MatchQueryBuilder q2 = QueryBuilders.matchQuery("describe", key);
boolQuery.should(q1);
boolQuery.should(q2);
searchSourceBuilder.query(boolQuery);
//分页
searchSourceBuilder.from((start-1)*row);
searchSourceBuilder.size(row);
//高亮
//设置高亮字段
HighlightBuilder highlightBuilder = new HighlightBuilder()
.field("title")
.field("describe"); //支持多个高亮字段
//设置高亮字段的前缀后缀
highlightBuilder.preTags("<font color='red'>");
highlightBuilder.postTags("</font>");
searchSourceBuilder.highlighter(highlightBuilder);
//指定查询条件
searchRequest.source(searchSourceBuilder);
//执行查询操作
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
//存储返回给页面的数据
HashMap<String, Object> map = new HashMap<>();
//获取查询到的返回结果
SearchHits hits = searchResponse.getHits();
//获取数据总量
long numHits = hits.getTotalHits().value;
map.put("count",numHits);
List<Scenery> sceneries = new ArrayList<>();
//获取具体内容
SearchHit[] searchHits = hits.getHits();
//迭代解析具体内容
for (SearchHit hit:searchHits){
Map<String, Object> sourceAsMap = hit.getSourceAsMap();
StringBuilder title = new StringBuilder(sourceAsMap.get("title").toString());
StringBuilder describe = new StringBuilder(sourceAsMap.get("describe").toString());
String firstImg = sourceAsMap.get("firstImg").toString();
String score;
try {
score = sourceAsMap.get("score").toString();
} catch (NullPointerException e) {
score = "3.0";
e.printStackTrace();
}
String address = "";
try {
address = sourceAsMap.get("address").toString();
} catch (NullPointerException e) {
e.printStackTrace();
}
String level="";
try {
level = sourceAsMap.get("level").toString();
} catch (NullPointerException e) {
level = "3A";
e.printStackTrace();
}
String point="";
try {
point = sourceAsMap.get("point").toString();
} catch (NullPointerException e) {
e.printStackTrace();
}
String price="";
try {
price = sourceAsMap.get("price").toString();
} catch (NullPointerException e) {
price = "30.0";
e.printStackTrace();
}
//获取高亮字段
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
//获取title字段的高亮内容
HighlightField titleHighLight = highlightFields.get("title");
if (titleHighLight != null){
title = new StringBuilder();
Text[] fragments = titleHighLight.getFragments();
for (Text t:fragments){
title.append(t);
}
}
//获取describe的高亮字段内容
HighlightField describeHighLight = highlightFields.get("describe");
if (describeHighLight!=null){
describe = new StringBuilder();
Text[] fragments = describeHighLight.getFragments();
for (Text t:fragments){
describe.append(t);
}
}
//把信息封装到Scenery对象中
Scenery scenery = new Scenery();
scenery.setDescribe(describe);
scenery.setTitle(title);
scenery.setDescribe(describe);
scenery.setPoint(point);
scenery.setFirstImg(firstImg);
scenery.setLevel(level);
scenery.setTicketPrice(Double.parseDouble(price));
scenery.setAddress(address);
scenery.setScore(new Double(score));
sceneries.add(scenery);
}
map.put("dataList",sceneries);
return map;
}
}
对应的controller
package com.example.ssm.controller;
import com.example.ssm.utils.ESUtil;
import com.example.ssm.utils.HBaseUtil;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.util.Map;
import java.util.regex.Pattern;
/**
* @author brett
* @date 2022-05-9 14:54:03
*/
@RestController
@RequestMapping("/ES")
public class ESController {
@GetMapping("/searchData")
public Map<String,Object> getData(@RequestParam String key,@RequestParam int pageNo)
throws IOException {
return ESUtil.search(key,"scenery",pageNo,10);
}
@GetMapping("/selectDataByHbase")
public Map<String,String> getHbaseData(@RequestParam String rowkey)
throws IOException {
String regex = "^[\\u4e00-\\u9fa5]*$"; //匹配中文
StringBuilder key = new StringBuilder();
String[] arr = rowkey.split("");
for (String s:arr){
if (Pattern.matches(regex,s)){
key.append(s);
}
}
return HBaseUtil.getDataByRowkey("scenery", key.toString());
}
}
注: Elasticsearch、hadoop、および hbase 環境は、実行する前に仮想マシン環境で起動する必要があります。
4.es内のデータをMongoDBに転送する
logstash-output-mongdb プラグインをダウンロードします:
https://github.com/logstash-plugins/logstash-output-mongodb
開いたら、必ずバージョンを 3.1.5 に切り替えて、zip パッケージをダウンロードしてください。
logstash ディレクトリに解凍し、
logstash 内の Gemfile ファイルを変更し
、最後の行に gem "logstash-output-mongodb", :path => "logstash-output-mongodb-master" を追加します。
管理者を使用して CMD を開き
、logstash の bin ディレクトリに切り替えて、logstash-plugin install --no-verify を実行します。
ダウンロード後、インストール バージョンを確認します: logstash-plugin list --verbose
で構成ファイル mongdb-out を作成します。 .conf:
input {
stdin {
}
elasticsearch {
#ESIP地址与端口
hosts => "hadoop:9200"
#ES索引名称(自己定义的)
index => "scenery"
#自增ID编号
# document_id => "%{id}"
#定时字段 各字段含义(由左至右)分、时、天、月、年,全部为*默认含义为每分钟都更新
#schedule => "* * * * *"
#设定ES索引类型
type => "message"
}
}
filter {
json {
source => "message"
remove_field => ["message"]
}
}
#目标mongodb地址信息
output {
stdout {
codec => rubydebug }
mongodb {
#目标mongodb集合
collection => "scenery"
#目标库名称
database => "mydb"
uri => "mongodb://hadoop:27017"
}
}
コマンド logstash -f .../data/mongodb-out.conf を実行して、
データを正常にインポートします。!!
MongoDB でデータを表示:
データのインポートが成功しました
5. springboot は Hadoop を統合します
1. hadoop の依存関係を pom.xml ファイルにインポートします。
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>2.7.3</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
<exclusion>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>2.7.3</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
<exclusion>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>2.7.3</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
<exclusion>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</exclusion>
</exclusions>
</dependency>
hadoop和springboot冲突的主要有两个,一个是slf4j的日志包,一个是Tomcat 的servlet-api包,去掉这两个依赖就可以成功运行springboot了
application.yml 設定ファイルに基本的なスター情報を追加します。
@Value("${hadoop.name-node}")
private String nameNode;
@Test
public void createDir() throws URISyntaxException, IOException, InterruptedException {
//1获取文件系统
Configuration configuration = new Configuration();
FileSystem fileSystem = FileSystem.get(new URI(nameNode), configuration,"root");
//2执行操作
fileSystem.mkdirs(new Path("/jianxuhui"));
fileSystem.close();
System.out.println("结束!!!");
}
2. MongoDB から MapReduce 入力ソースとしてデータを読み取ります
mongodb-hadoop 依存関係を追加します。
<!-- mongodb整合hadoop,可以用mongodb作为mapreduce数据输入源 -->
<!-- https://mvnrepository.com/artifact/org.mongodb.mongo-hadoop/mongo-hadoop-core -->
<dependency>
<groupId>org.mongodb.mongo-hadoop</groupId>
<artifactId>mongo-hadoop-core</artifactId>
<version>2.0.2</version>
</dependency>
eg:从mongodb中将一个集合中的数据通过mapreduce处理,然后将结果返回到mongodb存储
package com.example.ssm.MR;
import com.mongodb.BasicDBObject;
import com.mongodb.hadoop.MongoInputFormat;
import com.mongodb.hadoop.MongoOutputFormat;
import com.mongodb.hadoop.io.BSONWritable;
import com.mongodb.hadoop.util.MongoConfigUtil;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.bson.BSONObject;
import java.io.IOException;
/**
* @author brett
* @date 2022-05-24 11:46:25
*
* 以mongodb数据作为mapreduce数据输入源
*/
public class mongodbSource {
static class mongodbMapper extends Mapper<Object, BSONObject, Text, BSONWritable>{
@Override
//实现mapper,mongodb读取出来就是一个BSONObject
protected void map(Object key, BSONObject value, Context context) throws IOException, InterruptedException {
System.out.println(key);
context.write(new Text(key.toString()),new BSONWritable(value));
}
}
static class mongodbReducer extends Reducer<Text,BSONWritable,Text,BSONWritable>{
@Override
//不做任何处理,直接将数据读取出来在存入到mongodb中
protected void reduce(Text key, Iterable<BSONWritable> values, Context context) throws IOException, InterruptedException {
for (BSONWritable value:values){
context.write(key,value);
}
}
}
public static void main(String[] args) throws IOException, InterruptedException, ClassNotFoundException {
args = new String[]{
"mongodb://192.168.111.137:27017/mydb.info",
"mongodb://192.168.111.137:27017/mydb.result"};
Configuration configuration = new Configuration();
MongoConfigUtil.setInputURI(configuration,args[0]);
MongoConfigUtil.setOutputURI(configuration,args[1]);
//可以构建条件BasicQuery查询
BasicDBObject query = new BasicDBObject();
MongoConfigUtil.setQuery(configuration,query);
MongoConfigUtil.setCreateInputSplits(configuration,false);
Job job = Job.getInstance(configuration,"Hadoop Mongodb");
job.setJarByClass(mongodbSource.class);
job.setMapperClass(mongodbMapper.class);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(BSONWritable.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(BSONWritable.class);
job.setReducerClass(mongodbReducer.class);
job.setInputFormatClass(MongoInputFormat.class);
job.setOutputFormatClass(MongoOutputFormat.class);
job.setNumReduceTasks(1);
System.exit(job.waitForCompletion(true)?0:1);
}
}
結果: 図に示すように、データは正常に転送されました。
3. Java は 2 つの場所の経度と緯度の間の距離を計算します
package com.example.ssm.utils;
/**
* @author brett
* @date 2022-05-26 22:28:51
*
* 通过经纬度计算距离
*/
public class CalculateDistance {
/*
S = acos( sin(第1个点纬度 * ∏ / 180) * sin(第2个点纬度 * ∏ / 180) + cos(第1个点纬度 * ∏ / 180) *
cos(第2个点纬度 * ∏ / 180) * cos(第2个点经度 * ∏ / 180 - 第1个点经度 * ∏ / 180)) * 地球半径
*/
//地球半径,单位km
private static final double EARTH_RADIUS = 6378.137;
public static double getDistance(Double lng1,Double lat1,Double lng2,Double lat2){
//纬度
double lat01 = Math.toRadians(lat1); //Math.toRadians()将数据转化为弧度制
double lat02 = Math.toRadians(lat2);
//经度
double lng01 = Math.toRadians(lng1);
double lng02 = Math.toRadians(lng2);
// 计算两点距离的公式
double s = Math.acos(Math.sin(lat01)*Math.sin(lat02)+Math.cos(lng01)*Math.cos(lng02)*Math.cos(lng02-lng01));
// 弧长乘地球半径, 返回单位: 千米
s = s * EARTH_RADIUS;
return s;
}
}
緯度と経度に基づいて州と都市を検索する
//传入经纬度, 返回查询的地区, lng: 经度, lat: 纬度
public static String findByLatAndLng(String lat, String lng) {
try {
//移除坐标前后的 空格
/*lng = lng.trim();
lat = lat.trim();*/
CloseableHttpClient httpClient = HttpClients.createDefault();
// url中的ak值要替换成自己的:
String url = "http://api.map.baidu.com/reverse_geocoding/v3/?ak=xeiyKzum1WHj3SE39P216gDI9cnIYbgi&output=json&coordtype=wgs84ll&location=" + lat + "," + lng;
//System.out.println(url);
HttpGet httpGet = new HttpGet(url);
CloseableHttpResponse response = httpClient.execute(httpGet);
HttpEntity httpEntity = response.getEntity();
String json = EntityUtils.toString(httpEntity);
Map<String, Object> result = JSONObject.parseObject(json, Map.class);
if (result.get("status").equals(0)) {
Map<String, Object> resultMap = (Map<String, Object>) result.get("result");
resultMap = (Map<String, Object>) resultMap.get("addressComponent");
String province = (String) resultMap.get("province");
String city = (String) resultMap.get("city");
return province + city;
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
4. SpringBoot は MapReduce の WordCount をカプセル化して文字統計を実行します
package com.example.ssm.MR;
import org.apache.hadoop.fs.FileSystem;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.net.URI;
/**
* @author brett
* @date 2022-05-28 10:13:21
*
* hadoop配置类
*/
@Configuration
public class HadoopConfig {
@Bean("hdfsConfig")
public org.apache.hadoop.conf.Configuration hdfsChannerl(){
org.apache.hadoop.conf.Configuration conf = new org.apache.hadoop.conf.Configuration();
conf.set("dfs.replication","1");
conf.set("dfs.client.use.datanode.hostname","true");
conf.set("mapred.job.tracker", "hdfs://192.168.111.137:8020/");
conf.set("fs.defaultFS", "hdfs://192.168.111.137:8020/");
System.setProperty("HADOOP_USER_NAME","root");
return conf;
}
@Bean("fileSystem")
public FileSystem createFs(@Qualifier("hdfsConfig") org.apache.hadoop.conf.Configuration conf){
FileSystem fs = null;
try {
URI uri = new URI("hdfs://192.168.111.137:8020/");
fs = FileSystem.get(uri,conf);
} catch (Exception e) {
e.printStackTrace();
}
return fs;
}
}
hadoopController クラス
@RestController
@RequestMapping(value = "/hadoop")
public class HadoopController {
@Qualifier("hdfsConfig")
@Autowired
private org.apache.hadoop.conf.Configuration conf;
@GetMapping(value = "/reduce")
public Boolean reduce() throws IOException, ClassNotFoundException, InterruptedException {
Job job = Job.getInstance(conf);
job.setMapperClass(WordCountMapper.class);
job.setReducerClass(WordCountReducer.class);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(IntWritable.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
FileInputFormat.setInputPaths(job, new Path(template.getNameSpace())); //输入路径
FileOutputFormat.setOutputPath(job, new Path("/statics")); //输出路径
return job.waitForCompletion(true);
}
}
5. Vue+Baidu Map API が現在の場所を特定します
//js文件导出
export function MP(ak) {
return new Promise(function (resolve, reject) {
window.onload = function () {
resolve(window.BMap)//插入script标签后 会在window上挂一BMap属性,此为跨域获取的数据
};
var script = document.createElement("script");
script.type = "text/javascript";
script.src = "http://api.map.baidu.com/api?v=2.0&ak=" + ak + "&callback=init";
script.onerror = reject;
document.head.appendChild(script);//插入此标签后 会在window上挂一BMap属性,此为跨域获取的数据
})
}
//在vue 生命周期函数created()中引用getCity函数
getCity() {
this.$nextTick(()=>{
MP(this.ak).then(BMap=> {
//在此调用api
var geolocation = new BMap.Geolocation();
geolocation.getCurrentPosition(a=>{
console.log(a)
this.city = a.address.province+a.address.city;
this.latitude = a.latitude;
this.longitude = a.longitude;
})
})
})
}
スプリングブート+スカラ開発
添加依赖:
<!--添加依赖-->
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
</dependency>
...
<!-- 添加插件 -->
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<id>compile-scala</id>
<phase>compile</phase>
<goals>
<goal>add-source</goal>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile-scala</id>
<phase>test-compile</phase>
<goals>
<goal>add-source</goal>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
<configuration>
<recompileMode>incremental</recompileMode>
<scalaVersion>${scala.version}</scalaVersion>
<args>
<arg>-deprecation</arg>
</args>
<jvmArgs>
<jvmArg>-Xms64m</jvmArg>
<jvmArg>-Xmx1024m</jvmArg>
</jvmArgs>
</configuration>
</plugin>
vue+echarts で中国地図を構築
下载地图离线包:
<template>
<div class="content">
<div ref="charts" style="width: 70%; height: 800px"></div>
</div>
</template>
<script>
import zhongguo from "@/assets/china.json"
export default {
created () {
this.$nextTick(() => {
this.initCharts();
})
},
methods: {
initCharts() {
const charts = this.$echarts.init(this.$refs["charts"]);
const option = {
title: {
//标题样式
text: '各省份景点访问量',
x: "center",
textStyle: {
fontSize: 18,
color: "black"
},
},
// 背景颜色
backgroundColor: "#eff3e9",
// 提示浮窗样式
tooltip: {
//数据项图形触发
trigger: 'item',
//提示框浮层的背景颜色。 (鼠标悬浮后的提示框背景颜色)
backgroundColor: "white",
//字符串模板(地图): {a}(系列名称),{b}(区域名称),{c}(合并数值),{d}(无)
formatter: '地区:{b}<br/>客流量:{c}'
},
//视觉映射组件
visualMap: {
top: 'center',
left: 'left',
// 数据的范围
min: 10,
max: 500000,
text: ['High', 'Low'],
realtime: true, //拖拽时,是否实时更新
calculable: true, //是否显示拖拽用的手柄
inRange: {
// 颜色分布
color: ['lightskyblue', 'yellow', 'orangered']
}
},
series: [
{
name: '模拟数据',
type: 'map',
mapType: 'china',
roam: false,//是否开启鼠标缩放和平移漫游
itemStyle: {
//地图区域的多边形 图形样式
normal: {
//是图形在默认状态下的样式
label: {
show: true,//是否显示标签
textStyle: {
color: "black"
}
}
},
zoom: 1.5, //地图缩放比例,默认为1
emphasis: {
//是图形在高亮状态下的样式,比如在鼠标悬浮或者图例联动高亮时
label: {
show: true }
}
},
top: "3%",//组件距离容器的距离
bottom:"0%",
left:"3%",
right:"3%",
data: [
{
name: '北京', value: 350000 },
{
name: '天津', value: 120000 },
{
name: '上海', value: 300000 },
{
name: '重庆', value: 92000 },
{
name: '河北', value: 25000 },
{
name: '河南', value: 20000 },
{
name: '云南', value: 500 },
{
name: '辽宁', value: 3050 },
{
name: '黑龙江', value: 80000 },
{
name: '湖南', value: 2000 },
{
name: '安徽', value: 24580 },
{
name: '山东', value: 40629 },
{
name: '新疆', value: 36981 },
{
name: '江苏', value: 13569 },
{
name: '浙江', value: 24956 },
{
name: '江西', value: 15194 },
{
name: '湖北', value: 41398 },
{
name: '广西', value: 41150 },
{
name: '甘肃', value: 17630 },
{
name: '山西', value: 27370 },
{
name: '内蒙古', value: 27370 },
{
name: '陕西', value: 97208 },
{
name: '吉林', value: 88290 },
{
name: '福建', value: 19978 },
{
name: '贵州', value: 94485 },
{
name: '广东', value: 89426 },
{
name: '青海', value: 35484 },
{
name: '西藏', value: 97413 },
{
name: '四川', value: 54161 },
{
name: '宁夏', value: 56515 },
{
name: '海南', value: 54871 },
{
name: '台湾', value: 48544 },
{
name: '香港', value: 49474 },
{
name: '澳门', value: 34594 }
]
}
]
};
// 地图注册,第一个参数的名字必须和option.geo.map一致
this.$echarts.registerMap("china",zhongguo)
charts.setOption(option);
},
},
};
</script>
結果:
vuexの基本的な使い方
cnpm install vuex --save
vuex パッケージをインポートする
import Vuex from 'vuex'
Vue.use(Vuesx)
ストアオブジェクトの作成
const store = new Vuex.Store({
states:{
count:0
}
})
export default store
オブジェクトをグローバル vue インスタンスにマウントします
const vue = new Vue({
el:"#app",
rander :h=>h(app),
store
//将创建的共享数据对象,挂载到vue实例中
//所有组件,就可以直接从store中获取全局的数据了
})
ミューテーションは、ストア内のデータを変更するために使用されます。
① ストアのデータはミューテーションによってのみ変更でき、ストア内のデータを直接操作することはできません。
②このように操作は少し煩雑になりますが、すべてのデータの変更を一元的に監視できます。