Skip to content

Commit e838ad9

Browse files
authored
Add Neo4j examples after community migration (#155)
Add Neo4j examples after community migration - Neo4jEmbeddingStore configuration examples - Neo4jText2CypherRetriever example - Spring Boot example
1 parent 2d5f751 commit e838ad9

File tree

6 files changed

+273
-24
lines changed

6 files changed

+273
-24
lines changed

neo4j-example/pom.xml

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,32 +7,63 @@
77
<groupId>dev.langchain4j</groupId>
88
<artifactId>neo4j-example</artifactId>
99
<version>1.0.0-beta4</version>
10-
10+
<parent>
11+
<groupId>org.springframework.boot</groupId>
12+
<artifactId>spring-boot-starter-parent</artifactId>
13+
<version>3.2.5</version>
14+
<relativePath/>
15+
</parent>
16+
1117
<properties>
1218
<maven.compiler.source>17</maven.compiler.source>
1319
<maven.compiler.target>17</maven.compiler.target>
1420
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
21+
<java.version>17</java.version>
1522
</properties>
1623

1724
<dependencies>
18-
25+
<dependency>
26+
<groupId>org.springframework.boot</groupId>
27+
<artifactId>spring-boot-starter-web</artifactId>
28+
</dependency>
29+
<dependency>
30+
<groupId>dev.langchain4j</groupId>
31+
<artifactId>langchain4j-spring-boot-starter</artifactId>
32+
<version>${project.version}</version>
33+
</dependency>
1934
<dependency>
2035
<groupId>dev.langchain4j</groupId>
2136
<artifactId>langchain4j-community-neo4j</artifactId>
22-
<version>1.0.0-beta3</version>
37+
<version>${project.version}</version>
38+
</dependency>
39+
<dependency>
40+
<groupId>dev.langchain4j</groupId>
41+
<artifactId>langchain4j-community-neo4j-spring-boot-starter</artifactId>
42+
<version>${project.version}</version>
43+
</dependency>
44+
45+
<dependency>
46+
<groupId>dev.langchain4j</groupId>
47+
<artifactId>langchain4j-community-neo4j-retriever</artifactId>
48+
<version>${project.version}</version>
2349
</dependency>
2450

2551
<dependency>
2652
<groupId>dev.langchain4j</groupId>
2753
<artifactId>langchain4j-embeddings-all-minilm-l6-v2</artifactId>
28-
<version>1.0.0-beta4</version>
54+
<version>${project.version}</version>
2955
</dependency>
3056

3157
<dependency>
3258
<groupId>org.testcontainers</groupId>
3359
<artifactId>neo4j</artifactId>
3460
<version>1.19.6</version>
3561
</dependency>
62+
<dependency>
63+
<groupId>dev.langchain4j</groupId>
64+
<artifactId>langchain4j-open-ai</artifactId>
65+
<version>1.0.0-rc1</version>
66+
</dependency>
3667

3768
</dependencies>
3869

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import dev.langchain4j.community.rag.content.retriever.neo4j.Neo4jGraph;
2+
import dev.langchain4j.community.rag.content.retriever.neo4j.Neo4jText2CypherRetriever;
3+
import dev.langchain4j.model.openai.OpenAiChatModel;
4+
import dev.langchain4j.rag.content.Content;
5+
import dev.langchain4j.rag.query.Query;
6+
import org.neo4j.driver.AuthTokens;
7+
import org.neo4j.driver.Driver;
8+
import org.neo4j.driver.GraphDatabase;
9+
import org.neo4j.driver.Session;
10+
import org.testcontainers.containers.Neo4jContainer;
11+
12+
import java.util.List;
13+
14+
import static dev.langchain4j.internal.Utils.getOrDefault;
15+
import static dev.langchain4j.model.openai.OpenAiChatModelName.GPT_4_O_MINI;
16+
17+
public class Neo4jContentRetrieverExample {
18+
// You can use "demo" api key for demonstration purposes.
19+
// You can get your own OpenAI API key here: https://platform.openai.com/account/api-keys
20+
public static final String OPENAI_API_KEY = getOrDefault(System.getenv("OPENAI_API_KEY"), "demo");
21+
public static final String OPENAI_BASE_URL = getOrDefault(System.getenv("OPENAI_BASE_URL"), "http://langchain4j.dev/demo/openai/v1");
22+
23+
public static void main(String[] args) {
24+
final OpenAiChatModel chatLanguageModel = OpenAiChatModel.builder()
25+
.apiKey(OPENAI_API_KEY)
26+
.baseUrl(OPENAI_BASE_URL)
27+
.modelName(GPT_4_O_MINI)
28+
.build();
29+
30+
try (Neo4jContainer<?> neo4jContainer = new Neo4jContainer<>("neo4j:5.26")
31+
.withoutAuthentication()
32+
.withLabsPlugins("apoc")) {
33+
neo4jContainer.start();
34+
try (Driver driver = GraphDatabase.driver(neo4jContainer.getBoltUrl(), AuthTokens.none())) {
35+
try (Neo4jGraph graph = Neo4jGraph.builder().driver(driver).build()) {
36+
try (Session session = driver.session()) {
37+
session.run("CREATE (book:Book {title: 'Dune'})<-[:WROTE]-(author:Person {name: 'Frank Herbert'})");
38+
}
39+
// The refreshSchema is needed only if we execute write operation after the `Neo4jGraph` instance,
40+
// in this case `CREATE (book:Book...`
41+
// If CREATE (and in general write operations to the db) are performed externally before Neo4jGraph.builder(),
42+
// the refreshSchema() is not needed
43+
graph.refreshSchema();
44+
45+
Neo4jText2CypherRetriever retriever = Neo4jText2CypherRetriever.builder()
46+
.graph(graph)
47+
.chatModel(chatLanguageModel)
48+
.build();
49+
50+
Query query = new Query("Who is the author of the book 'Dune'?");
51+
52+
List<Content> contents = retriever.retrieve(query);
53+
54+
System.out.println(contents.get(0).textSegment().text()); // "Frank Herbert"
55+
}
56+
}
57+
}
58+
}
59+
}
Lines changed: 97 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import dev.langchain4j.community.store.embedding.neo4j.Neo4jEmbeddingStore;
2+
import dev.langchain4j.data.document.Metadata;
23
import dev.langchain4j.data.embedding.Embedding;
34
import dev.langchain4j.data.segment.TextSegment;
45
import dev.langchain4j.model.embedding.onnx.allminilml6v2.AllMiniLmL6V2EmbeddingModel;
@@ -12,35 +13,111 @@
1213

1314
public class Neo4jEmbeddingStoreExample {
1415

16+
private static EmbeddingStore<TextSegment> minimalEmbedding;
17+
private static final EmbeddingModel embeddingModel = new AllMiniLmL6V2EmbeddingModel();
18+
1519
public static void main(String[] args) {
16-
try (Neo4jContainer<?> neo4j = new Neo4jContainer<>("neo4j:5")) {
20+
try (Neo4jContainer<?> neo4j = new Neo4jContainer<>("neo4j:5.26")) {
1721
neo4j.start();
22+
minimalEmbedding = Neo4jEmbeddingStore.builder()
23+
.withBasicAuth(neo4j.getBoltUrl(), "neo4j", neo4j.getAdminPassword())
24+
.dimension(embeddingModel.dimension())
25+
.build();
1826

19-
EmbeddingModel embeddingModel = new AllMiniLmL6V2EmbeddingModel();
20-
21-
EmbeddingStore<TextSegment> embeddingStore = Neo4jEmbeddingStore.builder()
27+
searchEmbeddingsWithSingleMaxResult(minimalEmbedding);
28+
searchEmbeddingsWithAddAllAndSingleMaxResult();
29+
searchEmbeddingsWithAddAllWithMetadataMaxResultsAndMinScore();
30+
31+
// custom embeddingStore
32+
Neo4jEmbeddingStore customEmbeddingStore = Neo4jEmbeddingStore.builder()
2233
.withBasicAuth(neo4j.getBoltUrl(), "neo4j", neo4j.getAdminPassword())
2334
.dimension(embeddingModel.dimension())
35+
.indexName("customidx")
36+
.label("CustomLabel")
37+
.embeddingProperty("customProp")
38+
.idProperty("customId")
39+
.textProperty("customText")
2440
.build();
41+
searchEmbeddingsWithSingleMaxResult(customEmbeddingStore);
42+
}
43+
}
2544

26-
TextSegment segment1 = TextSegment.from("I like football.");
27-
Embedding embedding1 = embeddingModel.embed(segment1).content();
28-
embeddingStore.add(embedding1, segment1);
45+
private static void searchEmbeddingsWithSingleMaxResult(EmbeddingStore<TextSegment> minimalEmbedding) {
46+
47+
TextSegment segment1 = TextSegment.from("I like football.");
48+
Embedding embedding1 = embeddingModel.embed(segment1).content();
49+
minimalEmbedding.add(embedding1, segment1);
2950

30-
TextSegment segment2 = TextSegment.from("The weather is good today.");
31-
Embedding embedding2 = embeddingModel.embed(segment2).content();
32-
embeddingStore.add(embedding2, segment2);
51+
TextSegment segment2 = TextSegment.from("The weather is good today.");
52+
Embedding embedding2 = embeddingModel.embed(segment2).content();
53+
minimalEmbedding.add(embedding2, segment2);
3354

34-
Embedding queryEmbedding = embeddingModel.embed("What is your favourite sport?").content();
35-
EmbeddingSearchRequest embeddingSearchRequest = EmbeddingSearchRequest.builder()
36-
.queryEmbedding(queryEmbedding)
37-
.maxResults(1)
38-
.build();
39-
List<EmbeddingMatch<TextSegment>> matches = embeddingStore.search(embeddingSearchRequest).matches();
40-
EmbeddingMatch<TextSegment> embeddingMatch = matches.get(0);
55+
Embedding queryEmbedding = embeddingModel.embed("What is your favourite sport?").content();
56+
final EmbeddingSearchRequest request = EmbeddingSearchRequest.builder()
57+
.queryEmbedding(queryEmbedding)
58+
.maxResults(1)
59+
.build();
60+
List<EmbeddingMatch<TextSegment>> relevant = minimalEmbedding.search(request).matches();
61+
EmbeddingMatch<TextSegment> embeddingMatch = relevant.get(0);
4162

42-
System.out.println(embeddingMatch.score()); // 0.8144289255142212
43-
System.out.println(embeddingMatch.embedded().text()); // I like football.
44-
}
63+
System.out.println(embeddingMatch.score()); // 0.8144289255142212
64+
System.out.println(embeddingMatch.embedded().text()); // I like football.
65+
}
66+
67+
private static void searchEmbeddingsWithAddAllAndSingleMaxResult() {
68+
69+
TextSegment segment1 = TextSegment.from("I like football.");
70+
Embedding embedding1 = embeddingModel.embed(segment1).content();
71+
72+
TextSegment segment2 = TextSegment.from("The weather is good today.");
73+
Embedding embedding2 = embeddingModel.embed(segment2).content();
74+
75+
TextSegment segment3 = TextSegment.from("I like basketball.");
76+
Embedding embedding3 = embeddingModel.embed(segment3).content();
77+
minimalEmbedding.addAll(
78+
List.of(embedding1, embedding2, embedding3),
79+
List.of(segment1, segment2, segment3)
80+
);
81+
82+
Embedding queryEmbedding = embeddingModel.embed("What are your favourites sport?").content();
83+
final EmbeddingSearchRequest request = EmbeddingSearchRequest.builder()
84+
.queryEmbedding(queryEmbedding)
85+
.maxResults(1)
86+
.build();
87+
List<EmbeddingMatch<TextSegment>> relevant = minimalEmbedding.search(request).matches();
88+
89+
relevant.forEach(match -> {
90+
System.out.println(match.score()); // 0.8144289255142212
91+
System.out.println(match.embedded().text()); // I like football. || I like basketball.
92+
});
93+
94+
}
95+
96+
private static void searchEmbeddingsWithAddAllWithMetadataMaxResultsAndMinScore() {
97+
98+
TextSegment segment1 = TextSegment.from("I like football.", Metadata.from("test-key-1", "test-value-1"));
99+
Embedding embedding1 = embeddingModel.embed(segment1).content();
100+
101+
TextSegment segment2 = TextSegment.from("The weather is good today.", Metadata.from("test-key-2", "test-value-2"));
102+
Embedding embedding2 = embeddingModel.embed(segment2).content();
103+
104+
TextSegment segment3 = TextSegment.from("I like basketball.", Metadata.from("test-key-3", "test-value-3"));
105+
Embedding embedding3 = embeddingModel.embed(segment3).content();
106+
minimalEmbedding.addAll(
107+
List.of(embedding1, embedding2, embedding3),
108+
List.of(segment1, segment2, segment3)
109+
);
110+
111+
Embedding queryEmbedding = embeddingModel.embed("What are your favourite sports?").content();
112+
final EmbeddingSearchRequest request = EmbeddingSearchRequest.builder()
113+
.queryEmbedding(queryEmbedding)
114+
.maxResults(2)
115+
.minScore(0.15)
116+
.build();
117+
List<EmbeddingMatch<TextSegment>> relevant = minimalEmbedding.search(request).matches();
118+
relevant.forEach(match -> {
119+
System.out.println(match.score()); // 0.8144289255142212
120+
System.out.println(match.embedded().text()); // I like football. || I like basketball.
121+
});
45122
}
46123
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package com.example.demo;
2+
3+
import dev.langchain4j.data.embedding.Embedding;
4+
import dev.langchain4j.data.segment.TextSegment;
5+
import dev.langchain4j.model.embedding.EmbeddingModel;
6+
import dev.langchain4j.store.embedding.EmbeddingStore;
7+
import dev.langchain4j.store.embedding.EmbeddingSearchRequest;
8+
import org.springframework.web.bind.annotation.*;
9+
10+
import java.util.List;
11+
12+
@RestController
13+
@RequestMapping("/api/embeddings")
14+
public class EmbeddingController {
15+
16+
private final EmbeddingStore<TextSegment> store;
17+
private final EmbeddingModel model;
18+
19+
public EmbeddingController(EmbeddingStore<TextSegment> store, EmbeddingModel model) {
20+
this.store = store;
21+
this.model = model;
22+
}
23+
24+
@PostMapping("/add")
25+
public String add(@RequestBody String text) {
26+
TextSegment segment = TextSegment.from(text);
27+
Embedding embedding = model.embed(text).content();
28+
return store.add(embedding, segment);
29+
}
30+
31+
@PostMapping("/search")
32+
public List<String> search(@RequestBody String query) {
33+
Embedding queryEmbedding = model.embed(query).content();
34+
EmbeddingSearchRequest request = EmbeddingSearchRequest.builder()
35+
.queryEmbedding(queryEmbedding)
36+
.maxResults(5)
37+
.build();
38+
return store.search(request).matches()
39+
.stream()
40+
.map(i -> i.embedded().text()).toList();
41+
}
42+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package com.example.demo;
2+
3+
import dev.langchain4j.model.embedding.onnx.allminilml6v2.AllMiniLmL6V2EmbeddingModel;
4+
import org.springframework.boot.SpringApplication;
5+
import org.springframework.boot.autoconfigure.SpringBootApplication;
6+
import org.springframework.context.annotation.Bean;
7+
8+
/**
9+
* NOTE:
10+
* This example assumes we have a Neo4j instance with Bolt URI bolt://localhost:7687, username 'neo4j' and password 'pass1234'
11+
* as specified in the application.properties.
12+
* If needed, change the properties values to address a running instance.
13+
*
14+
* To add an embedding
15+
* curl -X POST localhost:8083/api/embeddings/add -H "Content-Type: text/plain" -d "embeddingTest"
16+
*
17+
* To search embeddings
18+
* curl -X POST localhost:8083/api/embeddings/search -H "Content-Type: text/plain" -d "querySearchTest"
19+
*/
20+
@SpringBootApplication
21+
public class SpringBootExample {
22+
23+
public static void main(String[] args) {
24+
SpringApplication.run(SpringBootExample.class, args);
25+
}
26+
27+
@Bean
28+
public AllMiniLmL6V2EmbeddingModel embeddingModel() {
29+
return new AllMiniLmL6V2EmbeddingModel();
30+
}
31+
32+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
spring.application.name=LangChain4j Spring Boot Example
2+
server.port=8083
3+
4+
langchain4j.community.neo4j.auth.uri=bolt://localhost:7687
5+
langchain4j.community.neo4j.auth.user=neo4j
6+
langchain4j.community.neo4j.auth.password=pass1234
7+
langchain4j.community.neo4j.label=CustomLabel
8+
langchain4j.community.neo4j.dimension=384

0 commit comments

Comments
 (0)