inital commit

This commit is contained in:
2024-12-12 15:52:34 +03:00
commit d7cc27f2bd
5 changed files with 441 additions and 0 deletions

71
README.md Normal file
View File

@@ -0,0 +1,71 @@
# Neo4j golang example
Команда запуска сервера в Docker для локальной разработки:
```sh
docker run -d --name neo4j -p7474:7474 -p7687:7687 -e NEO4J_AUTH=neo4j/password neo4j
```
- На порту `7474` будет доступен вебинтерфейс Neo4j: http://localhost:7474
- Порт `7687` используется для подключения к базе данных через Bolt-протокол.
- Про Bolt можно детальнее почитать [тут](https://neo4j.com/docs/bolt/current/bolt/)
Запросы к neo4j пишутся на языке [Cypher](https://opencypher.org).
Ниже приведены примеры запросов для создания узлов и связей между ними, а также для поиска связанных узлов.
## Создание узлов с помощью MERGE
Запрос:
```cypher
MERGE (a:Person {name: $name, age: $age})
```
**Объяснение**:
- `MERGE`: Проверяет, существует ли узел с указанными свойствами. Если не существует, создаёт его. Если существует, повторного создания не будет.
- `(a:Person ...)`: Создаётся или ищется узел с меткой Person. Метка (label) описывает тип сущности (например, Person для человека).
- `{name: $name, age: $age}`: Свойства узла. name и age задаются через параметры (обозначены как $name и $age), значения которых передаются из кода.
## Создание уникальных связей с помощью MERGE
Запрос:
```cypher
MATCH (a:Person {name: $name1}), (b:Person {name: $name2})
MERGE (a)-[:FRIENDS]->(b)
```
**Объяснение**:
- `MATCH (a:Person {name: $name1})`: Ищет узел с меткой Person, у которого свойство name равно значению параметра $name1. Этот узел связывается с переменной a.
- `MATCH (b:Person {name: $name2})`: Аналогично, ищет узел Person с именем $name2 и связывает его с переменной b.
- `MERGE (a)-[:FRIENDS]->(b`): Проверяет, существует ли связь FRIENDS от узла a к узлу b. Если такая связь существует, ничего не делает. Если не существует, создаёт её.
- `[:FRIENDS]:` Определяет тип связи. В данном случае это "дружба".
- `->`: Направление связи. Стрелка указывает, что a дружит с b.
## Поиск друзей конкретного человека
```cypher
MATCH (a:Person {name: $name})-[:FRIENDS]->(friend)
RETURN friend.name AS name, friend.age AS age
```
**Объяснение**:
- `MATCH (a:Person {name: $name})`: Ищет узел Person с именем, равным значению $name. Этот узел связывается с переменной a.
- `-[:FRIENDS]->(friend)`: Находит все узлы, связанные с узлом a связью типа FRIENDS. Эти узлы связываются с переменной friend.
- `RETURN friend.name AS name, friend.age AS age`: Возвращает свойства найденных узлов:
- `friend.name` — имя друга.
- `friend.age` — возраст друга.
- `AS` используется для задания алиасов, чтобы упростить доступ к возвращённым данным.
## Поиск кратчайшего пути между двумя узлами
Запрос:
```cypher
MATCH p = shortestPath((a:Person {name: $start})-[:FRIENDS*]-(b:Person {name: $end}))
RETURN p
```
**Объяснение**:
- `MATCH p = shortestPath((a:Person {name: $start})-[:FRIENDS*]-(b:Person {name: $end}))`: Находит кратчайший путь `p` между узлами `Person`, где начальный узел имеет имя, заданное параметром `$start`, и конечный узел имеет имя, заданное параметром `$end`. Связь между узлами должна быть типа `FRIENDS`.
- `RETURN p`: Возвращает найденный путь `p`.
## Поиск самого длинного пути между двумя узлами
Запрос:
```cypher
MATCH p = (a:Person {name: $start})-[:FRIENDS*]-(b:Person {name: $end})
RETURN p, length(p) AS pathLength
ORDER BY pathLength DESC
LIMIT 1
```
**Объяснение**:
- `MATCH p = (a:Person {name: $start})-[:FRIENDS*]-(b:Person {name: $end})`: Находит все пути `p` между узлами `Person`, где начальный узел имеет имя, заданное параметром `$start`, и конечный узел имеет имя, заданное параметром `$end`. Связь между узлами должна быть типа `FRIENDS`.
- `RETURN p, length(p) AS pathLength`: Возвращает каждый найденный путь `p` и его длину в виде `pathLength`.
- `ORDER BY pathLength DESC`: Сортирует пути по длине в убывающем порядке, чтобы самые длинные пути шли первыми.
- `LIMIT 1`: Ограничивает результат одним самым длинным найденным путем.

30
go.mod Normal file
View File

@@ -0,0 +1,30 @@
module github.com/maksim77/neo4j_example
go 1.23.0
require (
github.com/neo4j/neo4j-go-driver/v5 v5.27.0
github.com/raito-io/neo4j-tracing v0.0.5
go.opentelemetry.io/otel v1.32.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.32.0
go.opentelemetry.io/otel/sdk v1.32.0
)
require (
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 // indirect
go.opentelemetry.io/otel/metric v1.32.0 // indirect
go.opentelemetry.io/otel/trace v1.32.0 // indirect
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
golang.org/x/net v0.30.0 // indirect
golang.org/x/sys v0.27.0 // indirect
golang.org/x/text v0.20.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28 // indirect
google.golang.org/grpc v1.67.1 // indirect
google.golang.org/protobuf v1.35.1 // indirect
)

55
go.sum Normal file
View File

@@ -0,0 +1,55 @@
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 h1:ad0vkEBuk23VJzZR9nkLVG0YAoN9coASF1GusYX6AlU=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0/go.mod h1:igFoXX2ELCW06bol23DWPB5BEWfZISOzSP5K2sbLea0=
github.com/neo4j/neo4j-go-driver/v5 v5.27.0 h1:YdsIxDjAQbjlP/4Ha9B/gF8Y39UdgdTwCyihSxy8qTw=
github.com/neo4j/neo4j-go-driver/v5 v5.27.0/go.mod h1:Vff8OwT7QpLm7L2yYr85XNWe9Rbqlbeb9asNXJTHO4k=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/raito-io/neo4j-tracing v0.0.5 h1:Z1eEQl1Imm0DFkR2yfMc7jVW+ix4oZxAVOAWPQBgI2Q=
github.com/raito-io/neo4j-tracing v0.0.5/go.mod h1:m0utJXW1BPoBdKZ1cVhpyVZ1ChWttj8pSVtka/5j63s=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U=
go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0 h1:IJFEoHiytixx8cMiVAO+GmHR6Frwu+u5Ur8njpFO6Ac=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0/go.mod h1:3rHrKNtLIoS0oZwkY2vxi+oJcwFRWdtUyRII+so45p8=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.32.0 h1:9kV11HXBHZAvuPUZxmMWrH8hZn/6UnHX4K0mu36vNsU=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.32.0/go.mod h1:JyA0FHXe22E1NeNiHmVp7kFHglnexDQ7uRWDiiJ1hKQ=
go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M=
go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8=
go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4=
go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU=
go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM=
go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8=
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 h1:M0KvPgPmDZHPlbRbaNU1APr28TvwvvdUPlSv7PUvy8g=
google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28/go.mod h1:dguCy7UOdZhTvLzDyt15+rOrawrpM4q7DD9dQ1P11P4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28 h1:XVhgTWWV3kGQlwJHR3upFWZeTsei6Oks1apkZSeonIE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E=
google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

230
main.go Normal file
View File

@@ -0,0 +1,230 @@
package main
import (
"context"
"fmt"
"log"
"time"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
neo4j_tracing "github.com/raito-io/neo4j-tracing"
"go.opentelemetry.io/otel"
)
var tracer = otel.Tracer("neo4j_example")
func main() {
shutdown, err := InstallExportPipeline()
if err != nil {
log.Fatal(err.Error())
}
defer func() {
if err := shutdown(context.Background()); err != nil {
log.Fatal(err.Error())
}
}()
// Создаём общий контекст с таймаутом на 10 секунд
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
ctx, span := tracer.Start(ctx, "main")
defer span.End()
// Параметры подключения
uri := "neo4j://localhost:7687"
username := "neo4j"
password := "password"
driverFactory := neo4j_tracing.NewNeo4jTracer()
driver, err := driverFactory.NewDriverWithContext(uri, neo4j.BasicAuth(username, password, ""))
if err != nil {
panic(err)
}
// Создаем драйвер
// driver, err := neo4j.NewDriverWithContext(uri, neo4j.BasicAuth(username, password, ""))
// if err != nil {
// log.Fatalf("Ошибка подключения к Neo4j: %v", err)
// }
defer func() {
_ = driver.Close(ctx)
}()
// Создаем сессию
session := driver.NewSession(ctx, neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite})
defer func() {
_ = session.Close(ctx)
}()
// Запись данных
_, err = session.ExecuteWrite(ctx, func(tx neo4j.ManagedTransaction) (any, error) {
// Создание пользователей
queries := []struct {
query string
params map[string]interface{}
}{
{query: "MERGE (a:Person {name: $name, age: $age})", params: map[string]interface{}{"name": "Alice", "age": 30}},
{query: "MERGE (b:Person {name: $name, age: $age})", params: map[string]interface{}{"name": "Bob", "age": 25}},
{query: "MERGE (c:Person {name: $name, age: $age})", params: map[string]interface{}{"name": "Charlie", "age": 35}},
{query: "MERGE (d:Person {name: $name, age: $age})", params: map[string]interface{}{"name": "Diana", "age": 28}},
{query: "MERGE (e:Person {name: $name, age: $age})", params: map[string]interface{}{"name": "Eve", "age": 22}},
{query: "MERGE (f:Person {name: $name, age: $age})", params: map[string]interface{}{"name": "Frank", "age": 40}},
{query: "MERGE (g:Person {name: $name, age: $age})", params: map[string]interface{}{"name": "Grace", "age": 33}},
{query: "MERGE (h:Person {name: $name, age: $age})", params: map[string]interface{}{"name": "Hank", "age": 29}},
{query: "MERGE (i:Person {name: $name, age: $age})", params: map[string]interface{}{"name": "Ivy", "age": 31}},
{query: "MERGE (j:Person {name: $name, age: $age})", params: map[string]interface{}{"name": "Jack", "age": 26}},
}
for _, q := range queries {
_, err := tx.Run(ctx, q.query, q.params)
if err != nil {
return nil, err
}
}
// Создание связей
relationships := []struct {
query string
params map[string]interface{}
}{
// Основные связи
{query: "MATCH (a:Person {name: $name1}), (b:Person {name: $name2}) MERGE (a)-[:FRIENDS]->(b)", params: map[string]interface{}{"name1": "Alice", "name2": "Bob"}},
{query: "MATCH (a:Person {name: $name1}), (b:Person {name: $name2}) MERGE (a)-[:FRIENDS]->(b)", params: map[string]interface{}{"name1": "Bob", "name2": "Charlie"}},
{query: "MATCH (a:Person {name: $name1}), (b:Person {name: $name2}) MERGE (a)-[:FRIENDS]->(b)", params: map[string]interface{}{"name1": "Charlie", "name2": "Diana"}},
{query: "MATCH (a:Person {name: $name1}), (b:Person {name: $name2}) MERGE (a)-[:FRIENDS]->(b)", params: map[string]interface{}{"name1": "Diana", "name2": "Eve"}},
{query: "MATCH (a:Person {name: $name1}), (b:Person {name: $name2}) MERGE (a)-[:FRIENDS]->(b)", params: map[string]interface{}{"name1": "Eve", "name2": "Frank"}},
{query: "MATCH (a:Person {name: $name1}), (b:Person {name: $name2}) MERGE (a)-[:FRIENDS]->(b)", params: map[string]interface{}{"name1": "Frank", "name2": "Grace"}},
{query: "MATCH (a:Person {name: $name1}), (b:Person {name: $name2}) MERGE (a)-[:FRIENDS]->(b)", params: map[string]interface{}{"name1": "Grace", "name2": "Hank"}},
{query: "MATCH (a:Person {name: $name1}), (b:Person {name: $name2}) MERGE (a)-[:FRIENDS]->(b)", params: map[string]interface{}{"name1": "Hank", "name2": "Ivy"}},
{query: "MATCH (a:Person {name: $name1}), (b:Person {name: $name2}) MERGE (a)-[:FRIENDS]->(b)", params: map[string]interface{}{"name1": "Ivy", "name2": "Jack"}},
{query: "MATCH (a:Person {name: $name1}), (b:Person {name: $name2}) MERGE (a)-[:FRIENDS]->(b)", params: map[string]interface{}{"name1": "Jack", "name2": "Alice"}},
// Дополнительные связи для усложнения графа
{query: "MATCH (a:Person {name: $name1}), (b:Person {name: $name2}) MERGE (a)-[:FRIENDS]->(b)", params: map[string]interface{}{"name1": "Alice", "name2": "Charlie"}},
{query: "MATCH (a:Person {name: $name1}), (b:Person {name: $name2}) MERGE (a)-[:FRIENDS]->(b)", params: map[string]interface{}{"name1": "Bob", "name2": "Eve"}},
{query: "MATCH (a:Person {name: $name1}), (b:Person {name: $name2}) MERGE (a)-[:FRIENDS]->(b)", params: map[string]interface{}{"name1": "Charlie", "name2": "Frank"}},
{query: "MATCH (a:Person {name: $name1}), (b:Person {name: $name2}) MERGE (a)-[:FRIENDS]->(b)", params: map[string]interface{}{"name1": "Diana", "name2": "Hank"}},
{query: "MATCH (a:Person {name: $name1}), (b:Person {name: $name2}) MERGE (a)-[:FRIENDS]->(b)", params: map[string]interface{}{"name1": "Eve", "name2": "Ivy"}},
{query: "MATCH (a:Person {name: $name1}), (b:Person {name: $name2}) MERGE (a)-[:FRIENDS]->(b)", params: map[string]interface{}{"name1": "Frank", "name2": "Jack"}},
{query: "MATCH (a:Person {name: $name1}), (b:Person {name: $name2}) MERGE (a)-[:FRIENDS]->(b)", params: map[string]interface{}{"name1": "Grace", "name2": "Alice"}},
{query: "MATCH (a:Person {name: $name1}), (b:Person {name: $name2}) MERGE (a)-[:FRIENDS]->(b)", params: map[string]interface{}{"name1": "Hank", "name2": "Bob"}},
{query: "MATCH (a:Person {name: $name1}), (b:Person {name: $name2}) MERGE (a)-[:FRIENDS]->(b)", params: map[string]interface{}{"name1": "Ivy", "name2": "Charlie"}},
{query: "MATCH (a:Person {name: $name1}), (b:Person {name: $name2}) MERGE (a)-[:FRIENDS]->(b)", params: map[string]interface{}{"name1": "Jack", "name2": "Diana"}},
}
for _, r := range relationships {
_, err := tx.Run(ctx, r.query, r.params)
if err != nil {
return nil, err
}
}
return nil, nil
})
if err != nil {
log.Fatalf("Ошибка выполнения записи: %v", err)
}
// Чтение данных: находим друзей для Alice
_, err = session.ExecuteRead(ctx, func(tx neo4j.ManagedTransaction) (any, error) {
result, err := tx.Run(ctx,
"MATCH (a:Person {name: $name})-[:FRIENDS]->(friend) "+
"RETURN friend.name AS name, friend.age AS age",
map[string]interface{}{"name": "Alice"})
if err != nil {
return nil, err
}
// Обработка результата
fmt.Println("Друзья Alice:")
for result.Next(ctx) {
record := result.Record()
name, _ := record.Get("name")
age, _ := record.Get("age")
fmt.Printf("- %s (Возраст: %d)\n", name, age)
}
return nil, result.Err()
})
if err != nil {
log.Fatalf("Ошибка выполнения чтения: %v", err)
}
// Чтение данных: находим кратчайший путь от Alice до Diana
result, err := session.ExecuteRead(ctx, func(tx neo4j.ManagedTransaction) (any, error) {
query := `
MATCH p = shortestPath((a:Person {name: $start})-[:FRIENDS*]-(b:Person {name: $end}))
RETURN p
`
params := map[string]interface{}{
"start": "Alice",
"end": "Diana",
}
res, err := tx.Run(ctx, query, params)
if err != nil {
return nil, err
}
if res.Next(ctx) {
path, _ := res.Record().Get("p")
return path, nil
}
return nil, res.Err()
})
if err != nil {
log.Fatalf("Ошибка выполнения запроса: %v", err)
}
// Вывод результата:
if path, ok := result.(neo4j.Path); ok {
fmt.Println("Кратчайший путь от Alice до Diana:")
for _, node := range path.Nodes {
if name, exists := node.Props["name"].(string); exists {
fmt.Printf(" - %s\n", name)
}
}
} else {
fmt.Println("Путь не найден.")
}
// Чтение данных: находим самый длинный путь от Alice до Diana
result, err = session.ExecuteRead(ctx, func(tx neo4j.ManagedTransaction) (any, error) {
query := `
MATCH p = (a:Person {name: $start})-[:FRIENDS*]-(b:Person {name: $end})
RETURN p, length(p) AS pathLength
ORDER BY pathLength DESC
LIMIT 1
`
params := map[string]interface{}{
"start": "Alice",
"end": "Diana",
}
res, err := tx.Run(ctx, query, params)
if err != nil {
return nil, err
}
if res.Next(ctx) {
record := res.Record()
path, _ := record.Get("p")
return path, nil
}
return nil, res.Err()
})
if err != nil {
log.Fatalf("Ошибка выполнения запроса: %v", err)
}
// Вывод результата:
if path, ok := result.(neo4j.Path); ok {
fmt.Println("Самый длинный путь от Alice до Diana:")
for _, node := range path.Nodes {
if name, exists := node.Props["name"].(string); exists {
fmt.Printf(" - %s\n", name)
}
}
} else {
fmt.Println("Путь не найден.")
}
fmt.Println("Работа завершена успешно!")
}

55
otel.go Normal file
View File

@@ -0,0 +1,55 @@
package main
import (
"context"
"fmt"
"log"
"time"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
)
func InstallExportPipeline() (func(context.Context) error, error) {
traceClient := otlptracegrpc.NewClient(
otlptracegrpc.WithInsecure(),
otlptracegrpc.WithEndpoint("127.0.0.1:4317"),
)
sctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
traceExp, err := otlptrace.New(sctx, traceClient)
if err != nil {
log.Fatal(err)
}
if err != nil {
return nil, fmt.Errorf("creating stdout exporter: %w", err)
}
res, err := resource.New(context.Background(),
resource.WithFromEnv(),
resource.WithProcess(),
resource.WithTelemetrySDK(),
resource.WithHost(),
resource.WithAttributes(
semconv.ServiceNameKey.String("neo4j_example"),
),
)
if err != nil {
log.Fatal(err)
}
tracerProvider := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(traceExp),
sdktrace.WithResource(res),
)
otel.SetTracerProvider(tracerProvider)
otel.SetTextMapPropagator(propagation.TraceContext{})
return tracerProvider.Shutdown, nil
}