elasticsearch5.6.4 RestClient 索引操作API詳細的注釋給力篇值得擁有

最近項目有個需求,很棘手。有個統(tǒng)計功能,需要做到實時出結果。這個功能經(jīng)過好幾個人的處理都沒有解決客戶的問題。到了我這里,我就來填坑了??啾频某绦蛟?,苦逼的命。誰叫我是這個項目現(xiàn)任負責人。經(jīng)一番折騰大致了解到客戶對這個統(tǒng)計分析的需求之后,就進入技術選項。老套路,肯定先百度、谷歌、大神群各種咨詢、了解。最后決定使用elasticseaarch。至于為何選擇它,相關同學自己百度去。而我選擇的原因就是簡單、易于維護、成本低、性能也是gan gan 的。

接下來我們看看elasticsearch index api是如何使用java 來進行相關操作。強烈建議在閱讀以下所說的內容前必須先對elasticsearch有一定的了解

參考官網(wǎng)API

low level https://www.elastic.co/guide/en/elasticsearch/client/java-rest/5.6/java-rest-low.html
high level https://www.elastic.co/guide/en/elasticsearch/client/java-rest/5.6/java-rest-high.html

以下源碼下載連接:ESRestClient.java http://download.csdn.net/download/u013294278/10174249

pom.xml

<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>5.6.4</version>
</dependency>

創(chuàng)建連接

private static Logger log = LoggerFactory.getLogger(ESRestClient.class);
private static RestClient lowLevelRestClient = null;
private static RestHighLevelClient highLevelRestClient = null;

/**
* @Description: 初始化  
* @return void  
* @throws
* @author JornTang
* @date 2017年12月23日
*/

public void init(){
    RestClientBuilder builder = RestClient.builder(new HttpHost("127.0.0.1", 9200));
    builder.setRequestConfigCallback(new RestClientBuilder.RequestConfigCallback() {  
            @Override  
            public RequestConfig.Builder customizeRequestConfig(RequestConfig.Builder requestConfigBuilder) {  
                requestConfigBuilder.setConnectTimeout(10000);  
                requestConfigBuilder.setSocketTimeout(30000);  
                requestConfigBuilder.setConnectionRequestTimeout(10000);  
                return requestConfigBuilder;  
            }  
        }); 
   
    builder.setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() {
            @Override
            public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpClientBuilder) {
                return httpClientBuilder.setDefaultIOReactorConfig(
                        IOReactorConfig.custom()
                        .setIoThreadCount(100)//線程數(shù)配置
                    .setConnectTimeout(10000)
                    .setSoTimeout(10000)
                    .build());
            }
        });
  
//設置超時
builder.setMaxRetryTimeoutMillis(10000);
//構建low level client
lowLevelRestClient = builder.build();
//構建high level client
highLevelRestClient = new RestHighLevelClient(lowLevelRestClient);
log.info("ESRestClient 初始化完成。。。。。。。。。");
}

通過init方法構造RestClient 、RestHighLevelClient

那么如何操作index

單條處理

/**
* @Description: low level put index
* @throws IOException   
* @return void  
* @throws
* @author JornTang
* @date 2017年12月22日
*/
public static void lowLevelPutIndex(String idnex, String type, ArchiveMapperVo mapperVo) throws IOException{
Map<String, String> params = Collections.emptyMap();
ArchiveMapper mapper = new ArchiveMapper();
BeanUtils.copyProperties(mapperVo, mapper);
HttpEntity entity = new NStringEntity(JSON.toJSONString(mapper), ContentType.APPLICATION_JSON);
Response response = lowLevelRestClient.performRequest("PUT", "/"+idnex+"/"+type+"/" + mapperVo.getDocumentId(), params, entity);
StatusLine statusLine = response.getStatusLine();
log.info("索引執(zhí)行put:【" + statusLine.getStatusCode() + "】" + statusLine.toString());
}

/**
* @Description: low level delete index
* @throws IOException   
* @return void  
* @throws
* @author JornTang
* @date 2017年12月22日
*/
public static void lowLevelDeleteIndex(String idnex, String type, String documentId) throws IOException{
Map<String, String> params = Collections.emptyMap();
Response response = lowLevelRestClient.performRequest("DELETE", "/"+idnex+"/"+type+"/" + documentId, params); 
StatusLine statusLine = response.getStatusLine();
log.info("索引執(zhí)行delete:【" + statusLine.getStatusCode() + "】" + statusLine.toString());
}

