diff --git a/src/integration/java/io/pinecone/clients/ConnectionsMapTest.java b/src/integration/java/io/pinecone/clients/ConnectionsMapTest.java
index 7600fd7..78bf3f6 100644
--- a/src/integration/java/io/pinecone/clients/ConnectionsMapTest.java
+++ b/src/integration/java/io/pinecone/clients/ConnectionsMapTest.java
@@ -1,5 +1,6 @@
package io.pinecone.clients;
+import io.pinecone.configs.PineconeConfig;
import io.pinecone.configs.PineconeConnection;
import io.pinecone.exceptions.PineconeNotFoundException;
import io.pinecone.helpers.RandomStringBuilder;
@@ -57,6 +58,10 @@ public void testMultipleIndexesWithMultipleClients() throws InterruptedException
// Get index1's host
String host1 = indexModel1.getHost();
+ // Create config1 for getting index connection and set the host
+ PineconeConfig config1 = new PineconeConfig(System.getenv("PINECONE_API_KEY"));
+ config1.setHost(host1);
+
// Create index-2
pinecone1.createServerlessIndex(indexName2,
null,
@@ -72,6 +77,11 @@ public void testMultipleIndexesWithMultipleClients() throws InterruptedException
// Get index2's host
String host2 = indexModel2.getHost();
+ // Create config2 for getting index connection and set the host
+ PineconeConfig config2 = new PineconeConfig(System.getenv("PINECONE_API_KEY"));
+ config1.setHost(host2);
+
+
// Establish grpc connection for index-1
Index index1_1 = pinecone1.getIndexConnection(indexName1);
// Get connections map
@@ -94,7 +104,7 @@ public void testMultipleIndexesWithMultipleClients() throws InterruptedException
assertEquals(host2, connectionsMap1_2.get(indexName2).toString());
// Establishing connections with index1 and index2 using another pinecone client
- pinecone2.getConnection(indexName1);
+ pinecone2.getConnection(indexName1, config1);
ConcurrentHashMap connectionsMap2_1 = pinecone1.getConnectionsMap();
// Verify the new connections map is pointing to the same reference
assert connectionsMap2_1 == connectionsMap1_2;
@@ -103,7 +113,7 @@ public void testMultipleIndexesWithMultipleClients() throws InterruptedException
// Verify the connection value for index1 is host1
assertEquals(host1, connectionsMap2_1.get(indexName1).toString());
- pinecone2.getConnection(indexName2);
+ pinecone2.getConnection(indexName2, config2);
ConcurrentHashMap connectionsMap2_2 = pinecone1.getConnectionsMap();
// Verify the new connections map is pointing to the same reference
assert connectionsMap2_1 == connectionsMap2_2;
diff --git a/src/integration/java/io/pinecone/integration/controlPlane/serverless/DeletionProtectionTest.java b/src/integration/java/io/pinecone/integration/controlPlane/serverless/DeletionProtectionTest.java
index dbabbb2..115802d 100644
--- a/src/integration/java/io/pinecone/integration/controlPlane/serverless/DeletionProtectionTest.java
+++ b/src/integration/java/io/pinecone/integration/controlPlane/serverless/DeletionProtectionTest.java
@@ -44,12 +44,12 @@ public void createPodIndexWithDeletionProtectionDisabled() {
Map actualTags = indexModel.getTags();
Assertions.assertEquals(expectedTags, actualTags);
// Configure index to enable deletionProtection
- controlPlaneClient.configureServerlessIndex(indexName, DeletionProtection.ENABLED, expectedTags);
+ controlPlaneClient.configureServerlessIndex(indexName, DeletionProtection.ENABLED, expectedTags, null);
indexModel = controlPlaneClient.describeIndex(indexName);
deletionProtection = indexModel.getDeletionProtection();
Assertions.assertEquals(deletionProtection, DeletionProtection.ENABLED);
// Configure index to disable deletionProtection
- controlPlaneClient.configureServerlessIndex(indexName, DeletionProtection.DISABLED, expectedTags);
+ controlPlaneClient.configureServerlessIndex(indexName, DeletionProtection.DISABLED, expectedTags, null);
// Delete index
controlPlaneClient.deleteIndex(indexName);
}
diff --git a/src/integration/java/io/pinecone/integration/controlPlane/serverless/SparseIndexTest.java b/src/integration/java/io/pinecone/integration/controlPlane/serverless/SparseIndexTest.java
index ee74997..fc5ff0e 100644
--- a/src/integration/java/io/pinecone/integration/controlPlane/serverless/SparseIndexTest.java
+++ b/src/integration/java/io/pinecone/integration/controlPlane/serverless/SparseIndexTest.java
@@ -63,7 +63,7 @@ public void configureSparseIndex() throws InterruptedException {
waitUntilIndexIsReady(pinecone, indexName, 200000);
// Disable deletion protection and add more index tags
- pinecone.configureServerlessIndex(indexName, DeletionProtection.DISABLED, tags);
+ pinecone.configureServerlessIndex(indexName, DeletionProtection.DISABLED, tags, null);
Thread.sleep(7000);
// Describe index to confirm deletion protection is disabled
diff --git a/src/integration/java/io/pinecone/integration/dataPlane/QueryErrorTest.java b/src/integration/java/io/pinecone/integration/dataPlane/QueryErrorTest.java
index 5928357..e8146a8 100644
--- a/src/integration/java/io/pinecone/integration/dataPlane/QueryErrorTest.java
+++ b/src/integration/java/io/pinecone/integration/dataPlane/QueryErrorTest.java
@@ -34,7 +34,7 @@ public static void setUp() throws IOException, InterruptedException {
when(connectionMock.getBlockingStub()).thenReturn(stubMock);
when(connectionMock.getAsyncStub()).thenReturn(asyncStubMock);
- index = new Index(connectionMock, "some-index-name");
+ index = new Index(config, connectionMock, "some-index-name");
asyncIndex = new AsyncIndex(config, connectionMock, "some-index-name");
}
diff --git a/src/integration/java/io/pinecone/integration/dataPlane/UpsertAndSearchRecordsTest.java b/src/integration/java/io/pinecone/integration/dataPlane/UpsertAndSearchRecordsTest.java
new file mode 100644
index 0000000..d83d591
--- /dev/null
+++ b/src/integration/java/io/pinecone/integration/dataPlane/UpsertAndSearchRecordsTest.java
@@ -0,0 +1,95 @@
+package io.pinecone.integration.dataPlane;
+
+import io.pinecone.clients.Index;
+import io.pinecone.clients.Pinecone;
+import io.pinecone.helpers.RandomStringBuilder;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.openapitools.db_control.client.model.CreateIndexForModelRequest;
+import org.openapitools.db_control.client.model.CreateIndexForModelRequestEmbed;
+import org.openapitools.db_control.client.model.DeletionProtection;
+import org.openapitools.db_data.client.ApiException;
+import org.openapitools.db_data.client.model.SearchRecordsRequestQuery;
+import org.openapitools.db_data.client.model.SearchRecordsRequestRerank;
+import org.openapitools.db_data.client.model.SearchRecordsResponse;
+
+import java.util.*;
+
+public class UpsertAndSearchRecordsTest {
+ @Test
+ public void upsertAndSearchRecordsTest() throws ApiException, org.openapitools.db_control.client.ApiException, InterruptedException {
+ Pinecone pinecone = new Pinecone.Builder(System.getenv("PINECONE_API_KEY")).build();
+ String indexName = RandomStringBuilder.build("inf", 8);
+ HashMap fieldMap = new HashMap<>();
+ fieldMap.put("text", "chunk_text");
+ CreateIndexForModelRequestEmbed embed = new CreateIndexForModelRequestEmbed()
+ .model("multilingual-e5-large")
+ .fieldMap(fieldMap);
+ pinecone.createIndexForModel(indexName, CreateIndexForModelRequest.CloudEnum.AWS, "us-west-2", embed, DeletionProtection.DISABLED, new HashMap<>());
+
+ // Wait for index to be created
+ Thread.sleep(10000);
+
+ Index index = pinecone.getIndexConnection(indexName);
+ ArrayList
+ *
+ * @param id The ID of the record to be searched within the Pinecone index.
+ * The ID must exist within the specified namespace for a valid search.
+ * @param namespace The namespace within the Pinecone index where the search will be performed.
+ * The namespace must exist and contain records to search through.
+ * @param fields A list of fields to be searched within the records. These fields define which parts of the records
+ * are considered during the search.
+ * @param topK The maximum number of results to be returned by the search.
+ * @param filter (Optional) A filter to apply to the search query. It can be used to narrow down the search
+ * based on specific criteria.
+ * @param rerank (Optional) A reranking operation that can be applied to refine or reorder the search results.
+ * Pass null if no reranking is required.
+ * @return A {@link SearchRecordsResponse} object containing the search results, including the top matching records.
+ * @throws ApiException If there is an issue with the search operation. This could include network errors,
+ * invalid input data, or issues communicating with the Pinecone service.
+ */
+ public SearchRecordsResponse searchRecordsById(String id,
+ String namespace,
+ List fields,
+ int topK,
+ Map filter,
+ SearchRecordsRequestRerank rerank) throws ApiException {
+ SearchRecordsRequestQuery query = new SearchRecordsRequestQuery()
+ .id(id)
+ .topK(topK)
+ .filter(filter);
+
+ SearchRecordsRequest request = new SearchRecordsRequest()
+ .query(query)
+ .fields(fields)
+ .rerank(rerank);
+
+ return vectorOperations.searchRecordsNamespace(namespace, request);
+ }
+
+ /**
+ * Searches for records in a Pinecone index by a vector. The vector represents the search query in vector space.
+ *
+ * This method converts the given vector into a query and searches for the most relevant records within the Pinecone index.
+ * You can limit the results with the topK parameter, apply a filter, and optionally apply a reranking operation.
+ *
+ * The {@link SearchRecordsVector} class represents a vector in the query. It contains the following optional fields:
+ * - `values`: A list of floats representing the dense vector values. If this field is provided, it is used for the search.
+ * - `sparseValues`: An optional list of non-zero values for sparse vectors. If provided, the search will use sparse vector representation.
+ * - `sparseIndices`: An optional list of indices corresponding to the non-zero values in the sparse vector. If provided along with `sparseValues`, it will enable sparse vector search.
+ *
+ * Example:
+ *
{@code
+ * SearchRecordsVector vector = new SearchRecordsVector();
+ * vector.setValues(Arrays.asList(1.0f, 2.0f, 3.0f)); // Dense vector values
+ * // Or, for sparse vectors:
+ * // vector.setSparseValues(Arrays.asList(1.0f, 2.0f));
+ * // vector.setSparseIndices(Arrays.asList(0, 2));
+ * String namespace = "example-namespace";
+ * List fields = new ArrayList<>();
+ * fields.add("category");
+ * fields.add("chunk_text");
+ * int topK = 3;
+ *
+ * SearchRecordsResponse recordsResponse = index.searchRecordsByVector(vector, namespace, fields, topK, null, null);
+ * }
+ *
+ * @param vector The vector representing the search query, which is used to find the closest matching records.
+ * The vector can be a dense vector (via `values`) or a sparse vector (via `sparseValues` and `sparseIndices`).
+ * All fields in the vector are optional. If none are provided, the search will fail due to missing query data.
+ * @param namespace The namespace within the Pinecone index where the search will be performed.
+ * The namespace must exist and contain records to search through.
+ * @param fields A list of fields to be searched within the records. These fields define which parts of the records
+ * are considered during the search.
+ * @param topK The maximum number of results to be returned by the search.
+ * @param filter (Optional) A filter to apply to the search query. It can be used to narrow down the search
+ * based on specific criteria.
+ * @param rerank (Optional) A reranking operation that can be applied to refine or reorder the search results.
+ * Pass null if no reranking is required.
+ * @return A {@link SearchRecordsResponse} object containing the search results, including the top matching records.
+ * @throws ApiException If there is an issue with the search operation. This could include network errors,
+ * invalid input data, or issues communicating with the Pinecone service.
+ */
+ public SearchRecordsResponse searchRecordsByVector(SearchRecordsVector vector,
+ String namespace,
+ List fields,
+ int topK,
+ Map filter,
+ SearchRecordsRequestRerank rerank) throws ApiException {
+ SearchRecordsRequestQuery query = new SearchRecordsRequestQuery()
+ .vector(vector)
+ .topK(topK)
+ .filter(filter);
+
+ SearchRecordsRequest request = new SearchRecordsRequest()
+ .query(query)
+ .fields(fields)
+ .rerank(rerank);
+
+ return vectorOperations.searchRecordsNamespace(namespace, request);
+ }
+
+ /**
+ * Searches for records in a Pinecone index using a text query. The text is converted into a vector for the search operation.
+ *
+ * This method converts the given text into a vector representation and performs a search within the Pinecone index.
+ * You can limit the results with the topK parameter, apply a filter, and optionally apply a reranking operation.
+ *
+ * Example:
+ *
{@code
+ * String text = "Disease prevention";
+ * String namespace = "example-namespace";
+ * List fields = new ArrayList<>();
+ * fields.add("category");
+ * fields.add("chunk_text");
+ * int topK = 3;
+ *
+ * SearchRecordsResponse recordsResponse = index.searchRecordsByText(text, namespace, fields, topK, null, null);
+ * }
+ *
+ * @param text The text used in the query for searching.
+ * @param namespace The namespace within the Pinecone index where the search will be performed.
+ * The namespace must exist and contain records to search through.
+ * @param fields A list of fields to be searched within the records. These fields define which parts of the records
+ * are considered during the search.
+ * @param topK The maximum number of results to be returned by the search.
+ * @param filter (Optional) A filter to apply to the search query. It can be used to narrow down the search
+ * based on specific criteria.
+ * @param rerank (Optional) A reranking operation that can be applied to refine or reorder the search results.
+ * Pass null if no reranking is required.
+ * @return A {@link SearchRecordsResponse} object containing the search results, including the top matching records.
+ * @throws ApiException If there is an issue with the search operation. This could include network errors,
+ * invalid input data, or issues communicating with the Pinecone service.
+ */
+ public SearchRecordsResponse searchRecordsByText(String text,
+ String namespace,
+ List fields,
+ int topK,
+ Map filter,
+ SearchRecordsRequestRerank rerank) throws ApiException {
+ HashMap inputs = new HashMap<>();
+ inputs.put("text", text);
+
+ SearchRecordsRequestQuery query = new SearchRecordsRequestQuery()
+ .inputs(inputs)
+ .topK(topK)
+ .filter(filter);
+
+ SearchRecordsRequest request = new SearchRecordsRequest()
+ .query(query)
+ .fields(fields)
+ .rerank(rerank);
+
+ return vectorOperations.searchRecordsNamespace(namespace, request);
+ }
/**
* {@inheritDoc}
diff --git a/src/main/java/io/pinecone/clients/Pinecone.java b/src/main/java/io/pinecone/clients/Pinecone.java
index 42a5f76..077db05 100644
--- a/src/main/java/io/pinecone/clients/Pinecone.java
+++ b/src/main/java/io/pinecone/clients/Pinecone.java
@@ -215,6 +215,50 @@ public IndexModel createSparseServelessIndex(String indexName,
return indexModel;
}
+ /**
+ * Creates a new serverless index with an associated embedding model.
+ *
+ * Example:
+ *
{@code
+ * client.createIndexForModel("my-index", CreateIndexForModelRequest.CloudEnum.AWS,
+ * "us-west-2", embedConfig, DeletionProtection.DISABLED, tags);
+ * }
+ *
+ * @param name The name of the index to be created. The name must be between 1 and 45 characters,
+ * start and end with an alphanumeric character, and consist only of lowercase alphanumeric
+ * characters or hyphens ('-').
+ * @param cloud The cloud provider where the index will be hosted. Must be one of the supported cloud providers.
+ * @param region The cloud region where the index will be created.
+ * @param embed The embedding model configuration. Once set, the model cannot be changed, but configurations
+ * such as field map and parameters can be updated.
+ * @param deletionProtection Whether deletion protection is enabled for the index. If enabled, the index
+ * cannot be deleted. Defaults to disabled if not provided.
+ * @param tags A map of custom user tags to associate with the index. Keys must be alphanumeric or contain
+ * underscores ('_') or hyphens ('-'). Values must be alphanumeric, or contain characters such
+ * as ';', '@', '_', '-', '.', '+', or spaces.
+ * @return {@link IndexModel} representing the created serverless index with the associated embedding model.
+ * @throws PineconeException if the API encounters an error during index creation, or if any of the arguments
+ * are invalid.
+ * @throws ApiException if an error occurs while communicating with the API.
+ */
+ public IndexModel createIndexForModel(String name,
+ CreateIndexForModelRequest.CloudEnum cloud,
+ String region,
+ CreateIndexForModelRequestEmbed embed,
+ DeletionProtection deletionProtection,
+ Map tags) throws PineconeException, ApiException {
+
+ CreateIndexForModelRequest createIndexForModelRequest = new CreateIndexForModelRequest()
+ .name(name)
+ .cloud(cloud)
+ .region(region)
+ .embed(embed)
+ .deletionProtection(deletionProtection)
+ .tags(tags);
+
+ return manageIndexesApi.createIndexForModel(createIndexForModelRequest);
+ }
+
/**
* Overload for creating a new pods index with environment and podType, the minimum required parameters.
*
@@ -716,8 +760,17 @@ public IndexModel configurePodsIndex(String indexName, DeletionProtection deleti
* import org.openapitools.control.client.model.IndexModel;
* ...
*
+ * HashMap tags = new HashMap<>();
+ * tags.put("env", "test);
+ *
+ * ConfigureIndexRequestEmbed embed = new ConfigureIndexRequestEmbed();
+ * embed.model("multilingual-e5-large");
+ * HashMap fieldMap = new HashMap<>();
+ * fieldMap.put("text", "your-text-field");
+ * embed.fieldMap(fieldMap);
+ *
* // Make a configuration change
- * IndexModel indexModel = client.configureServerlessIndex("YOUR-INDEX", DeletionProtection.ENABLED);
+ * IndexModel indexModel = client.configureServerlessIndex("YOUR-INDEX", DeletionProtection.ENABLED, tags, embed);
*
* // Call describeIndex to see the index status as the change is applied.
* indexModel = client.describeIndex("YOUR-INDEX");
@@ -726,10 +779,16 @@ public IndexModel configurePodsIndex(String indexName, DeletionProtection deleti
* @param indexName The name of the index to configure.
* @param deletionProtection Enable or disable deletion protection for the index.
* @param tags A map of tags to associate with the Index.
+ * @param embed Convert an existing index to an integrated index by specifying the embedding model and field_map.
+ * The index vector type and dimension must match the model vector type and dimension, and the index
+ * similarity metric must be supported by the model
* @return {@link IndexModel} representing the configured index.
* @throws PineconeException if an error occurs during the operation, the index does not exist, or if any of the arguments are invalid.
*/
- public IndexModel configureServerlessIndex(String indexName, DeletionProtection deletionProtection, Map tags) throws PineconeException {
+ public IndexModel configureServerlessIndex(String indexName,
+ DeletionProtection deletionProtection,
+ Map tags,
+ ConfigureIndexRequestEmbed embed) throws PineconeException {
if (indexName == null || indexName.isEmpty()) {
throw new PineconeValidationException("indexName cannot be null or empty");
}
@@ -742,6 +801,10 @@ public IndexModel configureServerlessIndex(String indexName, DeletionProtection
configureIndexRequest.tags(tags);
}
+ if(embed != null) {
+ configureIndexRequest.embed(embed);
+ }
+
IndexModel indexModel = null;
try {
indexModel = manageIndexesApi.configureIndex(indexName, configureIndexRequest);
@@ -949,8 +1012,11 @@ public Index getIndexConnection(String indexName) throws PineconeValidationExcep
throw new PineconeValidationException("Index name cannot be null or empty");
}
- PineconeConnection connection = getConnection(indexName);
- return new Index(connection, indexName);
+ PineconeConfig perConnectionConfig = new PineconeConfig(config.getApiKey(), config.getSourceTag());
+ perConnectionConfig.setHost(getIndexHost(indexName));
+
+ PineconeConnection connection = getConnection(indexName, perConnectionConfig);
+ return new Index(perConnectionConfig, connection, indexName);
}
/**
@@ -978,7 +1044,10 @@ public AsyncIndex getAsyncIndexConnection(String indexName) throws PineconeValid
throw new PineconeValidationException("Index name cannot be null or empty");
}
- PineconeConnection connection = getConnection(indexName);
+ PineconeConfig perConnectionConfig = new PineconeConfig(config.getApiKey(), config.getSourceTag());
+ perConnectionConfig.setHost(getIndexHost(indexName));
+
+ PineconeConnection connection = getConnection(indexName, perConnectionConfig);
return new AsyncIndex(config, connection, indexName);
}
@@ -994,9 +1063,7 @@ public Inference getInferenceClient() {
return new Inference(config);
}
- PineconeConnection getConnection(String indexName) {
- PineconeConfig perConnectionConfig = new PineconeConfig(config.getApiKey(), config.getSourceTag());
- perConnectionConfig.setHost(getIndexHost(indexName));
+ PineconeConnection getConnection(String indexName, PineconeConfig perConnectionConfig) {
return connectionsMap.computeIfAbsent(indexName, key -> new PineconeConnection(perConnectionConfig));
}
diff --git a/src/main/java/org/openapitools/db_data/client/ApiClient.java b/src/main/java/org/openapitools/db_data/client/ApiClient.java
index 67632fb..b4f5b45 100644
--- a/src/main/java/org/openapitools/db_data/client/ApiClient.java
+++ b/src/main/java/org/openapitools/db_data/client/ApiClient.java
@@ -931,6 +931,18 @@ public RequestBody serialize(Object obj, String contentType) throws ApiException
return RequestBody.create((File) obj, MediaType.parse(contentType));
} else if ("text/plain".equals(contentType) && obj instanceof String) {
return RequestBody.create((String) obj, MediaType.parse(contentType));
+ } else if ("application/x-ndjson".equals(contentType)) {
+ // Handle NDJSON (Newline Delimited JSON)
+ if (obj instanceof Iterable) { // If obj is a collection of objects
+ StringBuilder ndjsonContent = new StringBuilder();
+ for (Object item : (Iterable>) obj) {
+ String json = JSON.serialize(item);
+ ndjsonContent.append(json).append("\n");
+ }
+ return RequestBody.create(ndjsonContent.toString(), MediaType.parse(contentType));
+ } else {
+ throw new ApiException("NDJSON content requires a collection of objects.");
+ }
} else if (isJsonMime(contentType)) {
String content;
if (obj != null) {
diff --git a/src/test/java/io/pinecone/ListEndpointValidationTest.java b/src/test/java/io/pinecone/ListEndpointValidationTest.java
index 01a1754..3576b6b 100644
--- a/src/test/java/io/pinecone/ListEndpointValidationTest.java
+++ b/src/test/java/io/pinecone/ListEndpointValidationTest.java
@@ -1,6 +1,7 @@
package io.pinecone;
import io.pinecone.clients.Index;
+import io.pinecone.configs.PineconeConfig;
import io.pinecone.configs.PineconeConnection;
import io.pinecone.exceptions.PineconeValidationException;
import io.pinecone.proto.VectorServiceGrpc;
@@ -18,21 +19,19 @@
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class ListEndpointValidationTest {
- private String indexName;
- private PineconeConnection connectionMock;
- private VectorServiceGrpc.VectorServiceBlockingStub stubMock;
private Index index;
@BeforeAll
public void setUp() {
- indexName = "test-index";
+ String indexName = "test-index";
// Mock sync Pinecone connection
- connectionMock = mock(PineconeConnection.class);
- stubMock = mock(VectorServiceGrpc.VectorServiceBlockingStub.class);
+ PineconeConfig config = mock(PineconeConfig.class);
+ PineconeConnection connectionMock = mock(PineconeConnection.class);
+ VectorServiceGrpc.VectorServiceBlockingStub stubMock = mock(VectorServiceGrpc.VectorServiceBlockingStub.class);
when(connectionMock.getBlockingStub()).thenReturn(stubMock);
- index = new Index(connectionMock, indexName);
+ index = new Index(config, connectionMock, indexName);
}
@Test