ElasticSearch
Sito ufficiale: Elasticsearch: Motore di ricerca e analisi distribuito ufficiale | Elastic
Elasticsearch è un motore di ricerca e analisi dati distribuito, in stile RESTful, in grado di risolvere vari casi d'uso che emergono continuamente. Come cuore dell'Elastic Stack, Elasticsearch archivia centralmente i tuoi dati, consentendoti di eseguire ricerche velocissime, ottimizzare la pertinenza, eseguire analisi potenti e scalare facilmente. Questo articolo spiegherà come utilizzare Go per eseguire alcune operazioni di base su Elasticsearch, come aggiunta, eliminazione, modifica e ricerca. Se non hai familiarità con Elasticsearch, ti preghiamo di studiarlo prima autonomamente.
Dipendenze
Scarica la libreria ufficiale delle dipendenze
$ github.com/elastic/go-elasticsearch/v7Se stai utilizzando ES8, cambia versione
$ github.com/elastic/go-elasticsearch/v8TIP
Questo articolo utilizza ES8 per la dimostrazione
Connessione
Utilizza la funzione elasticsearch.NewClient per stabilire una nuova connessione
func NewClient(cfg Config) (*Client, error)ES8+ utilizza la connessione HTTPS per impostazione predefinita. Quando si stabilisce una connessione HTTPS, è possibile utilizzare il certificato CA o l'impronta digitale CA, entrambi generati sul server Elasticsearch. Un esempio è il seguente
client, err := elasticsearch.NewClient(elasticsearch.Config{
Addresses: []string{"https://192.168.153.132:9200"},
Username: "elastic",
Password: "TETJ8IY+ifbt8SLc+RRQ",
CertificateFingerprint: "C0E9867C7D446BFF72FE46E7E9FE3455E970A8ADB0D3DF0E1472D55DB2612CD5",
})Le API Go fornite da elasticsearch sono fondamentalmente tutte funzioni con opzioni. Ad esempio, testare se il servizio è disponibile tramite l'API ping
pingResp, err := client.Ping(client.Ping.WithPretty(), client.Ping.WithHuman())
if err != nil {
panic(err)
}
fmt.Println(pingResp)Output
[200 OK]Un altro esempio, verificare lo stato del servizio tramite l'API Info
infoResp, err := client.Info(client.Info.WithHuman())
if err != nil {
panic(err)
}
fmt.Println(infoResp)Output
[200 OK] {
"name" : "db-debian12",
"cluster_name" : "docker-cluster",
"cluster_uuid" : "OMbDIsNwTFiuyjNF9Xnpbw",
"version" : {
"number" : "8.15.0",
"build_flavor" : "default",
"build_type" : "docker",
"build_hash" : "1a77947f34deddb41af25e6f0ddb8e830159c179",
"build_date" : "2024-08-05T10:05:34.233336849Z",
"build_snapshot" : false,
"lucene_version" : "9.11.1",
"minimum_wire_compatibility_version" : "7.17.0",
"minimum_index_compatibility_version" : "7.0.0"
},
"tagline" : "You Know, for Search"
}TIP
Per qualsiasi domanda sulle API ES, consultare la documentazione ufficiale ES Restful API.
Indici
Operare gli indici tramite le API Go. Tutte le API relative alle operazioni sugli indici si trovano nella struttura esapi.Indices
// Indices contiene le API Indices
type Indices struct {
AddBlock IndicesAddBlock
Analyze IndicesAnalyze
ClearCache IndicesClearCache
Clone IndicesClone
Close IndicesClose
...
...
ValidateQuery IndicesValidateQuery
}Creazione
Creare un indice, come mostrato di seguito
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 2
},
"mappings": {
"properties": {
"name": {
"type": "text"
},
"age": {
"type": "long"
},
"salary": {
"type": "double"
}
}
}
}Operazione effettiva, simile all'invio di una richiesta HTTP
func main() {
client, err := newClient()
if err != nil {
panic(err)
}
dsl := bytes.NewBufferString(`{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 2
},
"mappings": {
"properties": {
"name": {
"type": "text"
},
"age": {
"type": "long"
},
"salary": {
"type": "double"
}
}
}
}`)
createIndices := client.Indices.Create
resp, err := createIndices("user", createIndices.WithBody(dsl))
if err != nil {
panic(err)
}
fmt.Println(resp)
}Output
[200 OK] {"acknowledged":true,"shards_acknowledged":true,"index":"user"}Recupero
Recuperare informazioni su uno o più indici
func main() {
client, err := newClient()
if err != nil {
panic(err)
}
get := client.Indices.Get
response, err := get([]string{"user"}, get.WithPretty(), get.WithHuman())
if err != nil {
panic(err)
}
fmt.Println(response)
}Output
[200 OK] {
"user" : {
"aliases" : { },
"mappings" : {
"properties" : {
"age" : {
"type" : "long"
},
"name" : {
"type" : "text"
},
"salary" : {
"type" : "double"
}
}
},
"settings" : {
"index" : {
"creation_date_string" : "2024-09-23T04:35:04.528Z",
"routing" : {
"allocation" : {
"include" : {
"_tier_preference" : "data_content"
}
}
},
"number_of_shards" : "3",
"provided_name" : "user",
"creation_date" : "1727066104528",
"number_of_replicas" : "2",
"uuid" : "AvhhuqV2ShGkRP9z7XbdDA",
"version" : {
"created_string" : "8.14.4-snapshot[8512000]",
"created" : "8512000"
}
}
}
}
}Analisi
Analizzare stringhe di testo per indici specificati e restituire i risultati. Testo come segue
{
"analyzer": "standard",
"text": ["this is a test", "the second text"]
}Codice
func main() {
client, err := newClient()
if err != nil {
panic(err)
}
analyze := client.Indices.Analyze
dsl := bytes.NewBufferString(`{
"analyzer" : "standard",
"text" : ["this is a test", "the second text"]
}`)
response, err := analyze(analyze.WithIndex("user"), analyze.WithBody(dsl), analyze.WithPretty(), analyze.WithHuman())
if err != nil {
panic(err)
}
fmt.Println(response)
}Output
[200 OK] {
"tokens" : [
{
"token" : "this",
"start_offset" : 0,
"end_offset" : 4,
"type" : "<ALPHANUM>",
"position" : 0
},
{
"token" : "is",
"start_offset" : 5,
"end_offset" : 7,
"type" : "<ALPHANUM>",
"position" : 1
},
{
"token" : "a",
"start_offset" : 8,
"end_offset" : 9,
"type" : "<ALPHANUM>",
"position" : 2
},
{
"token" : "test",
"start_offset" : 10,
"end_offset" : 14,
"type" : "<ALPHANUM>",
"position" : 3
},
{
"token" : "the",
"start_offset" : 15,
"end_offset" : 18,
"type" : "<ALPHANUM>",
"position" : 104
},
{
"token" : "second",
"start_offset" : 19,
"end_offset" : 25,
"type" : "<ALPHANUM>",
"position" : 105
},
{
"token" : "text",
"start_offset" : 26,
"end_offset" : 30,
"type" : "<ALPHANUM>",
"position" : 106
}
]
}Eliminazione
Eliminare uno o più indici specificati
func main() {
client, err := newClient()
if err != nil {
panic(err)
}
indicesDelete := client.Indices.Delete
response, err := indicesDelete([]string{"user"})
if err != nil {
panic(err)
}
fmt.Println(response)
}Output
[200 OK] {"acknowledged":true}Nelle API sopra, si può vedere che il corpo della richiesta deve essere serializzato manualmente, l'ufficialità non lo ha mappato in strutture Go, e anche il corpo della risposta deve essere gestito manualmente. Queste sono le API più comunemente utilizzate, altre sono più o meno simili, senza grandi differenze.
Documenti
Creazione
Creare un documento come segue
{
"name": "jack",
"age": 12,
"salary": 5701.1
}Codice
func main() {
client, err := newClient()
if err != nil {
panic(err)
}
doc := bytes.NewBufferString(`{
"name": "jack",
"age": 12,
"salary": 5701.1
}`)
create := client.Create
response, err := create("user", "1", doc, create.WithPretty())
if err != nil {
panic(err)
}
fmt.Println(response)
}Output
[201 Created] {
"_index" : "user",
"_id" : "1",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 3,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 0,
"_primary_term" : 1
}Recupero
Recuperare un documento con un ID specificato
func main() {
client, err := newClient()
if err != nil {
panic(err)
}
get := client.Get
response, err := get("user", "1", get.WithPretty())
if err != nil {
panic(err)
}
fmt.Println(response)
}Output
[200 OK] {
"_index" : "user",
"_id" : "1",
"_version" : 1,
"_seq_no" : 0,
"_primary_term" : 1,
"found" : true,
"_source" : {
"name" : "jack",
"age" : 12,
"salary" : 5701.1
}
}Aggiornamento
Aggiornare il contenuto del documento
{
"doc": {
"name": "jack",
"age": 35,
"salary": 5701.1
}
}Codice
func main() {
client, err := newClient()
if err != nil {
panic(err)
}
doc := bytes.NewBufferString(`{
"doc": { "name": "jack",
"age": 35,
"salary": 5701.1
}}`)
update := client.Update
response, err := update("user", "1", doc, update.WithPretty())
if err != nil {
panic(err)
}
fmt.Println(response)
}Output
[200 OK] {
"_index" : "user",
"_id" : "1",
"_version" : 2,
"result" : "updated",
"_shards" : {
"total" : 3,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 1,
"_primary_term" : 1
}L'API Update supporta anche operazioni come upsert tramite script. Per ulteriori informazioni, consultare Update API.
Eliminazione
Eliminare un documento specificato tramite ID
func main() {
client, err := newClient()
if err != nil {
panic(err)
}
deleteDoc := client.Delete
response, err := deleteDoc("user", "1", deleteDoc.WithPretty())
if err != nil {
panic(err)
}
fmt.Println(response)
}Output
[200 OK] {
"_index" : "user",
"_id" : "1",
"_version" : 3,
"result" : "deleted",
"_shards" : {
"total" : 3,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 2,
"_primary_term" : 1
}Ricerca
L'API di ricerca è l'API più comunemente utilizzata in ES. Di seguito verrà dimostrata brevemente l'utilizzo. Prima preparare i dati.
func main() {
client, err := newClient()
if err != nil {
panic(err)
}
for i := range 10 {
doc := bytes.NewBufferString(fmt.Sprintf(`{
"name": "%s",
"age": %d,
"salary": %f
}`, randomName(), rand.Intn(18)+18, rand.Float64()))
create := client.Create
response, err := create("user", string('0'+i), doc, create.WithPretty())
if err != nil {
panic(err)
}
fmt.Println(response)
}
}
func randomName() string {
var b []byte
for range 10 {
b = append(b, byte(rand.Intn(26)+'a'))
}
return string(b)
}L'API di ricerca funziona esattamente come le normali API HTTP.
Interrogare tutti i documenti
func main() {
client, err := newClient()
if err != nil {
panic(err)
}
dsl := bytes.NewBufferString(`{"query": {"match_all":{}}, "size": 1}`)
search := client.Search
response, err := search(search.WithBody(dsl), search.WithPretty())
if err != nil {
panic(err)
}
fmt.Println(response)
}Corrispondenza di un campo specifico
func main() {
client, err := newClient()
if err != nil {
panic(err)
}
dsl := bytes.NewBufferString(`{"query": {"term":{ "age": 22 } }, "size": 1}`)
search := client.Search
response, err := search(search.WithBody(dsl), search.WithPretty())
if err != nil {
panic(err)
}
fmt.Println(response)
}Riepilogo
Le operazioni di base sono più o meno queste, funzionano esattamente come le API HTTP. Una volta appreso ES, non ci saranno problemi con le API Go. Per operazioni più avanzate come cluster, data stream e altre API simili, si prega di esplorare autonomamente.
