diff --git a/README.md b/README.md
index f839635cffb..c57ad89e468 100644
--- a/README.md
+++ b/README.md
@@ -25,7 +25,7 @@ You can find more details in the [Reference Documentation](https://docs.spring.i
- [Moderation](https://docs.spring.io/spring-ai/reference/api/index.html#api/moderation)
* Portable API support across AI providers for both synchronous and streaming API options are supported. Access to [model-specific features](https://docs.spring.io/spring-ai/reference/api/chatmodel.html#_chat_options) is also available.
* [Structured Outputs](https://docs.spring.io/spring-ai/reference/api/structured-output-converter.html) - Mapping of AI Model output to POJOs.
-* Support for all major [Vector Database providers](https://docs.spring.io/spring-ai/reference/api/vectordbs.html) such as *Apache Cassandra, Azure Vector Search, Chroma, Milvus, MongoDB Atlas, Neo4j, Oracle, PostgreSQL/PGVector, PineCone, Qdrant, Redis, and Weaviate*.
+* Support for all major [Vector Database providers](https://docs.spring.io/spring-ai/reference/api/vectordbs.html) such as *Apache Cassandra, Azure Vector Search, Chroma, Milvus, MongoDB Atlas, MariaDB, Neo4j, Oracle, PostgreSQL/PGVector, PineCone, Qdrant, Redis, and Weaviate*.
* Portable API across Vector Store providers, including a novel SQL-like [metadata filter API](https://docs.spring.io/spring-ai/reference/api/vectordbs.html#metadata-filters).
* [Tools/Function Calling](https://docs.spring.io/spring-ai/reference/api/functions.html) - permits the model to request the execution of client-side tools and functions, thereby accessing necessary real-time information as required.
* [Observability](https://docs.spring.io/spring-ai/reference/observability/index.html) - Provides insights into AI-related operations.
diff --git a/pom.xml b/pom.xml
index 96243bee263..3cc026aa9a6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -52,6 +52,7 @@
vector-stores/spring-ai-elasticsearch-store
vector-stores/spring-ai-gemfire-store
vector-stores/spring-ai-hanadb-store
+ vector-stores/spring-ai-mariadb-store
vector-stores/spring-ai-milvus-store
vector-stores/spring-ai-mongodb-atlas-store
vector-stores/spring-ai-neo4j-store
@@ -73,6 +74,7 @@
spring-ai-spring-boot-starters/spring-ai-starter-elasticsearch-store
spring-ai-spring-boot-starters/spring-ai-starter-gemfire-store
spring-ai-spring-boot-starters/spring-ai-starter-hanadb-store
+ spring-ai-spring-boot-starters/spring-ai-starter-mariadb-store
spring-ai-spring-boot-starters/spring-ai-starter-milvus-store
spring-ai-spring-boot-starters/spring-ai-starter-mongodb-atlas-store
spring-ai-spring-boot-starters/spring-ai-starter-neo4j-store
@@ -212,6 +214,7 @@
1.9.1
0.5.0
2.10.1
+ 3.5.1
0.22.0
@@ -678,13 +681,13 @@
org.springframework.ai.vectorstore**/Hana**IT.java
org.springframework.ai.vectorstore**/Hana**IT.java
org.springframework.ai.vectorstore**/Milvus**IT.java
- org.springframework.ai.vectorstore**/Mongo**IT.java
+ org.springframework.ai.vectorstore**/MariaDB**IT.java
org.springframework.ai.vectorstore**/Mongo**IT.java
org.springframework.ai.vectorstore**/Neo4j**IT.java
org.springframework.ai.vectorstore**/OpenSearch**IT.java
org.springframework.ai.vectorstore**/Oracle**IT.java
- org.springframework.ai.vectorstore**/Pinecone**IT.java
+ org.springframework.ai.vectorstore**/Pinecone**IT.java
org.springframework.ai.vectorstore.qdrant/**/**IT.java
org.springframework.ai.vectorstore**/Qdrant**IT.java
org.springframework.ai.vectorstore**/Redis**IT.java
@@ -692,14 +695,14 @@
org.springframework.ai.vectorstore**/Weaviate**IT.java
-
+
org.springframework.ai.autoconfigure.anthropic/**/**IT.java
org.springframework.ai.autoconfigure.azure/**/**IT.java
org.springframework.ai.autoconfigure.bedrock/**/**IT.java
org.springframework.ai.autoconfigure.huggingface/**/**IT.java
-
+
org.springframework.ai.autoconfigure.chat/**/**IT.java
org.springframework.ai.autoconfigure.embedding/**/**IT.java
org.springframework.ai.autoconfigure.image/**/**IT.java
@@ -713,24 +716,24 @@
org.springframework.ai.autoconfigure.postgresml/**/**IT.java
org.springframework.ai.autoconfigure.qianfan/**/**IT.java
-
+
org.springframework.ai.autoconfigure.retry/**/**IT.java
-
+
org.springframework.ai.autoconfigure.stabilityai/**/**IT.java
org.springframework.ai.autoconfigure.transformers/**/**IT.java
-
- org.springframework.ai.autoconfigure.vectorstore/**/**IT.java
-
+
+ org.springframework.ai.autoconfigure.vectorstore/**/**IT.java
+
org.springframework.ai.autoconfigure.vertexai/**/**IT.java
org.springframework.ai.autoconfigure.watsonxai/**/**IT.java
org.springframework.ai.autoconfigure.zhipuai/**/**IT.java
-
+
org.springframework.ai.autoconfigure.zhipuai/**/**IT.java
-
-
+
+
org.springframework.ai.testcontainers/**/**IT.java
-
+
package org.springframework.ai.docker.compose/**/**IT.java
diff --git a/spring-ai-bom/pom.xml b/spring-ai-bom/pom.xml
index c1b53ed9b96..fb8fee83f4e 100644
--- a/spring-ai-bom/pom.xml
+++ b/spring-ai-bom/pom.xml
@@ -285,6 +285,12 @@
${project.version}
+
+ org.springframework.ai
+ spring-ai-mariadb-store
+ ${project.version}
+
+
org.springframework.ai
@@ -455,6 +461,12 @@
${project.version}
+
+ org.springframework.ai
+ spring-ai-mariadb-store-spring-boot-starter
+ ${project.version}
+
+
org.springframework.ai
spring-ai-stability-ai-spring-boot-starter
diff --git a/spring-ai-core/src/main/java/org/springframework/ai/observation/conventions/VectorStoreProvider.java b/spring-ai-core/src/main/java/org/springframework/ai/observation/conventions/VectorStoreProvider.java
index f43e564fa4e..a65bb57c3aa 100644
--- a/spring-ai-core/src/main/java/org/springframework/ai/observation/conventions/VectorStoreProvider.java
+++ b/spring-ai-core/src/main/java/org/springframework/ai/observation/conventions/VectorStoreProvider.java
@@ -67,6 +67,11 @@ public enum VectorStoreProvider {
*/
HANA("hana"),
+ /**
+ * Vector store provided by MariaDB.
+ */
+ MARIADB("mariadb"),
+
/**
* Vector store provided by Milvus.
*/
diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/nav.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/nav.adoc
index b07bc2f4ae7..98ab6b8370c 100644
--- a/spring-ai-docs/src/main/antora/modules/ROOT/nav.adoc
+++ b/spring-ai-docs/src/main/antora/modules/ROOT/nav.adoc
@@ -80,6 +80,7 @@
** xref:api/vectordbs/chroma.adoc[]
** xref:api/vectordbs/elasticsearch.adoc[]
** xref:api/vectordbs/gemfire.adoc[GemFire]
+** xref:api/vectordbs/mariadb.adoc[]
** xref:api/vectordbs/milvus.adoc[]
** xref:api/vectordbs/mongodb.adoc[]
** xref:api/vectordbs/neo4j.adoc[]
diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs.adoc
index a645f6bb727..15addeb628e 100644
--- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs.adoc
+++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs.adoc
@@ -207,6 +207,7 @@ These are the available implementations of the `VectorStore` interface:
* xref:api/vectordbs/chroma.adoc[Chroma Vector Store] - The https://www.trychroma.com/[Chroma] vector store.
* xref:api/vectordbs/elasticsearch.adoc[Elasticsearch Vector Store] - The https://www.elastic.co/[Elasticsearch] vector store.
* xref:api/vectordbs/gemfire.adoc[GemFire Vector Store] - The https://tanzu.vmware.com/content/blog/vmware-gemfire-vector-database-extension[GemFire] vector store.
+* xref:api/vectordbs/mariadb.adoc[MariaDB Vector Store] - The https://mariadb.com/[MariaDB] vector store.
* xref:api/vectordbs/milvus.adoc[Milvus Vector Store] - The https://milvus.io/[Milvus] vector store.
* xref:api/vectordbs/mongodb.adoc[MongoDB Atlas Vector Store] - The https://www.mongodb.com/atlas/database[MongoDB Atlas] vector store.
* xref:api/vectordbs/neo4j.adoc[Neo4j Vector Store] - The https://neo4j.com/[Neo4j] vector store.
diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs/mariadb.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs/mariadb.adoc
new file mode 100644
index 00000000000..c793a8979e9
--- /dev/null
+++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs/mariadb.adoc
@@ -0,0 +1,187 @@
+= MariaDB Vector
+
+This section walks you through setting up the MariaDB `VectorStore` to store document embeddings and perform similarity searches.
+
+link:https://mariadb.org/projects/mariadb-vector/[MariaDB vector] is part of MariaDB 11.7 and enables storing and searching over machine learning-generated embeddings.
+
+== Auto-Configuration
+
+Add the MariaDBVectorStore boot starter dependency to your project:
+
+[source,xml]
+----
+
+ org.springframework.ai
+ spring-ai-mariadb-store-spring-boot-starter
+
+----
+
+or to your Gradle `build.gradle` build file.
+
+[source,groovy]
+----
+dependencies {
+ implementation 'org.springframework.ai:spring-ai-mariadb-store-spring-boot-starter'
+}
+----
+
+The vector store implementation can initialize the required schema for you, but you must opt-in by specifying the `initializeSchema` boolean in the appropriate constructor or by setting `...initialize-schema=true` in the `application.properties` file.
+
+The Vector Store also requires an `EmbeddingModel` instance to calculate embeddings for the documents.
+You can pick one of the available xref:api/embeddings.adoc#available-implementations[EmbeddingModel Implementations].
+
+For example, to use the xref:api/embeddings/openai-embeddings.adoc[OpenAI EmbeddingModel], add the following dependency to your project:
+
+[source,xml]
+----
+
+ org.springframework.ai
+ spring-ai-openai-spring-boot-starter
+
+----
+
+or to your Gradle `build.gradle` build file.
+
+[source,groovy]
+----
+dependencies {
+ implementation 'org.springframework.ai:spring-ai-openai-spring-boot-starter'
+}
+----
+
+TIP: Refer to the xref:getting-started.adoc#dependency-management[Dependency Management] section to add the Spring AI BOM to your build file.
+Refer to the xref:getting-started.adoc#repositories[Repositories] section to add Milestone and/or Snapshot Repositories to your build file.
+
+To connect to and configure the `MariaDBVectorStore`, you need to provide access details for your instance.
+A simple configuration can be provided via Spring Boot's `application.yml`.
+
+[yml]
+----
+spring:
+ datasource:
+ url: jdbc:mariadb://localhost/db
+ username: myUser
+ password: myPassword
+ ai:
+ vectorstore:
+ mariadbvector:
+ distance-type: COSINE
+ dimensions: 1536
+----
+
+TIP: If you run MariaDBvector as a Spring Boot dev service via link:https://docs.spring.io/spring-boot/reference/features/dev-services.html#features.dev-services.docker-compose[Docker Compose]
+or link:https://docs.spring.io/spring-boot/reference/features/dev-services.html#features.dev-services.testcontainers[Testcontainers],
+you don't need to configure URL, username and password since they are autoconfigured by Spring Boot.
+
+TIP: Check the list of xref:#mariadbvector-properties[configuration parameters] to learn about the default values and configuration options.
+
+Now you can auto-wire the `MariaDBVectorStore` in your application and use it
+
+[source,java]
+----
+@Autowired VectorStore vectorStore;
+
+// ...
+
+List documents = List.of(
+ new Document("Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!!", Map.of("meta1", "meta1")),
+ new Document("The World is Big and Salvation Lurks Around the Corner"),
+ new Document("You walk forward facing the past and you turn back toward the future.", Map.of("meta2", "meta2")));
+
+// Add the documents to PGVector
+vectorStore.add(documents);
+
+// Retrieve documents similar to a query
+List results = this.vectorStore.similaritySearch(SearchRequest.query("Spring").withTopK(5));
+----
+
+[[mariadbvector-properties]]
+=== Configuration properties
+
+You can use the following properties in your Spring Boot configuration to customize the MariaDB vector store.
+
+[cols="2,5,1",stripes=even]
+|===
+|Property| Description | Default value
+
+|`spring.ai.vectorstore.mariadb.distance-type`| Search distance type. Defaults to `COSINE`. But if vectors are normalized to length 1, you can use `EUCLIDEAN` for best performance.| COSINE
+|`spring.ai.vectorstore.mariadb.dimensions`| Embeddings dimension. If not specified explicitly the PgVectorStore will retrieve the dimensions form the provided `EmbeddingModel`. Dimensions are set to the embedding column the on table creation. If you change the dimensions your would have to re-create the vector_store table as well. | -
+|`spring.ai.vectorstore.mariadb.remove-existing-vector-store-table` | Deletes the existing `vector_store` table on start up. | false
+|`spring.ai.vectorstore.mariadb.initialize-schema` | Whether to initialize the required schema | false
+|`spring.ai.vectorstore.mariadb.schema-name` | Vector store schema name | null
+|`spring.ai.vectorstore.mariadb.table-name` | Vector store table name | `vector_store`
+|`spring.ai.vectorstore.mariadb.schema-validation` | Enables schema and table name validation to ensure they are valid and existing objects. | false
+
+|===
+
+TIP: If you configure a custom schema and/or table name, consider enabling schema validation by setting `spring.ai.vectorstore.mariadb.schema-validation=true`.
+This ensures the correctness of the names and reduces the risk of SQL injection attacks.
+
+== Metadata filtering
+
+You can leverage the generic, portable link:https://docs.spring.io/spring-ai/reference/api/vectordbs.html#_metadata_filters[metadata filters] with the MariaDB Vector store.
+
+For example, you can use either the text expression language:
+
+[source,java]
+----
+vectorStore.similaritySearch(
+ SearchRequest.defaults()
+ .withQuery("The World")
+ .withTopK(TOP_K)
+ .withSimilarityThreshold(SIMILARITY_THRESHOLD)
+ .withFilterExpression("author in ['john', 'jill'] && article_type == 'blog'"));
+----
+
+or programmatically using the `Filter.Expression` DSL:
+
+[source,java]
+----
+FilterExpressionBuilder b = new FilterExpressionBuilder();
+
+vectorStore.similaritySearch(SearchRequest.defaults()
+ .withQuery("The World")
+ .withTopK(TOP_K)
+ .withSimilarityThreshold(SIMILARITY_THRESHOLD)
+ .withFilterExpression(b.and(
+ b.in("author","john", "jill"),
+ b.eq("article_type", "blog")).build()));
+----
+
+NOTE: These filter expressions are converted into the equivalent PgVector filters.
+
+== Manual Configuration
+
+Instead of using the Spring Boot auto-configuration, you can manually configure the `MariaDBVectorStore`.
+For this you need to add the MariaDB connector and `JdbcTemplate` auto-configuration dependencies to your project:
+
+[source,xml]
+----
+
+ org.springframework.boot
+ spring-boot-starter-jdbc
+
+
+
+ org.mariadb.jdbc
+ mariadb-java-client
+ runtime
+
+
+
+ org.springframework.ai
+ spring-ai-mariadb-store
+
+----
+
+TIP: Refer to the xref:getting-started.adoc#dependency-management[Dependency Management] section to add the Spring AI BOM to your build file.
+
+To configure MariaDB Vector in your application, you can use the following setup:
+
+[source,java]
+----
+@Bean
+public VectorStore vectorStore(JdbcTemplate jdbcTemplate, EmbeddingModel embeddingModel) {
+ return new MariaDBVectorStore(jdbcTemplate, embeddingModel);
+}
+----
diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/index.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/index.adoc
index 484b9a0a643..a560889d98d 100644
--- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/index.adoc
+++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/index.adoc
@@ -1,5 +1,4 @@
[[introduction]]
-= Introduction
image::spring_ai_logo_with_text.svg[Integration Problem, width=300, align="left"]
@@ -26,7 +25,7 @@ Spring AI provides the following features:
** xref:api/audio/speech.adoc[Text to Speech]
** xref:api/moderation[Moderation]
* xref:api/structured-output-converter.adoc[Structured Outputs] - Mapping of AI Model output to POJOs.
-* Support for all major xref:api/vectordbs.adoc[Vector Database providers] such as Apache Cassandra, Azure Cosmos DB, Azure Vector Search, Chroma, Elasticsearch, GemFire, Milvus, MongoDB Atlas, Neo4j, OpenSearch, Oracle, PostgreSQL/PGVector, PineCone, Qdrant, Redis, SAP Hana, Typesense and Weaviate.
+* Support for all major xref:api/vectordbs.adoc[Vector Database providers] such as Apache Cassandra, Azure Cosmos DB, Azure Vector Search, Chroma, Elasticsearch, GemFire, MariaDB, Milvus, MongoDB Atlas, Neo4j, OpenSearch, Oracle, PostgreSQL/PGVector, PineCone, Qdrant, Redis, SAP Hana, Typesense and Weaviate.
* Portable API across Vector Store providers, including a novel SQL-like metadata filter API.
* xref:api/functions.adoc[Tools/Function Calling] - permits the model to request the execution of client-side tools and functions, thereby accessing necessary real-time information as required.
* xref:observability/index.adoc[Observability] - Provides insights into AI-related operations.
diff --git a/spring-ai-spring-boot-autoconfigure/pom.xml b/spring-ai-spring-boot-autoconfigure/pom.xml
index 4ccc3c233ef..d2abc8de56e 100644
--- a/spring-ai-spring-boot-autoconfigure/pom.xml
+++ b/spring-ai-spring-boot-autoconfigure/pom.xml
@@ -178,6 +178,20 @@
true
+
+
+ org.springframework.ai
+ spring-ai-mariadb-store
+ ${project.parent.version}
+ true
+
+
+ org.mariadb.jdbc
+ mariadb-java-client
+ ${mariadb.version}
+ true
+
+
org.springframework.ai
@@ -457,6 +471,12 @@
test
+
+ org.testcontainers
+ mariadb
+ test
+
+
org.testcontainers
junit-jupiter
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/mariadb/MariaDbStoreAutoConfiguration.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/mariadb/MariaDbStoreAutoConfiguration.java
new file mode 100644
index 00000000000..1b940447bd4
--- /dev/null
+++ b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/mariadb/MariaDbStoreAutoConfiguration.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2023-2024 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.mariadb;
+
+import io.micrometer.observation.ObservationRegistry;
+import org.springframework.ai.embedding.BatchingStrategy;
+import org.springframework.ai.embedding.EmbeddingModel;
+import org.springframework.ai.embedding.TokenCountBatchingStrategy;
+import org.springframework.ai.vectorstore.MariaDBVectorStore;
+import org.springframework.ai.vectorstore.observation.VectorStoreObservationConvention;
+import org.springframework.beans.factory.ObjectProvider;
+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.jdbc.JdbcTemplateAutoConfiguration;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.jdbc.core.JdbcTemplate;
+
+import javax.sql.DataSource;
+
+/**
+ * @author Diego Dupin
+ * @since 1.0.0
+ */
+@AutoConfiguration(after = JdbcTemplateAutoConfiguration.class)
+@ConditionalOnClass({ MariaDBVectorStore.class, DataSource.class, JdbcTemplate.class })
+@EnableConfigurationProperties(MariaDbStoreProperties.class)
+public class MariaDbStoreAutoConfiguration {
+
+ @Bean
+ @ConditionalOnMissingBean(BatchingStrategy.class)
+ BatchingStrategy mariaDbStoreBatchingStrategy() {
+ return new TokenCountBatchingStrategy();
+ }
+
+ @Bean
+ @ConditionalOnMissingBean
+ public MariaDBVectorStore vectorStore(JdbcTemplate jdbcTemplate, EmbeddingModel embeddingModel,
+ MariaDbStoreProperties properties, ObjectProvider observationRegistry,
+ ObjectProvider customObservationConvention,
+ BatchingStrategy batchingStrategy) {
+
+ var initializeSchema = properties.isInitializeSchema();
+
+ return new MariaDBVectorStore.Builder(jdbcTemplate, embeddingModel).withSchemaName(properties.getSchemaName())
+ .withVectorTableName(properties.getTableName())
+ .withVectorTableValidationsEnabled(properties.isSchemaValidation())
+ .withDimensions(properties.getDimensions())
+ .withDistanceType(properties.getDistanceType())
+ .withContentFieldName(properties.getContentFieldName())
+ .withEmbeddingFieldName(properties.getEmbeddingFieldName())
+ .withIdFieldName(properties.getIdFieldName())
+ .withMetadataFieldName(properties.getMetadataFieldName())
+ .withRemoveExistingVectorStoreTable(properties.isRemoveExistingVectorStoreTable())
+ .withInitializeSchema(initializeSchema)
+ .withObservationRegistry(observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP))
+ .withSearchObservationConvention(customObservationConvention.getIfAvailable(() -> null))
+ .withBatchingStrategy(batchingStrategy)
+ .withMaxDocumentBatchSize(properties.getMaxDocumentBatchSize())
+ .build();
+ }
+
+}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/mariadb/MariaDbStoreProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/mariadb/MariaDbStoreProperties.java
new file mode 100644
index 00000000000..1451c557d13
--- /dev/null
+++ b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/mariadb/MariaDbStoreProperties.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2023-2024 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.mariadb;
+
+import org.springframework.ai.autoconfigure.vectorstore.CommonVectorStoreProperties;
+import org.springframework.ai.vectorstore.MariaDBVectorStore;
+import org.springframework.ai.vectorstore.MariaDBVectorStore.MariaDBDistanceType;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+/**
+ * @author Diego Dupin
+ */
+@ConfigurationProperties(MariaDbStoreProperties.CONFIG_PREFIX)
+public class MariaDbStoreProperties extends CommonVectorStoreProperties {
+
+ public static final String CONFIG_PREFIX = "spring.ai.vectorstore.mariadb";
+
+ private int dimensions = MariaDBVectorStore.INVALID_EMBEDDING_DIMENSION;
+
+ private MariaDBDistanceType distanceType = MariaDBDistanceType.COSINE;
+
+ private boolean removeExistingVectorStoreTable = false;
+
+ private String tableName = MariaDBVectorStore.DEFAULT_TABLE_NAME;
+
+ private String schemaName = null;
+
+ private String embeddingFieldName = MariaDBVectorStore.DEFAULT_COLUMN_EMBEDDING;
+
+ private String idFieldName = MariaDBVectorStore.DEFAULT_COLUMN_ID;
+
+ private String metadataFieldName = MariaDBVectorStore.DEFAULT_COLUMN_METADATA;
+
+ private String contentFieldName = MariaDBVectorStore.DEFAULT_COLUMN_CONTENT;
+
+ private boolean schemaValidation = MariaDBVectorStore.DEFAULT_SCHEMA_VALIDATION;
+
+ private int maxDocumentBatchSize = MariaDBVectorStore.MAX_DOCUMENT_BATCH_SIZE;
+
+ public int getDimensions() {
+ return this.dimensions;
+ }
+
+ public void setDimensions(int dimensions) {
+ this.dimensions = dimensions;
+ }
+
+ public MariaDBVectorStore.MariaDBDistanceType getDistanceType() {
+ return this.distanceType;
+ }
+
+ public void setDistanceType(MariaDBDistanceType distanceType) {
+ this.distanceType = distanceType;
+ }
+
+ public boolean isRemoveExistingVectorStoreTable() {
+ return this.removeExistingVectorStoreTable;
+ }
+
+ public void setRemoveExistingVectorStoreTable(boolean removeExistingVectorStoreTable) {
+ this.removeExistingVectorStoreTable = removeExistingVectorStoreTable;
+ }
+
+ public String getTableName() {
+ return this.tableName;
+ }
+
+ public void setTableName(String vectorTableName) {
+ this.tableName = vectorTableName;
+ }
+
+ public String getSchemaName() {
+ return this.schemaName;
+ }
+
+ public void setSchemaName(String schemaName) {
+ this.schemaName = schemaName;
+ }
+
+ public boolean isSchemaValidation() {
+ return this.schemaValidation;
+ }
+
+ public void setSchemaValidation(boolean schemaValidation) {
+ this.schemaValidation = schemaValidation;
+ }
+
+ public int getMaxDocumentBatchSize() {
+ return this.maxDocumentBatchSize;
+ }
+
+ public void setMaxDocumentBatchSize(int maxDocumentBatchSize) {
+ this.maxDocumentBatchSize = maxDocumentBatchSize;
+ }
+
+ public String getEmbeddingFieldName() {
+ return embeddingFieldName;
+ }
+
+ public void setEmbeddingFieldName(String embeddingFieldName) {
+ this.embeddingFieldName = embeddingFieldName;
+ }
+
+ public String getIdFieldName() {
+ return idFieldName;
+ }
+
+ public void setIdFieldName(String idFieldName) {
+ this.idFieldName = idFieldName;
+ }
+
+ public String getMetadataFieldName() {
+ return metadataFieldName;
+ }
+
+ public void setMetadataFieldName(String metadataFieldName) {
+ this.metadataFieldName = metadataFieldName;
+ }
+
+ public String getContentFieldName() {
+ return contentFieldName;
+ }
+
+ public void setContentFieldName(String contentFieldName) {
+ this.contentFieldName = contentFieldName;
+ }
+
+}
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-ai-spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
index b15feb0453f..f3e5633efc0 100644
--- a/spring-ai-spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
+++ b/spring-ai-spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -47,6 +47,7 @@ org.springframework.ai.autoconfigure.vectorstore.neo4j.Neo4jVectorStoreAutoConfi
org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStoreAutoConfiguration
org.springframework.ai.autoconfigure.vectorstore.hanadb.HanaCloudVectorStoreAutoConfiguration
org.springframework.ai.autoconfigure.vectorstore.cosmosdb.CosmosDBVectorStoreAutoConfiguration
+org.springframework.ai.autoconfigure.vectorstore.mariadb.MariaDbStoreAutoConfiguration
org.springframework.ai.autoconfigure.retry.SpringAiRetryAutoConfiguration
org.springframework.ai.autoconfigure.postgresml.PostgresMlAutoConfiguration
org.springframework.ai.autoconfigure.vectorstore.mongo.MongoDBAtlasVectorStoreAutoConfiguration
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/mariadb/MariaDbStoreAutoConfigurationIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/mariadb/MariaDbStoreAutoConfigurationIT.java
new file mode 100644
index 00000000000..ad6e565b8b2
--- /dev/null
+++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/mariadb/MariaDbStoreAutoConfigurationIT.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright 2023-2024 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.mariadb;
+
+import io.micrometer.observation.tck.TestObservationRegistry;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+import org.springframework.ai.document.Document;
+import org.springframework.ai.embedding.EmbeddingModel;
+import org.springframework.ai.observation.conventions.VectorStoreProvider;
+import org.springframework.ai.transformers.TransformersEmbeddingModel;
+import org.springframework.ai.vectorstore.MariaDBVectorStore;
+import org.springframework.ai.vectorstore.SearchRequest;
+import org.springframework.ai.vectorstore.observation.VectorStoreObservationContext;
+import org.springframework.boot.autoconfigure.AutoConfigurations;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration;
+import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.io.DefaultResourceLoader;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.testcontainers.containers.MariaDBContainer;
+import org.testcontainers.junit.jupiter.Container;
+import org.testcontainers.junit.jupiter.Testcontainers;
+import org.testcontainers.utility.DockerImageName;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.Map;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.springframework.ai.autoconfigure.vectorstore.observation.ObservationTestUtil.assertObservationRegistry;
+
+/**
+ * @author Diego Dupin
+ */
+@Testcontainers
+public class MariaDbStoreAutoConfigurationIT {
+
+ public static final DockerImageName DEFAULT_IMAGE = DockerImageName.parse("mariadb:11.7-rc");
+
+ @Container
+ @SuppressWarnings("resource")
+ static MariaDBContainer> mariadbContainer = new MariaDBContainer<>(DEFAULT_IMAGE);
+
+ private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
+ .withConfiguration(AutoConfigurations.of(MariaDbStoreAutoConfiguration.class,
+ JdbcTemplateAutoConfiguration.class, DataSourceAutoConfiguration.class))
+ .withUserConfiguration(Config.class)
+ .withPropertyValues("spring.ai.vectorstore.mariadb.distanceType=COSINE",
+ "spring.ai.vectorstore.mariadb.initialize-schema=true",
+ // JdbcTemplate configuration
+ "spring.datasource.url=" + mariadbContainer.getJdbcUrl(),
+ "spring.datasource.username=" + mariadbContainer.getUsername(),
+ "spring.datasource.password=" + mariadbContainer.getPassword());
+
+ List documents = List.of(
+ new Document(getText("classpath:/test/data/spring.ai.txt"), Map.of("spring", "great")),
+ new Document(getText("classpath:/test/data/time.shelter.txt")),
+ new Document(getText("classpath:/test/data/great.depression.txt"), Map.of("depression", "bad")));
+
+ public static String getText(String uri) {
+ var resource = new DefaultResourceLoader().getResource(uri);
+ try {
+ return resource.getContentAsString(StandardCharsets.UTF_8);
+ }
+ catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static boolean isFullyQualifiedTableExists(ApplicationContext context, String schemaName,
+ String tableName) {
+ JdbcTemplate jdbcTemplate = context.getBean(JdbcTemplate.class);
+ if (schemaName == null) {
+ String sqlWithoutSchema = "SELECT EXISTS (SELECT * FROM information_schema.tables WHERE table_schema = SCHEMA() AND table_name = ?) as results";
+ return jdbcTemplate.queryForObject(sqlWithoutSchema, Boolean.class, tableName);
+ } else {
+ String sqlWithSchema = "SELECT EXISTS (SELECT * FROM information_schema.tables WHERE table_schema = ? AND table_name = ?) as results";
+ return jdbcTemplate.queryForObject(sqlWithSchema, Boolean.class, schemaName , tableName);
+ }
+ }
+
+ @Test
+ public void addAndSearch() {
+
+ this.contextRunner.run(context -> {
+
+ MariaDBVectorStore vectorStore = context.getBean(MariaDBVectorStore.class);
+ TestObservationRegistry observationRegistry = context.getBean(TestObservationRegistry.class);
+
+ assertThat(isFullyQualifiedTableExists(context, null, MariaDBVectorStore.DEFAULT_TABLE_NAME)).isTrue();
+
+ vectorStore.add(this.documents);
+
+ assertObservationRegistry(observationRegistry, VectorStoreProvider.MARIADB,
+ VectorStoreObservationContext.Operation.ADD);
+ observationRegistry.clear();
+
+ List results = vectorStore
+ .similaritySearch(SearchRequest.query("What is Great Depression?").withTopK(1));
+
+ assertThat(results).hasSize(1);
+ Document resultDoc = results.get(0);
+ assertThat(resultDoc.getId()).isEqualTo(this.documents.get(2).getId());
+ assertThat(resultDoc.getMetadata()).containsKeys("depression", "distance");
+
+ assertObservationRegistry(observationRegistry, VectorStoreProvider.MARIADB,
+ VectorStoreObservationContext.Operation.QUERY);
+ observationRegistry.clear();
+
+ // Remove all documents from the store
+ vectorStore.delete(this.documents.stream().map(doc -> doc.getId()).toList());
+
+ assertObservationRegistry(observationRegistry, VectorStoreProvider.MARIADB,
+ VectorStoreObservationContext.Operation.DELETE);
+
+ results = vectorStore.similaritySearch(SearchRequest.query("Great Depression").withTopK(1));
+ assertThat(results).hasSize(0);
+ observationRegistry.clear();
+ });
+ }
+
+ @ParameterizedTest(name = "{0} : {displayName} ")
+ @ValueSource(strings = { "test:vector_store:id:metadata:embedding:content",
+ "test:my_table:my_id:my_metadata:my_embedding:my_content" })
+ public void customSchemaNames(String schemaTableName) {
+ String schemaName = schemaTableName.split(":")[0];
+ String tableName = schemaTableName.split(":")[1];
+ String idName = schemaTableName.split(":")[2];
+ String metaName = schemaTableName.split(":")[3];
+ String embeddingName = schemaTableName.split(":")[4];
+ String contentName = schemaTableName.split(":")[5];
+
+ this.contextRunner
+ .withPropertyValues("spring.ai.vectorstore.mariadb.schema-name=" + schemaName,
+ "spring.ai.vectorstore.mariadb.table-name=" + tableName,
+ "spring.ai.vectorstore.mariadb.id-field-name=" + idName,
+ "spring.ai.vectorstore.mariadb.metadata-field-name=" + metaName,
+ "spring.ai.vectorstore.mariadb.embedding-field-name=" + embeddingName,
+ "spring.ai.vectorstore.mariadb.content-field-name=" + contentName)
+ .run(context -> {
+ assertThat(isFullyQualifiedTableExists(context, schemaName, tableName)).isTrue();
+ });
+ }
+
+ @ParameterizedTest(name = "{0} : {displayName} ")
+ @ValueSource(strings = { "test:vector_store", "test:my_table" })
+ public void disableSchemaInitialization(String schemaTableName) {
+ String schemaName = schemaTableName.split(":")[0];
+ String tableName = schemaTableName.split(":")[1];
+
+ this.contextRunner
+ .withPropertyValues("spring.ai.vectorstore.mariadb.schema-name=" + schemaName,
+ "spring.ai.vectorstore.mariadb.table-name=" + tableName,
+ "spring.ai.vectorstore.mariadb.initialize-schema=false")
+ .run(context -> {
+ assertThat(isFullyQualifiedTableExists(context, schemaName, tableName)).isFalse();
+ });
+ }
+
+ @Configuration(proxyBeanMethods = false)
+ static class Config {
+
+ @Bean
+ public TestObservationRegistry observationRegistry() {
+ return TestObservationRegistry.create();
+ }
+
+ @Bean
+ public EmbeddingModel embeddingModel() {
+ return new TransformersEmbeddingModel();
+ }
+
+ }
+
+}
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/mariadb/MariaDbStorePropertiesTests.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/mariadb/MariaDbStorePropertiesTests.java
new file mode 100644
index 00000000000..5e1b79e1e6c
--- /dev/null
+++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/mariadb/MariaDbStorePropertiesTests.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2023-2024 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.mariadb;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.ai.autoconfigure.vectorstore.mariadb.MariaDbStoreProperties;
+import org.springframework.ai.vectorstore.MariaDBVectorStore;
+import org.springframework.ai.vectorstore.MariaDBVectorStore.MariaDBDistanceType;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * @author Diego Dupin
+ */
+public class MariaDbStorePropertiesTests {
+
+ @Test
+ public void defaultValues() {
+ var props = new MariaDbStoreProperties();
+ assertThat(props.getDimensions()).isEqualTo(MariaDBVectorStore.INVALID_EMBEDDING_DIMENSION);
+ assertThat(props.getDistanceType()).isEqualTo(MariaDBDistanceType.COSINE);
+ assertThat(props.isRemoveExistingVectorStoreTable()).isFalse();
+
+ assertThat(props.isSchemaValidation()).isFalse();
+ assertThat(props.getSchemaName()).isNull();
+ assertThat(props.getTableName()).isEqualTo(MariaDBVectorStore.DEFAULT_TABLE_NAME);
+
+ }
+
+ @Test
+ public void customValues() {
+ var props = new MariaDbStoreProperties();
+
+ props.setDimensions(1536);
+ props.setDistanceType(MariaDBDistanceType.EUCLIDEAN);
+ props.setRemoveExistingVectorStoreTable(true);
+
+ props.setSchemaValidation(true);
+ props.setSchemaName("my_vector_schema");
+ props.setTableName("my_vector_table");
+ props.setIdFieldName("my_vector_id");
+ props.setMetadataFieldName("my_vector_meta");
+ props.setContentFieldName("my_vector_content");
+ props.setEmbeddingFieldName("my_vector_embedding");
+ props.setInitializeSchema(true);
+
+ assertThat(props.getDimensions()).isEqualTo(1536);
+ assertThat(props.getDistanceType()).isEqualTo(MariaDBDistanceType.EUCLIDEAN);
+ assertThat(props.isRemoveExistingVectorStoreTable()).isTrue();
+
+ assertThat(props.isSchemaValidation()).isTrue();
+ assertThat(props.getSchemaName()).isEqualTo("my_vector_schema");
+ assertThat(props.getTableName()).isEqualTo("my_vector_table");
+ assertThat(props.getIdFieldName()).isEqualTo("my_vector_id");
+ assertThat(props.getMetadataFieldName()).isEqualTo("my_vector_meta");
+ assertThat(props.getContentFieldName()).isEqualTo("my_vector_content");
+ assertThat(props.getEmbeddingFieldName()).isEqualTo("my_vector_embedding");
+ }
+
+}
diff --git a/spring-ai-spring-boot-starters/spring-ai-starter-mariadb-store/pom.xml b/spring-ai-spring-boot-starters/spring-ai-starter-mariadb-store/pom.xml
new file mode 100644
index 00000000000..5360a7ba223
--- /dev/null
+++ b/spring-ai-spring-boot-starters/spring-ai-starter-mariadb-store/pom.xml
@@ -0,0 +1,58 @@
+
+
+
+
+ 4.0.0
+
+ org.springframework.ai
+ spring-ai
+ 1.0.0-SNAPSHOT
+ ../../pom.xml
+
+ spring-ai-mariadb-store-spring-boot-starter
+ jar
+ Spring AI Starter - MariaDB Vector Store
+ Spring AI MariaDB Vector Store Auto Configuration
+ https://github.com/spring-projects/spring-ai
+
+
+ https://github.com/spring-projects/spring-ai
+ git://github.com/spring-projects/spring-ai.git
+ git@github.com:spring-projects/spring-ai.git
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+
+ org.springframework.ai
+ spring-ai-spring-boot-autoconfigure
+ ${project.parent.version}
+
+
+
+ org.springframework.ai
+ spring-ai-mariadb-store
+ ${project.parent.version}
+
+
+
+
diff --git a/vector-stores/spring-ai-mariadb-store/README.md b/vector-stores/spring-ai-mariadb-store/README.md
new file mode 100644
index 00000000000..175b193ba7e
--- /dev/null
+++ b/vector-stores/spring-ai-mariadb-store/README.md
@@ -0,0 +1 @@
+[MariaDB Vector Store Documentation](https://docs.spring.io/spring-ai/reference/api/vectordbs/mariadb.html)
\ No newline at end of file
diff --git a/vector-stores/spring-ai-mariadb-store/pom.xml b/vector-stores/spring-ai-mariadb-store/pom.xml
new file mode 100644
index 00000000000..18331999ab2
--- /dev/null
+++ b/vector-stores/spring-ai-mariadb-store/pom.xml
@@ -0,0 +1,128 @@
+
+
+
+
+ 4.0.0
+
+ org.springframework.ai
+ spring-ai
+ 1.0.0-SNAPSHOT
+ ../../pom.xml
+
+ spring-ai-mariadb-store
+ jar
+ Spring AI Vector Store - MariaDB
+ Spring AI MariaDB Vector Store
+ https://github.com/spring-projects/spring-ai
+
+
+ https://github.com/spring-projects/spring-ai
+ git://github.com/spring-projects/spring-ai.git
+ git@github.com:spring-projects/spring-ai.git
+
+
+
+
+ org.springframework.ai
+ spring-ai-core
+ ${project.parent.version}
+
+
+
+ com.zaxxer
+ HikariCP
+
+
+
+ org.springframework
+ spring-jdbc
+
+
+
+ org.mariadb.jdbc
+ mariadb-java-client
+ ${mariadb.version}
+
+
+
+
+ org.springframework.ai
+ spring-ai-openai
+ ${project.parent.version}
+ test
+
+
+
+
+ org.springframework.ai
+ spring-ai-test
+ ${project.parent.version}
+ test
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+ org.testcontainers
+ testcontainers
+ test
+
+
+ org.testcontainers
+ mariadb
+ test
+
+
+
+ org.testcontainers
+ junit-jupiter
+ test
+
+
+
+ io.micrometer
+ micrometer-observation-test
+ test
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-failsafe-plugin
+ ${maven-failsafe-plugin.version}
+
+ ${skip.vectorstore.mariadb}
+
+
+
+
+ integration-test
+ verify
+
+
+
+
+
+
+
diff --git a/vector-stores/spring-ai-mariadb-store/src/main/java/org/springframework/ai/vectorstore/MariaDBFilterExpressionConverter.java b/vector-stores/spring-ai-mariadb-store/src/main/java/org/springframework/ai/vectorstore/MariaDBFilterExpressionConverter.java
new file mode 100644
index 00000000000..b9e63864db3
--- /dev/null
+++ b/vector-stores/spring-ai-mariadb-store/src/main/java/org/springframework/ai/vectorstore/MariaDBFilterExpressionConverter.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2023-2024 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.vectorstore;
+
+import java.util.List;
+import org.springframework.ai.vectorstore.filter.Filter;
+import org.springframework.ai.vectorstore.filter.Filter.Expression;
+import org.springframework.ai.vectorstore.filter.Filter.Group;
+import org.springframework.ai.vectorstore.filter.Filter.Key;
+import org.springframework.ai.vectorstore.filter.converter.AbstractFilterExpressionConverter;
+
+/**
+ * Converts {@link Expression} into JSON metadata filter expression format.
+ * (https://mariadb.com/kb/en/json-functions/)
+ *
+ * @author Diego Dupin
+ */
+public class MariaDBFilterExpressionConverter extends AbstractFilterExpressionConverter {
+
+ private final String metadataFieldName;
+
+ public MariaDBFilterExpressionConverter(String metadataFieldName) {
+ this.metadataFieldName = metadataFieldName;
+ }
+
+ @Override
+ protected void doExpression(Expression expression, StringBuilder context) {
+ this.convertOperand(expression.left(), context);
+ context.append(getOperationSymbol(expression));
+ this.convertOperand(expression.right(), context);
+ }
+
+ private void convertToConditions(Expression expression, StringBuilder context) {
+ Filter.Value right = (Filter.Value) expression.right();
+ Object value = right.value();
+ if (!(value instanceof List)) {
+ throw new IllegalArgumentException("Expected a List, but got: " + value.getClass().getSimpleName());
+ }
+ List