批量處理

/**

  • @Description: high level bulk put index ps:need jdk1.8
  • @param idnex 索引
  • @param type 類型
  • @param mappers 索引數(shù)據(jù)集合
  • @throws Exception
  • @return void
  • @throws
  • @author JornTang
  • @date 2017年12月22日
    */
    public static void bulkPutIndex(String idnex, String type, List<ArchiveMapper> mappers) throws Exception{
    BulkRequest request = new BulkRequest();
    if(mappers== null || mappers.size()< 1){
    throw new ESIndexException("mappers can not be empty");
    }
    for (int i = 0; i < mappers.size(); i++) {
    ArchiveMapper mapper = mappers.get(i);
    request.add(new IndexRequest(idnex, type, mapper.getId())
    .opType("create")
    .source(JSON.toJSONString(mapper),XContentType.JSON));
    }
    BulkResponse bulkResponse = highLevelRestClient.bulk(request);
    RestStatus stat = doSuccessful(bulkResponse);
    //log.info("索引執(zhí)行bulk put:【" + stat.getStatus() + "】" + stat.toString());
    }

是不是覺得非常簡單呢?上面的代碼不能直接使用要結合自己的實際情況。

比如:數(shù)據(jù)建模、model定義

數(shù)據(jù)建模參考:

PUT archives
{
  "mappings": {
    "archive": {
      "_all": {
        "enabled": false 
      },
      "properties": {
        "id": {
          "type": "keyword"
        },
"arch_type": {
          "type": "integer"
        },


"archive_id":{
"type":"integer"
},
"room_id":{
"type":"integer"
},
"if_store":{
"type":"integer"
},
"handover_state":{
"type":"integer"
},
"crt_date":{
"type":"long"
},
"if_hasfile":{
"type":"integer"
},
“doc_date”:{
"type":"long"
},
"doc_year":{
"type":"integer"
},
"fond_code":{
"type":"keyword"
},
"cate_code":{
"type":"keyword"
},


"keep_time":{
"type":"integer"
},
"file_click":{
"type":"integer"
},
"storage_date":{
"type":"long"
},
"storage_year":{
"type":"integer"
},
"into_archive_date":{
"type":"long"
},


"into_archive_year":{
"type":"integer"
},
"vol_in_num":{
"type":"integer"
},
"pro_vol":{
"type":"integer"
},
"pro_doc":{
"type":"integer"
},
"file_num":{
"type":"integer"
},
"file_size":{
"type":"long"
},
"page_nmbr":{
"type":"integer"
}
      }
    }
  },
  "settings": {


"number_of_shards":3,


    "number_of_replicas":0


  }
}

屬性類型定義參考:

常用的數(shù)據(jù)類型
? Boolean 布爾類型

布爾字段接受JSON true和false值,但也可以接受被解釋為true或false的字符串

示例如下:

PUT my_index

{

  "mappings": {

    "my_type": {

      "properties": {

        "is_published": {

          "type": "boolean"

        }

      }

    }

  }

}



POST my_index/my_type/1

{

  "is_published": "true"

}



GET my_index/_search

{

  "query": {

    "term": {

      "is_published": true

    }

  }

}

以下參數(shù)被boolean字段接受:

boost

映射字段級查詢時間提升。接受一個浮點數(shù),默認為1.0。

doc_values

該字段是否應該以列步方式存儲在磁盤上,以便稍后用于排序,聚合或腳本?接受true (默認)或false。

index

該領域應該搜索?接受true(默認)和false。

null_value

接受上面列出的任何真值或假值。該值被替換為任何顯式null值。默認為null,這意味著該字段被視為丟失。

store

字段值是否應該與_source字段分開存儲和檢索。接受true或false (默認)。

? Date 日期類型
JSON沒有日期數(shù)據(jù)類型,所以Elasticsearch中的日期可以是:

· 包含格式的日期,如字符串"2015-01-01"或"2015/01/01 12:10:30"。

· 一個代表毫秒數(shù)的長數(shù)字。

· 自始至終秒的整數(shù)

