摘要:開(kāi)發(fā)環(huán)境這里講的是實(shí)戰(zhàn)對(duì)于如何安裝,配置環(huán)境變量,配置不做講解詳細(xì)請(qǐng)查看學(xué)習(xí)筆記四在上安裝和啟動(dòng)增量同步數(shù)據(jù)到方式實(shí)現(xiàn)從零到一超級(jí)詳里面有做詳細(xì)講解,對(duì)于搭建集群目前還沒(méi)有寫(xiě)相關(guān)文章,讀者可以自行在項(xiàng)目的文件中添加的依賴(lài)實(shí)現(xiàn)員工信息
開(kāi)發(fā)環(huán)境:
elasticsearch:7.0.0
kibana:7.0.0
JDK: 1.8.0_201
maven: 3.6.1
詳細(xì)請(qǐng)查看:
elasticsearch學(xué)習(xí)筆記(四)——在windows上安裝和啟動(dòng)Elasticsearch
https://segmentfault.com/a/11...
增量同步mysql 數(shù)據(jù)到elasticsearch canal adapter方式(binlog)實(shí)現(xiàn)(從零到一超級(jí)詳
https://segmentfault.com/a/11...
里面有做詳細(xì)講解,對(duì)于搭建ES集群目前還沒(méi)有寫(xiě)相關(guān)文章,讀者可以自行g(shù)oogle
2、實(shí)現(xiàn)員工信息的增刪改查 (1)構(gòu)建clientorg.elasticsearch elasticsearch 7.0.0 org.elasticsearch.client elasticsearch-rest-high-level-client 7.0.0
我的ES機(jī)器IP是192.168.254.131,讀者可自行更換
RestHighLevelClient client = new RestHighLevelClient( RestClient.builder( new HttpHost("192.168.254.131", 9200, "http") ) ); client.close();
對(duì)于多個(gè)ip的情況,直接使用如下格式即可:
RestHighLevelClient client = new RestHighLevelClient( RestClient.builder( new HttpHost("IP", PORT, "http"), new HttpHost("IP", PORT, "http") ... ) ); client.close();(2)創(chuàng)建員工信息
我個(gè)人比較喜歡這種格式的實(shí)現(xiàn):
還有基于json字符串和map的,讀者可查看:
https://www.elastic.co/guide/...
格式如下
XContentBuilder builder = XContentFactory.jsonBuilder(); builder.startObject(); { builder.field(name, value); builder.field(name, value); builder.field(name, value); builder.field(name, value); builder.field(name, value); ... } builder.endObject(); IndexRequest request = new IndexRequest(index).id(id).source(builder); IndexResponse indexResponse = client.index(request, RequestOptions.DEFAULT);
示例:
XContentBuilder builder = XContentFactory.jsonBuilder(); builder.startObject(); { builder.field("name", "jack"); builder.field("age", 27); builder.field("position", "technique"); builder.field("country", "china"); builder.field("join_date", "2017-01-01"); builder.field("salary", 10000); } builder.endObject(); IndexRequest request = new IndexRequest("employee").id("1").source(builder); IndexResponse indexResponse = client.index(request, RequestOptions.DEFAULT);(3)根據(jù)ID獲取員工信息
詳細(xì)請(qǐng)查看:
https://www.elastic.co/guide/...
格式:
GetRequest request = new GetRequest(index, id); GetResponse response = client.get(request, RequestOptions.DEFAULT);
示例:
GetRequest request = new GetRequest("employee", "1"); GetResponse response = client.get(request, RequestOptions.DEFAULT);(4)修改員工信息
詳細(xì)請(qǐng)查看:
https://www.elastic.co/guide/...
格式:
XContentBuilder builder = XContentFactory.jsonBuilder(); builder.startObject(); { builder.field(name, value); } builder.endObject(); UpdateRequest request = new UpdateRequest(index, id).doc(builder); UpdateResponse response = client.update(request, RequestOptions.DEFAULT);
示例:
XContentBuilder builder = XContentFactory.jsonBuilder(); builder.startObject(); { builder.field("position", "technique"); } builder.endObject(); UpdateRequest request = new UpdateRequest("employee", "1").doc(builder); UpdateResponse response = client.update(request, RequestOptions.DEFAULT);(5)刪除員工信息
詳細(xì)請(qǐng)查看:
https://www.elastic.co/guide/...
格式:
DeleteRequest request = new DeleteRequest(index, id); DeleteResponse response = client.delete(request, RequestOptions.DEFAULT);
示例:
DeleteRequest request = new DeleteRequest("employee", "1"); DeleteResponse response = client.delete(request, RequestOptions.DEFAULT);(6)測(cè)試員工信息的增刪改查
完整測(cè)試代碼:
/** * 創(chuàng)建員工信息 * @param client * @throws Exception */ private static void createEmployee(RestHighLevelClient client) throws Exception { XContentBuilder builder = XContentFactory.jsonBuilder(); builder.startObject(); { builder.field("name", "jack"); builder.field("age", 27); builder.field("position", "technique"); builder.field("country", "china"); builder.field("join_date", "2017-01-01"); builder.field("salary", 10000); } builder.endObject(); IndexRequest request = new IndexRequest("employee").id("1").source(builder); IndexResponse indexResponse = client.index(request, RequestOptions.DEFAULT); System.out.println(indexResponse.getResult()); } /** * 獲取員工信息 * @param client */ private static void getEmployee(RestHighLevelClient client) throws IOException { GetRequest request = new GetRequest("employee", "1"); GetResponse response = client.get(request, RequestOptions.DEFAULT); System.out.println(response.getSourceAsString()); } /** * 修改員工信息 * @param client */ private static void updateEmployee(RestHighLevelClient client) throws IOException { XContentBuilder builder = XContentFactory.jsonBuilder(); builder.startObject(); { builder.field("position", "technique"); } builder.endObject(); UpdateRequest request = new UpdateRequest("employee", "1").doc(builder); UpdateResponse response = client.update(request, RequestOptions.DEFAULT); System.out.println(response.getResult()); } /** * 刪除員工信息 * @param client */ private static void deleteEmployee(RestHighLevelClient client) throws IOException { DeleteRequest request = new DeleteRequest("employee", "1"); DeleteResponse response = client.delete(request, RequestOptions.DEFAULT); System.out.println(response.getResult()); }
1、測(cè)試新增
public static void main(String[] args) throws Exception { RestHighLevelClient client = new RestHighLevelClient( RestClient.builder( new HttpHost("192.168.254.131", 9200, "http") ) ); createEmployee(client); client.close(); }
結(jié)果:
CREATED
2、測(cè)試獲取數(shù)據(jù)
public static void main(String[] args) throws Exception { RestHighLevelClient client = new RestHighLevelClient( RestClient.builder( new HttpHost("192.168.254.131", 9200, "http") ) ); getEmployee(client); client.close(); }
結(jié)果:
{"name":"jack","age":27,"position":"technique manager","country":"china","join_date":"2017-01-01","salary":10000}
3、測(cè)試更新數(shù)據(jù)
public static void main(String[] args) throws Exception { RestHighLevelClient client = new RestHighLevelClient( RestClient.builder( new HttpHost("192.168.254.131", 9200, "http") ) ); getEmployee(client); updateEmployee(client); getEmployee(client) client.close(); }
結(jié)果:
{"name":"jack","age":27,"position":"technique manager","country":"china","join_date":"2017-01-01","salary":10000} UPDATE {"name":"jack","age":27,"position":"technique","country":"china","join_date":"2017-01-01","salary":10000}
4、測(cè)試刪除數(shù)據(jù)
public static void main(String[] args) throws Exception { RestHighLevelClient client = new RestHighLevelClient( RestClient.builder( new HttpHost("192.168.254.131", 9200, "http") ) ); getEmployee(client); deleteEmployee(client); getEmployee(client); client.close(); }
結(jié)果:
{"name":"jack","age":27,"position":"technique","country":"china","join_date":"2017-01-01","salary":10000} DELETED null3、對(duì)員工信息進(jìn)行復(fù)雜的搜索操作
需求:
(1)搜索職位中包含technique的員工
(2)同時(shí)要求age在30到40歲之間
(3)分頁(yè)查詢(xún),查找第一頁(yè)
準(zhǔn)備數(shù)據(jù):
private static void prepareData(RestHighLevelClient client) throws IOException { client.index(new IndexRequest("employee").id("1").source(XContentFactory.jsonBuilder() .startObject() .field("name", "jack") .field("age", 27) .field("position", "technique software") .field("country", "china") .field("join_date", "2017-01-01") .field("salary", 10000) .endObject() ), RequestOptions.DEFAULT); client.index(new IndexRequest("employee").id("2").source(XContentFactory.jsonBuilder() .startObject() .field("name", "marry") .field("age", 35) .field("position", "technique manager") .field("country", "china") .field("join_date", "2017-01-01") .field("salary", 12000) .endObject() ), RequestOptions.DEFAULT); client.index(new IndexRequest("employee").id("3").source(XContentFactory.jsonBuilder() .startObject() .field("name", "tom") .field("age", 32) .field("position", "senior technique software") .field("country", "china") .field("join_date", "2016-01-01") .field("salary", 11000) .endObject() ), RequestOptions.DEFAULT); client.index(new IndexRequest("employee").id("4").source(XContentFactory.jsonBuilder() .startObject() .field("name", "jen") .field("age", 25) .field("position", "junior finance") .field("country", "usa") .field("join_date", "2016-01-01") .field("salary", 7000) .endObject() ), RequestOptions.DEFAULT); client.index(new IndexRequest("employee").id("5").source(XContentFactory.jsonBuilder() .startObject() .field("name", "mike") .field("age", 37) .field("position", "finance manager") .field("country", "usa") .field("join_date", "2015-01-01") .field("salary", 15000) .endObject() ), RequestOptions.DEFAULT); }
執(zhí)行:
public static void main(String[] args) throws Exception { RestHighLevelClient client = new RestHighLevelClient( RestClient.builder( new HttpHost("192.168.254.131", 9200) ) ); prepareData(client); client.close(); }
然后構(gòu)建查詢(xún)語(yǔ)句:
GET /employee/_search { "query": { "bool": { "must": [ { "match": { "position": "technique" } } ], "filter": { "range": { "age": { "gte": 30, "lte": 40 } } } } }, "from": 0, "size": 1 } { "took" : 3, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 2, "relation" : "eq" }, "max_score" : 0.5598161, "hits" : [ { "_index" : "employee", "_type" : "_doc", "_id" : "2", "_score" : 0.5598161, "_source" : { "name" : "marry", "age" : 35, "position" : "technique manager", "country" : "china", "join_date" : "2017-01-01", "salary" : 12000 } } ] } }
代碼:
private static void executeSearch(RestHighLevelClient client) throws IOException { SearchSourceBuilder builder = new SearchSourceBuilder(); builder.query(QueryBuilders.matchQuery("position", "technique")); builder.postFilter(QueryBuilders.rangeQuery("age").from(30).to(40)); builder.from(0); builder.size(1); SearchRequest request = new SearchRequest(); request.indices("employee"); request.source(builder); SearchResponse response = client.search(request, RequestOptions.DEFAULT); SearchHit[] hits = response.getHits().getHits(); for (SearchHit hit : hits) { System.out.println(hit.getSourceAsString()); } }
執(zhí)行:
public static void main(String[] args) throws Exception { RestHighLevelClient client = new RestHighLevelClient( RestClient.builder( new HttpHost("192.168.254.131", 9200) ) ); executeSearch(client); client.close(); }
結(jié)果:
{"name":"marry","age":35,"position":"technique manager","country":"china","join_date":"2017-01-01","salary":12000}4、對(duì)員工信息進(jìn)行聚合分析
需求:
(1)首先按照country國(guó)家來(lái)進(jìn)行分組
(2)然后在每個(gè)country分組內(nèi),再按照入職年限進(jìn)行分組
(3)最后計(jì)算每個(gè)分組內(nèi)的平均薪資
GET /employee/_search { "size": 0, "aggs": { "group_by_country": { "terms": { "field": "country.keyword" }, "aggs": { "group_by_join_date": { "date_histogram": { "field": "join_date", "interval": "year" }, "aggs": { "avg_salary": { "avg": { "field": "salary" } } } } } } } }
{ "took" : 46, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 5, "relation" : "eq" }, "max_score" : null, "hits" : [ ] }, "aggregations" : { "group_by_country" : { "doc_count_error_upper_bound" : 0, "sum_other_doc_count" : 0, "buckets" : [ { "key" : "china", "doc_count" : 3, "group_by_join_date" : { "buckets" : [ { "key_as_string" : "2016-01-01T00:00:00.000Z", "key" : 1451606400000, "doc_count" : 1, "avg_salary" : { "value" : 11000.0 } }, { "key_as_string" : "2017-01-01T00:00:00.000Z", "key" : 1483228800000, "doc_count" : 2, "avg_salary" : { "value" : 11000.0 } } ] } }, { "key" : "usa", "doc_count" : 2, "group_by_join_date" : { "buckets" : [ { "key_as_string" : "2015-01-01T00:00:00.000Z", "key" : 1420070400000, "doc_count" : 1, "avg_salary" : { "value" : 15000.0 } }, { "key_as_string" : "2016-01-01T00:00:00.000Z", "key" : 1451606400000, "doc_count" : 1, "avg_salary" : { "value" : 7000.0 } } ] } } ] } } }
執(zhí)行:
public static void main(String[] args) throws Exception { RestHighLevelClient client = new RestHighLevelClient( RestClient.builder( new HttpHost("192.168.254.131", 9200) ) ); SearchSourceBuilder builder = new SearchSourceBuilder(); builder.aggregation(AggregationBuilders.terms("group_by_country").field("country.keyword") .subAggregation(AggregationBuilders.dateHistogram("group_by_join_date").field("join_date") .dateHistogramInterval(DateHistogramInterval.YEAR) .subAggregation(AggregationBuilders.avg("avg_salary").field("salary")))); SearchRequest request = new SearchRequest(); request.indices("employee"); request.source(builder); SearchResponse response = client.search(request, RequestOptions.DEFAULT); MapaggregationMap = response.getAggregations().asMap(); ParsedStringTerms group_by_country = (ParsedStringTerms) aggregationMap.get("group_by_country"); for (Terms.Bucket groupByCountryBucket : group_by_country.getBuckets()) { System.out.println(groupByCountryBucket.getKey() + ":" + groupByCountryBucket.getDocCount()); Histogram groupByJoinDate = (Histogram) groupByCountryBucket.getAggregations().asMap().get("group_by_join_date"); for (Histogram.Bucket groupByJoinDateBucket : groupByJoinDate.getBuckets()) { System.out.println(groupByJoinDateBucket.getKey() + ":" + groupByJoinDateBucket.getDocCount()); Avg avg = (Avg) groupByJoinDateBucket.getAggregations().asMap().get("avg_salary"); System.out.println(avg.getValue()); } } client.close(); }
結(jié)果:
china:3 2016-01-01T00:00Z:1 11000.0 2017-01-01T00:00Z:2 11000.0 usa:2 2015-01-01T00:00Z:1 15000.0 2016-01-01T00:00Z:1 7000.0附上完整代碼:
EmployeeCRUDApp.java
import org.apache.http.HttpHost; import org.elasticsearch.action.delete.DeleteRequest; import org.elasticsearch.action.delete.DeleteResponse; import org.elasticsearch.action.get.GetRequest; import org.elasticsearch.action.get.GetResponse; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.index.IndexResponse; import org.elasticsearch.action.update.UpdateRequest; import org.elasticsearch.action.update.UpdateResponse; import org.elasticsearch.client.RequestOptions; import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestHighLevelClient; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; import java.io.IOException; public class EmployeeCRUDApp { public static void main(String[] args) throws Exception { RestHighLevelClient client = new RestHighLevelClient( RestClient.builder( new HttpHost("127.0.0.1", 9200, "http") ) ); createEmployee(client); getEmployee(client); updateEmployee(client); getEmployee(client); deleteEmployee(client); getEmployee(client); client.close(); } /** * 創(chuàng)建員工信息 * @param client * @throws Exception */ private static void createEmployee(RestHighLevelClient client) throws Exception { XContentBuilder builder = XContentFactory.jsonBuilder(); builder.startObject(); { builder.field("name", "jack"); builder.field("age", 27); builder.field("position", "technique manager"); builder.field("country", "china"); builder.field("join_date", "2017-01-01"); builder.field("salary", 10000); } builder.endObject(); IndexRequest request = new IndexRequest("employee").id("1").source(builder); IndexResponse indexResponse = client.index(request, RequestOptions.DEFAULT); System.out.println(indexResponse.getResult()); } /** * 獲取員工信息 * @param client */ private static void getEmployee(RestHighLevelClient client) throws IOException { GetRequest request = new GetRequest("employee", "1"); GetResponse response = client.get(request, RequestOptions.DEFAULT); System.out.println(response.getSourceAsString()); } /** * 修改員工信息 * @param client */ private static void updateEmployee(RestHighLevelClient client) throws IOException { XContentBuilder builder = XContentFactory.jsonBuilder(); builder.startObject(); { builder.field("position", "technique"); } builder.endObject(); UpdateRequest request = new UpdateRequest("employee", "1").doc(builder); UpdateResponse response = client.update(request, RequestOptions.DEFAULT); System.out.println(response.getResult()); } /** * 刪除員工信息 * @param client */ private static void deleteEmployee(RestHighLevelClient client) throws IOException { DeleteRequest request = new DeleteRequest("employee", "1"); DeleteResponse response = client.delete(request, RequestOptions.DEFAULT); System.out.println(response.getResult()); } }
EmployeeSearchApp.java
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.client.RequestOptions; import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestHighLevelClient; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.builder.SearchSourceBuilder; import java.io.IOException; /** * 員工搜索應(yīng)用程序 */ public class EmployeeSearchApp { public static void main(String[] args) throws Exception { RestHighLevelClient client = new RestHighLevelClient( RestClient.builder( new HttpHost("127.0.0.1", 9200) ) ); prepareData(client); executeSearch(client); client.close(); } /** * 執(zhí)行搜索操作 * @param client */ private static void executeSearch(RestHighLevelClient client) throws IOException { SearchSourceBuilder builder = new SearchSourceBuilder(); builder.query(QueryBuilders.matchQuery("position", "technique")); builder.postFilter(QueryBuilders.rangeQuery("age").from(30).to(40)); builder.from(0); builder.size(1); SearchRequest request = new SearchRequest(); request.indices("employee"); request.source(builder); SearchResponse response = client.search(request, RequestOptions.DEFAULT); SearchHit[] hits = response.getHits().getHits(); for (SearchHit hit : hits) { System.out.println(hit.getSourceAsString()); } } /** * 準(zhǔn)備數(shù)據(jù) * @param client */ private static void prepareData(RestHighLevelClient client) throws IOException { client.index(new IndexRequest("employee").id("1").source(XContentFactory.jsonBuilder() .startObject() .field("name", "jack") .field("age", 27) .field("position", "technique software") .field("country", "china") .field("join_date", "2017-01-01") .field("salary", 10000) .endObject() ), RequestOptions.DEFAULT); client.index(new IndexRequest("employee").id("2").source(XContentFactory.jsonBuilder() .startObject() .field("name", "marry") .field("age", 35) .field("position", "technique manager") .field("country", "china") .field("join_date", "2017-01-01") .field("salary", 12000) .endObject() ), RequestOptions.DEFAULT); client.index(new IndexRequest("employee").id("3").source(XContentFactory.jsonBuilder() .startObject() .field("name", "tom") .field("age", 32) .field("position", "senior technique software") .field("country", "china") .field("join_date", "2016-01-01") .field("salary", 11000) .endObject() ), RequestOptions.DEFAULT); client.index(new IndexRequest("employee").id("4").source(XContentFactory.jsonBuilder() .startObject() .field("name", "jen") .field("age", 25) .field("position", "junior finance") .field("country", "usa") .field("join_date", "2016-01-01") .field("salary", 7000) .endObject() ), RequestOptions.DEFAULT); client.index(new IndexRequest("employee").id("5").source(XContentFactory.jsonBuilder() .startObject() .field("name", "mike") .field("age", 37) .field("position", "finance manager") .field("country", "usa") .field("join_date", "2015-01-01") .field("salary", 15000) .endObject() ), RequestOptions.DEFAULT); } }
EmployeeAggrApp.java
public class EmployeeAggrApp { public static void main(String[] args) throws Exception { RestHighLevelClient client = new RestHighLevelClient( RestClient.builder( new HttpHost("127.0.0.1", 9200) ) ); SearchSourceBuilder builder = new SearchSourceBuilder(); builder.aggregation(AggregationBuilders.terms("group_by_country").field("country.keyword") .subAggregation(AggregationBuilders.dateHistogram("group_by_join_date").field("join_date") .dateHistogramInterval(DateHistogramInterval.YEAR) .subAggregation(AggregationBuilders.avg("avg_salary").field("salary")))); SearchRequest request = new SearchRequest(); request.indices("employee"); request.source(builder); SearchResponse response = client.search(request, RequestOptions.DEFAULT); MapaggregationMap = response.getAggregations().asMap(); ParsedStringTerms group_by_country = (ParsedStringTerms) aggregationMap.get("group_by_country"); for (Terms.Bucket groupByCountryBucket : group_by_country.getBuckets()) { System.out.println(groupByCountryBucket.getKey() + ":" + groupByCountryBucket.getDocCount()); Histogram groupByJoinDate = (Histogram) groupByCountryBucket.getAggregations().asMap().get("group_by_join_date"); for (Histogram.Bucket groupByJoinDateBucket : groupByJoinDate.getBuckets()) { System.out.println(groupByJoinDateBucket.getKey() + ":" + groupByJoinDateBucket.getDocCount()); Avg avg = (Avg) groupByJoinDateBucket.getAggregations().asMap().get("avg_salary"); System.out.println(avg.getValue()); } } client.close(); } }
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/74474.html
摘要:入門(mén)本節(jié)描述從獲取工件到在應(yīng)用程序中使用它如何開(kāi)始使用高級(jí)別客戶(hù)端。保證能夠與運(yùn)行在相同主版本和大于或等于的次要版本上的任何節(jié)點(diǎn)通信。與具有相同的發(fā)布周期,將版本替換為想要的客戶(hù)端版本。 Java High Level REST Client 入門(mén) 本節(jié)描述從獲取工件到在應(yīng)用程序中使用它如何開(kāi)始使用高級(jí)別REST客戶(hù)端。 兼容性 Java High Level REST Client需...
摘要:用于的官方高級(jí)別客戶(hù)端,基于低級(jí)別客戶(hù)端,它公開(kāi)特定的方法,并負(fù)責(zé)請(qǐng)求編組和響應(yīng)反編組。入門(mén)初始化執(zhí)行請(qǐng)求讀取響應(yīng)日志記錄通用配置嗅探器在中被添加。依賴(lài)于核心項(xiàng)目,它接受與相同的請(qǐng)求參數(shù),并返回相同的響應(yīng)對(duì)象。 Elasticsearch Java REST Client Java REST Client有兩種類(lèi)型: Java Low Level REST Client:用于Elast...
摘要:搜索請(qǐng)求用于與搜索文檔聚合相關(guān)的任何操作,還提供了在結(jié)果文檔上請(qǐng)求高亮的方法。將字段高光色添加到高亮構(gòu)建器。稍后可以從中檢索高亮的文本片段。 Search API 搜索請(qǐng)求 SearchRequest用于與搜索文檔、聚合、suggestions相關(guān)的任何操作,還提供了在結(jié)果文檔上請(qǐng)求高亮的方法。 在最基本的表單中,我們可以向請(qǐng)求添加查詢(xún): SearchRequest searchReq...
閱讀 1322·2021-11-15 11:37
閱讀 2580·2021-09-22 10:56
閱讀 3401·2021-09-06 15:11
閱讀 814·2021-08-31 09:45
閱讀 2914·2021-07-28 11:16
閱讀 1816·2019-08-30 15:44
閱讀 487·2019-08-30 13:22
閱讀 3354·2019-08-30 13:18