Remove Neo4j driver auto-config from the module.
- Neo4j vector store documentation update.
This commit is contained in:
committed by
Christian Tzolov
parent
1fa2036120
commit
10438c55a3
@@ -6,24 +6,19 @@ link:https://neo4j.com[Neo4j] is an open-source NoSQL graph database.
|
||||
It is a fully transactional database (ACID) that stores data structured as graphs consisting of nodes, connected by relationships.
|
||||
Inspired by the structure of the real world, it allows for high query performance on complex data while remaining intuitive and simple for the developer.
|
||||
|
||||
The link:https://neo4j.com/docs/cypher-manual/current/indexes-for-vector-search/[Neo4j's Vector Search] allows users to query vector embeddings from large datasets. An embedding is a numerical representation of a data object, such as text, image, audio, or document.
|
||||
The link:https://neo4j.com/docs/cypher-manual/current/indexes-for-vector-search/[Neo4j's Vector Search] allows users to query vector embeddings from large datasets.
|
||||
An embedding is a numerical representation of a data object, such as text, image, audio, or document.
|
||||
Embeddings can be stored on _Node_ properties and can be queried with the `db.index.vector.queryNodes()` function.
|
||||
Those indexes are powered by Lucene using a Hierarchical Navigable Small World Graph (HNSW) to perform a k approximate nearest neighbors (k-ANN) query over the vector fields.
|
||||
|
||||
== Prerequisites
|
||||
|
||||
* You would need an xref:api/embeddings.adoc#available-implementations[EmbeddingClient] to generate the embeddings stored in the `Neo4jVectorStore`.
|
||||
* A running Neo4j (5.13+) instance. Following options are available:
|
||||
** link:https://hub.docker.com/_/neo4j[Docker] image `neo4j:5.16.0`
|
||||
* A running Neo4j (5.13+) instance. The following options are available:
|
||||
** link:https://hub.docker.com/_/neo4j[Docker] image
|
||||
** link:https://neo4j.com/download/[Neo4j Desktop]
|
||||
** link:https://neo4j.com/cloud/aura-free/[Neo4j Aura]
|
||||
** link:https://neo4j.com/deployment-center/[Neo4j Server] instance
|
||||
|
||||
== Configuration
|
||||
|
||||
To connect to Neo4j and use the `Neo4jVectorStore`, you need to provide (e.g. via environment variables) access details for your instance.
|
||||
|
||||
TIP: Additionally, you will need a configured xref:api/embeddings.adoc#available-implementations[EmbeddingClient].
|
||||
* If required, an API key for the xref:api/embeddings.adoc#available-implementations[EmbeddingClient] to generate the embeddings stored by the `Neo4jVectorStore`.
|
||||
|
||||
== Dependencies
|
||||
|
||||
@@ -49,46 +44,93 @@ dependencies {
|
||||
|
||||
TIP: Refer to the xref:getting-started.adoc#_dependency_management[Dependency Management] section to add Milestone and/or Snapshot Repositories to your build file.
|
||||
|
||||
== Sample Code
|
||||
== Configuration
|
||||
|
||||
To configure `Neo4jVectorStore` in your application, you can use the following setup:
|
||||
To connect to Neo4j and use the `Neo4jVectorStore`, you need to provide access details for your instance.
|
||||
A simple configuration can either be provided via Spring Boot's _application.properties_,
|
||||
|
||||
Add to your environment (using your own Neo4j credentials and the appropriate access protocol+endpoint) the following properties either by updating and executing the following commands or creating a shell script to be run from your command prompt (Linux/Mac/WSL2):
|
||||
[source,properties]
|
||||
----
|
||||
spring.neo4j.uri=<uri_for_your_neo4j_instance>
|
||||
spring.neo4j.authentication.username=<your_username>
|
||||
spring.neo4j.authentication.password=<your_password>
|
||||
# API key if needed, e.g. OpenAI
|
||||
spring.ai.openai.api.key=<api-key>
|
||||
----
|
||||
|
||||
environment variables,
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
export SPRING_NEO4J_URI=<uri_for_your_neo4j_instance>
|
||||
export SPRING_NEO4J_AUTHENTICATION_USERNAME=<your_username>
|
||||
export SPRING_NEO4J_AUTHENTICATION_PASSWORD=<your_password>
|
||||
# API key if needed, e.g. OpenAI
|
||||
export SPRING_AI_OPENAI_API_KEY=<api-key>
|
||||
----
|
||||
|
||||
or can be a mix of those.
|
||||
For example, if you want to store your API key as an environment variable but keep the rest in the plain _application.properties_ file.
|
||||
|
||||
NOTE: If you choose to create a shell script for ease in future work, be sure to run it prior to starting your application by "sourcing" the file, i.e. `source <your_script_name>.sh`.
|
||||
|
||||
You'll need a `VectorStore` to store the embeddings. You can use the `Neo4jVectorStore` for this purpose, but first, you must create two beans the `Neo4jVectorStore` constructor requires. Here are examples of all of the beans you'll need:
|
||||
NOTE: Besides _application.properties_ and environment variables, Spring Boot offers https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.external-config[additional configuration options].
|
||||
|
||||
Spring Boot's auto-configuration feature for the Neo4j Driver will create a bean instance that will be used by the `Neo4jVectorStore`.
|
||||
|
||||
== Auto-configuration
|
||||
|
||||
Spring AI provides Spring Boot auto-configuration for the Neo4j Vector Sore.
|
||||
To enable it, add the following dependency to your project's Maven `pom.xml` file:
|
||||
|
||||
[source, xml]
|
||||
----
|
||||
<dependency>
|
||||
<groupId>org.springframework.ai</groupId>
|
||||
<artifactId>spring-ai-neo4j-store-spring-boot-starter</artifactId>
|
||||
<version>0.8.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
----
|
||||
|
||||
or to your Gradle `build.gradle` build file.
|
||||
|
||||
[source,groovy]
|
||||
----
|
||||
dependencies {
|
||||
implementation 'org.springframework.ai:spring-ai-neo4j-store-spring-boot-starter:0.8.0-SNAPSHOT'
|
||||
}
|
||||
----
|
||||
|
||||
Please have a look at the list of xref:#_neo4jvectorstore_properties[configuration parameters] for the vector store to learn about the default values and configuration options.
|
||||
|
||||
TIP: Refer to the xref:getting-started.adoc#_dependency_management[Dependency Management] section to add Milestone and/or Snapshot Repositories to your build file.
|
||||
|
||||
Additionally, you will need a configured `EmbeddingClient` bean. Refer to the xref:api/embeddings.adoc#available-implementations[EmbeddingClient] section for more information.
|
||||
|
||||
Here is an example of the needed bean:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
public EmbeddingClient embeddingClient() {
|
||||
// Can be any other EmbeddingClient implementation.
|
||||
// Can be any other EmbeddingClient implementation.
|
||||
return new OpenAiEmbeddingClient(new OpenAiApi(System.getenv("SPRING_AI_OPENAI_API_KEY")));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Driver driver() {
|
||||
return GraphDatabase.driver(System.getenv("SPRING_NEO4J_URI"),
|
||||
AuthTokens.basic(System.getenv("SPRING_NEO4J_AUTHENTICATION_USERNAME"),
|
||||
System.getenv("SPRING_NEO4J_AUTHENTICATION_PASSWORD")));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public VectorStore vectorStore(Driver driver, EmbeddingClient embeddingClient) {
|
||||
return new Neo4jVectorStore(driver, embeddingClient,
|
||||
Neo4jVectorStore.Neo4jVectorStoreConfig.defaultConfig());
|
||||
}
|
||||
----
|
||||
|
||||
The `Neo4jVectorStore` is now ready to be used in your application. You can use it to store embeddings and perform similarity searches.
|
||||
In cases where the Spring Boot auto-configured Neo4j `Driver` bean is not what you want or need, you can still define your own bean.
|
||||
Please read the https://neo4j.com/docs/java-manual/current/client-applications/[Neo4j Java Driver reference] for more in-depth information about the configuration of a custom driver.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
public Driver driver() {
|
||||
return GraphDatabase.driver("neo4j://<host>:<bolt-port>",
|
||||
AuthTokens.basic("<username>", "<password>"));
|
||||
}
|
||||
----
|
||||
|
||||
Now you can auto-wire the `Neo4jVectorStore` as a vector store in your application.
|
||||
|
||||
== Metadata filtering
|
||||
|
||||
@@ -135,31 +177,17 @@ is converted into the proprietary Neo4j filter format:
|
||||
node.`metadata.author` IN ["john","jill"] AND node.`metadata.'article_type'` = "blog"
|
||||
```
|
||||
|
||||
== Auto-configuration
|
||||
== Neo4jVectorStore properties
|
||||
|
||||
Spring AI provides Spring Boot auto-configuration for the Neo4j Vector Sore.
|
||||
To enable it add the following dependency to your project's Maven `pom.xml` file:
|
||||
You can use the following properties in your Spring Boot configuration to customize the Neo4j vector store.
|
||||
|
||||
[source, xml]
|
||||
----
|
||||
<dependency>
|
||||
<groupId>org.springframework.ai</groupId>
|
||||
<artifactId>spring-ai-neo4j-store-spring-boot-starter</artifactId>
|
||||
<version>0.8.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
----
|
||||
|===
|
||||
|Property|Default value
|
||||
|
||||
or to your Gradle `build.gradle` build file.
|
||||
|
||||
[source,groovy]
|
||||
----
|
||||
dependencies {
|
||||
implementation 'org.springframework.ai:spring-ai-neo4j-store-spring-boot-starter:0.8.0-SNAPSHOT'
|
||||
}
|
||||
----
|
||||
|
||||
TIP: Additionally, you will need a configured `EmbeddingClient` bean. Refer to the xref:api/embeddings.adoc#available-implementations[EmbeddingClient] section for more information.
|
||||
|
||||
TIP: Refer to the xref:getting-started.adoc#_dependency_management[Dependency Management] section to add Milestone and/or Snapshot Repositories to your build file.
|
||||
|
||||
Now you can auto-wire the `Neo4jVectorStore` as a vector store in your application.
|
||||
|`spring.ai.vectorstore.neo4j.database-name`|neo4j
|
||||
|`spring.ai.vectorstore.neo4j.embedding-dimension`|1536
|
||||
|`spring.ai.vectorstore.neo4j.distance-type`|cosine
|
||||
|`spring.ai.vectorstore.neo4j.label`|Document
|
||||
|`spring.ai.vectorstore.neo4j.embedding-property`|embedding
|
||||
|`spring.ai.vectorstore.neo4j.index-name`|spring-ai-document-index
|
||||
|===
|
||||
|
||||
@@ -1,289 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.ai.autoconfigure.vectorstore.neo4j;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.time.Duration;
|
||||
|
||||
/**
|
||||
* Properties for Neo4j driver
|
||||
*
|
||||
* @author Jingzhou Ou
|
||||
*/
|
||||
@ConfigurationProperties(Neo4jDriverProperties.CONFIG_PREFIX)
|
||||
public class Neo4jDriverProperties {
|
||||
|
||||
public static final String CONFIG_PREFIX = "spring.ai.vectorstore.neo4j.driver";
|
||||
|
||||
/**
|
||||
* supports bolt or neo4j as schemes.
|
||||
*/
|
||||
private URI uri;
|
||||
|
||||
/**
|
||||
* optional
|
||||
*/
|
||||
private Authentication authentication = new Authentication();
|
||||
|
||||
/**
|
||||
* connection pool configuration
|
||||
*/
|
||||
private PoolSettings pool = new PoolSettings();
|
||||
|
||||
/**
|
||||
* Detailed driver configuration of the driver
|
||||
*/
|
||||
private DriverSettings config = new DriverSettings();
|
||||
|
||||
public URI getUri() {
|
||||
return this.uri;
|
||||
}
|
||||
|
||||
public void setUri(URI uri) {
|
||||
this.uri = uri;
|
||||
}
|
||||
|
||||
public Authentication getAuthentication() {
|
||||
return this.authentication;
|
||||
}
|
||||
|
||||
public void setAuthentication(Authentication authentication) {
|
||||
this.authentication = authentication;
|
||||
}
|
||||
|
||||
public PoolSettings getPool() {
|
||||
return this.pool;
|
||||
}
|
||||
|
||||
public void setPool(PoolSettings pool) {
|
||||
this.pool = pool;
|
||||
}
|
||||
|
||||
public DriverSettings getConfig() {
|
||||
return this.config;
|
||||
}
|
||||
|
||||
public void setConfig(DriverSettings config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
public static class Authentication {
|
||||
|
||||
private String username;
|
||||
|
||||
private String password;
|
||||
|
||||
private String realm;
|
||||
|
||||
/**
|
||||
* kerberos authentication
|
||||
*/
|
||||
private String kerberosTicket;
|
||||
|
||||
public String getUsername() {
|
||||
return this.username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return this.password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getRealm() {
|
||||
return this.realm;
|
||||
}
|
||||
|
||||
public void setRealm(String realm) {
|
||||
this.realm = realm;
|
||||
}
|
||||
|
||||
public String getKerberosTicket() {
|
||||
return this.kerberosTicket;
|
||||
}
|
||||
|
||||
public void setKerberosTicket(String kerberosTicket) {
|
||||
this.kerberosTicket = kerberosTicket;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class PoolSettings {
|
||||
|
||||
private boolean metricsEnabled = false;
|
||||
|
||||
private boolean logLeakedSessions = false;
|
||||
|
||||
private int maxConnectionPoolSize = org.neo4j.driver.internal.async.pool.PoolSettings.DEFAULT_MAX_CONNECTION_POOL_SIZE;
|
||||
|
||||
private Duration idleTimeBeforeConnectionTest;
|
||||
|
||||
private Duration maxConnectionLifetime = Duration
|
||||
.ofMillis(org.neo4j.driver.internal.async.pool.PoolSettings.DEFAULT_MAX_CONNECTION_LIFETIME);
|
||||
|
||||
private Duration connectionAcquisitionTimeout = Duration
|
||||
.ofMillis(org.neo4j.driver.internal.async.pool.PoolSettings.DEFAULT_CONNECTION_ACQUISITION_TIMEOUT);
|
||||
|
||||
public boolean isLogLeakedSessions() {
|
||||
return this.logLeakedSessions;
|
||||
}
|
||||
|
||||
public void setLogLeakedSessions(boolean logLeakedSessions) {
|
||||
this.logLeakedSessions = logLeakedSessions;
|
||||
}
|
||||
|
||||
public int getMaxConnectionPoolSize() {
|
||||
return this.maxConnectionPoolSize;
|
||||
}
|
||||
|
||||
public void setMaxConnectionPoolSize(int maxConnectionPoolSize) {
|
||||
this.maxConnectionPoolSize = maxConnectionPoolSize;
|
||||
}
|
||||
|
||||
public Duration getIdleTimeBeforeConnectionTest() {
|
||||
return this.idleTimeBeforeConnectionTest;
|
||||
}
|
||||
|
||||
public void setIdleTimeBeforeConnectionTest(Duration idleTimeBeforeConnectionTest) {
|
||||
this.idleTimeBeforeConnectionTest = idleTimeBeforeConnectionTest;
|
||||
}
|
||||
|
||||
public Duration getMaxConnectionLifetime() {
|
||||
return this.maxConnectionLifetime;
|
||||
}
|
||||
|
||||
public void setMaxConnectionLifetime(Duration maxConnectionLifetime) {
|
||||
this.maxConnectionLifetime = maxConnectionLifetime;
|
||||
}
|
||||
|
||||
public Duration getConnectionAcquisitionTimeout() {
|
||||
return this.connectionAcquisitionTimeout;
|
||||
}
|
||||
|
||||
public void setConnectionAcquisitionTimeout(Duration connectionAcquisitionTimeout) {
|
||||
this.connectionAcquisitionTimeout = connectionAcquisitionTimeout;
|
||||
}
|
||||
|
||||
public boolean isMetricsEnabled() {
|
||||
return this.metricsEnabled;
|
||||
}
|
||||
|
||||
public void setMetricsEnabled(boolean metricsEnabled) {
|
||||
this.metricsEnabled = metricsEnabled;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class DriverSettings {
|
||||
|
||||
private boolean encrypted = false;
|
||||
|
||||
private TrustSettings trustSettings = new TrustSettings();
|
||||
|
||||
private Duration connectionTimeout = Duration.ofSeconds(30);
|
||||
|
||||
private Duration maxTransactionRetryTime = Duration
|
||||
.ofMillis(org.neo4j.driver.internal.retry.RetrySettings.DEFAULT.maxRetryTimeMs());
|
||||
|
||||
public boolean isEncrypted() {
|
||||
return this.encrypted;
|
||||
}
|
||||
|
||||
public void setEncrypted(boolean encrypted) {
|
||||
this.encrypted = encrypted;
|
||||
}
|
||||
|
||||
public TrustSettings getTrustSettings() {
|
||||
return this.trustSettings;
|
||||
}
|
||||
|
||||
public void setTrustSettings(TrustSettings trustSettings) {
|
||||
this.trustSettings = trustSettings;
|
||||
}
|
||||
|
||||
public Duration getConnectionTimeout() {
|
||||
return this.connectionTimeout;
|
||||
}
|
||||
|
||||
public void setConnectionTimeout(Duration connectionTimeout) {
|
||||
this.connectionTimeout = connectionTimeout;
|
||||
}
|
||||
|
||||
public Duration getMaxTransactionRetryTime() {
|
||||
return this.maxTransactionRetryTime;
|
||||
}
|
||||
|
||||
public void setMaxTransactionRetryTime(Duration maxTransactionRetryTime) {
|
||||
this.maxTransactionRetryTime = maxTransactionRetryTime;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class TrustSettings {
|
||||
|
||||
public enum Strategy {
|
||||
|
||||
TRUST_ALL_CERTIFICATES,
|
||||
|
||||
TRUST_CUSTOM_CA_SIGNED_CERTIFICATES,
|
||||
|
||||
TRUST_SYSTEM_CA_SIGNED_CERTIFICATES
|
||||
|
||||
}
|
||||
|
||||
private TrustSettings.Strategy strategy = Strategy.TRUST_SYSTEM_CA_SIGNED_CERTIFICATES;
|
||||
|
||||
private File certFile;
|
||||
|
||||
private boolean hostnameVerificationEnabled = false;
|
||||
|
||||
public TrustSettings.Strategy getStrategy() {
|
||||
return this.strategy;
|
||||
}
|
||||
|
||||
public void setStrategy(TrustSettings.Strategy strategy) {
|
||||
this.strategy = strategy;
|
||||
}
|
||||
|
||||
public File getCertFile() {
|
||||
return this.certFile;
|
||||
}
|
||||
|
||||
public void setCertFile(File certFile) {
|
||||
this.certFile = certFile;
|
||||
}
|
||||
|
||||
public boolean isHostnameVerificationEnabled() {
|
||||
return this.hostnameVerificationEnabled;
|
||||
}
|
||||
|
||||
public void setHostnameVerificationEnabled(boolean hostnameVerificationEnabled) {
|
||||
this.hostnameVerificationEnabled = hostnameVerificationEnabled;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,34 +16,23 @@
|
||||
|
||||
package org.springframework.ai.autoconfigure.vectorstore.neo4j;
|
||||
|
||||
import org.neo4j.driver.AuthToken;
|
||||
import org.neo4j.driver.AuthTokens;
|
||||
import org.neo4j.driver.Config;
|
||||
import org.neo4j.driver.Driver;
|
||||
import org.neo4j.driver.GraphDatabase;
|
||||
import org.neo4j.driver.internal.Scheme;
|
||||
import org.springframework.ai.embedding.EmbeddingClient;
|
||||
import org.springframework.ai.vectorstore.Neo4jVectorStore;
|
||||
import org.springframework.ai.vectorstore.VectorStore;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.neo4j.Neo4jAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.source.InvalidConfigurationPropertyValueException;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author Jingzhou Ou
|
||||
*/
|
||||
@AutoConfiguration
|
||||
@ConditionalOnClass({ Neo4jVectorStore.class, EmbeddingClient.class })
|
||||
@EnableConfigurationProperties({ Neo4jVectorStoreProperties.class, Neo4jDriverProperties.class })
|
||||
@AutoConfiguration(after = Neo4jAutoConfiguration.class)
|
||||
@ConditionalOnClass({ Neo4jVectorStore.class, EmbeddingClient.class, Driver.class })
|
||||
@EnableConfigurationProperties({ Neo4jVectorStoreProperties.class })
|
||||
public class Neo4jVectorStoreAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@@ -62,128 +51,4 @@ public class Neo4jVectorStoreAutoConfiguration {
|
||||
return new Neo4jVectorStore(driver, embeddingClient, config);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(Driver.class)
|
||||
Driver neo4jDriver(Neo4jDriverProperties driverProperties) {
|
||||
AuthToken authToken = getAuthToken(driverProperties);
|
||||
Config config = getDriverConfig(driverProperties);
|
||||
return GraphDatabase.driver(driverProperties.getUri(), authToken, config);
|
||||
}
|
||||
|
||||
private Config getDriverConfig(Neo4jDriverProperties driverProperties) {
|
||||
Config.ConfigBuilder builder = Config.builder();
|
||||
buildWithPoolSettings(builder, driverProperties.getPool());
|
||||
URI uri = driverProperties.getUri();
|
||||
String scheme = uri == null ? "bolt" : uri.getScheme();
|
||||
buildWithDriverSettings(builder, driverProperties.getConfig(), isSimpleScheme(scheme));
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private AuthToken getAuthToken(Neo4jDriverProperties driverProperties) {
|
||||
String username = driverProperties.getAuthentication().getUsername();
|
||||
String password = driverProperties.getAuthentication().getPassword();
|
||||
String kerberosTicket = driverProperties.getAuthentication().getKerberosTicket();
|
||||
String realm = driverProperties.getAuthentication().getRealm();
|
||||
|
||||
boolean hasUsername = StringUtils.hasText(username);
|
||||
boolean hasPassword = StringUtils.hasText(password);
|
||||
boolean hasKerberosTicket = StringUtils.hasText(kerberosTicket);
|
||||
|
||||
if (hasUsername && hasKerberosTicket) {
|
||||
throw new InvalidConfigurationPropertyValueException("spring.ai.vectorstore.neo4j.driver.authentication",
|
||||
"username=" + username + ",kerberos-ticket=" + kerberosTicket,
|
||||
"Cannot specify both username and kerberos ticket.");
|
||||
}
|
||||
|
||||
if (hasUsername && hasPassword) {
|
||||
return AuthTokens.basic(username, password, realm);
|
||||
}
|
||||
|
||||
if (hasKerberosTicket) {
|
||||
return AuthTokens.kerberos(kerberosTicket);
|
||||
}
|
||||
|
||||
return AuthTokens.none();
|
||||
}
|
||||
|
||||
private void buildWithPoolSettings(Config.ConfigBuilder builder, Neo4jDriverProperties.PoolSettings poolSettings) {
|
||||
if (poolSettings.isLogLeakedSessions()) {
|
||||
builder.withLeakedSessionsLogging();
|
||||
}
|
||||
builder.withMaxConnectionPoolSize(poolSettings.getMaxConnectionPoolSize());
|
||||
if (poolSettings.getIdleTimeBeforeConnectionTest() != null) {
|
||||
builder.withConnectionLivenessCheckTimeout(poolSettings.getIdleTimeBeforeConnectionTest().toMillis(),
|
||||
TimeUnit.MILLISECONDS);
|
||||
}
|
||||
builder.withMaxConnectionLifetime(poolSettings.getMaxConnectionLifetime().toMillis(), TimeUnit.MILLISECONDS);
|
||||
builder.withConnectionAcquisitionTimeout(poolSettings.getConnectionAcquisitionTimeout().toMillis(),
|
||||
TimeUnit.MILLISECONDS);
|
||||
|
||||
if (poolSettings.isMetricsEnabled()) {
|
||||
builder.withDriverMetrics();
|
||||
}
|
||||
else {
|
||||
builder.withoutDriverMetrics();
|
||||
}
|
||||
}
|
||||
|
||||
private void buildWithDriverSettings(Config.ConfigBuilder builder,
|
||||
Neo4jDriverProperties.DriverSettings driverSettings, boolean withEncryptionAndTrustSettings) {
|
||||
if (withEncryptionAndTrustSettings) {
|
||||
if (driverSettings.isEncrypted()) {
|
||||
builder.withEncryption();
|
||||
}
|
||||
else {
|
||||
builder.withoutEncryption();
|
||||
}
|
||||
builder.withTrustStrategy(getTrustStrategy(driverSettings.getTrustSettings()));
|
||||
}
|
||||
builder.withConnectionTimeout(driverSettings.getConnectionTimeout().toMillis(), TimeUnit.MILLISECONDS);
|
||||
builder.withMaxTransactionRetryTime(driverSettings.getMaxTransactionRetryTime().toMillis(),
|
||||
TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
private boolean isSimpleScheme(String scheme) {
|
||||
String lowerCaseScheme = scheme.toLowerCase(Locale.ENGLISH);
|
||||
try {
|
||||
Scheme.validateScheme(lowerCaseScheme);
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
throw new IllegalArgumentException(String.format("'%s' is not a supported scheme.", scheme));
|
||||
}
|
||||
return lowerCaseScheme.equals("bolt") || lowerCaseScheme.equals("neo4j");
|
||||
}
|
||||
|
||||
private Config.TrustStrategy getTrustStrategy(Neo4jDriverProperties.TrustSettings trustSettings) {
|
||||
String propertyName = "spring.ai.vectorstore.neo4j.driver.config.trust-settings";
|
||||
Config.TrustStrategy internalRepresentation;
|
||||
File certFile = trustSettings.getCertFile();
|
||||
switch (trustSettings.getStrategy()) {
|
||||
case TRUST_ALL_CERTIFICATES:
|
||||
internalRepresentation = Config.TrustStrategy.trustAllCertificates();
|
||||
break;
|
||||
case TRUST_SYSTEM_CA_SIGNED_CERTIFICATES:
|
||||
internalRepresentation = Config.TrustStrategy.trustSystemCertificates();
|
||||
break;
|
||||
case TRUST_CUSTOM_CA_SIGNED_CERTIFICATES:
|
||||
if (certFile == null || !certFile.isFile()) {
|
||||
throw new InvalidConfigurationPropertyValueException(propertyName,
|
||||
trustSettings.getStrategy().name(),
|
||||
"Configured trust strategy requires a certificate file.");
|
||||
}
|
||||
internalRepresentation = Config.TrustStrategy.trustCustomCertificateSignedBy(certFile);
|
||||
break;
|
||||
default:
|
||||
throw new InvalidConfigurationPropertyValueException(propertyName, trustSettings.getStrategy().name(),
|
||||
"Unknown strategy.");
|
||||
}
|
||||
if (trustSettings.isHostnameVerificationEnabled()) {
|
||||
internalRepresentation.withHostnameVerification();
|
||||
}
|
||||
else {
|
||||
internalRepresentation.withoutHostnameVerification();
|
||||
}
|
||||
return internalRepresentation;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.autoconfigure.neo4j.Neo4jAutoConfiguration;
|
||||
import org.testcontainers.containers.Neo4jContainer;
|
||||
import org.testcontainers.junit.jupiter.Container;
|
||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
@@ -56,11 +57,11 @@ public class Neo4jVectorStoreAutoConfigurationIT {
|
||||
ResourceUtils.getText("classpath:/test/data/great.depression.txt"), Map.of("depression", "bad")));
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(Neo4jVectorStoreAutoConfiguration.class))
|
||||
.withConfiguration(AutoConfigurations.of(Neo4jAutoConfiguration.class, Neo4jVectorStoreAutoConfiguration.class))
|
||||
.withUserConfiguration(Config.class)
|
||||
.withPropertyValues("spring.ai.vectorstore.neo4j.driver.uri=" + neo4jContainer.getBoltUrl(),
|
||||
"spring.ai.vectorstore.neo4j.driver.authentication.username=" + "neo4j",
|
||||
"spring.ai.vectorstore.neo4j.driver.authentication.password=" + neo4jContainer.getAdminPassword());
|
||||
.withPropertyValues("spring.neo4j.uri=" + neo4jContainer.getBoltUrl(),
|
||||
"spring.neo4j.authentication.username=" + "neo4j",
|
||||
"spring.neo4j.authentication.password=" + neo4jContainer.getAdminPassword());
|
||||
|
||||
@Test
|
||||
void addAndSearch() {
|
||||
|
||||
Reference in New Issue
Block a user