在內部,日期轉換為UTC(如果指定時區(qū))并存儲為表示毫秒以后的長數(shù)字。

日期格式可以自定義,但如果沒有format指定,則使用默認值:

"strict_date_optional_time || epoch_millis"

示例如下:

PUT my_index

{

  "mappings": {

    "my_type": {

      "properties": {

        "date": {

          "type": "date"

        }

      }

    }

  }

}

PUT my_index/my_type/1

{ "date": "2015-01-01" }



PUT my_index/my_type/2

{ "date": "2015-01-01T12:10:30Z" }



PUT my_index/my_type/3

{ "date": 1420070400001 }



GET my_index/_search

{

  "sort": { "date": "asc"}

}

定義多個日期格式

多個格式可以通過分隔||作為分隔符來指定。每個格式將依次嘗試,直到找到匹配的格式。第一種格式將被用于將毫秒自時代的值轉換回字符串

PUT my_index

{

  "mappings":{

    "my_type":{

      "properties":{

        "date":{

          "type":"date",

          "format":"yyyy-MM-dd HH:mm:ss || yyyy- MM-dd || epoch_millis" 

        }

      }

    }

  }

}

以下參數(shù)被date字段接受:

boost

映射字段級查詢時間提升。接受一個浮點數(shù),默認為1.0。

doc_values

該字段是否應該以列步方式存儲在磁盤上,以便稍后用于排序,聚合或腳本?接受true (默認)或false。

format

可以解析的日期格式。默認為 strict_date_optional_time||epoch_millis。

locale

自從月份以來,解析日期時使用的語言環(huán)境在所有語言中都沒有相同的名稱和/或縮寫。默認是 ROOT語言環(huán)境,

ignore_malformed

如果true,格式不正確的號碼被忽略。如果false(默認),格式不正確的數(shù)字會拋出異常并拒絕整個文檔。

index

該領域應該搜索?接受true(默認)和false。

null_value

接受其中一個配置的日期值format作為代替任何顯式null值的字段。默認為null,這意味著該字段被視為丟失。

store

字段值是否應該與_source字段分開存儲和檢索。接受true或false (默認)。

? Keyword 關鍵字類型
用于索引結構化內容的字段,如電子郵件地址,主機名,狀態(tài)碼,郵政編碼或標簽。

它們通常用于過濾(找到我的所有博客文章,其中 status為published),排序,和聚合。關鍵字字段只能按其確切值進行搜索。

如果您需要索引全文內容(如電子郵件正文或產(chǎn)品說明),則可能需要使用text字段。

示例如下:

PUT my_index

{

  "mappings": {

    "my_type": {

      "properties": {

        "tags": {

          "type":  "keyword"

        }

      }

    }

  }

}

以下參數(shù)被keyword字段接受:

boost

映射字段級查詢時間提升。接受一個浮點數(shù),默認為1.0。

doc_values

該字段是否應該以列步方式存儲在磁盤上,以便稍后用于排序,聚合或腳本?接受true (默認)或false。

eager_global_ordinals

全球序言是否應該在刷新的時候急切地加載?接受true或false (默認)。對于經(jīng)常用于術語聚合的字段,啟用此功能是一個不錯的主意。

fields

多字段允許為了不同的目的以多種方式對相同的字符串值進行索引,例如用于搜索的字段和用于排序和聚合的多字段。

ignore_above

不要索引任何比這個值長的字符串。默認為 2147483647所有值都將被接受。

index

該領域應該搜索?接受true(默認)或false。

index_options

為了評分目的,應該在索引中存儲哪些信息。默認為,docs但也可以設置為freqs在計算分數(shù)時考慮術語頻率。

norms

評分查詢時是否應考慮字段長度。接受true或false(默認)。

null_value

接受一個替代任何顯式null 值的字符串值。默認為null,這意味著該字段被視為丟失。

store

字段值是否應該與_source字段分開存儲和檢索。接受true或false (默認)。

similarity

應使用 哪種評分算法或相似性。默認為BM25。

normalizer

如何在索引之前預先處理關鍵字。默認為null,意味著關鍵字保持原樣。

? Number 數(shù)字類型
以下數(shù)字類型受支持:

long

