diff --git a/README.md b/README.md index 44e74176..d402d9ad 100644 --- a/README.md +++ b/README.md @@ -697,6 +697,36 @@ List values = Arrays.asList(1F, 2F, 3F); UpdateResponse updateResponse = index.update("v1", values, "example-namespace"); ``` +## Create namespace + +The following example shows how to create a namespace. + +```java +import io.pinecone.clients.AsyncIndex; +import io.pinecone.clients.Index; +import io.pinecone.clients.Pinecone; +import io.pinecone.proto.MetadataFieldProperties; +import io.pinecone.proto.MetadataSchema; +import io.pinecone.proto.NamespaceDescription; + +import java.util.concurrent.ExecutionException; +... + +String indexName = "PINECONE_INDEX_NAME"; +Pinecone pinecone = new Pinecone.Builder("PINECONE_API_KEY").build(); +Index index = pinecone.getIndexConnection(indexName); + +// create a namespace +NamespaceDescription namespaceDescription = index.createNamespace("some-namespace"); + +// create a namespace with metadata schema +MetadataSchema schema = MetadataSchema.newBuilder() + .putFields("genre", MetadataFieldProperties.newBuilder().setFilterable(true).build()) + .putFields("year", MetadataFieldProperties.newBuilder().setFilterable(true).build()) + .build(); +NamespaceDescription namespaceWithSchema = index.createNamespace("some-namespace", schema); +``` + ## List namespaces The following example shows various methods to list namespaces. @@ -722,6 +752,19 @@ listNamespacesResponse = index.listNamespaces("some-pagination-token"); // list namespaces with pagination token and a custom limit of 5 listNamespacesResponse = index.listNamespaces("some-pagination-token", 5); + +// The totalCount field returns the total number of namespaces in the index +// When prefix filtering is used, it returns the count of namespaces matching the prefix +int totalCount = listNamespacesResponse.getTotalCount(); + +// list namespaces with prefix filtering +// Prefix filtering allows you to filter namespaces that start with a specific prefix +listNamespacesResponse = index.listNamespaces("test-", null, 10); +totalCount = listNamespacesResponse.getTotalCount(); // Total count of namespaces matching "test-" prefix + +// list namespaces with prefix, pagination token, and limit +listNamespacesResponse = index.listNamespaces("test-", "some-pagination-token", 10); +totalCount = listNamespacesResponse.getTotalCount(); ``` ## Describe namespace diff --git a/src/integration/java/io/pinecone/integration/dataPlane/NamespacesTest.java b/src/integration/java/io/pinecone/integration/dataPlane/NamespacesTest.java index 5ed28256..dfd61de8 100644 --- a/src/integration/java/io/pinecone/integration/dataPlane/NamespacesTest.java +++ b/src/integration/java/io/pinecone/integration/dataPlane/NamespacesTest.java @@ -5,6 +5,7 @@ import io.pinecone.helpers.RandomStringBuilder; import io.pinecone.helpers.TestResourcesManager; import io.pinecone.proto.ListNamespacesResponse; +import io.pinecone.proto.NamespaceDescription; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Test; @@ -12,6 +13,8 @@ import static io.pinecone.helpers.BuildUpsertRequest.generateVectorValuesByDimension; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; public class NamespacesTest { private static final TestResourcesManager indexManager = TestResourcesManager.getInstance(); @@ -27,12 +30,19 @@ public static void cleanUp() { @Test public void namespacesSyncTest() throws InterruptedException { - String[] namespaces = new String[3]; + String[] namespaces = new String[4]; index = indexManager.getOrCreateServerlessIndexConnection(); ListNamespacesResponse listNamespacesResponse = index.listNamespaces(); int namespaceCount = listNamespacesResponse.getNamespacesCount(); - for(int i=0; i<3; i++) { + // Create namespace explicitly using createNamespace + namespaces[0] = RandomStringBuilder.build("namespace-", 3); + NamespaceDescription createdNamespace = index.createNamespace(namespaces[0]); + assertNotNull(createdNamespace); + assertEquals(namespaces[0], createdNamespace.getName()); + + // Create namespaces implicitly by upserting vectors + for (int i=1; i<4; i++) { namespaces[i] = RandomStringBuilder.build("namespace-", 3); index.upsert("v"+i, generateVectorValuesByDimension(dimension), namespaces[i]); } @@ -40,7 +50,7 @@ public void namespacesSyncTest() throws InterruptedException { // wait for vectors to be upserted Thread.sleep(5000); listNamespacesResponse = index.listNamespaces(); - assertEquals(listNamespacesResponse.getNamespacesCount(), namespaceCount + 3); + assertEquals(listNamespacesResponse.getNamespacesCount(), namespaceCount + 4); index.describeNamespace(namespaces[0]); index.deleteNamespace(namespaces[0]); @@ -48,18 +58,43 @@ public void namespacesSyncTest() throws InterruptedException { // wait for namespace to be deleted Thread.sleep(3000); listNamespacesResponse = index.listNamespaces(); - assertEquals(listNamespacesResponse.getNamespacesCount(), namespaceCount + 2); + assertEquals(listNamespacesResponse.getNamespacesCount(), namespaceCount + 3); + + // Test prefix filtering and total count + String prefix = "namespace-"; + ListNamespacesResponse prefixResponse = index.listNamespaces(prefix, null, 100); + assertNotNull(prefixResponse); + assertTrue(prefixResponse.getTotalCount() >= 3, "totalCount should be at least 3"); + assertTrue(prefixResponse.getNamespacesCount() >= 3, "Should return at least 3 namespaces with prefix"); + + // Verify all returned namespaces start with the prefix + for (int i = 0; i < prefixResponse.getNamespacesCount(); i++) { + String namespaceName = prefixResponse.getNamespaces(i).getName(); + assertTrue(namespaceName.startsWith(prefix), + "Namespace " + namespaceName + " should start with prefix " + prefix); + } + + // Verify totalCount is at least the number of namespaces returned + assertTrue(prefixResponse.getTotalCount() >= prefixResponse.getNamespacesCount(), + "totalCount should be at least equal to the number of namespaces returned"); } @Test public void namespacesAsyncTest() throws InterruptedException, ExecutionException { - String[] namespaces = new String[3]; + String[] namespaces = new String[4]; asyncIndex = indexManager.getOrCreateServerlessAsyncIndexConnection(); ListNamespacesResponse listNamespacesResponse = asyncIndex.listNamespaces().get(); int namespaceCount = listNamespacesResponse.getNamespacesCount(); - for(int i=0; i<3; i++) { + // Create namespace explicitly using createNamespace + namespaces[0] = RandomStringBuilder.build("namespace-", 3); + NamespaceDescription createdNamespace = asyncIndex.createNamespace(namespaces[0]).get(); + assertNotNull(createdNamespace); + assertEquals(namespaces[0], createdNamespace.getName()); + + // Create namespaces implicitly by upserting vectors + for (int i=1; i<4; i++) { namespaces[i] = RandomStringBuilder.build("namespace-", 3); asyncIndex.upsert("v"+i, generateVectorValuesByDimension(dimension), namespaces[i]); } @@ -67,7 +102,7 @@ public void namespacesAsyncTest() throws InterruptedException, ExecutionExceptio // wait for vectors to be upserted Thread.sleep(5000); listNamespacesResponse = asyncIndex.listNamespaces().get(); - assertEquals(listNamespacesResponse.getNamespacesCount(), namespaceCount + 3); + assertEquals(listNamespacesResponse.getNamespacesCount(), namespaceCount + 4); asyncIndex.describeNamespace(namespaces[0]); asyncIndex.deleteNamespace(namespaces[0]); @@ -75,6 +110,24 @@ public void namespacesAsyncTest() throws InterruptedException, ExecutionExceptio // wait for namespace to be deleted Thread.sleep(3000); listNamespacesResponse = asyncIndex.listNamespaces().get(); - assertEquals(listNamespacesResponse.getNamespacesCount(), namespaceCount + 2); + assertEquals(listNamespacesResponse.getNamespacesCount(), namespaceCount + 3); + + // Test prefix filtering and total count + String prefix = "namespace-"; + ListNamespacesResponse prefixResponse = asyncIndex.listNamespaces(prefix, null, 100).get(); + assertNotNull(prefixResponse); + assertTrue(prefixResponse.getTotalCount() >= 3, "totalCount should be at least 3"); + assertTrue(prefixResponse.getNamespacesCount() >= 3, "Should return at least 3 namespaces with prefix"); + + // Verify all returned namespaces start with the prefix + for (int i = 0; i < prefixResponse.getNamespacesCount(); i++) { + String namespaceName = prefixResponse.getNamespaces(i).getName(); + assertTrue(namespaceName.startsWith(prefix), + "Namespace " + namespaceName + " should start with prefix " + prefix); + } + + // Verify totalCount is at least the number of namespaces returned + assertTrue(prefixResponse.getTotalCount() >= prefixResponse.getNamespacesCount(), + "totalCount should be at least equal to the number of namespaces returned"); } -} +} \ No newline at end of file diff --git a/src/main/java/io/pinecone/clients/AsyncIndex.java b/src/main/java/io/pinecone/clients/AsyncIndex.java index 3b221a70..688deb43 100644 --- a/src/main/java/io/pinecone/clients/AsyncIndex.java +++ b/src/main/java/io/pinecone/clients/AsyncIndex.java @@ -9,11 +9,13 @@ import io.pinecone.configs.PineconeConnection; import io.pinecone.exceptions.PineconeValidationException; import io.pinecone.proto.*; +import io.pinecone.proto.CreateNamespaceRequest; import io.pinecone.proto.DeleteRequest; import io.pinecone.proto.DescribeIndexStatsRequest; import io.pinecone.proto.FetchResponse; import io.pinecone.proto.ListNamespacesResponse; import io.pinecone.proto.ListResponse; +import io.pinecone.proto.MetadataSchema; import io.pinecone.proto.NamespaceDescription; import io.pinecone.proto.QueryRequest; import io.pinecone.proto.QueryResponse; @@ -1124,6 +1126,66 @@ public ListenableFuture listNamespaces(String pagination return asyncStub.listNamespaces(listNamespacesRequest.build()); } + /** + *
+     * Get list of all namespaces within an index with optional prefix filtering, pagination token, and limit.
+     * @param prefix The prefix to filter namespaces by. Only namespaces starting with this prefix will be returned.
+     *               If null, no prefix filtering is applied.
+     * @param paginationToken The token to paginate through the list of namespaces. If null, it'll be ignored.
+     * @param limit The maximum number of namespaces you want to retrieve.
+     * @return {@link ListenableFuture} The response for the list namespace operation. The totalCount field
+     *         indicates the total number of namespaces matching the prefix (if provided).
+     * 
+ */ + @Override + public ListenableFuture listNamespaces(String prefix, String paginationToken, int limit) { + ListNamespacesRequest.Builder listNamespacesRequest = ListNamespacesRequest + .newBuilder() + .setLimit(limit); + if(prefix != null && !prefix.isEmpty()) { + listNamespacesRequest.setPrefix(prefix); + } + if(paginationToken != null) { + listNamespacesRequest.setPaginationToken(paginationToken); + } + return asyncStub.listNamespaces(listNamespacesRequest.build()); + } + + /** + *
+     * Create a namespace within an index.
+     * @param name The name of the namespace to create.
+     * @return {@link ListenableFuture} The response for the create namespace operation.
+     * 
+ */ + @Override + public ListenableFuture createNamespace(String name) { + CreateNamespaceRequest createNamespaceRequest = CreateNamespaceRequest + .newBuilder() + .setName(name) + .build(); + return asyncStub.createNamespace(createNamespaceRequest); + } + + /** + *
+     * Create a namespace within an index with a metadata schema.
+     * @param name The name of the namespace to create.
+     * @param schema The metadata schema for the namespace.
+     * @return {@link ListenableFuture} The response for the create namespace operation.
+     * 
+ */ + @Override + public ListenableFuture createNamespace(String name, MetadataSchema schema) { + CreateNamespaceRequest.Builder builder = CreateNamespaceRequest + .newBuilder() + .setName(name); + if (schema != null) { + builder.setSchema(schema); + } + return asyncStub.createNamespace(builder.build()); + } + /** *
      * Describe a namespace within an index, showing the vector count within the namespace.
diff --git a/src/main/java/io/pinecone/clients/Index.java b/src/main/java/io/pinecone/clients/Index.java
index c62ab93f..48c5adb1 100644
--- a/src/main/java/io/pinecone/clients/Index.java
+++ b/src/main/java/io/pinecone/clients/Index.java
@@ -6,11 +6,13 @@
 import io.pinecone.configs.PineconeConnection;
 import io.pinecone.exceptions.PineconeValidationException;
 import io.pinecone.proto.*;
+import io.pinecone.proto.CreateNamespaceRequest;
 import io.pinecone.proto.DeleteRequest;
 import io.pinecone.proto.DescribeIndexStatsRequest;
 import io.pinecone.proto.FetchResponse;
 import io.pinecone.proto.ListNamespacesResponse;
 import io.pinecone.proto.ListResponse;
+import io.pinecone.proto.MetadataSchema;
 import io.pinecone.proto.NamespaceDescription;
 import io.pinecone.proto.QueryRequest;
 import io.pinecone.proto.UpdateRequest;
@@ -1034,6 +1036,66 @@ public ListNamespacesResponse listNamespaces(String paginationToken, int limit)
         return blockingStub.listNamespaces(listNamespacesRequest.build());
     }
 
+    /**
+     * 
+     * Get list of all namespaces within an index with optional prefix filtering, pagination token, and limit.
+     * @param prefix The prefix to filter namespaces by. Only namespaces starting with this prefix will be returned.
+     *               If null, no prefix filtering is applied.
+     * @param paginationToken The token to paginate through the list of namespaces. If null, it'll be ignored.
+     * @param limit The maximum number of namespaces you want to retrieve.
+     * @return {@link ListNamespacesResponse} The response for the list namespace operation. The totalCount field
+     *         indicates the total number of namespaces matching the prefix (if provided).
+     * 
+ */ + @Override + public ListNamespacesResponse listNamespaces(String prefix, String paginationToken, int limit) { + ListNamespacesRequest.Builder listNamespacesRequest = ListNamespacesRequest + .newBuilder() + .setLimit(limit); + if(prefix != null && !prefix.isEmpty()) { + listNamespacesRequest.setPrefix(prefix); + } + if(paginationToken != null) { + listNamespacesRequest.setPaginationToken(paginationToken); + } + return blockingStub.listNamespaces(listNamespacesRequest.build()); + } + + /** + *
+     * Create a namespace within an index.
+     * @param name The name of the namespace to create.
+     * @return {@link NamespaceDescription} The response for the create namespace operation.
+     * 
+ */ + @Override + public NamespaceDescription createNamespace(String name) { + CreateNamespaceRequest createNamespaceRequest = CreateNamespaceRequest + .newBuilder() + .setName(name) + .build(); + return blockingStub.createNamespace(createNamespaceRequest); + } + + /** + *
+     * Create a namespace within an index with a metadata schema.
+     * @param name The name of the namespace to create.
+     * @param schema The metadata schema for the namespace.
+     * @return {@link NamespaceDescription} The response for the create namespace operation.
+     * 
+ */ + @Override + public NamespaceDescription createNamespace(String name, MetadataSchema schema) { + CreateNamespaceRequest.Builder builder = CreateNamespaceRequest + .newBuilder() + .setName(name); + if (schema != null) { + builder.setSchema(schema); + } + return blockingStub.createNamespace(builder.build()); + } + /** *
      * Describe a namespace within an index, showing the vector count within the namespace.
diff --git a/src/main/java/io/pinecone/commons/IndexInterface.java b/src/main/java/io/pinecone/commons/IndexInterface.java
index e7e1eb78..ea8e9acc 100644
--- a/src/main/java/io/pinecone/commons/IndexInterface.java
+++ b/src/main/java/io/pinecone/commons/IndexInterface.java
@@ -854,6 +854,38 @@ default void validateListEndpointParameters(String namespace, String prefix, Str
      */
     A listNamespaces(String paginationToken, int limit);
 
