diff --git a/composites/ebean-pgvector/pom.xml b/composites/ebean-pgvector/pom.xml
new file mode 100644
index 0000000000..2aece5d752
--- /dev/null
+++ b/composites/ebean-pgvector/pom.xml
@@ -0,0 +1,85 @@
+
+
+ 4.0.0
+
+ ebean-parent
+ io.ebean
+ 16.1.1
+ ../..
+
+
+ ebean-pgvector
+ ebean-pgvector composite
+ ebean-pgvector
+
+
+ 0.1.6
+ 42.7.2
+
+
+
+
+
+ io.ebean
+ ebean-api
+ 16.1.1
+
+
+
+ io.ebean
+ ebean-core
+ 16.1.1
+
+
+
+ io.ebean
+ ebean-datasource
+ ${ebean-datasource.version}
+
+
+
+ io.ebean
+ ebean-migration
+ ${ebean-migration.version}
+
+
+
+
+ io.ebean
+ ebean-querybean
+ 16.1.1
+
+
+
+ io.ebean
+ ebean-platform-postgres
+ 16.1.1
+
+
+
+ io.ebean
+ ebean-pgvector-types
+ 16.1.1
+
+
+
+ org.postgresql
+ postgresql
+ ${postgres.jdbc.version}
+
+
+
+ *
+ *
+
+
+
+
+
+ com.pgvector
+ pgvector
+ ${pgvector.version}
+
+
+
+
diff --git a/composites/ebean-pgvector/src/main/java/io/ebean/pgvector/assembly/Assembly.java b/composites/ebean-pgvector/src/main/java/io/ebean/pgvector/assembly/Assembly.java
new file mode 100644
index 0000000000..f4d7a7b0ad
--- /dev/null
+++ b/composites/ebean-pgvector/src/main/java/io/ebean/pgvector/assembly/Assembly.java
@@ -0,0 +1,7 @@
+package io.ebean.pgvector.assembly;
+
+/**
+ * Nothing interesting here - required placeholder for javadoc.
+ */
+public class Assembly {
+}
diff --git a/composites/ebean-pgvector/src/main/java/module-info.java b/composites/ebean-pgvector/src/main/java/module-info.java
new file mode 100644
index 0000000000..1937be9827
--- /dev/null
+++ b/composites/ebean-pgvector/src/main/java/module-info.java
@@ -0,0 +1,9 @@
+module io.ebean.pgvector {
+
+ requires transitive io.ebean.api;
+ requires transitive io.ebean.core;
+ requires transitive io.ebean.datasource;
+ requires transitive io.ebean.querybean;
+ requires transitive io.ebean.platform.postgres;
+
+}
diff --git a/composites/pom.xml b/composites/pom.xml
index 24ba6ce76c..69a176b86a 100644
--- a/composites/pom.xml
+++ b/composites/pom.xml
@@ -25,6 +25,7 @@
ebean-postgres
ebean-postgis
ebean-net-postgis
+ ebean-pgvector
ebean-sqlite
ebean-sqlserver
diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/DbPlatformTypeMapping.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/DbPlatformTypeMapping.java
index 3dfbdf7487..dfb5d885fb 100644
--- a/ebean-api/src/main/java/io/ebean/config/dbplatform/DbPlatformTypeMapping.java
+++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/DbPlatformTypeMapping.java
@@ -46,6 +46,11 @@ protected void renderLengthScale(int deployLength, int deployScale, StringBuilde
private static final DbPlatformType MULTILINESTRING = new DbPlatformType("multilinestring");
private static final DbPlatformType MULTIPOLYGON = new DbPlatformType("multipolygon");
+ private static final DbPlatformType VECTOR = new DbPlatformType("vector", 2000, null);
+ private static final DbPlatformType VECTOR_HALF = new DbPlatformType("halfvec", 4000, null);
+ private static final DbPlatformType VECTOR_BIT = new DbPlatformType("bit", 64000, null);
+ private static final DbPlatformType VECTOR_SPARSE = new DbPlatformType("sparsevec", 1000, null);
+
private final Map typeMap = new EnumMap<>(DbType.class);
/**
@@ -93,6 +98,10 @@ private void loadDefaults(boolean logicalTypes) {
put(DbType.MULTIPOINT, MULTIPOINT);
put(DbType.MULTILINESTRING, MULTILINESTRING);
put(DbType.MULTIPOLYGON, MULTIPOLYGON);
+ put(DbType.VECTOR, VECTOR);
+ put(DbType.VECTOR_HALF, VECTOR_HALF);
+ put(DbType.VECTOR_BIT, VECTOR_BIT);
+ put(DbType.VECTOR_SPARSE, VECTOR_SPARSE);
if (logicalTypes) {
// keep it logical for 2 layer DDL generation
diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/DbType.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/DbType.java
index eee8aa44b3..74ef868c56 100644
--- a/ebean-api/src/main/java/io/ebean/config/dbplatform/DbType.java
+++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/DbType.java
@@ -51,7 +51,12 @@ public enum DbType {
JSONB(ExtraDbTypes.JSONB),
JSONCLOB(ExtraDbTypes.JSONClob),
JSONBLOB(ExtraDbTypes.JSONBlob),
- JSONVARCHAR(ExtraDbTypes.JSONVarchar);
+ JSONVARCHAR(ExtraDbTypes.JSONVarchar),
+
+ VECTOR(ExtraDbTypes.VECTOR),
+ VECTOR_HALF(ExtraDbTypes.VECTOR_HALF),
+ VECTOR_BIT(ExtraDbTypes.VECTOR_BIT),
+ VECTOR_SPARSE(ExtraDbTypes.VECTOR_SPARSE);
private final int id;
diff --git a/ebean-api/src/main/java/io/ebean/config/dbplatform/ExtraDbTypes.java b/ebean-api/src/main/java/io/ebean/config/dbplatform/ExtraDbTypes.java
index 91f77fdbea..447205f696 100644
--- a/ebean-api/src/main/java/io/ebean/config/dbplatform/ExtraDbTypes.java
+++ b/ebean-api/src/main/java/io/ebean/config/dbplatform/ExtraDbTypes.java
@@ -74,4 +74,24 @@ public interface ExtraDbTypes {
*/
int MULTILINESTRING = 6007;
+ /**
+ * PGVector base type
+ */
+ int VECTOR = 7000;
+
+ /**
+ * PGVector half precision float type
+ */
+ int VECTOR_HALF = 7001;
+
+ /**
+ * PGVector binary type (bit)
+ */
+ int VECTOR_BIT = 7002;
+
+ /**
+ * PGVector sparse type
+ */
+ int VECTOR_SPARSE = 7003;
+
}
diff --git a/ebean-bom/pom.xml b/ebean-bom/pom.xml
index 02c1caffe9..a941551273 100644
--- a/ebean-bom/pom.xml
+++ b/ebean-bom/pom.xml
@@ -256,6 +256,18 @@
16.1.1
+
+ io.ebean
+ ebean-pgvector
+ 16.1.1
+
+
+
+ io.ebean
+ ebean-pgvector-types
+ 16.1.1
+
+
io.ebean
ebean-sqlite
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/Binder.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/Binder.java
index b83fffe6df..a049adc305 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/persist/Binder.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/persist/Binder.java
@@ -329,6 +329,13 @@ private void bindSimpleData(DataBind b, int dataType, Object data) {
geoTypeBinder.bind(b, dataType, data);
break;
+ case DbPlatformType.VECTOR:
+ case DbPlatformType.VECTOR_HALF:
+ case DbPlatformType.VECTOR_BIT:
+ case DbPlatformType.VECTOR_SPARSE:
+ b.setObject(data);
+ break;
+
case java.sql.Types.OTHER:
b.setObject(data, dataType);
break;
diff --git a/ebean-core/src/main/java/module-info.java b/ebean-core/src/main/java/module-info.java
index 47f425cb6d..572b535e89 100644
--- a/ebean-core/src/main/java/module-info.java
+++ b/ebean-core/src/main/java/module-info.java
@@ -69,7 +69,7 @@
exports io.ebeaninternal.server.querydefn to io.ebean.autotune, io.ebean.querybean, io.ebean.test, io.ebean.elastic;
exports io.ebeaninternal.server.rawsql to io.ebean.test;
exports io.ebeaninternal.server.json to io.ebean.test, io.ebean.elastic;
- exports io.ebeaninternal.server.type to io.ebean.postgis, io.ebean.test, io.ebean.postgis.types;
+ exports io.ebeaninternal.server.type to io.ebean.postgis, io.ebean.test, io.ebean.postgis.types, io.ebean.pgvector;
exports io.ebeaninternal.server.transaction to io.ebean.test, io.ebean.elastic, io.ebean.spring.txn, io.ebean.k8scache;
exports io.ebeaninternal.server.util to io.ebean.querybean;
diff --git a/ebean-pgvector-types/.editorconfig b/ebean-pgvector-types/.editorconfig
new file mode 100644
index 0000000000..d67326c696
--- /dev/null
+++ b/ebean-pgvector-types/.editorconfig
@@ -0,0 +1,17 @@
+# editorconfig.org
+
+root = true
+
+[*]
+charset = utf-8
+end_of_line = lf
+indent_size = 2
+indent_style = space
+insert_final_newline = true
+trim_trailing_whitespace = true
+spaces_around_operators = true
+max_line_length = 130
+
+[pom.xml]
+# Because of
+
+ 4.0.0
+
+ ebean-parent
+ io.ebean
+ 16.1.1
+
+
+ ebean pgvector types
+ ebean-pgvector-types
+
+
+ 0.1.6
+
+
+
+
+
+ io.ebean
+ ebean-platform-postgres
+ 16.1.1
+
+
+
+
+ io.ebean
+ ebean-core
+ 16.1.1
+ provided
+
+
+
+ com.fasterxml.jackson.core
+ jackson-core
+ ${jackson.version}
+ true
+
+
+
+ com.pgvector
+ pgvector
+ ${pgvector.version}
+
+
+
+
+ org.postgresql
+ postgresql
+ 42.7.2
+ provided
+
+
+
+ io.ebean
+ ebean-test
+ 16.1.1
+ test
+
+
+
+ org.avaje.composite
+ logback
+ 1.1
+ test
+
+
+
+
+
+
+
+
+
+ io.ebean
+ ebean-maven-plugin
+ ${ebean-maven-plugin.version}
+
+
+ test
+ process-test-classes
+
+ debug=0
+
+
+ testEnhance
+
+
+
+
+
+
+
+
+
+
diff --git a/ebean-pgvector-types/src/main/java/io/ebean/pgvector/PGvectorExtraTypeFactory.java b/ebean-pgvector-types/src/main/java/io/ebean/pgvector/PGvectorExtraTypeFactory.java
new file mode 100644
index 0000000000..993805bbc6
--- /dev/null
+++ b/ebean-pgvector-types/src/main/java/io/ebean/pgvector/PGvectorExtraTypeFactory.java
@@ -0,0 +1,20 @@
+package io.ebean.pgvector;
+
+import io.ebean.DatabaseBuilder;
+import io.ebean.core.type.ExtraTypeFactory;
+import io.ebean.core.type.ScalarType;
+
+import java.util.List;
+
+public final class PGvectorExtraTypeFactory implements ExtraTypeFactory {
+
+ @Override
+ public List extends ScalarType>> createTypes(DatabaseBuilder.Settings config, Object objectMapper) {
+ return List.of(
+ new ScalarTypePGvector(),
+ new ScalarTypePGhalfvec(),
+ new ScalarTypePGsparsevec(),
+ new ScalarTypePGbit()
+ );
+ }
+}
diff --git a/ebean-pgvector-types/src/main/java/io/ebean/pgvector/PGvectorNewConnectionInitializer.java b/ebean-pgvector-types/src/main/java/io/ebean/pgvector/PGvectorNewConnectionInitializer.java
new file mode 100644
index 0000000000..af454d5854
--- /dev/null
+++ b/ebean-pgvector-types/src/main/java/io/ebean/pgvector/PGvectorNewConnectionInitializer.java
@@ -0,0 +1,21 @@
+package io.ebean.pgvector;
+
+import com.pgvector.PGbit;
+import com.pgvector.PGvector;
+import io.ebean.datasource.NewConnectionInitializer;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+public final class PGvectorNewConnectionInitializer implements NewConnectionInitializer {
+
+ @Override
+ public void preInitialize(Connection connection) {
+ try {
+ PGvector.registerTypes(connection);
+ PGbit.registerType(connection);
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/ebean-pgvector-types/src/main/java/io/ebean/pgvector/ScalarTypePGbase.java b/ebean-pgvector-types/src/main/java/io/ebean/pgvector/ScalarTypePGbase.java
new file mode 100644
index 0000000000..4bb7baa34a
--- /dev/null
+++ b/ebean-pgvector-types/src/main/java/io/ebean/pgvector/ScalarTypePGbase.java
@@ -0,0 +1,96 @@
+package io.ebean.pgvector;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonParser;
+import io.ebean.core.type.DataBinder;
+import io.ebean.core.type.DataReader;
+import io.ebean.core.type.DocPropertyType;
+import io.ebean.core.type.ScalarType;
+import org.postgresql.util.PGobject;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.sql.SQLException;
+import java.sql.Types;
+
+abstract class ScalarTypePGbase implements ScalarType {
+
+ private final int jdbcType;
+ private final Class cls;
+
+ public ScalarTypePGbase(int jdbcType, Class cls) {
+ this.jdbcType = jdbcType;
+ this.cls = cls;
+ }
+
+ @Override
+ public T read(DataReader reader) throws SQLException {
+ var obj=reader.getObject();
+ if(obj==null) return null;
+ return cls.cast(obj);
+ }
+
+ @Override
+ public void bind(DataBinder binder, T value) throws SQLException {
+ if(value==null) {
+ binder.setNull(Types.NULL);
+ } else {
+ binder.setObject(value);
+ }
+ }
+
+ @Override
+ public boolean jdbcNative() {
+ return true;
+ }
+
+ @Override
+ public int jdbcType() {
+ return jdbcType;
+ }
+
+ @Override
+ public Class type() {
+ return cls;
+ }
+
+ @Override
+ public T readData(DataInput dataInput) {
+ return null;
+ }
+
+ @Override
+ public void writeData(DataOutput dataOutput, T v) {
+
+ }
+
+ @Override
+ public Object toJdbcType(Object value) {
+ return null;
+ }
+
+ @Override
+ public T toBeanType(Object value) {
+ return null;
+ }
+
+ @Override
+ public String formatValue(T value) {
+ return value.toString();
+ }
+
+ @Override
+ public DocPropertyType docType() {
+ return null;
+ }
+
+ @Override
+ public T jsonRead(JsonParser parser) {
+ return null;
+ }
+
+ @Override
+ public void jsonWrite(JsonGenerator writer, T value) {
+
+ }
+}
diff --git a/ebean-pgvector-types/src/main/java/io/ebean/pgvector/ScalarTypePGbit.java b/ebean-pgvector-types/src/main/java/io/ebean/pgvector/ScalarTypePGbit.java
new file mode 100644
index 0000000000..1a0593fd64
--- /dev/null
+++ b/ebean-pgvector-types/src/main/java/io/ebean/pgvector/ScalarTypePGbit.java
@@ -0,0 +1,22 @@
+package io.ebean.pgvector;
+
+import com.pgvector.PGbit;
+import io.ebean.config.dbplatform.ExtraDbTypes;
+
+import java.sql.SQLException;
+
+public final class ScalarTypePGbit extends ScalarTypePGbase {
+
+ public ScalarTypePGbit() {
+ super(ExtraDbTypes.VECTOR_BIT, PGbit.class);
+ }
+
+ @Override
+ public PGbit parse(String value) {
+ try {
+ return new PGbit(value);
+ } catch (SQLException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+}
diff --git a/ebean-pgvector-types/src/main/java/io/ebean/pgvector/ScalarTypePGhalfvec.java b/ebean-pgvector-types/src/main/java/io/ebean/pgvector/ScalarTypePGhalfvec.java
new file mode 100644
index 0000000000..65f8a05a6d
--- /dev/null
+++ b/ebean-pgvector-types/src/main/java/io/ebean/pgvector/ScalarTypePGhalfvec.java
@@ -0,0 +1,22 @@
+package io.ebean.pgvector;
+
+import com.pgvector.PGhalfvec;
+import io.ebean.config.dbplatform.ExtraDbTypes;
+
+import java.sql.SQLException;
+
+public final class ScalarTypePGhalfvec extends ScalarTypePGbase {
+
+ public ScalarTypePGhalfvec() {
+ super(ExtraDbTypes.VECTOR_HALF, PGhalfvec.class);
+ }
+
+ @Override
+ public PGhalfvec parse(String value) {
+ try {
+ return new PGhalfvec(value);
+ } catch (SQLException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+}
diff --git a/ebean-pgvector-types/src/main/java/io/ebean/pgvector/ScalarTypePGsparsevec.java b/ebean-pgvector-types/src/main/java/io/ebean/pgvector/ScalarTypePGsparsevec.java
new file mode 100644
index 0000000000..9749b7953e
--- /dev/null
+++ b/ebean-pgvector-types/src/main/java/io/ebean/pgvector/ScalarTypePGsparsevec.java
@@ -0,0 +1,22 @@
+package io.ebean.pgvector;
+
+import com.pgvector.PGsparsevec;
+import io.ebean.config.dbplatform.ExtraDbTypes;
+
+import java.sql.SQLException;
+
+public final class ScalarTypePGsparsevec extends ScalarTypePGbase {
+
+ public ScalarTypePGsparsevec() {
+ super(ExtraDbTypes.VECTOR_SPARSE, PGsparsevec.class);
+ }
+
+ @Override
+ public PGsparsevec parse(String value) {
+ try {
+ return new PGsparsevec(value);
+ } catch (SQLException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+}
diff --git a/ebean-pgvector-types/src/main/java/io/ebean/pgvector/ScalarTypePGvector.java b/ebean-pgvector-types/src/main/java/io/ebean/pgvector/ScalarTypePGvector.java
new file mode 100644
index 0000000000..c5ca67eca9
--- /dev/null
+++ b/ebean-pgvector-types/src/main/java/io/ebean/pgvector/ScalarTypePGvector.java
@@ -0,0 +1,20 @@
+package io.ebean.pgvector;
+
+import com.pgvector.PGvector;
+import io.ebean.config.dbplatform.ExtraDbTypes;
+
+public final class ScalarTypePGvector extends ScalarTypePGbase {
+
+ public ScalarTypePGvector() {
+ super(ExtraDbTypes.VECTOR, PGvector.class);
+ }
+
+ @Override
+ public PGvector parse(String value) {
+ try {
+ return new PGvector(value);
+ } catch (Exception e) {
+ throw new IllegalStateException(e);
+ }
+ }
+}
diff --git a/ebean-pgvector-types/src/main/resources/META-INF/services/io.ebean.core.type.ExtraTypeFactory b/ebean-pgvector-types/src/main/resources/META-INF/services/io.ebean.core.type.ExtraTypeFactory
new file mode 100644
index 0000000000..09973ed327
--- /dev/null
+++ b/ebean-pgvector-types/src/main/resources/META-INF/services/io.ebean.core.type.ExtraTypeFactory
@@ -0,0 +1 @@
+io.ebean.pgvector.PGvectorExtraTypeFactory
diff --git a/ebean-pgvector-types/src/main/resources/META-INF/services/io.ebean.datasource.NewConnectionInitializer b/ebean-pgvector-types/src/main/resources/META-INF/services/io.ebean.datasource.NewConnectionInitializer
new file mode 100644
index 0000000000..11a2134f75
--- /dev/null
+++ b/ebean-pgvector-types/src/main/resources/META-INF/services/io.ebean.datasource.NewConnectionInitializer
@@ -0,0 +1 @@
+io.ebean.pgvector.PGvectorNewConnectionInitializer
diff --git a/ebean-pgvector-types/src/test/java/org/example/domain/BaseEntity.java b/ebean-pgvector-types/src/test/java/org/example/domain/BaseEntity.java
new file mode 100644
index 0000000000..6dc59d1e20
--- /dev/null
+++ b/ebean-pgvector-types/src/test/java/org/example/domain/BaseEntity.java
@@ -0,0 +1,20 @@
+package org.example.domain;
+
+import jakarta.persistence.Id;
+import jakarta.persistence.MappedSuperclass;
+
+@MappedSuperclass
+abstract class BaseEntity {
+
+ @Id
+ Long id;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+}
diff --git a/ebean-pgvector-types/src/test/java/org/example/domain/CachedBean.java b/ebean-pgvector-types/src/test/java/org/example/domain/CachedBean.java
new file mode 100644
index 0000000000..1a227eeb09
--- /dev/null
+++ b/ebean-pgvector-types/src/test/java/org/example/domain/CachedBean.java
@@ -0,0 +1,69 @@
+package org.example.domain;
+
+import com.pgvector.PGbit;
+import com.pgvector.PGhalfvec;
+import com.pgvector.PGsparsevec;
+import com.pgvector.PGvector;
+import io.ebean.annotation.Cache;
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.Table;
+
+@Entity
+@Table(name="mybean_cached")
+@Cache
+public class CachedBean extends BaseEntity {
+ String name;
+
+ @Column(length = 800)
+ PGvector vector;
+
+ @Column(length = 200)
+ PGsparsevec sparsevec;
+
+ @Column(length = 200)
+ PGbit bit;
+
+ @Column(length = 200)
+ PGhalfvec halfvec;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public PGvector getVector() {
+ return vector;
+ }
+
+ public void setVector(PGvector vector) {
+ this.vector = vector;
+ }
+
+ public PGsparsevec getSparsevec() {
+ return sparsevec;
+ }
+
+ public void setSparsevec(PGsparsevec sparsevec) {
+ this.sparsevec = sparsevec;
+ }
+
+ public PGbit getBit() {
+ return bit;
+ }
+
+ public void setBit(PGbit bit) {
+ this.bit = bit;
+ }
+
+ public PGhalfvec getHalfvec() {
+ return halfvec;
+ }
+
+ public void setHalfvec(PGhalfvec halfvec) {
+ this.halfvec = halfvec;
+ }
+}
diff --git a/ebean-pgvector-types/src/test/java/org/example/domain/MyBean.java b/ebean-pgvector-types/src/test/java/org/example/domain/MyBean.java
new file mode 100644
index 0000000000..3052b0750c
--- /dev/null
+++ b/ebean-pgvector-types/src/test/java/org/example/domain/MyBean.java
@@ -0,0 +1,69 @@
+package org.example.domain;
+
+import com.pgvector.PGbit;
+import com.pgvector.PGhalfvec;
+import com.pgvector.PGsparsevec;
+import com.pgvector.PGvector;
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.Table;
+
+@Entity
+@Table(name="mybean")
+public class MyBean extends BaseEntity {
+
+ String name;
+
+ @Column(length = 200)
+ PGvector vector;
+
+ @Column(length = 350)
+ PGsparsevec sparse;
+
+ @Column(length = 1200)
+ PGbit bit;
+
+ @Column(length = 420)
+ PGhalfvec halfvec;
+
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public PGvector getVector() {
+ return vector;
+ }
+
+ public void setVector(PGvector vector) {
+ this.vector = vector;
+ }
+
+ public PGsparsevec getSparse() {
+ return sparse;
+ }
+
+ public void setSparse(PGsparsevec sparse) {
+ this.sparse = sparse;
+ }
+
+ public PGbit getBit() {
+ return bit;
+ }
+
+ public void setBit(PGbit bit) {
+ this.bit = bit;
+ }
+
+ public PGhalfvec getHalfvec() {
+ return halfvec;
+ }
+
+ public void setHalfvec(PGhalfvec halfvec) {
+ this.halfvec = halfvec;
+ }
+}
diff --git a/ebean-pgvector-types/src/test/java/org/example/domain/TestCachedBean.java b/ebean-pgvector-types/src/test/java/org/example/domain/TestCachedBean.java
new file mode 100644
index 0000000000..994ee98688
--- /dev/null
+++ b/ebean-pgvector-types/src/test/java/org/example/domain/TestCachedBean.java
@@ -0,0 +1,35 @@
+package org.example.domain;
+
+import com.pgvector.PGbit;
+import com.pgvector.PGvector;
+import io.ebean.DB;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+class TestCachedBean {
+
+ @Test
+ void testCache() {
+ var v = new PGvector(TestInsertQuery.randomVector(800));
+ var b = new PGbit((TestInsertQuery.randomBitArray(200)));
+ CachedBean cb = new CachedBean();
+ cb.setName("test");
+ cb.setVector(v);
+ cb.setBit(b);
+ DB.insert(cb);
+
+ CachedBean r1 = DB.find(CachedBean.class, cb.getId());
+ assertNotNull(r1);
+ assertEquals(v, r1.getVector());
+ assertEquals(b, r1.getBit());
+
+ CachedBean r2 = DB.find(CachedBean.class, cb.getId());
+ assertNotNull(r2);
+ assertEquals(v, r2.getVector());
+ assertEquals(b, r2.getBit());
+
+ DB.delete(r2);
+ }
+}
diff --git a/ebean-pgvector-types/src/test/java/org/example/domain/TestInsertQuery.java b/ebean-pgvector-types/src/test/java/org/example/domain/TestInsertQuery.java
new file mode 100644
index 0000000000..9530440b08
--- /dev/null
+++ b/ebean-pgvector-types/src/test/java/org/example/domain/TestInsertQuery.java
@@ -0,0 +1,136 @@
+package org.example.domain;
+
+import com.pgvector.PGbit;
+import com.pgvector.PGhalfvec;
+import com.pgvector.PGsparsevec;
+import com.pgvector.PGvector;
+import io.ebean.DB;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+import java.util.Random;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class TestInsertQuery {
+
+ static float[] randomVector(int dim) {
+ Random rnd = new Random();
+ float[] vector = new float[dim];
+ for (int i = 0; i < dim; i++) {
+ vector[i] = rnd.nextFloat();
+ }
+ return vector;
+ }
+
+ static boolean compareHalfVectors(PGhalfvec v1, PGhalfvec v2) {
+ if (v1 == null || v2 == null) return v1 == v2;
+ float[] a1 = v1.toArray();
+ float[] a2 = v2.toArray();
+ if (a1.length != a2.length) return false;
+ for (int i = 0; i < a1.length; i++) {
+ if (Math.abs(a1[i] - a2[i]) > 0.001) return false;
+ }
+ return true;
+ }
+
+ static float[] randomSparseVector(int dim, int nonZeroCount) {
+ Random rnd = new Random();
+ float[] vector = new float[dim];
+ for (int i = 0; i < nonZeroCount; i++) {
+ int index;
+ do {
+ index = rnd.nextInt(dim);
+ } while (vector[index] != 0);
+ vector[index] = rnd.nextFloat();
+ }
+ return vector;
+ }
+
+ static boolean[] randomBitArray(int length) {
+ Random rnd = new Random();
+ boolean[] bits = new boolean[length];
+ for (int i = 0; i < length; i++) {
+ bits[i] = rnd.nextBoolean();
+ }
+ return bits;
+ }
+
+ @Test
+ public void insert() {
+ List list = DB.find(MyBean.class).findList();
+ for (MyBean MyBean : list) {
+ System.out.println(MyBean.getVector());
+ }
+
+ var v1 = new PGvector(randomVector(200));
+
+ MyBean myBean = new MyBean();
+ myBean.setName("test");
+ myBean.setVector(v1);
+ DB.save(myBean);
+
+ var dbBean = DB.find(MyBean.class, myBean.getId());
+ assertNotNull(dbBean);
+ assertEquals(myBean.getVector().toString(), dbBean.getVector().toString());
+ }
+
+ @Test
+ void differentTypes() {
+ var rv1 = new PGvector(randomVector(200));
+ var rv2 = new PGvector(randomVector(200));
+ var rh1 = new PGhalfvec(randomVector(420));
+ var rh2 = new PGhalfvec(randomVector(420));
+ var rb1 = new PGbit(randomBitArray(1200));
+ var rb2 = new PGbit(randomBitArray(1200));
+ var rs1 = new PGsparsevec(randomSparseVector(350, 2));
+ var rs2 = new PGsparsevec(randomSparseVector(350, 2));
+
+ MyBean b1 = new MyBean();
+ b1.setName("testTypes");
+ b1.setVector(rv1);
+ b1.setHalfvec(rh1);
+ b1.setBit(rb1);
+ b1.setSparse(rs1);
+ DB.save(b1);
+
+ MyBean b2 = new MyBean();
+ b2.setName("testTypes2");
+ b2.setVector(rv2);
+ b2.setHalfvec(rh2);
+ b2.setBit(rb2);
+ b2.setSparse(rs2);
+ DB.save(b2);
+
+ var f1 = DB.find(MyBean.class).where().eq("vector", rv1).findOne();
+ assertNotNull(f1);
+ assertEquals(b1.getId(), f1.getId());
+ assertEquals(b1.getVector(), f1.getVector());
+ assertTrue(compareHalfVectors(b1.getHalfvec(), f1.getHalfvec()));
+ assertEquals(b1.getBit(), f1.getBit());
+ assertEquals(b1.getSparse(), f1.getSparse());
+
+ var f2 = DB.find(MyBean.class).where().eq("sparse", rs2).findOne();
+ assertNotNull(f2);
+ assertEquals(b2.getId(), f2.getId());
+ assertEquals(b2.getVector(), f2.getVector());
+ assertTrue(compareHalfVectors(b2.getHalfvec(), f2.getHalfvec()));
+ assertEquals(b2.getBit(), f2.getBit());
+ assertEquals(b2.getSparse(), f2.getSparse());
+
+ var f3 = DB.find(MyBean.class).where().eq("bit", rb1).findOne();
+ assertNotNull(f3);
+ assertEquals(b1.getId(), f3.getId());
+ assertEquals(b1.getVector(), f3.getVector());
+ assertTrue(compareHalfVectors(b1.getHalfvec(), f3.getHalfvec()));
+ assertEquals(b1.getBit(), f3.getBit());
+ assertEquals(b1.getSparse(), f3.getSparse());
+
+ DB.delete(f1);
+
+ assertEquals(1, DB.find(MyBean.class).where().eq("halfvec", rh2).delete());
+ assertNull(DB.find(MyBean.class).where().eq("sparse", rs2).findOne());
+ assertNull(DB.find(MyBean.class).where().eq("bit", rb1).findOne());
+
+ }
+}
diff --git a/ebean-pgvector-types/src/test/resources/application-test.yml b/ebean-pgvector-types/src/test/resources/application-test.yml
new file mode 100644
index 0000000000..cb42472f7d
--- /dev/null
+++ b/ebean-pgvector-types/src/test/resources/application-test.yml
@@ -0,0 +1,14 @@
+ebean:
+ dbSchema: mypgvectorapp
+
+ test:
+ # useDocker: false
+ # shutdown: stop # stop | remove
+ platform: pgvector
+ ddlMode: dropCreate # none | dropCreate | create | migration | createOnly | migrationDropCreate
+ dbName: mypgvectorapp
+
+ pgvector:
+ containerName: ebeanbuild_pgvector
+ port: 8432
+ image: pgvector/pgvector:pg18
diff --git a/ebean-pgvector-types/src/test/resources/logback-test.xml b/ebean-pgvector-types/src/test/resources/logback-test.xml
new file mode 100644
index 0000000000..11ba301e03
--- /dev/null
+++ b/ebean-pgvector-types/src/test/resources/logback-test.xml
@@ -0,0 +1,23 @@
+
+
+
+
+ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ebean-postgis-types/src/test/java/org/example/domain/TestInsertQuery.java b/ebean-postgis-types/src/test/java/org/example/domain/TestInsertQuery.java
index c12d1765eb..6d35b4cecb 100644
--- a/ebean-postgis-types/src/test/java/org/example/domain/TestInsertQuery.java
+++ b/ebean-postgis-types/src/test/java/org/example/domain/TestInsertQuery.java
@@ -12,14 +12,13 @@
import java.sql.SQLException;
import java.util.List;
-public class TestInsertQuery {
+class TestInsertQuery {
/**
* Not automated this test yet.
*/
@Test
- public void insert() throws SQLException {
-
+ void insert() throws SQLException {
List list = DB.find(MyBean.class).findList();
for (MyBean MyBean : list) {
diff --git a/ebean-test/src/main/java/io/ebean/test/config/platform/PGvectorSetup.java b/ebean-test/src/main/java/io/ebean/test/config/platform/PGvectorSetup.java
new file mode 100644
index 0000000000..29f4a755fc
--- /dev/null
+++ b/ebean-test/src/main/java/io/ebean/test/config/platform/PGvectorSetup.java
@@ -0,0 +1,49 @@
+package io.ebean.test.config.platform;
+
+import java.util.Properties;
+
+final class PGvectorSetup implements PlatformSetup {
+
+ @Override
+ public Properties setup(Config config) {
+ int defaultPort = config.isUseDocker() ? 8432 : 5432;
+ config.setDockerPlatform("pgvector");
+ config.ddlMode("dropCreate");
+ config.setDefaultPort(defaultPort);
+ config.setUsernameDefault();
+ config.setPasswordDefault();
+ config.setUrl("jdbc:postgresql://${host}:${port}/${databaseName}");
+ String schema = config.getSchema();
+ if (schema != null && !schema.equals(config.getUsername())) {
+ config.urlAppend("?currentSchema=" + schema);
+ }
+ config.setDriver("org.postgresql.Driver");
+ config.datasourceDefaults();
+ return dockerProperties(config);
+ }
+
+ private Properties dockerProperties(Config config) {
+ if (!config.isUseDocker()) {
+ return new Properties();
+ }
+ config.setExtensions("vector");
+ config.setDockerContainerName("ut_pgvector");
+ config.setDockerVersion("pg18");
+ return config.getDockerProperties();
+ }
+
+ @Override
+ public void setupExtraDbDataSource(Config config) {
+ int defaultPort = config.isUseDocker() ? 8432 : 5432;
+ config.setDefaultPort(defaultPort);
+ config.setExtraUsernameDefault();
+ config.setExtraDbPasswordDefault();
+ config.setExtraUrl("jdbc:postgresql://${host}:${port}/${databaseName}");
+ config.extraDatasourceDefaults();
+ }
+
+ @Override
+ public boolean isLocal() {
+ return false;
+ }
+}
diff --git a/ebean-test/src/main/java/io/ebean/test/config/platform/PlatformAutoConfig.java b/ebean-test/src/main/java/io/ebean/test/config/platform/PlatformAutoConfig.java
index 8ef841025b..0f471f81a4 100644
--- a/ebean-test/src/main/java/io/ebean/test/config/platform/PlatformAutoConfig.java
+++ b/ebean-test/src/main/java/io/ebean/test/config/platform/PlatformAutoConfig.java
@@ -26,6 +26,7 @@ public class PlatformAutoConfig {
KNOWN_PLATFORMS.put("sqlite", new SqliteSetup());
KNOWN_PLATFORMS.put("postgres", new PostgresSetup());
KNOWN_PLATFORMS.put("postgis", new PostgisSetup());
+ KNOWN_PLATFORMS.put("pgvector", new PGvectorSetup());
KNOWN_PLATFORMS.put("nuodb", new NuoDBSetup());
KNOWN_PLATFORMS.put("mysql", new MySqlSetup());
KNOWN_PLATFORMS.put("mariadb", new MariaDBSetup());
diff --git a/platforms/postgres/src/main/java/io/ebean/platform/postgres/PostgresPlatform.java b/platforms/postgres/src/main/java/io/ebean/platform/postgres/PostgresPlatform.java
index 463ddd9547..fec2401d8f 100644
--- a/platforms/postgres/src/main/java/io/ebean/platform/postgres/PostgresPlatform.java
+++ b/platforms/postgres/src/main/java/io/ebean/platform/postgres/PostgresPlatform.java
@@ -79,6 +79,11 @@ public PostgresPlatform() {
dbTypeMap.put(DbType.CLOB, dbTypeText);
dbTypeMap.put(DbType.LONGVARBINARY, dbBytea);
dbTypeMap.put(DbType.LONGVARCHAR, dbTypeText);
+
+ dbTypeMap.put(DbType.VECTOR, new DbPlatformType("vector", 512, 2000, null));
+ dbTypeMap.put(DbType.VECTOR_HALF, new DbPlatformType("halfvec", 512, 4000, null));
+ dbTypeMap.put(DbType.VECTOR_BIT, new DbPlatformType("bit", 512, 64000, null));
+ dbTypeMap.put(DbType.VECTOR_SPARSE, new DbPlatformType("sparsevec", 512, 64000, null));
}
@Override
diff --git a/pom.xml b/pom.xml
index 8e1b8b8f8e..cacdc504fa 100644
--- a/pom.xml
+++ b/pom.xml
@@ -48,8 +48,8 @@
2.3
1.2
14.3.0
- 7.15
- 10.1
+ 7.17
+ 10.2
16.1.1
16.1.1
false
@@ -91,6 +91,7 @@
ebean-querybean
ebean-postgis-types
ebean-net-postgis-types
+ ebean-pgvector-types
ebean-redis
platforms
composites