一個帶符號的64位整數(shù),其最小值為-263,最大值為。 263-1

integer

一個帶符號的32位整數(shù),其最小值為-231,最大值為。 231-1

short

一個帶符號的16位整數(shù),其最小值為-32,768,最大值為32,767。

byte

一個有符號的8位整數(shù),其最小值為-128,最大值為127。

double

雙精度64位IEEE 754浮點。

float

單精度32位IEEE 754浮點。

half_float

一個半精度的16位IEEE 754浮點。

scaled_float

一個由long一個固定比例因子支持的浮點。

示例如下:

PUT my_index

{

  "mappings": {

    "my_type": {

      "properties": {

        "number_of_bytes": {

          "type": "integer"

        },

        "time_in_seconds": {

          "type": "float"

        },

        "price": {

          "type": "scaled_float",

          "scaling_factor": 100

        }

      }

    }

  }

}

對于double,float和half_float類型需要考慮-0.0和 +0.0不同的值存儲。做一個term查詢 -0.0將不匹配+0.0,反之亦然。同樣適用于范圍查詢真:如果上限是-0.0那么+0.0將不匹配,如果下界+0.0那么-0.0將不匹配

那么我們應該如何使用數(shù)字類型。應遵循以下規(guī)則

l 至于整數(shù)類型(byte,short,integer和long)而言,你應該選擇這是足以讓你的用例最小的類型。這將有助于索引和搜索更高效。但請注意,考慮到根據(jù)存儲的實際值對存儲進行優(yōu)化,選擇一種類型將不會影響存儲要求

l 對于浮點類型,使用縮放因子將浮點數(shù)據(jù)存儲到整數(shù)中通常會更高效,這正是scaled_float 類型所做的。例如,一個price字段可以存儲在一個 scaled_floatwith scaling_factor中100。所有的API都可以工作,就好像字段被存儲為double一樣,但是在elasticsearch下面,將會使用分數(shù)price*100,這是一個整數(shù)。這對于節(jié)省磁盤空間非常有幫助,因為整數(shù)比浮點更容易壓縮。scaled_float交易磁盤空間的準確性也很好用。例如,想象一下,您正在跟蹤CPU利用率,并將其作為介于0和之間的數(shù)字1。它通常沒有多大的CPU使用率是12.7%或13%,所以你可以使用一個scaled_float 帶有scaling_factor的100,以節(jié)省空間,以全面cpu利用率到最近的百分比

如果scaled_float是不適合,那么你應該選擇一個適合的浮點類型中的用例是足夠最小的類型:double,float和half_float。這里是一個比較這些類型的表格,以幫助做出決定

類型

最小值

最大值

重要的位/數(shù)字

double

2-1074

(2-2-52)·21023

53 / 15.95

float

2-149

(2-2-23)·2127

24 / 7.22

half_float

2-24

65504

11 / 3.31

以下參數(shù)被數(shù)字類型接受:

coerce

嘗試將字符串轉換為數(shù)字并截斷整數(shù)的分數(shù)。接受true(默認)和false。

boost

映射字段級查詢時間提升。接受一個浮點數(shù),默認為1.0。

doc_values

該字段是否應該以列步方式存儲在磁盤上,以便稍后用于排序,聚合或腳本?接受true (默認)或false。

ignore_malformed

如果true,格式不正確的號碼被忽略。如果false(默認),格式不正確的數(shù)字會拋出異常并拒絕整個文檔。

index

該領域應該搜索?接受true(默認)和false。

null_value

接受與type替換任何顯式null值的字段相同的數(shù)字值。默認為null,這意味著該字段被視為丟失。

store

字段值是否應該與_source字段分開存儲和檢索。接受true或false (默認)。

注:參數(shù)scaled_float

scaled_float 接受一個額外的參數(shù):

scaling_factor

編碼值時使用的縮放因子。在索引時間,數(shù)值將乘以該因子,并四舍五入到最接近的長整數(shù)值。例如,scaled_float用scaling_factor的10將內部存儲2.34的23所有搜索時操作(查詢,匯總,排序)的行為就好像該文件是一個值2.3。scaling_factor提高精確度的值較高,但也增加了空間要求。該參數(shù)是必需的。