+    /**
+     * 
+     * Get list of all namespaces within an index with optional prefix filtering, pagination token, and limit.
+     * @param prefix The prefix to filter namespaces by. Only namespaces starting with this prefix will be returned.
+     *               If null, no prefix filtering is applied.
+     * @param paginationToken The token to paginate through the list of namespaces. If null, it'll be ignored.
+     * @param limit The maximum number of namespaces you want to retrieve.
+     * @return {@link ListNamespacesResponse} The response for the list namespace operation. The totalCount field
+     *         indicates the total number of namespaces matching the prefix (if provided).
+     * 
+ */ + A listNamespaces(String prefix, String paginationToken, int limit); + + /** + *
+     * Create a namespace within an index.
+     * @param name The name of the namespace to create.
+     * @return {@link NamespaceDescription} The response for the create namespace operation.
+     * 
+ */ + B createNamespace(String name); + + /** + *
+     * Create a namespace within an index with a metadata schema.
+     * @param name The name of the namespace to create.
+     * @param schema The metadata schema for the namespace.
+     * @return {@link NamespaceDescription} The response for the create namespace operation.
+     * 
+ */ + B createNamespace(String name, io.pinecone.proto.MetadataSchema schema); + /** *
      * Describe a namespace within an index, showing the vector count within the namespace.