? Text 文本數(shù)據(jù)類型
用于索引全文值的字段,例如電子郵件的正文或產(chǎn)品的說明。這些字段是analyzed,即它們通過 分析器來轉換字符串成索引之前的單個條目列表。分析過程允許Elasticsearch 在 每個全文字段中搜索單個單詞。文本字段不用于排序,也很少用于聚合(盡管 重要的文本聚合 是一個明顯的例外)。

如果您需要為電子郵件地址,主機名,狀態(tài)碼或標簽等結構化內容編制索引,則可能需要使用keyword字段。

示例如下:

PUT my_index

{

  "mappings": {

    "my_type": {

      "properties": {

        "full_name": {

          "type":  "text"

        }

      }

    }

  }

}

有時同一字段的全文本(text)和關鍵字(keyword)版本都是有用的:一個用于全文搜索,另一個用于聚合和排序。這可以通過多領域來實現(xiàn) 。

以下參數(shù)被text字段接受:

analyzer

的分析器,其應該被用于 analyzed既在索引時間和搜索時間(除非被重寫字符串字段 search_analyzer)。默認為默認的索引分析器或 standard分析器。

boost

映射字段級查詢時間提升。接受一個浮點數(shù),默認為1.0。

eager_global_ordinals

全球序言是否應該在刷新的時候急切地加載?接受true或false (默認)。在經(jīng)常用于(重要)術語聚合的字段上啟用此功能是一個好主意。

fielddata

該字段可以使用內存中的字段數(shù)據(jù)進行排序,聚合或腳本嗎?接受true或false(默認)。

fielddata_frequency_filter

專家設置允許決定fielddata 啟用時在內存中加載哪些值。默認情況下所有的值都被加載。

fields

多字段允許為了不同的目的以多種方式對相同的字符串值進行索引,例如一個字段用于搜索,一個多字段用于排序和聚合,或者由不同的分析器分析相同的字符串值。

index

該領域應該搜索?接受true(默認)或false。

index_options

索引中應該存儲哪些信息,用于搜索和突出顯示目的。默認為positions。

norms

評分查詢時是否應考慮字段長度。接受true(默認)或false。

position_increment_gap

應該插入到一個字符串數(shù)組的每個元素之間的假詞位置的數(shù)量。默認為position_increment_gap 默認分析儀上的配置100。100被選中是因為它可以通過字段值匹配術語來防止具有相當大的短語(小于100)的短語查詢。

store

字段值是否應該與_source字段分開存儲和檢索。接受true或false (默認)。

search_analyzer

本analyzer應該在搜索時使用 analyzed的字段。默認為analyzer設置。

search_quote_analyzer

本analyzer應在搜索時遇到一個短語時使用。默認為search_analyzer設置。

similarity

應使用 哪種評分算法或相似性。默認為BM25。

term_vector

術語向量是否應該存儲在一個analyzed 字段中。默認為no。

? Feilds 多領域參數(shù)使用
為了不同的目的,以不同的方式對相同的字段進行索引通常是有用的。這是多領域的目的。例如,一個string 字段可以映射為text全文搜索的字段,也可以映射keyword為排序或聚合的字段:

示例如下:

PUT my_index

{

  "mappings": {

    "my_type": {

      "properties": {

        "city": {

          "type": "text",

          "fields": {

            "raw": {

              "type":  "keyword"

            }

          }

        }

      }

    }

  }

}

PUT my_index/my_type/1

{

  "city": "New York"

}



PUT my_index/my_type/2

{

  "city": "York"

}



GET my_index/_search

{

  "query": {

    "match": {

      "city": "york"

    }

  },

  "sort": {

    "city.raw": "asc"

  },

  "aggs": {

    "Cities": {

      "terms": {

        "field": "city.raw"

      }

    }

  }

}

該city.raw字段是該字段的一個keyword版本city。

該city字段可用于全文搜索。

該city.raw字段可用于排序和聚合

使用多個分析器進行多字段編輯

另一個多字段的用例是以不同的方式分析相同的字段以獲得更好的相關性。例如,我們可以用standard分析器對字段進行索引,該 分析器將文本分解成文字,再用english分析器 將文字分解為詞根:

PUT my_index

{

  "mappings": {

    "my_type": {

      "properties": {

        "text": {

          "type": "text",

          "fields": {

            "english": {

              "type":     "text",

              "analyzer": "english"

            }

          }

        }

      }

    }

  }

}



PUT my_index/my_type/1

{ "text": "quick brown fox" }



PUT my_index/my_type/2

{ "text": "quick brown foxes" }



GET my_index/_search

{

  "query": {

    "multi_match": {

      "query": "quick brown foxes",

      "fields": [

        "text",

        "text.english"

      ],

      "type": "most_fields"

    }

  }

}

該text字段使用的standard分析儀。

該text.english字段使用的english分析儀。

索引兩個文件,一個與fox另一個foxes。

查詢text和text.english字段并結合分數(shù)。

該text字段包含fox第一個文檔和foxes第二個文檔中的術語。該text.english字段包含fox這兩個文檔,因為foxes被阻止fox。

查詢字符串也由standard分析器分析該text 字段,并由english分析器分析text.english字段。詞干字段允許查詢foxes也匹配包含正義的文檔fox。這使我們能盡可能多地匹配文檔。通過查詢未定text域,我們提高了foxes準確匹配的文檔的相關性得分。

? 元數(shù)據(jù)字段
_all 字段:一個把其它字段值((("metadata, document", "_all field")))((("_all field", sortas="all field")))當作一個大字符串來索引的特殊字段。query_string 查詢子句(搜索?q=john )在沒有指定字段時默認使用_all 字段

禁用示例

PUT /my_index/_mapping/my_type

{

"my_type": {

    "_all": { "enabled": false }

}

}

如果_all字段被禁用,則URI搜索請求與 query_string和simple_query_string查詢將無法用它來查詢,但是你可以設置一個指定字段。如下示例

PUT my_index

{

"mappings": {

"my_type": {

      //禁用_all字段

  "_all": {

    "enabled": false

  },

  "properties": {

    "content": {

      "type": "text"

    }

  }

}

},

"settings": {

//設置query_string 查詢指定的字段

"index.query.default_field": "content"

}

}

_source字段:該_source字段包含在索引時間傳遞的原始JSON文檔正文。這個 _source字段本身沒有索引(因此是不可搜索的),但是它被存儲以便在執(zhí)行獲取請求(比如get或者search)的時候返回 。

_source段會在索引內產(chǎn)生存儲開銷。出于這個原因,它可以被禁用。如下:

PUT my_index

{

  "mappings":{

    "my_type":{

      " _ source":{

        "enabled":false

      }

    }

  }

}

如果_source字段被禁用,以下功能將不在被支持

· update,update_by_query和reindexAPI。

· 高亮顯示

· 將索引從一個Elasticsearch索引重新索引到另一個索引的能力,可以更改映射或分析,也可以將索引升級到新的主要版本。

· 通過查看索引時使用的原始文檔來調試查詢或聚合的能力。

· 在將來有可能自動修復索引損壞。

如果磁盤空間是一個問題,而是增加 壓縮級別而不是禁用_source。

在Elasticsearch中,對文檔的個別字段設置存儲的做法通常不是最優(yōu)的。整個文檔已經(jīng)被存儲為_source 字段。使用_source 參數(shù)提取你需要的字段總是更好的。

這個時候你可能會不想保存所有字段信息,那么你講可以通過以下方式指定你要存儲的字段。如下

PUT my_index

{

  "mappings": {

    "my_type": {

      "_source": {

//包含,支持通配符定義

        "includes": [

          "*.count",

          "meta.*"

        ],

//不包含,支持通配符定義

        "excludes": [

          "meta.description",

          "meta.other.*"

        ]

      }

    }

  }

}



PUT my_index/my_type/1

{

  "requests": {

    "count": 10,

    "foo": "bar"

  },

  "meta": {

    "name": "Some metric",

    "description": "Some metric description",

    "other": {

      "foo": "one",

      "baz": "two"

    }

  }

}

注:以上方式只是過濾存儲的字段,但每個字段依舊可以被搜索

寫在最后,針對具體相關api請參考elasticsearch官網(wǎng)。

建議:在實際項目中請結合線程池可以在創(chuàng)建索引時大大的提升性能
【其他鏈接】檔案管理系統(tǒng)

本篇文章由一文多發(fā)平臺ArtiPub自動發(fā)布

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容