diff --git a/.vscode/java-format-settings.xml b/.vscode/java-format-settings.xml new file mode 100644 index 00000000..127f078d --- /dev/null +++ b/.vscode/java-format-settings.xml @@ -0,0 +1,378 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index e485a97c..e8af6620 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,7 +9,11 @@ "other": "on", "comments": "inline", "strings": "inline" - } + }, + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[mdx]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" }, "files.associations": { "*.mdx": "markdown" @@ -108,4 +112,5 @@ "editor.insertSpaces": true }, "java.configuration.updateBuildConfiguration": "interactive", + "java.compile.nullAnalysis.mode": "automatic", } diff --git a/_includes/code/client-libraries/get_started.py b/_includes/code/client-libraries/get_started.py index 0bb7d662..d6b5c909 100644 --- a/_includes/code/client-libraries/get_started.py +++ b/_includes/code/client-libraries/get_started.py @@ -7,10 +7,16 @@ client = weaviate.connect_to_local() # highlight-end +# END GetStarted +client.collections.delete("Question") # Clean up from previous runs +# START GetStarted # highlight-start questions = client.collections.create( name="Question", - vector_config=Configure.Vectors.text2vec_ollama(), # Configure the Ollama embedding model + vector_config=Configure.Vectors.text2vec_ollama( + api_endpoint="http://ollama:11434", # If using Docker you might need: http://host.docker.internal:11434 + model="nomic-embed-text", # The model to use + ), # Configure the Ollama embedding model ) # highlight-end @@ -38,6 +44,10 @@ if failed_objects: print(f"Number of failed imports: {len(failed_objects)}") print(f"First failed object: {failed_objects[0]}") +# END GetStarted +import time +time.sleep(2) # Wait for the batch to be indexed +# START GetStarted # highlight-start response = questions.query.near_text(query="biology", limit=2) diff --git a/_includes/code/connections/timeouts-cloud.mdx b/_includes/code/connections/timeouts-cloud.mdx index 1d9a16d2..f46421ab 100644 --- a/_includes/code/connections/timeouts-cloud.mdx +++ b/_includes/code/connections/timeouts-cloud.mdx @@ -1,25 +1,43 @@ -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; -import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBlock'; +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; +import FilteredTextBlock from "@site/src/components/Documentation/FilteredTextBlock"; -import PyV4Code from '!!raw-loader!/_includes/code/connections/connect-python-v4.py'; -import TsV3Code from '!!raw-loader!/_includes/code/connections/connect-ts-v3.ts'; +import PyV4Code from "!!raw-loader!/_includes/code/connections/connect-python-v4.py"; +import TsV3Code from "!!raw-loader!/_includes/code/connections/connect-ts-v3.ts"; +import JavaV6Code from "!!raw-loader!/_includes/code/java-v6/src/test/java/ConnectionTest.java"; +import CSharpCode from "!!raw-loader!/_includes/code/csharp/ConnectionTest.cs"; - - - - - - + + + + + + + + + + + + diff --git a/_includes/code/connections/timeouts-custom.mdx b/_includes/code/connections/timeouts-custom.mdx index 07445281..c1686865 100644 --- a/_includes/code/connections/timeouts-custom.mdx +++ b/_includes/code/connections/timeouts-custom.mdx @@ -1,25 +1,43 @@ -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; -import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBlock'; +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; +import FilteredTextBlock from "@site/src/components/Documentation/FilteredTextBlock"; -import PyV4Code from '!!raw-loader!/_includes/code/connections/connect-python-v4.py'; -import TsV3Code from '!!raw-loader!/_includes/code/connections/connect-ts-v3.ts'; +import PyV4Code from "!!raw-loader!/_includes/code/connections/connect-python-v4.py"; +import TsV3Code from "!!raw-loader!/_includes/code/connections/connect-ts-v3.ts"; +import JavaV6Code from "!!raw-loader!/_includes/code/java-v6/src/test/java/ConnectionTest.java"; +import CSharpCode from "!!raw-loader!/_includes/code/csharp/ConnectionTest.cs"; - - - - - - + + + + + + + + + + + + diff --git a/_includes/code/connections/timeouts-local.mdx b/_includes/code/connections/timeouts-local.mdx index 44336424..3fdf8226 100644 --- a/_includes/code/connections/timeouts-local.mdx +++ b/_includes/code/connections/timeouts-local.mdx @@ -1,26 +1,43 @@ -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; -import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBlock'; - -import PyV4Code from '!!raw-loader!/_includes/code/connections/connect-python-v4.py'; -import TsV3Code from '!!raw-loader!/_includes/code/connections/connect-ts-v3.ts'; +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; +import FilteredTextBlock from "@site/src/components/Documentation/FilteredTextBlock"; +import PyV4Code from "!!raw-loader!/_includes/code/connections/connect-python-v4.py"; +import TsV3Code from "!!raw-loader!/_includes/code/connections/connect-ts-v3.ts"; +import JavaV6Code from "!!raw-loader!/_includes/code/java-v6/src/test/java/ConnectionTest.java"; +import CSharpCode from "!!raw-loader!/_includes/code/csharp/ConnectionTest.cs"; - - - - - - + + + + + + + + + + + + diff --git a/_includes/code/csharp/ConfigureBQTest.cs b/_includes/code/csharp/ConfigureBQTest.cs new file mode 100644 index 00000000..ccb33963 --- /dev/null +++ b/_includes/code/csharp/ConfigureBQTest.cs @@ -0,0 +1,106 @@ +using Xunit; +using Weaviate.Client; +using Weaviate.Client.Models; +using System; +using System.Threading.Tasks; + +namespace WeaviateProject.Tests; + +public class ConfigureBQTest : IAsyncLifetime +{ + private WeaviateClient client; + private const string COLLECTION_NAME = "MyCollection"; + + // Runs before each test + public async Task InitializeAsync() + { + // START ConnectCode + // Note: The C# client doesn't support setting headers like 'X-OpenAI-Api-Key' via the constructor for local connections. + // This must be configured in Weaviate's environment variables. + client = new WeaviateClient(new ClientConfiguration { RestAddress = "localhost", RestPort = 8080 }); + // END ConnectCode + + // Clean slate for each test + if (await client.Collections.Exists(COLLECTION_NAME)) + { + await client.Collections.Delete(COLLECTION_NAME); + } + } + + // Runs after each test + public Task DisposeAsync() + { + // No action needed here, as cleanup happens in InitializeAsync before the next test. + return Task.CompletedTask; + } + + [Fact] + public async Task TestEnableBQ() + { + // START EnableBQ + await client.Collections.Create(new Collection + { + Name = "MyCollection", + Properties = [Property.Text("title")], + VectorConfig = new VectorConfig( + "default", + new Vectorizer.Text2VecContextionary(), + new VectorIndex.HNSW + { + // highlight-start + Quantizer = new VectorIndex.Quantizers.BQ() + // highlight-end + } + ) + }); + // END EnableBQ + } + + [Fact] + public async Task TestUpdateSchema() + { + // Note: Updating quantization settings on an existing collection is not supported by Weaviate + // and will result in an error, as noted in the Java test. This test demonstrates the syntax for attempting the update. + var collection = await client.Collections.Create(new Collection + { + Name = "MyCollection", + Properties = [Property.Text("title")], + VectorConfig = new VectorConfig("default", new Vectorizer.Text2VecContextionary()) + }); + + // START UpdateSchema + await collection.Config.Update(c => + { + var vectorConfig = c.VectorConfig["default"]; + vectorConfig.VectorIndexConfig.UpdateHNSW(h => h.Quantizer = new VectorIndex.Quantizers.BQ()); + }); + // END UpdateSchema + } + + [Fact] + public async Task TestBQWithOptions() + { + // START BQWithOptions + await client.Collections.Create(new Collection + { + Name = "MyCollection", + Properties = [Property.Text("title")], + VectorConfig = new VectorConfig( + "default", + new Vectorizer.Text2VecContextionary(), + // highlight-start + new VectorIndex.HNSW + { + VectorCacheMaxObjects = 100000, + Quantizer = new VectorIndex.Quantizers.BQ + { + Cache = true, + RescoreLimit = 200 + } + } + // highlight-end + ) + }); + // END BQWithOptions + } +} \ No newline at end of file diff --git a/_includes/code/csharp/ConfigurePQTest.cs b/_includes/code/csharp/ConfigurePQTest.cs new file mode 100644 index 00000000..b85f234d --- /dev/null +++ b/_includes/code/csharp/ConfigurePQTest.cs @@ -0,0 +1,218 @@ +using Xunit; +using Weaviate.Client; +using Weaviate.Client.Models; +using System; +using System.Threading.Tasks; +using System.Collections.Generic; +using System.Text.Json; +using System.Net.Http; +using System.Linq; +using System.Text.Json.Serialization; + +namespace WeaviateProject.Tests; + +public class ConfigurePQTest : IAsyncLifetime +{ + private static WeaviateClient client; + private static List data; + private const string COLLECTION_NAME = "Question"; + + // Runs once before any tests in the class + public async Task InitializeAsync() + { + // START DownloadData + using var httpClient = new HttpClient(); + var responseBody = await httpClient.GetStringAsync( + "https://raw.githubusercontent.com/weaviate-tutorials/intro-workshop/main/data/jeopardy_1k.json"); + + data = JsonSerializer.Deserialize>(responseBody); + + Console.WriteLine($"Data type: {data.GetType().Name}, Length: {data.Count}"); + Console.WriteLine(JsonSerializer.Serialize(data[1], new JsonSerializerOptions { WriteIndented = true })); + // END DownloadData + + // START ConnectCode + // Note: The C# client doesn't support setting headers like 'X-OpenAI-Api-Key' via the constructor for local connections. + // This must be configured in Weaviate's environment variables. + client = new WeaviateClient(new ClientConfiguration { RestAddress = "localhost", RestPort = 8080 }); + + var meta = await client.GetMeta(); + Console.WriteLine($"Weaviate info: {meta.Version}"); + // END ConnectCode + } + + // Runs once after all tests in the class + public async Task DisposeAsync() + { + if (await client.Collections.Exists(COLLECTION_NAME)) + { + await client.Collections.Delete(COLLECTION_NAME); + } + } + + // This helper runs before each test to ensure a clean collection state + private async Task BeforeEach() + { + if (await client.Collections.Exists(COLLECTION_NAME)) + { + await client.Collections.Delete(COLLECTION_NAME); + } + } + + // TODO[g-despot] Why is Encoder required? + // TODO[g-despot] Why are properties required? ERROR: didn't find a single property which is of type string or text and is not excluded from indexing + [Fact] + public async Task TestCollectionWithAutoPQ() + { + await BeforeEach(); + + // START CollectionWithAutoPQ + await client.Collections.Create(new Collection + { + Name = "Question", + VectorConfig = new VectorConfig( + "default", + new Vectorizer.Text2VecContextionary(), + new VectorIndex.HNSW + { + // highlight-start + Quantizer = new VectorIndex.Quantizers.PQ + { + TrainingLimit = 50000, // Set the threshold to begin training + Encoder = new VectorIndex.Quantizers.PQ.EncoderConfig + { + Type = VectorIndex.Quantizers.EncoderType.Tile, + Distribution = VectorIndex.Quantizers.DistributionType.Normal, + }, + } + // highlight-end + } + ), + Properties = + [ + Property.Text("question"), + Property.Text("answer"), + Property.Text("category") + ] + }); + // END CollectionWithAutoPQ + + // Confirm that the collection has been created with the right settings + var collection = client.Collections.Use(COLLECTION_NAME); + var config = await collection.Get(); + Assert.NotNull(config); + var hnswConfig = config.VectorConfig["default"].VectorIndexConfig as VectorIndex.HNSW; + Assert.NotNull(hnswConfig?.Quantizer); + } + + [Fact] + public async Task TestUpdateSchemaWithPQ() + { + await BeforeEach(); + + // START InitialSchema + await client.Collections.Create(new Collection + { + Name = "Question", + Description = "A Jeopardy! question", + VectorConfig = new VectorConfig("default", new Vectorizer.Text2VecContextionary()), + Properties = + [ + Property.Text("question"), + Property.Text("answer"), + Property.Text("category") + ] + }); + // END InitialSchema + + var collection = client.Collections.Use(COLLECTION_NAME); + var initialConfig = await collection.Get(); + Assert.NotNull(initialConfig); + + // START LoadData + var objectList = data.Select(obj => new + { + question = obj.GetProperty("Question").GetString(), + answer = obj.GetProperty("Answer").GetString() + }).ToArray(); + + await collection.Data.InsertMany(objectList); + // END LoadData + + var aggregateResponse = await collection.Aggregate.OverAll(totalCount: true); + Assert.Equal(1000, aggregateResponse.TotalCount); + + // START UpdateSchema + await collection.Config.Update(c => + { + var vectorConfig = c.VectorConfig["default"]; + vectorConfig.VectorIndexConfig.UpdateHNSW(h => + h.Quantizer = new VectorIndex.Quantizers.PQ + { + TrainingLimit = 50000, + Encoder = new VectorIndex.Quantizers.PQ.EncoderConfig + { + Type = VectorIndex.Quantizers.EncoderType.Tile, + Distribution = VectorIndex.Quantizers.DistributionType.Normal, + }, + }); + }); + // END UpdateSchema + + var updatedConfig = await collection.Get(); + Assert.NotNull(updatedConfig); + var hnswConfig = updatedConfig.VectorConfig["default"].VectorIndexConfig as VectorIndex.HNSW; + Assert.IsType(hnswConfig?.Quantizer); + } + + [Fact] + public async Task TestGetSchema() + { + await BeforeEach(); + + // Create a collection with PQ enabled to inspect its schema + await client.Collections.Create(new Collection + { + Name = "Question", + VectorConfig = new VectorConfig( + "default", + new Vectorizer.Text2VecContextionary(), + new VectorIndex.HNSW + { + Quantizer = new VectorIndex.Quantizers.PQ + { + Encoder = new VectorIndex.Quantizers.PQ.EncoderConfig + { + Type = VectorIndex.Quantizers.EncoderType.Tile, + Distribution = VectorIndex.Quantizers.DistributionType.Normal, + }, + TrainingLimit = 50000 + } + } + ), + Properties = + [ + Property.Text("question"), + Property.Text("answer"), + Property.Text("category") + ] + }); + + // START GetSchema + var jeopardy = client.Collections.Use("Question"); + var config = await jeopardy.Get(); + + Console.WriteLine(JsonSerializer.Serialize(config, new JsonSerializerOptions { WriteIndented = true })); + // END GetSchema + Assert.NotNull(config); + + var hnswConfig = config.VectorConfig["default"].VectorIndexConfig as VectorIndex.HNSW; + var pqConfig = hnswConfig?.Quantizer as VectorIndex.Quantizers.PQ; + Assert.NotNull(pqConfig); + + // Print some of the config properties + Console.WriteLine($"Training: {pqConfig.TrainingLimit}"); + Console.WriteLine($"Segments: {pqConfig.Segments}"); + Console.WriteLine($"Centroids: {pqConfig.Centroids}"); + } +} \ No newline at end of file diff --git a/_includes/code/csharp/ConfigureRQTest.cs b/_includes/code/csharp/ConfigureRQTest.cs new file mode 100644 index 00000000..f2bebf56 --- /dev/null +++ b/_includes/code/csharp/ConfigureRQTest.cs @@ -0,0 +1,166 @@ +// using Xunit; +// using Weaviate.Client; +// using Weaviate.Client.Models; +// using System; +// using System.Threading.Tasks; + +// namespace WeaviateProject.Tests; + +// public class ConfigureRQTest : IAsyncLifetime +// { +// private WeaviateClient client; +// private const string COLLECTION_NAME = "MyCollection"; + +// // Runs before each test +// public async Task InitializeAsync() +// { +// // START ConnectCode +// // Note: The C# client doesn't support setting headers like 'X-OpenAI-Api-Key' via the constructor for local connections. +// // This must be configured in Weaviate's environment variables. +// client = new WeaviateClient(new ClientConfiguration { RestAddress = "localhost", RestPort = 8080 }); +// // END ConnectCode + +// // Clean slate for each test +// if (await client.Collections.Exists(COLLECTION_NAME)) +// { +// await client.Collections.Delete(COLLECTION_NAME); +// } +// } + +// // Runs after each test +// public Task DisposeAsync() +// { +// // No action needed here, as cleanup happens in InitializeAsync before the next test. +// return Task.CompletedTask; +// } + +// [Fact] +// public async Task TestEnableRQ() +// { +// // START EnableRQ +// await client.Collections.Create(new Collection +// { +// Name = "MyCollection", +// Properties = [Property.Text("title")], +// VectorConfig = new VectorConfig( +// "default", +// new Vectorizer.Text2VecContextionary(), +// new VectorIndex.HNSW +// { +// // highlight-start +// Quantizer = new VectorIndex.Quantizers.RQ() +// // highlight-end +// } +// ) +// }); +// // END EnableRQ +// } + +// [Fact] +// public async Task Test1BitEnableRQ() +// { +// // START 1BitEnableRQ +// await client.Collections.Create(new Collection +// { +// Name = "MyCollection", +// Properties = [Property.Text("title")], +// VectorConfig = new VectorConfig( +// "default", +// new Vectorizer.Text2VecContextionary(), +// new VectorIndex.HNSW +// { +// // highlight-start +// Quantizer = new VectorIndex.Quantizers.RQ { Bits = 1 } +// // highlight-end +// } +// ) +// }); +// // END 1BitEnableRQ +// } + +// [Fact] +// public async Task TestUncompressed() +// { +// // START Uncompressed +// await client.Collections.Create(new Collection +// { +// Name = "MyCollection", +// Properties = [Property.Text("title")], +// VectorConfig = new VectorConfig( +// "default", +// new Vectorizer.Text2VecContextionary(), +// // highlight-start +// // Omitting the Quantizer property results in an uncompressed index. +// new VectorIndex.HNSW() +// // highlight-end +// ) +// }); +// // END Uncompressed +// } + +// [Fact] +// public async Task TestRQWithOptions() +// { +// // START RQWithOptions +// await client.Collections.Create(new Collection +// { +// Name = "MyCollection", +// Properties = [Property.Text("title")], +// VectorConfig = new VectorConfig( +// "default", +// new Vectorizer.Text2VecContextionary(), +// new VectorIndex.HNSW +// { +// // highlight-start +// Quantizer = new VectorIndex.Quantizers.RQ +// { +// Bits = 8, // Optional: Number of bits +// RescoreLimit = 20 // Optional: Number of candidates to fetch before rescoring +// } +// // highlight-end +// } +// ) +// }); +// // END RQWithOptions +// } + +// [Fact] +// public async Task TestUpdateSchema() +// { +// // Note: Updating quantization settings on an existing collection is not supported by Weaviate +// // and will result in an error, as noted in the Java test. This test demonstrates the syntax for attempting the update. +// var collection = await client.Collections.Create(new Collection +// { +// Name = "MyCollection", +// Properties = [Property.Text("title")], +// VectorConfig = new VectorConfig("default", new Vectorizer.Text2VecContextionary()) +// }); + +// // START UpdateSchema +// await collection.Config.Update(c => +// { +// var vectorConfig = c.VectorConfig["default"]; +// vectorConfig.VectorIndexConfig.UpdateHNSW(h => h.Quantizer = new VectorIndex.Quantizers.RQ()); +// }); +// // END UpdateSchema +// } + +// [Fact] +// public async Task Test1BitUpdateSchema() +// { +// var collection = await client.Collections.Create(new Collection +// { +// Name = "MyCollection", +// Properties = [Property.Text("title")], +// VectorConfig = new VectorConfig("default", new Vectorizer.Text2VecContextionary()) +// }); + +// // START 1BitUpdateSchema +// await collection.Config.Update(c => +// { +// var vectorConfig = c.VectorConfig["default"]; +// vectorConfig.VectorIndexConfig.UpdateHNSW(h => h.Quantizer = new VectorIndex.Quantizers.RQ { Bits = 1 }); +// }); +// // END 1BitUpdateSchema +// } +// } \ No newline at end of file diff --git a/_includes/code/csharp/ConfigureSQTest.cs b/_includes/code/csharp/ConfigureSQTest.cs new file mode 100644 index 00000000..9ee88c52 --- /dev/null +++ b/_includes/code/csharp/ConfigureSQTest.cs @@ -0,0 +1,108 @@ +using Xunit; +using Weaviate.Client; +using Weaviate.Client.Models; +using System; +using System.Threading.Tasks; + +namespace WeaviateProject.Tests; + +public class ConfigureSQTest : IAsyncLifetime +{ + private WeaviateClient client; + private const string COLLECTION_NAME = "MyCollection"; + + // Runs before each test + public async Task InitializeAsync() + { + // START ConnectCode + // Note: The C# client doesn't support setting headers like 'X-OpenAI-Api-Key' via the constructor for local connections. + // This must be configured in Weaviate's environment variables. + client = new WeaviateClient(new ClientConfiguration { RestAddress = "localhost", RestPort = 8080 }); + // END ConnectCode + + // Clean slate for each test + if (await client.Collections.Exists(COLLECTION_NAME)) + { + await client.Collections.Delete(COLLECTION_NAME); + } + } + + // Runs after each test + public Task DisposeAsync() + { + // No action needed here, as cleanup happens in InitializeAsync before the next test. + return Task.CompletedTask; + } + + [Fact] + public async Task TestEnableSQ() + { + // START EnableSQ + await client.Collections.Create(new Collection + { + Name = "MyCollection", + Properties = [Property.Text("title")], + VectorConfig = new VectorConfig( + "default", + new Vectorizer.Text2VecContextionary(), + new VectorIndex.HNSW + { + // highlight-start + Quantizer = new VectorIndex.Quantizers.SQ() + // highlight-end + } + ) + }); + // END EnableSQ + } + + [Fact] + public async Task TestUpdateSchema() + { + // Note: Updating quantization settings on an existing collection is not supported by Weaviate + // and will result in an error, as noted in the Java test. This test demonstrates the syntax for attempting the update. + var collection = await client.Collections.Create(new Collection + { + Name = "MyCollection", + Properties = [Property.Text("title")], + VectorConfig = new VectorConfig("default", new Vectorizer.Text2VecContextionary()) + }); + + // START UpdateSchema + await collection.Config.Update(c => + { + var vectorConfig = c.VectorConfig["default"]; + vectorConfig.VectorIndexConfig.UpdateHNSW(h => h.Quantizer = new VectorIndex.Quantizers.SQ()); + }); + // END UpdateSchema + } + + // TODO[g-despot] Missing cache + [Fact] + public async Task TestSQWithOptions() + { + // START SQWithOptions + await client.Collections.Create(new Collection + { + Name = "MyCollection", + Properties = [Property.Text("title")], + VectorConfig = new VectorConfig( + "default", + new Vectorizer.Text2VecContextionary(), + // highlight-start + new VectorIndex.HNSW + { + VectorCacheMaxObjects = 100000, + Quantizer = new VectorIndex.Quantizers.SQ + { + //Cache = true, + TrainingLimit = 50000, + RescoreLimit = 200 + } + } + // highlight-end + ) + }); + // END SQWithOptions + } +} \ No newline at end of file diff --git a/_includes/code/csharp/ConnectionTest.cs b/_includes/code/csharp/ConnectionTest.cs new file mode 100644 index 00000000..691b52d9 --- /dev/null +++ b/_includes/code/csharp/ConnectionTest.cs @@ -0,0 +1,208 @@ +using Weaviate.Client; +using System; +using System.Threading.Tasks; +using Xunit; + +namespace WeaviateProject.Examples; + +public class ConnectionTest +{ + //TODO[g-despot] Replace with readiness check + [Fact] + public async Task TestConnectLocalWithCustomUrl() + { + // START CustomURL + var config = new ClientConfiguration + { + RestAddress = "127.0.0.1", + RestPort = 8080, + GrpcAddress = "127.0.0.1", + GrpcPort = 50051 // Default gRPC port + }; + using var client = new WeaviateClient(config); + + var meta = await client.GetMeta(); + Console.WriteLine(meta); + + // The 'using' statement handles freeing up resources automatically. + // END CustomURL + } + + // TODO[g-despot] How to add timeout + // START TimeoutLocal + // Coming soon + // END TimeoutLocal + // START TimeoutCustom + // Coming soon + // END TimeoutCustom + + [Fact] + public async Task TestConnectWCDWithApiKey() + { + // START APIKeyWCD + // Best practice: store your credentials in environment variables + string weaviateUrl = Environment.GetEnvironmentVariable("WEAVIATE_URL"); + string weaviateApiKey = Environment.GetEnvironmentVariable("WEAVIATE_API_KEY"); + + using var client = Connect.Cloud( + weaviateUrl, // Replace with your Weaviate Cloud URL + weaviateApiKey // Replace with your Weaviate Cloud key + ); + + var meta = await client.GetMeta(); + Console.WriteLine(meta); + + // The 'using' statement handles freeing up resources automatically. + // END APIKeyWCD + } + + [Fact] + public async Task TestCustomConnection() + { + // START CustomConnect + // Best practice: store your credentials in environment variables + string httpHost = Environment.GetEnvironmentVariable("WEAVIATE_HTTP_HOST"); + string grpcHost = Environment.GetEnvironmentVariable("WEAVIATE_GRPC_HOST"); + string weaviateApiKey = Environment.GetEnvironmentVariable("WEAVIATE_API_KEY"); + string cohereApiKey = Environment.GetEnvironmentVariable("COHERE_API_KEY"); + + var config = new ClientConfiguration + { + UseSsl = true, // Corresponds to scheme("https") + RestAddress = httpHost, + RestPort = 443, + GrpcAddress = grpcHost, + GrpcPort = 443, + Credentials = Auth.ApiKey(weaviateApiKey), + // Headers = new Dictionary + // { + // { "X-Cohere-Api-Key", cohereApiKey } + // } + }; + using var client = new WeaviateClient(config); + + var meta = await client.GetMeta(); + Console.WriteLine(meta); + + // The 'using' statement handles freeing up resources automatically. + // END CustomConnect + } + + [Fact] + public async Task TestCustomApiKeyConnection() + { + // START ConnectWithApiKeyExample + // Best practice: store your credentials in environment variables + string httpHost = Environment.GetEnvironmentVariable("WEAVIATE_HTTP_HOST"); + string grpcHost = Environment.GetEnvironmentVariable("WEAVIATE_GRPC_HOST"); + string weaviateApiKey = Environment.GetEnvironmentVariable("WEAVIATE_API_KEY"); + string cohereApiKey = Environment.GetEnvironmentVariable("COHERE_API_KEY"); + + var config = new ClientConfiguration + { + UseSsl = true, // Corresponds to scheme("https") + RestAddress = httpHost, + RestPort = 443, + GrpcAddress = grpcHost, + GrpcPort = 443, + Credentials = Auth.ApiKey(weaviateApiKey), + // Headers = new Dictionary + // { + // { "X-Cohere-Api-Key", cohereApiKey } + // } + }; + using var client = new WeaviateClient(config); + + var meta = await client.GetMeta(); + Console.WriteLine(meta); + + // The 'using' statement handles freeing up resources automatically. + // END ConnectWithApiKeyExample + } + + [Fact] + public async Task TestConnectLocalNoAuth() + { + // START LocalNoAuth + using var client = Connect.Local(); + + var meta = await client.GetMeta(); + Console.WriteLine(meta); + + // The 'using' statement handles freeing up resources automatically. + // END LocalNoAuth + } + + [Fact] + public async Task TestConnectLocalWithAuth() + { + // START LocalAuth + // Best practice: store your credentials in environment variables + string weaviateApiKey = Environment.GetEnvironmentVariable("WEAVIATE_LOCAL_API_KEY"); + + // The Connect.Local() helper doesn't support auth, so we must use a custom configuration. + var config = new ClientConfiguration + { + Credentials = Auth.ApiKey(weaviateApiKey) + }; + using var client = new WeaviateClient(config); + + var meta = await client.GetMeta(); + Console.WriteLine(meta); + + // The 'using' statement handles freeing up resources automatically. + // END LocalAuth + } + + [Fact] + public async Task TestConnectLocalWithThirdPartyKeys() + { + // START LocalThirdPartyAPIKeys + // Best practice: store your credentials in environment variables + string cohereApiKey = Environment.GetEnvironmentVariable("COHERE_API_KEY"); + + var config = new ClientConfiguration + { + // Headers = new Dictionary + // { + // { "X-Cohere-Api-Key", cohereApiKey } + // } + }; + using var client = new WeaviateClient(config); + + var meta = await client.GetMeta(); + Console.WriteLine(meta); + + // The 'using' statement handles freeing up resources automatically. + // END LocalThirdPartyAPIKeys + } + + [Fact] + public async Task TestConnectWCDWithThirdPartyKeys() + { + // START ThirdPartyAPIKeys + // Best practice: store your credentials in environment variables + string weaviateUrl = Environment.GetEnvironmentVariable("WEAVIATE_URL"); + string weaviateApiKey = Environment.GetEnvironmentVariable("WEAVIATE_API_KEY"); + string cohereApiKey = Environment.GetEnvironmentVariable("COHERE_API_KEY"); + + using var client = Connect.Cloud( + weaviateUrl, // Replace with your Weaviate Cloud URL + weaviateApiKey // Replace with your Weaviate Cloud key + // headers: new Dictionary + // { + // { "X-Cohere-Api-Key", cohereApiKey } + // } + ); + + var meta = await client.GetMeta(); + Console.WriteLine(meta); + + // The 'using' statement handles freeing up resources automatically. + // END ThirdPartyAPIKeys + } + + // START TimeoutWCD + // Coming soon + // END TimeoutWCD +} \ No newline at end of file diff --git a/_includes/code/csharp/GetStartedTest.cs b/_includes/code/csharp/GetStartedTest.cs new file mode 100644 index 00000000..f6e1deb9 --- /dev/null +++ b/_includes/code/csharp/GetStartedTest.cs @@ -0,0 +1,79 @@ +using Xunit; +using Weaviate.Client; +using Weaviate.Client.Models; +using System.Collections.Generic; +using System.Net.Http; +using System.Text.Json; +using System.Linq; +using System.Threading.Tasks; +// ... other usings + +// 1. Define your strongly-typed class +public class JeopardyQuestion +{ + public string? question { get; set; } + public string? answer { get; set; } + public string? category { get; set; } +} + +public class GetStartedTests +{ + [Fact] + public async Task GetStarted() + { + var client = Connect.Local(); + const string collectionName = "Question"; + + try + { + if (await client.Collections.Exists(collectionName)) + { + await client.Collections.Delete(collectionName); + } + + var questions = await client.Collections.Create(new Collection() + { + Name = collectionName, + VectorConfig = new VectorConfig("default", new Vectorizer.Text2VecOllama()), + Properties = new List + { + Property.Text("answer"), + Property.Text("question"), + Property.Text("category"), + } + }); + + // Download and parse data as before... + using var httpClient = new HttpClient(); + var resp = await httpClient.GetAsync( + "https://raw.githubusercontent.com/weaviate-tutorials/quickstart/main/data/jeopardy_tiny.json" + ); + resp.EnsureSuccessStatusCode(); + var jsonString = await resp.Content.ReadAsStringAsync(); + var data = JsonSerializer.Deserialize>(jsonString); + + // ============================= YOUR NEW, CLEAN CODE ============================= + // 2. Prepare the data by mapping it to your new class + var dataObjects = data.Select(d => new JeopardyQuestion + { + answer = d.GetProperty("Answer").GetString(), + question = d.GetProperty("Question").GetString(), + category = d.GetProperty("Category").GetString() + }).ToList(); + // ============================================================================== + + var importResult = await questions.Data.InsertMany(dataObjects); + + var response = await questions.Query.NearText("biology", limit: 2); + // ... rest of the test + Assert.Equal(2, response.Objects.Count()); + } + finally + { + if (await client.Collections.Exists(collectionName)) + { + await client.Collections.Delete(collectionName); + } + } + } +} \ No newline at end of file diff --git a/_includes/code/csharp/ManageCollectionsAliasTest.cs b/_includes/code/csharp/ManageCollectionsAliasTest.cs new file mode 100644 index 00000000..e69de29b diff --git a/_includes/code/csharp/ManageCollectionsCrossReferencesTest.cs b/_includes/code/csharp/ManageCollectionsCrossReferencesTest.cs new file mode 100644 index 00000000..975a0b50 --- /dev/null +++ b/_includes/code/csharp/ManageCollectionsCrossReferencesTest.cs @@ -0,0 +1,317 @@ +using Xunit; +using Weaviate.Client; +using Weaviate.Client.Models; +using System; +using System.Threading.Tasks; +using System.Linq; +using System.Collections.Generic; + +namespace WeaviateProject.Tests; + +public class ManageCollectionsCrossReferencesTest : IAsyncLifetime +{ + private WeaviateClient client; + + // Runs before each test + public Task InitializeAsync() + { + // Note: The C# client doesn't support setting headers like 'X-OpenAI-Api-Key' via the constructor for local connections. + // This must be configured in Weaviate's environment variables. + client = new WeaviateClient(new ClientConfiguration { RestAddress = "localhost", RestPort = 8080 }); + return Task.CompletedTask; + } + + // Runs after each test + public async Task DisposeAsync() + { + // Clean up collections after each test + await client.Collections.Delete("JeopardyQuestion"); + await client.Collections.Delete("JeopardyCategory"); + } + + [Fact] + public async Task TestCrossRefDefinition() + { + // START CrossRefDefinition + await client.Collections.Create(new Collection + { + Name = "JeopardyCategory", + Description = "A Jeopardy! category", + Properties = new() { Property.Text("title") } + }); + + await client.Collections.Create(new Collection + { + Name = "JeopardyQuestion", + Description = "A Jeopardy! question", + Properties = new() + { + Property.Text("question"), + Property.Text("answer") + }, + // highlight-start + References = new() + { + new Reference("hasCategory", "JeopardyCategory") + } + // highlight-end + }); + // END CrossRefDefinition + + // Verify collections were created properly + var questionConfig = await client.Collections.Export("JeopardyQuestion"); + Assert.Single(questionConfig.References); + Assert.Equal("hasCategory", questionConfig.References.First().Name); + } + + [Fact] + public async Task TestObjectWithCrossRef() + { + await SetupCollections(); + var categories = client.Collections.Use("JeopardyCategory"); + var categoryUuid = await categories.Data.Insert(new { title = "Weaviate" }); + var properties = new { question = "What tooling helps make Weaviate scalable?", answer = "Sharding, multi-tenancy, and replication" }; + + // START ObjectWithCrossRef + var questions = client.Collections.Use("JeopardyQuestion"); + + var newObject = await questions.Data.Insert( + properties, // The properties of the object + // highlight-start + references: [new ObjectReference("hasCategory", categoryUuid)] + // highlight-end + ); + // END ObjectWithCrossRef + + // Test results + var fetchedObj = await questions.Query.FetchObjectByID(newObject, returnReferences: [new QueryReference("hasCategory")]); + Assert.NotNull(fetchedObj); + Assert.True(fetchedObj.References.ContainsKey("hasCategory")); + } + + [Fact] + public async Task TestOneWay() + { + await SetupCollections(); + var questions = client.Collections.Use("JeopardyQuestion"); + var categories = client.Collections.Use("JeopardyCategory"); + + var questionObjId = await questions.Data.Insert(new { question = "This city is known for the Golden Gate Bridge", answer = "San Francisco" }); + var categoryObjId = await categories.Data.Insert(new { title = "U.S. CITIES" }); + + // START OneWayCrossReferences + await questions.Data.ReferenceAdd( + from: questionObjId, + fromProperty: "hasCategory", + // highlight-start + to: categoryObjId + // highlight-end + ); + // END OneWayCrossReferences + + var result = await questions.Query.FetchObjectByID(questionObjId, returnReferences: [new QueryReference("hasCategory")]); + Assert.NotNull(result); + Assert.True(result.References.ContainsKey("hasCategory")); + } + + [Fact] + public async Task TestTwoWay() + { + // START TwoWayCategory1CrossReferences + await client.Collections.Create(new Collection + { + Name = "JeopardyCategory", + Description = "A Jeopardy! category", + Properties = new() { Property.Text("title") } + }); + // END TwoWayCategory1CrossReferences + + // START TwoWayQuestionCrossReferences + await client.Collections.Create(new Collection + { + Name = "JeopardyQuestion", + Description = "A Jeopardy! question", + Properties = new() { Property.Text("question"), Property.Text("answer") }, + // highlight-start + References = new() { new Reference("hasCategory", "JeopardyCategory") } + // highlight-end + }); + // END TwoWayQuestionCrossReferences + + // START TwoWayCategoryCrossReferences + var category = client.Collections.Use("JeopardyCategory"); + await category.Config.AddProperty( + // highlight-start + Property.Reference("hasQuestion", "JeopardyQuestion") + // highlight-end + ); + // END TwoWayCategoryCrossReferences + + var questions = client.Collections.Use("JeopardyQuestion"); + var categories = client.Collections.Use("JeopardyCategory"); + + var questionObjId = await questions.Data.Insert(new { question = "This city is known for the Golden Gate Bridge", answer = "San Francisco" }); + var categoryObjId = await categories.Data.Insert(new { title = "U.S. CITIES" }); + + // START TwoWayCrossReferences + // For the "San Francisco" JeopardyQuestion object, add a cross-reference to the "U.S. CITIES" JeopardyCategory object + // highlight-start + await questions.Data.ReferenceAdd(from: questionObjId, fromProperty: "hasCategory", to: categoryObjId); + // highlight-end + + // For the "U.S. CITIES" JeopardyCategory object, add a cross-reference to "San Francisco" + // highlight-start + await categories.Data.ReferenceAdd(from: categoryObjId, fromProperty: "hasQuestion", to: questionObjId); + // highlight-end + // END TwoWayCrossReferences + + var result = await categories.Query.FetchObjectByID(categoryObjId, returnReferences: [new QueryReference("hasQuestion")]); + Assert.NotNull(result); + Assert.True(result.References.ContainsKey("hasQuestion")); + } + + [Fact] + public async Task TestMultiple() + { + await SetupCollections(); + var questions = client.Collections.Use("JeopardyQuestion"); + var categories = client.Collections.Use("JeopardyCategory"); + + var questionObjId = await questions.Data.Insert(new { question = "This city is known for the Golden Gate Bridge", answer = "San Francisco" }); + var categoryObjId = await categories.Data.Insert(new { title = "U.S. CITIES" }); + var categoryObjIdAlt = await categories.Data.Insert(new { title = "MUSEUMS" }); + + // START MultipleCrossReferences + // highlight-start + // Add multiple references - need to add them individually + foreach (var tempUuid in new[] { categoryObjId, categoryObjIdAlt }) + { + await questions.Data.ReferenceAdd( + from: questionObjId, + fromProperty: "hasCategory", + to: tempUuid + ); + } + // highlight-end + // END MultipleCrossReferences + + var result = await questions.Query.FetchObjectByID(questionObjId, returnReferences: [new QueryReference("hasCategory")]); + Assert.NotNull(result); + Assert.True(result.References.ContainsKey("hasCategory")); + Assert.Equal(2, result.References["hasCategory"].Count); + } + + [Fact] + public async Task TestReadCrossRef() + { + await SetupCollections(); + var questions = client.Collections.Use("JeopardyQuestion"); + var categories = client.Collections.Use("JeopardyCategory"); + + var categoryResult = await categories.Data.Insert(new { title = "SCIENCE" }); + var questionObjId = await questions.Data.Insert( + new { question = "What is H2O?", answer = "Water" }, + references: [new ObjectReference("hasCategory", categoryResult)] + ); + + // START ReadCrossRef + // Include the cross-references in a query response + // highlight-start + var response = await questions.Query.FetchObjects( // Or `Hybrid`, `NearText`, etc. + limit: 2, + returnReferences: [new QueryReference("hasCategory", fields: ["title"])] + ); + // highlight-end + + // Or include cross-references in a single-object retrieval + // highlight-start + var obj = await questions.Query.FetchObjectByID( + questionObjId, + returnReferences: [new QueryReference("hasCategory", fields: ["title"])] + ); + // highlight-end + // END ReadCrossRef + + Assert.NotEmpty(response.Objects); + Assert.NotNull(obj); + Assert.True(obj.References.ContainsKey("hasCategory")); + } + + // TODO[g-despot] ERROR: Unexpected status code NoContent. Expected: OK. reference delete. + [Fact] + public async Task TestDelete() + { + await SetupCollections(); + var questions = client.Collections.Use("JeopardyQuestion"); + var categories = client.Collections.Use("JeopardyCategory"); + + var categoryObjId = await categories.Data.Insert(new { title = "MUSEUMS" }); + var questionObjId = await questions.Data.Insert( + new { question = "This city is known for the Golden Gate Bridge", answer = "San Francisco" }, + references: [new ObjectReference("hasCategory", categoryObjId)] + ); + + // START DeleteCrossReference + // From the "San Francisco" JeopardyQuestion object, delete the "MUSEUMS" category cross-reference + // highlight-start + await questions.Data.ReferenceDelete( + // highlight-end + from: questionObjId, + fromProperty: "hasCategory", + to: categoryObjId + ); + // END DeleteCrossReference + + var result = await questions.Query.FetchObjectByID(questionObjId, returnReferences: [new QueryReference("hasCategory")]); + Assert.NotNull(result); + Assert.False(result.References.ContainsKey("hasCategory")); + } + + [Fact] + public async Task TestUpdate() + { + await SetupCollections(); + var questions = client.Collections.Use("JeopardyQuestion"); + var categories = client.Collections.Use("JeopardyCategory"); + + var categoryObjId = await categories.Data.Insert(new { title = "MUSEUMS" }); + await categories.Data.Insert(new { title = "U.S. CITIES" }); + var questionObjId = await questions.Data.Insert(new { question = "This city is known for the Golden Gate Bridge", answer = "San Francisco" }); + + // START UpdateCrossReference + // In the "San Francisco" JeopardyQuestion object, set the "hasCategory" cross-reference only to "MUSEUMS" + // highlight-start + await questions.Data.ReferenceReplace( + // highlight-end + from: questionObjId, + fromProperty: "hasCategory", + to: [categoryObjId] + ); + // END UpdateCrossReference + + var result = await questions.Query.FetchObjectByID(questionObjId, returnReferences: [new QueryReference("hasCategory")]); + Assert.NotNull(result); + Assert.True(result.References.ContainsKey("hasCategory")); + Assert.Single(result.References["hasCategory"]); + Assert.Equal(categoryObjId, result.References["hasCategory"][0].ID); + } + + // Helper method to set up collections + private async Task SetupCollections() + { + await client.Collections.Create(new Collection + { + Name = "JeopardyCategory", + Description = "A Jeopardy! category", + Properties = new() { Property.Text("title") } + }); + + await client.Collections.Create(new Collection + { + Name = "JeopardyQuestion", + Description = "A Jeopardy! question", + Properties = new() { Property.Text("question"), Property.Text("answer") }, + References = new() { new Reference("hasCategory", "JeopardyCategory") } + }); + } +} \ No newline at end of file diff --git a/_includes/code/csharp/ManageCollectionsMigrateDataTest.cs b/_includes/code/csharp/ManageCollectionsMigrateDataTest.cs new file mode 100644 index 00000000..53f48f35 --- /dev/null +++ b/_includes/code/csharp/ManageCollectionsMigrateDataTest.cs @@ -0,0 +1,276 @@ +using Xunit; +using Weaviate.Client; +using Weaviate.Client.Models; +using System; +using System.Threading.Tasks; +using System.Linq; +using System.Collections.Generic; +using System.Text.Json; + +namespace WeaviateProject.Tests; + +public class ManageCollectionsMigrateDataTest : IAsyncLifetime +{ + private static WeaviateClient clientSrc; + private static WeaviateClient clientTgt; + private const int DATASET_SIZE = 50; + + // Runs once before any tests in the class + public async Task InitializeAsync() + { + string openaiApiKey = Environment.GetEnvironmentVariable("OPENAI_APIKEY"); + + // Connect to the source Weaviate instance + clientSrc = new WeaviateClient(new ClientConfiguration { RestAddress = "localhost", RestPort = 8080 }); + // Connect to the target Weaviate instance + clientTgt = new WeaviateClient(new ClientConfiguration + { + RestAddress = "localhost", + RestPort = 8090, + GrpcPort = 50061, + }); + + // Simulate weaviate-datasets by creating and populating source collections + await CreateCollection(clientSrc, "WineReview", false); + await CreateCollection(clientSrc, "WineReviewMT", true); + + var wineReview = clientSrc.Collections.Use("WineReview"); + var wineReviewData = Enumerable.Range(0, DATASET_SIZE) + .Select(i => new { title = $"Review {i}" }) + .ToArray(); + await wineReview.Data.InsertMany(wineReviewData); + + var wineReviewMT = clientSrc.Collections.Use("WineReviewMT"); + await wineReviewMT.Tenants.Add(new Tenant { Name = "tenantA" }); + await wineReviewMT.WithTenant("tenantA").Data.InsertMany(wineReviewData); + } + + // Runs once after all tests in the class + public async Task DisposeAsync() + { + // Clean up collections on both clients + await clientSrc.Collections.DeleteAll(); + await clientTgt.Collections.DeleteAll(); + } + + // START CreateCollectionCollectionToCollection + // START CreateCollectionCollectionToTenant + // START CreateCollectionTenantToCollection + // START CreateCollectionTenantToTenant + private static async Task> CreateCollection(WeaviateClient clientIn, + string collectionName, bool enableMt) + // END CreateCollectionCollectionToCollection + // END CreateCollectionCollectionToTenant + // END CreateCollectionTenantToCollection + // END CreateCollectionTenantToTenant + { + if (await clientIn.Collections.Exists(collectionName)) + { + await clientIn.Collections.Delete(collectionName); + } + // START CreateCollectionCollectionToCollection + // START CreateCollectionCollectionToTenant + // START CreateCollectionTenantToCollection + // START CreateCollectionTenantToTenant + return await clientIn.Collections.Create(new Collection + { + Name = collectionName, + MultiTenancyConfig = new MultiTenancyConfig { Enabled = enableMt }, + // Additional settings not shown + VectorConfig = new VectorConfig("default", new Vectorizer.Text2VecContextionary()), + GenerativeConfig = new Generative.CohereConfig(), + Properties = new() + { + Property.Text("review_body"), + Property.Text("title"), + Property.Text("country"), + Property.Int("points"), + Property.Number("price") + } + }); + } + // END CreateCollectionCollectionToCollection + // END CreateCollectionCollectionToTenant + // END CreateCollectionTenantToCollection + // END CreateCollectionTenantToTenant + + // START CollectionToCollection + // START TenantToCollection + // START CollectionToTenant + // START TenantToTenant + private async Task MigrateData(CollectionClient collectionSrc, + CollectionClient collectionTgt) + { + Console.WriteLine("Starting data migration..."); + var sourceObjects = new List(); + + // FIX 1: Use FetchObjects instead of Iterator for better tenant support + var response = await collectionSrc.Query.FetchObjects(limit: 10000, returnMetadata: MetadataOptions.Vector); + + foreach (var obj in response.Objects) + { + sourceObjects.Add(new WeaviateObject + { + ID = obj.ID, + // FIX 2: Cast the dynamic properties to a supported dictionary type + Properties = (IDictionary)obj.Properties, + Vectors = obj.Vectors + }); + } + + // FIX 3 (from previous advice): Convert the List to an Array before inserting + await collectionTgt.Data.InsertMany(sourceObjects.ToArray()); + + Console.WriteLine("Data migration complete."); + } + // END CollectionToCollection + // END TenantToCollection + // END CollectionToTenant + // END TenantToTenant + + private async Task VerifyMigration(CollectionClient collectionSrc, + CollectionClient collectionTgt, int numSamples) + { + // FIX 1: Use FetchObjects instead of Iterator + var srcResponse = await collectionSrc.Query.FetchObjects(limit: 10000); + var srcObjects = srcResponse.Objects; + + if (!srcObjects.Any()) + { + Console.WriteLine("No objects in source collection"); + return false; + } + + var sampledObjects = srcObjects.OrderBy(x => Guid.NewGuid()).Take(numSamples).ToList(); + + Console.WriteLine($"Verifying {sampledObjects.Count} random objects..."); + foreach (var srcObj in sampledObjects) + { + var tgtObj = await collectionTgt.Query.FetchObjectByID(srcObj.ID.Value); + if (tgtObj == null) + { + Console.WriteLine($"Object {srcObj.ID} not found in target collection"); + return false; + } + + var srcJson = JsonSerializer.Serialize(srcObj.Properties); + var tgtJson = JsonSerializer.Serialize(tgtObj.Properties); + if (srcJson != tgtJson) + { + Console.WriteLine($"Properties mismatch for object {srcObj.ID}"); + return false; + } + } + Console.WriteLine("All sampled objects verified successfully!"); + return true; + } + + // START CreateCollectionCollectionToCollection + private async Task CreateCollectionToCollection() + { + await CreateCollection(clientTgt, "WineReview", false); + } + // END CreateCollectionCollectionToCollection + + [Fact] + // START CollectionToCollection + public async Task TestCollectionToCollection() + { + await CreateCollectionToCollection(); + + var reviewsSrc = clientSrc.Collections.Use("WineReview"); + var reviewsTgt = clientTgt.Collections.Use("WineReview"); + await MigrateData(reviewsSrc, reviewsTgt); + // END CollectionToCollection + + Assert.Equal(DATASET_SIZE, (await reviewsTgt.Aggregate.OverAll(totalCount: true)).TotalCount); + Assert.True(await VerifyMigration(reviewsSrc, reviewsTgt, 5)); + } + + // START CreateCollectionTenantToCollection + private async Task CreateTenantToCollection() + { + await CreateCollection(clientTgt, "WineReview", false); + } + // END CreateCollectionTenantToCollection + + [Fact] + // START TenantToCollection + public async Task TestTenantToCollection() + { + await CreateTenantToCollection(); + + var reviewsSrc = clientSrc.Collections.Use("WineReviewMT"); + var reviewsTgt = clientTgt.Collections.Use("WineReview"); + var reviewsSrcTenantA = reviewsSrc.WithTenant("tenantA"); + await MigrateData(reviewsSrcTenantA, reviewsTgt); + // END TenantToCollection + + Assert.Equal(DATASET_SIZE, (await reviewsTgt.Aggregate.OverAll(totalCount: true)).TotalCount); + Assert.True(await VerifyMigration(reviewsSrcTenantA, reviewsTgt, 5)); + } + + // START CreateCollectionCollectionToTenant + private async Task CreateCollectionToTenant() + { + await CreateCollection(clientTgt, "WineReviewMT", true); + } + // END CreateCollectionCollectionToTenant + + // START CreateTenants + // START CreateCollectionTenantToTenant + private async Task CreateTenants() + { + var reviewsMtTgt = clientTgt.Collections.Use("WineReviewMT"); + + var tenantsTgt = new[] { new Tenant { Name = "tenantA" }, new Tenant { Name = "tenantB" } }; + await reviewsMtTgt.Tenants.Add(tenantsTgt); + } + // END CreateTenants + // END CreateCollectionTenantToTenant + + [Fact] + // START CollectionToTenant + public async Task TestCollectionToTenant() + { + await CreateCollectionToTenant(); + await CreateTenants(); + + var reviewsMtTgt = clientTgt.Collections.Use("WineReviewMT"); + var reviewsSrc = clientSrc.Collections.Use("WineReview"); + + var reviewsTgtTenantA = reviewsMtTgt.WithTenant("tenantA"); + + await MigrateData(reviewsSrc, reviewsTgtTenantA); + // END CollectionToTenant + + Assert.Equal(DATASET_SIZE, (await reviewsTgtTenantA.Aggregate.OverAll(totalCount: true)).TotalCount); + Assert.True(await VerifyMigration(reviewsSrc, reviewsTgtTenantA, 5)); + } + + // START CreateCollectionTenantToTenant + private async Task CreateTenantToTenant() + { + await CreateCollection(clientTgt, "WineReviewMT", true); + } + // END CreateCollectionTenantToTenant + + [Fact] + // START TenantToTenant + public async Task TestTenantToTenant() + { + await CreateTenantToTenant(); + await CreateTenants(); + + var reviewsMtSrc = clientSrc.Collections.Use("WineReviewMT"); + var reviewsMtTgt = clientTgt.Collections.Use("WineReviewMT"); + var reviewsSrcTenantA = reviewsMtSrc.WithTenant("tenantA"); + var reviewsTgtTenantA = reviewsMtTgt.WithTenant("tenantA"); + + await MigrateData(reviewsSrcTenantA, reviewsTgtTenantA); + // END TenantToTenant + + Assert.Equal(DATASET_SIZE, (await reviewsTgtTenantA.Aggregate.OverAll(totalCount: true)).TotalCount); + Assert.True(await VerifyMigration(reviewsSrcTenantA, reviewsTgtTenantA, 5)); + } +} \ No newline at end of file diff --git a/_includes/code/csharp/ManageCollectionsTest.cs b/_includes/code/csharp/ManageCollectionsTest.cs new file mode 100644 index 00000000..95337ca8 --- /dev/null +++ b/_includes/code/csharp/ManageCollectionsTest.cs @@ -0,0 +1,688 @@ +using Xunit; +using Weaviate.Client; +using Weaviate.Client.Models; +using System; +using System.Threading.Tasks; +using System.Collections.Generic; +using System.Linq; + +// This attribute ensures that tests in this class do not run in parallel, +// which is important because they share a client and perform cleanup operations +// that could otherwise interfere with each other. +[Collection("Sequential")] +public class ManageCollectionsTest : IAsyncLifetime +{ + private static readonly WeaviateClient client; + + // Static constructor acts like JUnit's @BeforeAll + static ManageCollectionsTest() + { + // Instantiate the client with the OpenAI API key + string openaiApiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY"); + if (string.IsNullOrWhiteSpace(openaiApiKey)) + { + throw new ArgumentException("Please set the OPENAI_API_KEY environment variable."); + } + + // TODO[g-despot] Headers are currently not supported + var headers = new Dictionary { { "X-OpenAI-Api-Key", openaiApiKey } }; + var config = new ClientConfiguration + { + RestAddress = "localhost", + RestPort = 8080, + //Headers = headers + }; + client = new WeaviateClient(config); + } + + // InitializeAsync is called before each test. We ensure all collections are deleted. + public async Task InitializeAsync() + { + await client.Collections.DeleteAll(); + } + + // DisposeAsync acts like JUnit's @AfterEach, cleaning up after every test. + public Task DisposeAsync() + { + // No action needed here since cleanup happens in InitializeAsync before the next test. + return Task.CompletedTask; + } + + [Fact] + public async Task TestBasicCreateCollection() + { + // START BasicCreateCollection + await client.Collections.Create(new Collection { Name = "Article" }); + // END BasicCreateCollection + + bool exists = await client.Collections.Exists("Article"); + Assert.True(exists); + } + + [Fact] + public async Task TestCreateCollectionWithProperties() + { + // START CreateCollectionWithProperties + await client.Collections.Create(new Collection + { + Name = "Article", + Properties = + [ + Property.Text("title"), + Property.Text("body"), + ] + }); + // END CreateCollectionWithProperties + + var config = await client.Collections.Export("Article"); + Assert.Equal(2, config.Properties.Count); + } + + [Fact] + public async Task TestAddProperties() + { + await client.Collections.Create(new Collection + { + Name = "Article", + Properties = + [ + Property.Text("title"), + Property.Text("body"), + ] + }); + + // START AddProperty + CollectionClient articles = client.Collections.Use("Article"); + await articles.Config.AddProperty(Property.Text("description")); + // END AddProperty + + var config = await client.Collections.Export("Article"); + Assert.Contains(config.Properties, p => p.Name == "description"); + } + + [Fact] + public async Task TestCreateCollectionWithVectorizer() + { + // START Vectorizer + await client.Collections.Create(new Collection + { + Name = "Article", + VectorConfig = new VectorConfigList + { + new VectorConfig("default", new Vectorizer.Text2VecContextionary()) + }, + Properties = + [ + Property.Text("title"), + Property.Text("body"), + ] + }); + // END Vectorizer + + var config = await client.Collections.Export("Article"); + Assert.True(config.VectorConfig.ContainsKey("default")); + Assert.Equal("text2vec-contextionary", config.VectorConfig["default"].Vectorizer.Identifier); + } + + [Fact] + public async Task TestCreateCollectionWithNamedVectors() + { + // START BasicNamedVectors + await client.Collections.Create(new Collection + { + Name = "ArticleNV", + VectorConfig = new VectorConfigList + { + new VectorConfig("title", new Vectorizer.Text2VecContextionary + { + SourceProperties = ["title"] + }, new VectorIndex.HNSW()), + new VectorConfig("title_country", new Vectorizer.Text2VecContextionary + { + SourceProperties = ["title", "country"] + }, new VectorIndex.HNSW()), + new VectorConfig("custom_vector", new Vectorizer.SelfProvided(), new VectorIndex.HNSW()) + }, + Properties = + [ + Property.Text("title"), + Property.Text("country"), + ] + }); + // END BasicNamedVectors + + var config = await client.Collections.Export("ArticleNV"); + Assert.Equal(3, config.VectorConfig.Count); + Assert.Contains("title", config.VectorConfig.Keys); + Assert.Contains("title_country", config.VectorConfig.Keys); + Assert.Contains("custom_vector", config.VectorConfig.Keys); + Assert.Contains(config.Properties, p => p.Name == "title"); + Assert.Contains(config.Properties, p => p.Name == "country"); + } + + [Fact] + public async Task ConfigurePropertyModuleSettings() + { + await client.Collections.Create(new Collection + { + Name = "Article", + VectorConfig = new VectorConfigList + { + new VectorConfig("default", new Vectorizer.Text2VecContextionary()) + }, + Properties = + [ + Property.Text("title"), + Property.Text("body"), + ] + }); + var articles = client.Collections.Use("Article"); + // START AddNamedVectors + await articles.Config.AddVector(Configure.Vectors.Text2VecCohere().New("body_vector", sourceProperties: "body")); + // END AddNamedVectors + + Collection config = await client.Collections.Export("Article"); + Assert.Equal(2, config.VectorConfig.Count); + //Assert.NotNull(config.VectorConfig["body_vector"]); + } + + // START AddNamedVectors + // Coming soon + // END AddNamedVectors + + // TODO[g-despot] {"error":[{"message":"parse vector index config: parse vector config for jina_colbert: multi vector index configured but vectorizer: \"text2vec-jinaai\" doesn't support multi vectors"}]} + [Fact] + public async Task CreateCollectionWithMultiVectors() + { + // START MultiValueVectorCollection + await client.Collections.Create(new Collection + { + Name = "DemoCollection", + VectorConfig = new VectorConfigList + { + // The factory function will automatically enable multi-vector support for the HNSW index + // Configure.MultiVectors.Text2VecJinaAI().New("jina_colbert"), + // Must explicitly enable multi-vector support for the HNSW index + Configure.MultiVectors.SelfProvided("custom_multi_vector"), + }, + Properties = new List { Property.Text("text") }, + }); + // END MultiValueVectorCollection + + // Assert + Collection config = await client.Collections.Export("DemoCollection"); + Assert.True(config.VectorConfig.ContainsKey("jina_colbert")); + Assert.True(config.VectorConfig.ContainsKey("custom_multi_vector")); + } + + // START MultiValueVectorCollection + // Coming soon + // END MultiValueVectorCollection + + [Fact] + public async Task TestSetVectorIndexType() + { + // START SetVectorIndexType + await client.Collections.Create(new Collection + { + Name = "Article", + VectorConfig = new VectorConfigList + { + new VectorConfig("default", + new Vectorizer.Text2VecContextionary(), + new VectorIndex.HNSW() + ) + }, + Properties = + [ + Property.Text("title"), + Property.Text("body"), + ] + }); + // END SetVectorIndexType + + var config = await client.Collections.Export("Article"); + Assert.IsType(config.VectorConfig["default"].VectorIndexConfig); + } + + [Fact] + public async Task TestSetVectorIndexParams() + { + // START SetVectorIndexParams + await client.Collections.Create(new Collection + { + Name = "Article", + VectorConfig = new VectorConfigList + { + new VectorConfig("default", + new Vectorizer.Text2VecContextionary(), + new VectorIndex.HNSW + { + EfConstruction = 300, + Distance = VectorIndexConfig.VectorDistance.Cosine + } + ) + }, + Properties = + [ + Property.Text("title") + ] + }); + // END SetVectorIndexParams + + var config = await client.Collections.Export("Article"); + var hnswConfig = Assert.IsType(config.VectorConfig["default"].VectorIndexConfig); + Assert.Equal(300, hnswConfig.EfConstruction); + Assert.Equal(VectorIndexConfig.VectorDistance.Cosine, hnswConfig.Distance); + } + + [Fact] + public async Task TestSetInvertedIndexParams() + { + // START SetInvertedIndexParams + await client.Collections.Create(new Collection + { + Name = "Article", + Properties = + [ + Property.Text("title", indexFilterable: true, indexSearchable: true), + Property.Text("chunk", indexFilterable: true, indexSearchable: true), + Property.Int("chunk_number", indexRangeFilters: true), + ], + InvertedIndexConfig = new InvertedIndexConfig + { + Bm25 = new BM25Config { B = 1, K1 = 2 }, + IndexNullState = true, + IndexPropertyLength = true, + IndexTimestamps = true, + } + }); + // END SetInvertedIndexParams + + var config = await client.Collections.Export("Article"); + Assert.Equal(1, config.InvertedIndexConfig.Bm25.B); + Assert.Equal(2, config.InvertedIndexConfig.Bm25.K1); + Assert.Equal(3, config.Properties.Count); + } + + [Fact] + public async Task TestSetReranker() + { + // START SetReranker + await client.Collections.Create(new Collection + { + Name = "Article", + VectorConfig = new VectorConfigList + { + new VectorConfig("default", new Vectorizer.Text2VecContextionary()) + }, + RerankerConfig = new Reranker.Cohere(), + Properties = + [ + Property.Text("title") + ] + }); + // END SetReranker + + var config = await client.Collections.Export("Article"); + Assert.NotNull(config.RerankerConfig); + Assert.Equal("reranker-cohere", (config.RerankerConfig as Reranker.Cohere)?.Type); + } + + [Fact] + public async Task TestUpdateReranker() + { + await client.Collections.Create(new Collection { Name = "Article" }); + + // START UpdateReranker + var collection = client.Collections.Use("Article"); + await collection.Config.Update(c => + { + c.RerankerConfig = new Reranker.Cohere(); + }); + // END UpdateReranker + + var config = await client.Collections.Export("Article"); + Assert.NotNull(config.RerankerConfig); + Assert.Equal("reranker-cohere", (config.RerankerConfig as Reranker.Cohere)?.Type); + } + + [Fact] + public async Task TestSetGenerative() + { + // START SetGenerative + await client.Collections.Create(new Collection + { + Name = "Article", + VectorConfig = new VectorConfigList + { + new VectorConfig("default", new Vectorizer.Text2VecContextionary()) + }, + GenerativeConfig = new Generative.CohereConfig(), + Properties = + [ + Property.Text("title") + ] + }); + // END SetGenerative + + var config = await client.Collections.Export("Article"); + Assert.NotNull(config.GenerativeConfig); + Assert.Equal("generative-cohere", (config.GenerativeConfig as Generative.CohereConfig)?.Type); + } + + [Fact] + public async Task TestUpdateGenerative() + { + await client.Collections.Create(new Collection { Name = "Article" }); + + // START UpdateGenerative + var collection = client.Collections.Use("Article"); + await collection.Config.Update(c => + { + c.GenerativeConfig = new Generative.CohereConfig(); + }); + // END UpdateGenerative + + var config = await client.Collections.Export("Article"); + Assert.NotNull(config.GenerativeConfig); + Assert.Equal("generative-cohere", (config.GenerativeConfig as Generative.CohereConfig)?.Type); + } + + // START ModuleSettings + // Coming soon + // END ModuleSettings + + // TODO[g-despot]: Missing vectorizePropertyName + [Fact] + public async Task TestCreateCollectionWithPropertyConfig() + { + // START PropModuleSettings + await client.Collections.Create(new Collection + { + Name = "Article", + Properties = new List + { + Property.Text( + "title", + // vectorizePropertyName: true, + tokenization: PropertyTokenization.Lowercase + ), + Property.Text( + "body", + // skipVectorization: true, + tokenization: PropertyTokenization.Whitespace + ), + }, + }); + // END PropModuleSettings + + var config = await client.Collections.Export("Article"); + Assert.Equal(2, config.Properties.Count); + } + + [Fact] + public async Task TestCreateCollectionWithTrigramTokenization() + { + // START TrigramTokenization + await client.Collections.Create(new Collection + { + Name = "Article", + VectorConfig = new VectorConfigList + { + new VectorConfig("default", new Vectorizer.Text2VecContextionary()) + }, + Properties = + [ + Property.Text("title", tokenization: PropertyTokenization.Trigram) + ] + }); + // END TrigramTokenization + + var config = await client.Collections.Export("Article"); + Assert.Single(config.Properties); + Assert.Equal(PropertyTokenization.Trigram, config.Properties.First().PropertyTokenization); + } + + [Fact] + public async Task TestDistanceMetric() + { + // START DistanceMetric + await client.Collections.Create(new Collection + { + Name = "Article", + VectorConfig = new VectorConfigList + { + new VectorConfig("default", + new Vectorizer.Text2VecContextionary(), + new VectorIndex.HNSW + { + Distance = VectorIndexConfig.VectorDistance.Cosine + } + ) + }, + Properties = + [ + Property.Text("title") + ] + }); + // END DistanceMetric + + var config = await client.Collections.Export("Article"); + var hnswConfig = Assert.IsType(config.VectorConfig["default"].VectorIndexConfig); + Assert.Equal(VectorIndexConfig.VectorDistance.Cosine, hnswConfig.Distance); + } + + [Fact] + public async Task TestReplicationSettings() + { + // START ReplicationSettings + await client.Collections.Create(new Collection + { + Name = "Article", + ReplicationConfig = new ReplicationConfig { Factor = 1 } + }); + // END ReplicationSettings + + var config = await client.Collections.Export("Article"); + Assert.Equal(1, config.ReplicationConfig.Factor); + } + + [Fact] + public async Task TestAsyncRepair() + { + // START AsyncRepair + await client.Collections.Create(new Collection + { + Name = "Article", + ReplicationConfig = new ReplicationConfig + { + Factor = 1, + AsyncEnabled = true + } + }); + // END AsyncRepair + + var config = await client.Collections.Export("Article"); + Assert.True(config.ReplicationConfig.AsyncEnabled); + } + + [Fact] + public async Task TestAllReplicationSettings() + { + // START AllReplicationSettings + await client.Collections.Create(new Collection + { + Name = "Article", + ReplicationConfig = new ReplicationConfig + { + Factor = 1, + AsyncEnabled = true, + DeletionStrategy = DeletionStrategy.TimeBasedResolution, + }, + }); + // END AllReplicationSettings + + var config = await client.Collections.Export("Article"); + Assert.Equal(1, config.ReplicationConfig.Factor); + Assert.True(config.ReplicationConfig.AsyncEnabled); + } + + [Fact] + public async Task TestShardingSettings() + { + // START ShardingSettings + await client.Collections.Create(new Collection + { + Name = "Article", + ShardingConfig = new ShardingConfig + { + VirtualPerPhysical = 128, + DesiredCount = 1, + DesiredVirtualCount = 128, + } + }); + // END ShardingSettings + + var config = await client.Collections.Export("Article"); + Assert.Equal(128, config.ShardingConfig.VirtualPerPhysical); + Assert.Equal(1, config.ShardingConfig.DesiredCount); + Assert.Equal(128, config.ShardingConfig.DesiredVirtualCount); + } + + [Fact] + public async Task TestMultiTenancy() + { + // START Multi-tenancy + await client.Collections.Create(new Collection + { + Name = "Article", + MultiTenancyConfig = new MultiTenancyConfig + { + Enabled = true, + AutoTenantCreation = true, + AutoTenantActivation = true + } + }); + // END Multi-tenancy + + var config = await client.Collections.Export("Article"); + Assert.True(config.MultiTenancyConfig.AutoTenantActivation); + } + + [Fact] + public async Task TestReadOneCollection() + { + await client.Collections.Create(new Collection { Name = "Article" }); + + // START ReadOneCollection + var articles = client.Collections.Use("Article"); + var articlesConfig = await articles.Get(); + + Console.WriteLine(articlesConfig); + // END ReadOneCollection + + Assert.NotNull(articlesConfig); + Assert.Equal("Article", articlesConfig.Name); + } + + [Fact] + public async Task TestReadAllCollections() + { + await client.Collections.Create(new Collection { Name = "Article" }); + await client.Collections.Create(new Collection { Name = "Publication" }); + + // START ReadAllCollections + var response = new List(); + await foreach (var collection in client.Collections.List()) + { + response.Add(collection); + Console.WriteLine(collection); + } + // END ReadAllCollections + + Assert.Equal(2, response.Count); + Assert.Contains(response, c => c.Name == "Article"); + Assert.Contains(response, c => c.Name == "Publication"); + } + + [Fact] + public async Task TestUpdateCollection() + { + await client.Collections.Create(new Collection + { + Name = "Article", + InvertedIndexConfig = new InvertedIndexConfig + { + Bm25 = new BM25Config { K1 = 10 } + } + }); + + // START UpdateCollection + var articles = client.Collections.Use("Article"); + + await articles.Config.Update(c => + { + c.Description = "An updated collection description."; + c.InvertedIndexConfig.Bm25.K1 = 1.5f; + }); + // END UpdateCollection + + var config = await articles.Get(); + Assert.Equal("An updated collection description.", config.Description); + Assert.Equal(1.5f, config.InvertedIndexConfig.Bm25.K1); + } + + [Fact] + public async Task TestDeleteCollection() + { + string collectionName = "Article"; + await client.Collections.Create(new Collection { Name = collectionName }); + Assert.True(await client.Collections.Exists(collectionName)); + + // START DeleteCollection + await client.Collections.Delete(collectionName); + // END DeleteCollection + + Assert.False(await client.Collections.Exists(collectionName)); + } + + // [Fact] + // public async Task TestInspectCollectionShards() + // { + // await client.Collections.Create(new Collection { Name = "Article" }); + + // var articles = client.Collections.Use("Article"); + + // var articleShards = await articles.Shards.Get(); + // Console.WriteLine(string.Join(", ", articleShards.Select(s => s.Name))); + + // Assert.NotNull(articleShards); + // Assert.Single(articleShards); + // } + // START InspectCollectionShards + // Coming soon + // END InspectCollectionShards + + // [Fact] + // public async Task TestUpdateCollectionShards() + // { + // await client.Collections.Create(new Collection { Name = "Article" }); + // var initialShards = await client.Collections.Use("Article").Shards.Get(); + // var shardName = initialShards.First().Name; + + // var articles = client.Collections.Use("Article"); + + // var articleShards = await articles.Shards.Update(shardName, "READONLY"); + // Console.WriteLine(string.Join(", ", articleShards.Select(s => s.Status))); + + // Assert.NotNull(articleShards); + // Assert.Single(articleShards); + // Assert.Equal("READONLY", articleShards.First().Status); + // } + // START UpdateCollectionShards + // Coming soon + // END UpdateCollectionShards + + +} diff --git a/_includes/code/csharp/ManageObjectsCreateTest.cs b/_includes/code/csharp/ManageObjectsCreateTest.cs new file mode 100644 index 00000000..1fc05cd1 --- /dev/null +++ b/_includes/code/csharp/ManageObjectsCreateTest.cs @@ -0,0 +1,307 @@ +using Xunit; +using Weaviate.Client; +using Weaviate.Client.Models; +using System; +using System.Threading.Tasks; +using System.Collections.Generic; +using System.Security.Cryptography; +using System.Text; +using System.Text.Json; + +namespace WeaviateProject.Tests; + +[Collection("Sequential")] +public class ManageObjectsCreateTest : IAsyncLifetime +{ + private static readonly WeaviateClient client; + + // A helper method to generate a deterministic UUID v5 from a seed. + private static Guid GenerateUuid5(string seed) + { + // Namespace for UUIDv5, can be any valid Guid. + var namespaceId = Guid.Parse("00000000-0000-0000-0000-000000000000"); + + var namespaceBytes = namespaceId.ToByteArray(); + var nameBytes = Encoding.UTF8.GetBytes(seed); + + // Concatenate namespace and name bytes + var combinedBytes = new byte[namespaceBytes.Length + nameBytes.Length]; + Buffer.BlockCopy(namespaceBytes, 0, combinedBytes, 0, namespaceBytes.Length); + Buffer.BlockCopy(nameBytes, 0, combinedBytes, namespaceBytes.Length, nameBytes.Length); + + using (var sha1 = SHA1.Create()) + { + var hash = sha1.ComputeHash(combinedBytes); + var newGuid = new byte[16]; + Array.Copy(hash, 0, newGuid, 0, 16); + + // Set version to 5 + newGuid[6] = (byte)((newGuid[6] & 0x0F) | (5 << 4)); + // Set variant to RFC 4122 + newGuid[8] = (byte)((newGuid[8] & 0x3F) | 0x80); + + // In-place byte swap for correct Guid constructor order + (newGuid[0], newGuid[3]) = (newGuid[3], newGuid[0]); + (newGuid[1], newGuid[2]) = (newGuid[2], newGuid[1]); + (newGuid[4], newGuid[5]) = (newGuid[5], newGuid[4]); + (newGuid[6], newGuid[7]) = (newGuid[7], newGuid[6]); + + return new Guid(newGuid); + } + } + + // Static constructor acts like JUnit's @BeforeAll for one-time setup. + static ManageObjectsCreateTest() + { + // START INSTANTIATION-COMMON + client = new WeaviateClient(new ClientConfiguration { RestAddress = "localhost", RestPort = 8080 }); + // END INSTANTIATION-COMMON + } + + // InitializeAsync runs once before the class tests start. + public async Task InitializeAsync() + { + await client.Collections.DeleteAll(); // Clean slate before tests + + // START Define the class + await client.Collections.Create(new Collection + { + Name = "JeopardyQuestion", + Properties = + [ + Property.Text("title", description: "Question title") + ], + VectorConfig = new VectorConfigList + { + new VectorConfig("default", new Vectorizer.Text2VecContextionary()) + } + }); + + await client.Collections.Create(new Collection + { + Name = "WineReviewNV", + Properties = + [ + Property.Text("review_body", description: "Review body"), + Property.Text("title", description: "Name of the wine"), + Property.Text("country", description: "Originating country") + ], + VectorConfig = new VectorConfigList + { + new VectorConfig("title", new Vectorizer.Text2VecContextionary()), + new VectorConfig("review_body", new Vectorizer.Text2VecContextionary()), + new VectorConfig("title_country", new Vectorizer.Text2VecContextionary()) + } + }); + // END Define the class + + // Additional collections for other tests + await client.Collections.Create(new Collection + { + Name = "Publication", + Properties = + [ + Property.GeoCoordinate("headquartersGeoLocation") + ] + }); + await client.Collections.Create(new Collection + { + Name = "Author", + VectorConfig = new VectorConfigList + { + new VectorConfig("default", new Vectorizer.SelfProvided()) + } + }); + } + + // DisposeAsync acts like JUnit's @AfterAll for one-time teardown. + public async Task DisposeAsync() + { + await client.Collections.DeleteAll(); + } + + [Fact] + public async Task TestCreateObject() + { + // START CreateSimpleObject + var jeopardy = client.Collections.Use("JeopardyQuestion"); + + // highlight-start + var uuid = await jeopardy.Data.Insert(new + { + // highlight-end + question = "This vector DB is OSS & supports automatic property type inference on import", + // answer = "Weaviate", // properties can be omitted + newProperty = 123 // will be automatically added as a number property + }); + + Console.WriteLine(uuid); // the return value is the object's UUID + // END CreateSimpleObject + + var result = await jeopardy.Query.FetchObjectByID(uuid); + Assert.NotNull(result); + var props = result.Properties as IDictionary; + Assert.Equal(123d, props["newProperty"]); + } + + [Fact] + public async Task TestCreateObjectWithVector() + { + // START CreateObjectWithVector + var jeopardy = client.Collections.Use("JeopardyQuestion"); + var uuid = await jeopardy.Data.Insert( + new + { + question = "This vector DB is OSS and supports automatic property type inference on import", + answer = "Weaviate" + }, + // highlight-start + vectors: new float[300] // Using a zero vector for demonstration + ); + // highlight-end + + Console.WriteLine(uuid); // the return value is the object's UUID + // END CreateObjectWithVector + + var result = await jeopardy.Query.FetchObjectByID(uuid); + Assert.NotNull(result); + } + + [Fact] + public async Task TestCreateObjectNamedVectors() + { + // START CreateObjectNamedVectors + var reviews = client.Collections.Use("WineReviewNV"); // This collection must have named vectors configured + var uuid = await reviews.Data.Insert( + new + { + title = "A delicious Riesling", + review_body = "This wine is a delicious Riesling which pairs well with seafood.", + country = "Germany" + }, + // highlight-start + // Specify the named vectors, following the collection definition + vectors: new Vectors + { + { "title", new float[1536] }, + { "review_body", new float[1536] }, + { "title_country", new float[1536] } + } + ); + // highlight-end + + Console.WriteLine(uuid); // the return value is the object's UUID + // END CreateObjectNamedVectors + + var result = await reviews.Query.FetchObjectByID(uuid, returnMetadata: MetadataOptions.Vector); + Assert.NotNull(result); + Assert.NotNull(result.Vectors); + Assert.Contains("title", result.Vectors.Keys); + Assert.Contains("review_body", result.Vectors.Keys); + Assert.Contains("title_country", result.Vectors.Keys); + } + + [Fact] + public async Task TestCreateObjectWithDeterministicId() + { + // START CreateObjectWithDeterministicId + // highlight-start + // In C#, you can generate a deterministic UUID from a string or bytes. + // This helper function creates a UUID v5 for this purpose. + // highlight-end + + var dataObject = new + { + question = "This vector DB is OSS and supports automatic property type inference on import", + answer = "Weaviate" + }; + var dataObjectString = JsonSerializer.Serialize(dataObject); + + var jeopardy = client.Collections.Use("JeopardyQuestion"); + var uuid = await jeopardy.Data.Insert( + dataObject, + // highlight-start + id: GenerateUuid5(dataObjectString) + ); + // highlight-end + // END CreateObjectWithDeterministicId + + Assert.Equal(GenerateUuid5(dataObjectString), uuid); + await jeopardy.Data.DeleteByID(uuid); // Clean up + } + + [Fact] + public async Task TestCreateObjectWithId() + { + // START CreateObjectWithId + var jeopardy = client.Collections.Use("JeopardyQuestion"); + var uuid = await jeopardy.Data.Insert( + new + { + question = "This vector DB is OSS and supports automatic property type inference on import", + answer = "Weaviate" + }, + // highlight-start + id: Guid.Parse("12345678-e64f-5d94-90db-c8cfa3fc1234") + ); + // highlight-end + + Console.WriteLine(uuid); // the return value is the object's UUID + // END CreateObjectWithId + + var result = await jeopardy.Query.FetchObjectByID(uuid); + Assert.NotNull(result); + var props = result.Properties as IDictionary; + Assert.Equal("This vector DB is OSS and supports automatic property type inference on import", props["question"]); + } + + [Fact] + public async Task TestWithGeoCoordinates() + { + // START WithGeoCoordinates + var publications = client.Collections.Use("Publication"); + + var uuid = await publications.Data.Insert( + new + { + headquartersGeoLocation = new GeoCoordinate(52.3932696f, 4.8374263f) + } + ); + // END WithGeoCoordinates + + var result = await publications.Query.FetchObjectByID(uuid); + Assert.NotNull(result); + await publications.Data.DeleteByID(uuid); + } + + [Fact] + public async Task TestCheckForAnObject() + { + // START CheckForAnObject + // generate uuid based on the key properties used during data insert + var objectUuid = GenerateUuid5("Author to fetch"); + // END CheckForAnObject + + var authors = client.Collections.Use("Author"); + await authors.Data.Insert( + new { name = "Author to fetch" }, + id: objectUuid, + vectors: new float[1536]); + + // START CheckForAnObject + // highlight-start + var authorExists = (await authors.Query.FetchObjectByID(objectUuid)) != null; + // highlight-end + + Console.WriteLine("Author exists: " + authorExists); + // END CheckForAnObject + + Assert.True(authorExists); + await authors.Data.DeleteByID(objectUuid); + Assert.False((await authors.Query.FetchObjectByID(objectUuid)) != null); + } + + // START ValidateObject + // Coming soon + // END ValidateObject +} \ No newline at end of file diff --git a/_includes/code/csharp/ManageObjectsDeleteTest.cs b/_includes/code/csharp/ManageObjectsDeleteTest.cs new file mode 100644 index 00000000..c506f820 --- /dev/null +++ b/_includes/code/csharp/ManageObjectsDeleteTest.cs @@ -0,0 +1,156 @@ +using Xunit; +using Weaviate.Client; +using Weaviate.Client.Models; +using System; +using System.Threading.Tasks; +using System.Linq; +using System.Text.Json; + +namespace WeaviateProject.Tests; + +public class ManageObjectsDeleteTest : IAsyncLifetime +{ + private static readonly WeaviateClient client; + private const string COLLECTION_NAME = "EphemeralObject"; + + // Static constructor for one-time setup (like @BeforeAll) + static ManageObjectsDeleteTest() + { + // Note: The C# client doesn't support setting headers like 'X-OpenAI-Api-Key' via the constructor. + // This must be configured in Weaviate's environment variables. + client = new WeaviateClient(new ClientConfiguration { RestAddress = "localhost", RestPort = 8080 }); + } + + // Runs before each test (like @BeforeEach) + public async Task InitializeAsync() + { + if (await client.Collections.Exists(COLLECTION_NAME)) + { + await client.Collections.Delete(COLLECTION_NAME); + } + await client.Collections.Create(new Collection + { + Name = COLLECTION_NAME, + Properties = new[] { Property.Text("name") }.ToList() + }); + } + + // Runs after each test (like @AfterEach and @AfterAll) + public async Task DisposeAsync() + { + if (await client.Collections.Exists(COLLECTION_NAME)) + { + await client.Collections.Delete(COLLECTION_NAME); + } + } + + [Fact] + public async Task TestDeleteObject() + { + var collection = client.Collections.Use(COLLECTION_NAME); + var uuidToDelete = await collection.Data.Insert(new { name = "EphemeralObjectA" }); + Assert.NotNull(await collection.Query.FetchObjectByID(uuidToDelete)); + + // START DeleteObject + await collection.Data.DeleteByID(uuidToDelete); + // END DeleteObject + + Assert.Null(await collection.Query.FetchObjectByID(uuidToDelete)); + } + + [Fact] + public async Task TestBatchDelete() + { + var collection = client.Collections.Use(COLLECTION_NAME); + var objects = Enumerable.Range(0, 5) + .Select(i => new { name = $"EphemeralObject_{i}" }) + .ToArray(); // Creates an array T[] + + await collection.Data.InsertMany(objects); + var initialCount = await collection.Aggregate.OverAll(totalCount: true); + Assert.Equal(5, initialCount.TotalCount); + + // START DeleteBatch + await collection.Data.DeleteMany( + // highlight-start + Filter.Property("name").Like("EphemeralObject*") + // highlight-end + ); + // END DeleteBatch + + var finalCount = await collection.Aggregate.OverAll(totalCount: true); + Assert.Equal(0, finalCount.TotalCount); + } + + [Fact] + public async Task TestDeleteContains() + { + // START DeleteContains + var collection = client.Collections.Use(COLLECTION_NAME); + await collection.Data.InsertMany(new[] + { + new { name = "asia" }, + new { name = "europe" } + }); + + await collection.Data.DeleteMany( + // highlight-start + Filter.Property("name").ContainsAny(["europe", "asia"]) + // highlight-end + ); + // END DeleteContains + } + + [Fact] + public async Task TestDryRun() + { + var collection = client.Collections.Use(COLLECTION_NAME); + var objects = Enumerable.Range(0, 5) + .Select(i => new { name = $"EphemeralObject_{i}" }) + .ToArray(); // Creates an array T[] + + await collection.Data.InsertMany(objects); + + // START DryRun + var result = await collection.Data.DeleteMany( + Filter.Property("name").Like("EphemeralObject*"), + // highlight-start + dryRun: true + // highlight-end + ); + + Console.WriteLine(JsonSerializer.Serialize(result)); + // END DryRun + + Assert.Equal(5, result.Matches); + var finalCount = await collection.Aggregate.OverAll(totalCount: true); + Assert.Equal(5, finalCount.TotalCount); // Objects should not be deleted + } + + [Fact] + public async Task TestBatchDeleteWithIDs() + { + var collection = client.Collections.Use(COLLECTION_NAME); + var objects = Enumerable.Range(0, 5) + .Select(i => new { name = $"EphemeralObject_{i}" }) + .ToArray(); // Creates an array T[] + + await collection.Data.InsertMany(objects); + var initialCount = await collection.Aggregate.OverAll(totalCount: true); + Assert.Equal(5, initialCount.TotalCount); + + // START DeleteByIDBatch + var queryResponse = await collection.Query.FetchObjects(limit: 3); + var ids = queryResponse.Objects.Select(obj => obj.ID.Value).ToList(); + + await collection.Data.DeleteMany( + // highlight-start + Filter.ID.ContainsAny(ids) // Delete the 3 objects + // highlight-end + ); + // END DeleteByIDBatch + + var finalCount = await collection.Aggregate.OverAll(totalCount: true); + Assert.Equal(2, finalCount.TotalCount); + } +} \ No newline at end of file diff --git a/_includes/code/csharp/ManageObjectsImportTest.cs b/_includes/code/csharp/ManageObjectsImportTest.cs new file mode 100644 index 00000000..fd37584e --- /dev/null +++ b/_includes/code/csharp/ManageObjectsImportTest.cs @@ -0,0 +1,446 @@ +using Xunit; +using Weaviate.Client; +using Weaviate.Client.Models; +using System; +using System.Threading.Tasks; +using System.Collections.Generic; +using System.Linq; +using System.IO; +using System.Text.Json; +using System.Security.Cryptography; +using System.Text; +using System.Net.Http; +using System.Globalization; +using CsvHelper; + +namespace WeaviateProject.Tests; + +[Collection("Sequential")] +public class ManageObjectsImportTest : IAsyncLifetime +{ + private static readonly WeaviateClient client; + private const string JsonDataFile = "jeopardy_1k.json"; + private const string CsvDataFile = "jeopardy_1k.csv"; + + // Static constructor for one-time setup (like @BeforeAll) + static ManageObjectsImportTest() + { + // START INSTANTIATION-COMMON + string openaiApiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY"); + if (string.IsNullOrWhiteSpace(openaiApiKey)) + { + throw new ArgumentException("Please set the OPENAI_API_KEY environment variable."); + } + + // Note: The C# client doesn't support setting headers like 'X-OpenAI-Api-Key' via the constructor. + // This must be configured in Weaviate's environment variables. + client = new WeaviateClient(new ClientConfiguration { RestAddress = "localhost", RestPort = 8080 }); + // END INSTANTIATION-COMMON + } + + // A helper method to generate a deterministic UUID from a seed + private static Guid GenerateUuid5(string seed) + { + var namespaceId = Guid.Empty; + var namespaceBytes = namespaceId.ToByteArray(); + var nameBytes = Encoding.UTF8.GetBytes(seed); + + var combinedBytes = new byte[namespaceBytes.Length + nameBytes.Length]; + Buffer.BlockCopy(namespaceBytes, 0, combinedBytes, 0, namespaceBytes.Length); + Buffer.BlockCopy(nameBytes, 0, combinedBytes, namespaceBytes.Length, nameBytes.Length); + + using (var sha1 = SHA1.Create()) + { + var hash = sha1.ComputeHash(combinedBytes); + var newGuid = new byte[16]; + Array.Copy(hash, 0, newGuid, 0, 16); + + newGuid[6] = (byte)((newGuid[6] & 0x0F) | (5 << 4)); + newGuid[8] = (byte)((newGuid[8] & 0x3F) | 0x80); + + (newGuid[0], newGuid[3]) = (newGuid[3], newGuid[0]); + (newGuid[1], newGuid[2]) = (newGuid[2], newGuid[1]); + (newGuid[4], newGuid[5]) = (newGuid[5], newGuid[4]); + (newGuid[6], newGuid[7]) = (newGuid[7], newGuid[6]); + + return new Guid(newGuid); + } + } + + // Runs once before any tests in the class + public async Task InitializeAsync() + { + using var httpClient = new HttpClient(); + var jsonData = await httpClient.GetStreamAsync("https://raw.githubusercontent.com/weaviate-tutorials/edu-datasets/main/jeopardy_1k.json"); + using var fileStream = new FileStream(JsonDataFile, FileMode.Create, FileAccess.Write); + await jsonData.CopyToAsync(fileStream); + } + + // Runs once after all tests in the class + public async Task DisposeAsync() + { + await client.Collections.DeleteAll(); + File.Delete(JsonDataFile); + if (File.Exists(CsvDataFile)) File.Delete(CsvDataFile); + } + + private async Task BeforeEach() + { + await client.Collections.DeleteAll(); + } + + [Fact] + public async Task TestBasicBatchImport() + { + await BeforeEach(); + await client.Collections.Create(new Collection + { + Name = "MyCollection", + VectorConfig = new VectorConfig("default", new Vectorizer.SelfProvided()) + }); + + // START BasicBatchImportExample + var dataRows = Enumerable.Range(0, 5).Select(i => new { title = $"Object {i + 1}" }).ToList(); + + var collection = client.Collections.Use("MyCollection"); + + // The Java client uses insertMany for batching. + // There is no direct equivalent of the Python client's stateful batch manager. + // You collect objects and send them in a single request. + // highlight-start + var response = await collection.Data.InsertMany(add => + { + foreach (var dataRow in dataRows) + { + add(dataRow); + } + }); + // highlight-end + + var failedObjects = response.Where(r => r.Error != null).ToList(); + if (failedObjects.Any()) + { + Console.WriteLine($"Number of failed imports: {failedObjects.Count}"); + Console.WriteLine($"First failed object: {failedObjects.First().Error}"); + } + // END BasicBatchImportExample + + var result = await collection.Aggregate.OverAll(totalCount: true); + Assert.Equal(5, result.TotalCount); + } + + // START ServerSideBatchImportExample + // Coming soon + // END ServerSideBatchImportExample + + [Fact] + public async Task TestBatchImportWithID() + { + await BeforeEach(); + await client.Collections.Create(new Collection + { + Name = "MyCollection", + VectorConfig = new VectorConfig("default", new Vectorizer.SelfProvided()) + }); + + // START BatchImportWithIDExample + var dataToInsert = new List<(object properties, Guid uuid)>(); + for (int i = 0; i < 5; i++) + { + var dataRow = new { title = $"Object {i + 1}" }; + var objUuid = GenerateUuid5(JsonSerializer.Serialize(dataRow)); + dataToInsert.Add((dataRow, objUuid)); + } + + var collection = client.Collections.Use("MyCollection"); + + // highlight-start + var response = await collection.Data.InsertMany(add => + { + foreach (var item in dataToInsert) + { + add(item.properties, item.uuid); + } + }); + // highlight-end + + var failedObjects = response.Where(r => r.Error != null).ToList(); + if (failedObjects.Any()) + { + Console.WriteLine($"Number of failed imports: {failedObjects.Count}"); + Console.WriteLine($"First failed object: {failedObjects.First().Error}"); + } + // END BatchImportWithIDExample + + var result = await collection.Aggregate.OverAll(totalCount: true); + Assert.Equal(5, result.TotalCount); + var lastUuid = dataToInsert[4].uuid; + Assert.NotNull(await collection.Query.FetchObjectByID(lastUuid)); + } + + [Fact] + public async Task TestBatchImportWithVector() + { + await BeforeEach(); + await client.Collections.Create(new Collection + { + Name = "MyCollection", + VectorConfig = new VectorConfig("default", new Vectorizer.SelfProvided()) + }); + + // START BatchImportWithVectorExample + var dataToInsert = new List<(object properties, Guid uuid, float[] vector)>(); + var vector = Enumerable.Repeat(0.1f, 10).ToArray(); + + for (int i = 0; i < 5; i++) + { + var dataRow = new { title = $"Object {i + 1}" }; + var objUuid = GenerateUuid5(JsonSerializer.Serialize(dataRow)); + dataToInsert.Add((dataRow, objUuid, vector)); + } + + var collection = client.Collections.Use("MyCollection"); + + // highlight-start + var response = await collection.Data.InsertMany(add => + { + foreach (var item in dataToInsert) + { + add(item.properties, item.uuid, item.vector); + } + }); + // highlight-end + + var failedObjects = response.Where(r => r.Error != null).ToList(); + if (failedObjects.Any()) + { + Console.WriteLine($"Number of failed imports: {failedObjects.Count}"); + Console.WriteLine($"First failed object: {failedObjects.First().Error}"); + } + // END BatchImportWithVectorExample + + var result = await collection.Aggregate.OverAll(totalCount: true); + Assert.Equal(5, result.TotalCount); + } + + [Fact] + public async Task TestBatchImportWithCrossReference() + { + await BeforeEach(); + await client.Collections.Create(new Collection { Name = "Publication", Properties = [Property.Text("title")] }); + await client.Collections.Create(new Collection + { + Name = "Author", + Properties = [Property.Text("name")], + References = [new Reference("writesFor", "Publication")] + }); + + var authors = client.Collections.Use("Author"); + var publications = client.Collections.Use("Publication"); + + var fromUuid = await authors.Data.Insert(new { name = "Jane Austen" }); + var targetUuid = await publications.Data.Insert(new { title = "Ye Olde Times" }); + + // START BatchImportWithRefExample + var collection = client.Collections.Use("Author"); + + var response = await collection.Data.ReferenceAddMany(new DataReference(fromUuid, "writesFor", targetUuid)); + + if (response.HasErrors) + { + Console.WriteLine($"Number of failed imports: {response.Errors.Count}"); + Console.WriteLine($"First failed object: {response.Errors.First()}"); + } + // END BatchImportWithRefExample + + var result = await collection.Query.FetchObjectByID(fromUuid, returnReferences: [new QueryReference("writesFor")]); + Assert.NotNull(result); + Assert.True(result.References.ContainsKey("writesFor")); + } + + [Fact] + public async Task TestImportWithNamedVectors() + { + await BeforeEach(); + await client.Collections.Create(new Collection + { + Name = "MyCollection", + VectorConfig = new[] + { + new VectorConfig("title", new Vectorizer.SelfProvided()), + new VectorConfig("body", new Vectorizer.SelfProvided()) + }, + Properties = [Property.Text("title"), Property.Text("body")] + }); + + // START BatchImportWithNamedVectors + // Prepare the data and vectors + var dataToInsert = new List<(object properties, Dictionary vectors)>(); + for (int i = 0; i < 5; i++) + { + var dataRow = new { title = $"Object {i + 1}", body = $"Body {i + 1}" }; + var titleVector = Enumerable.Repeat(0.12f, 1536).ToArray(); + var bodyVector = Enumerable.Repeat(0.34f, 1536).ToArray(); + // highlight-start + var namedVectors = new Dictionary + { + { "title", titleVector }, + { "body", bodyVector } + }; + dataToInsert.Add((dataRow, namedVectors)); + // highlight-end + } + + var collection = client.Collections.Use("MyCollection"); + + // Insert the data using InsertMany + // highlight-start + var response = await collection.Data.InsertMany(add => + { + foreach (var item in dataToInsert) + { + add(item.properties, vectors: item.vectors); + } + }); + // highlight-end + + // Check for errors + var failedObjects = response.Where(r => r.Error != null).ToList(); + if (failedObjects.Any()) + { + Console.WriteLine($"Number of failed imports: {failedObjects.Count}"); + Console.WriteLine($"First failed object error: {failedObjects.First().Error}"); + } + // END BatchImportWithNamedVectors + } + + [Fact] + public async Task TestJsonStreaming() + { + await BeforeEach(); + await client.Collections.Create(new Collection { Name = "JeopardyQuestion" }); + + // START JSON streaming + int batchSize = 100; + var batch = new List(batchSize); + var collection = client.Collections.Use("JeopardyQuestion"); + + Console.WriteLine("JSON streaming, to avoid running out of memory on large files..."); + using var fileStream = File.OpenRead(JsonDataFile); + var jsonObjects = JsonSerializer.DeserializeAsyncEnumerable>(fileStream); + + await foreach (var obj in jsonObjects) + { + if (obj == null) continue; + var properties = new { question = obj["Question"], answer = obj["Answer"] }; + batch.Add(properties); + + if (batch.Count == batchSize) + { + await collection.Data.InsertMany(add => + { + foreach (var item in batch) + { + add(item); + } + }); Console.WriteLine($"Imported {batch.Count} articles..."); + batch.Clear(); + } + } + + if (batch.Any()) + { + await collection.Data.InsertMany(add => + { + foreach (var item in batch) + { + add(item); + } + }); Console.WriteLine($"Imported remaining {batch.Count} articles..."); + } + + Console.WriteLine("Finished importing articles."); + // END JSON streaming + + var result = await collection.Aggregate.OverAll(totalCount: true); + Assert.Equal(1000, result.TotalCount); + } + + [Fact] + public async Task TestCsvStreaming() + { + await BeforeEach(); + // Create a CSV file from the JSON for the test + using (var fileStream = File.OpenRead(JsonDataFile)) + using (var writer = new StreamWriter(CsvDataFile)) + using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture)) + { + var jsonObjects = JsonSerializer.DeserializeAsyncEnumerable>(fileStream); + csv.WriteHeader(); + await csv.NextRecordAsync(); + await foreach (var obj in jsonObjects) + { + if (obj != null) + { + csv.WriteRecord(new JeopardyQuestion { Question = obj["Question"]?.ToString(), Answer = obj["Answer"]?.ToString() }); + await csv.NextRecordAsync(); + } + } + } + + await client.Collections.Create(new Collection { Name = "JeopardyQuestion" }); + + // START CSV streaming + int batchSize = 100; + var batch = new List(batchSize); + var collection = client.Collections.Use("JeopardyQuestion"); + + Console.WriteLine("CSV streaming to not load all records in RAM at once..."); + using (var reader = new StreamReader(CsvDataFile)) + using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture)) + { + var records = csv.GetRecords(); + foreach (var record in records) + { + var properties = new { question = record.Question, answer = record.Answer }; + batch.Add(properties); + + if (batch.Count == batchSize) + { + await collection.Data.InsertMany(add => + { + foreach (var item in batch) + { + add(item); + } + }); Console.WriteLine($"Imported {batch.Count} articles..."); + batch.Clear(); + } + } + } + + if (batch.Any()) + { + await collection.Data.InsertMany(add => + { + foreach (var item in batch) + { + add(item); + } + }); Console.WriteLine($"Imported remaining {batch.Count} articles..."); + } + + Console.WriteLine("Finished importing articles."); + // END CSV streaming + + var result = await collection.Aggregate.OverAll(totalCount: true); + Assert.Equal(1000, result.TotalCount); + } + + // Helper class for CSV parsing + private class JeopardyQuestion + { + public string? Question { get; set; } + public string? Answer { get; set; } + } +} \ No newline at end of file diff --git a/_includes/code/csharp/ManageObjectsReadAllTest.cs b/_includes/code/csharp/ManageObjectsReadAllTest.cs new file mode 100644 index 00000000..dfbb388b --- /dev/null +++ b/_includes/code/csharp/ManageObjectsReadAllTest.cs @@ -0,0 +1,125 @@ +using Xunit; +using Weaviate.Client; +using Weaviate.Client.Models; +using System; +using System.Threading.Tasks; +using System.Collections.Generic; +using System.Text.Json; + +namespace WeaviateProject.Tests; + +public class ManageObjectsReadAllTest : IAsyncLifetime +{ + private static readonly WeaviateClient client; + + // Static constructor for one-time setup (like @BeforeAll) + static ManageObjectsReadAllTest() + { + // Note: The C# client doesn't support setting headers like 'X-OpenAI-Api-Key' via the constructor. + // This must be configured in Weaviate's environment variables. + client = new WeaviateClient(new ClientConfiguration { RestAddress = "localhost", RestPort = 8080 }); + } + + // Runs once before any tests in the class + public async Task InitializeAsync() + { + // Simulate weaviate-datasets by creating and populating collections + // Create WineReview collection + if (await client.Collections.Exists("WineReview")) + { + await client.Collections.Delete("WineReview"); + } + + var wineReview = await client.Collections.Create(new Collection { Name = "WineReview" }); + await wineReview.Data.InsertMany(new[] + { + new { title = "Review A" }, + new { title = "Review B" } + }); + + // Create WineReviewMT collection + if (await client.Collections.Exists("WineReviewMT")) + { + await client.Collections.Delete("WineReviewMT"); + } + var wineReviewMT = await client.Collections.Create(new Collection + { + Name = "WineReviewMT", + MultiTenancyConfig = new MultiTenancyConfig { Enabled = true, AutoTenantCreation = true } + }); + + // Create and populate tenants + await wineReviewMT.Tenants.Add(["tenantA", "tenantB"]); + await wineReviewMT.WithTenant("tenantA").Data.Insert(new { title = "Tenant A Review 1" }); + await wineReviewMT.WithTenant("tenantB").Data.Insert(new { title = "Tenant B Review 1" }); + } + + // Runs once after all tests in the class + public async Task DisposeAsync() + { + await client.Collections.Delete("WineReview"); + await client.Collections.Delete("WineReviewMT"); + } + + [Fact] + public async Task TestReadAllProps() + { + // START ReadAllProps + var collection = client.Collections.Use("WineReview"); + + // highlight-start + await foreach (var item in collection.Iterator()) + { + // highlight-end + Console.WriteLine($"{item.ID} {JsonSerializer.Serialize(item.Properties)}"); + } + // END ReadAllProps + } + + [Fact] + public async Task TestReadAllVectors() + { + // START ReadAllVectors + var collection = client.Collections.Use("WineReview"); + + await foreach (var item in collection.Iterator( + // highlight-start + returnMetadata: MetadataOptions.Vector // If using named vectors, you can specify ones to include + )) + // highlight-end + { + Console.WriteLine(JsonSerializer.Serialize(item.Properties)); + // highlight-start + Console.WriteLine(JsonSerializer.Serialize(item.Vectors)); + // highlight-end + } + // END ReadAllVectors + } + + // TODO[g-despot] Grpc.Core.RpcException : Status(StatusCode="Unknown", Detail="explorer: list class: search: object search at index winereviewmt: class WineReviewMT has multi-tenancy enabled, but request was without tenant") + [Fact] + public async Task TestReadAllTenants() + { + // START ReadAllTenants + var multiCollection = client.Collections.Use("WineReviewMT"); + + // Get a list of tenants + // highlight-start + var tenants = await multiCollection.Tenants.List(); + // highlight-end + + // Iterate through tenants + foreach (var tenant in tenants) + { + // Iterate through objects within each tenant + // highlight-start + var tenantCollection = multiCollection.WithTenant(tenant.Name); + await foreach (var item in tenantCollection.Iterator()) + { + // highlight-end + Console.WriteLine($"{tenant.Name}: {JsonSerializer.Serialize(item.Properties)}"); + } + } + // END ReadAllTenants + } +} \ No newline at end of file diff --git a/_includes/code/csharp/ManageObjectsReadTest.cs b/_includes/code/csharp/ManageObjectsReadTest.cs new file mode 100644 index 00000000..5ed97582 --- /dev/null +++ b/_includes/code/csharp/ManageObjectsReadTest.cs @@ -0,0 +1,123 @@ +using Xunit; +using Weaviate.Client; +using Weaviate.Client.Models; +using System; +using System.Threading.Tasks; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; +using static Weaviate.Client.Auth; + +namespace WeaviateProject.Tests; + +public class ManageObjectsReadTest : IDisposable +{ + private static readonly WeaviateClient client; + + // Static constructor for one-time setup (like @BeforeAll) + static ManageObjectsReadTest() + { + // START INSTANTIATION-COMMON + // Best practice: store your credentials in environment variables + string weaviateUrl = Environment.GetEnvironmentVariable("WEAVIATE_URL"); + string weaviateApiKey = Environment.GetEnvironmentVariable("WEAVIATE_API_KEY"); + + client = Connect.Cloud(weaviateUrl, weaviateApiKey); + // END INSTANTIATION-COMMON + } + + // Dispose is called once after all tests in the class are finished (like @AfterAll) + public void Dispose() + { + // The C# client does not have a close() method; disposal is handled + // by the garbage collector when the static instance is no longer needed. + GC.SuppressFinalize(this); + } + + [Fact] + public async Task TestReadObject() + { + // START ReadSimpleObject + var jeopardy = client.Collections.Use("JeopardyQuestion"); + + // highlight-start + var dataObject = await jeopardy.Query.FetchObjectByID(Guid.Parse("00ff6900-e64f-5d94-90db-c8cfa3fc851b")); + // highlight-end + + if (dataObject != null) + { + Console.WriteLine(JsonSerializer.Serialize(dataObject.Properties)); + } + // END ReadSimpleObject + } + + [Fact] + public async Task TestReadObjectWithVector() + { + // START ReadObjectWithVector + var jeopardy = client.Collections.Use("JeopardyQuestion"); + + var dataObject = await jeopardy.Query.FetchObjectByID(Guid.Parse("00ff6900-e64f-5d94-90db-c8cfa3fc851b"), + // highlight-start + returnMetadata: MetadataOptions.Vector + ); + // highlight-end + + if (dataObject?.Vectors.ContainsKey("default") ?? false) + { + var vector = dataObject.Vectors["default"]; + Console.WriteLine(vector); + } + // END ReadObjectWithVector + } + + [Fact] + public async Task TestReadObjectNamedVectors() + { + // START ReadObjectNamedVectors + var reviews = client.Collections.Use("WineReviewNV"); // Collection with named + // END ReadObjectNamedVectors // vectors + + var someObjResponse = await reviews.Query.FetchObjects(limit: 1); + if (!someObjResponse.Objects.Any()) + { + return; // Skip if no data + } + var objUuid = someObjResponse.Objects.First().ID; + var vectorNames = new List { "title", "review_body" }; + + // START ReadObjectNamedVectors + var dataObject = await reviews.Query.FetchObjectByID(objUuid.Value, // Object UUID + // highlight-start + returnMetadata: MetadataOptions.Vector // Specify to include vectors + ); + // highlight-end + + // The vectors are returned in the `Vectors` property as a dictionary + if (dataObject != null) + { + foreach (var name in vectorNames) + { + if (dataObject.Vectors.TryGetValue(name, out var vector)) + { + Console.WriteLine(vector); + } + } + } + // END ReadObjectNamedVectors + } + + [Fact] + public async Task TestCheckObject() + { + // START CheckForAnObject + var jeopardy = client.Collections.Use("JeopardyQuestion"); + + // The C# client checks for existence by attempting to fetch an object and checking for null. + var dataObject = await jeopardy.Query.FetchObjectByID(Guid.Parse("00ff6900-e64f-5d94-90db-c8cfa3fc851b")); + bool exists = dataObject != null; + + Console.WriteLine(exists); + // END CheckForAnObject + } +} \ No newline at end of file diff --git a/_includes/code/csharp/ManageObjectsUpdateTest.cs b/_includes/code/csharp/ManageObjectsUpdateTest.cs new file mode 100644 index 00000000..cc4c885e --- /dev/null +++ b/_includes/code/csharp/ManageObjectsUpdateTest.cs @@ -0,0 +1,217 @@ +using Xunit; +using Weaviate.Client; +using Weaviate.Client.Models; +using System; +using System.Threading.Tasks; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; + +namespace WeaviateProject.Tests; + +public class ManageObjectsUpdateTest : IAsyncLifetime +{ + private static readonly WeaviateClient client; + + // Static constructor for one-time setup + static ManageObjectsUpdateTest() + { + // START INSTANTIATION-COMMON + // Note: The C# client doesn't support setting headers like 'X-OpenAI-Api-Key' via the constructor. + // This must be configured in Weaviate's environment variables. + client = new WeaviateClient(new ClientConfiguration { RestAddress = "localhost", RestPort = 8080 }); + // END INSTANTIATION-COMMON + } + + // Runs once before any tests in the class (like @BeforeAll) + public async Task InitializeAsync() + { + // Simulate weaviate-datasets and set up collections + if (await client.Collections.Exists("WineReviewNV")) + { + await client.Collections.Delete("WineReviewNV"); + } + await client.Collections.Create(new Collection + { + Name = "WineReviewNV", + Properties = + [ + Property.Text("review_body", description: "Review body"), + Property.Text("title", description: "Name of the wine"), + Property.Text("country", description: "Originating country") + ], + VectorConfig = new[] + { + new VectorConfig("title", new Vectorizer.Text2VecContextionary()), + new VectorConfig("review_body", new Vectorizer.Text2VecContextionary()), + new VectorConfig( + "title_country", + new Vectorizer.Text2VecContextionary { SourceProperties = ["title", "country"] } + ) + } + }); + + // highlight-start + // ===== Add three mock objects to the WineReviewNV collection ===== + var reviews = client.Collections.Use("WineReviewNV"); + await reviews.Data.InsertMany(new[] + { + new { title = "Mock Wine A", review_body = "A fine mock vintage.", country = "Mocktugal" }, + new { title = "Mock Wine B", review_body = "Notes of mockberry.", country = "Mockstralia" }, + new { title = "Mock Wine C", review_body = "Pairs well with mock turtle soup.", country = "Republic of Mockdova" } + }); + // highlight-end + + // START Define the class + if (await client.Collections.Exists("JeopardyQuestion")) + { + await client.Collections.Delete("JeopardyQuestion"); + } + await client.Collections.Create(new Collection + { + Name = "JeopardyQuestion", + Description = "A Jeopardy! question", + Properties = + [ + Property.Text("question", description: "The question"), + Property.Text("answer", description: "The answer"), + Property.Number("points", description: "The points the question is worth") + ], + VectorConfig = new VectorConfig("default", new Vectorizer.Text2VecContextionary()) + }); + // END Define the class + } + + // Runs once after all tests in the class (like @AfterAll) + public async Task DisposeAsync() + { + await client.Collections.Delete("WineReviewNV"); + await client.Collections.Delete("JeopardyQuestion"); + } + + // START DelProps + private static async Task DelProps(WeaviateClient client, Guid uuidToUpdate, string collectionName, + IEnumerable propNames) + { + var collection = client.Collections.Use(collectionName); + + // fetch the object to update + var objectData = await collection.Query.FetchObjectByID(uuidToUpdate); + if (objectData?.Properties is not IDictionary propertiesToUpdate) + { + return; + } + + // remove unwanted properties + foreach (var propName in propNames) + { + propertiesToUpdate.Remove(propName); + } + + // replace the properties + await collection.Data.Replace(uuidToUpdate, propertiesToUpdate); + } + // END DelProps + + [Fact] + public async Task TestUpdateAndReplaceFlow() + { + var jeopardy = client.Collections.Use("JeopardyQuestion"); + + var uuid = await jeopardy.Data.Insert(new + { + question = "Test question", + answer = "Test answer", + points = -1 + }); + + // START UpdateProps + await jeopardy.Data.Replace(uuid, + // highlight-start + data: new { points = 100 } + // highlight-end + ); + // END UpdateProps + + var result1 = await jeopardy.Query.FetchObjectByID(uuid); + Assert.NotNull(result1); + var props1 = result1.Properties as IDictionary; + Assert.Equal(100d, props1["points"]); + + + var vector = Enumerable.Repeat(0.12345f, 300).ToArray(); + + // TODO[g-despot] Not implemented + // START UpdateVector + // Coming soon + // END UpdateVector + // await jeopardy.Data.Update(uuid, + // properties: new { points = 100 }, + // // highlight-start + // vector: vector + // // highlight-end + // ); + + // var result2 = await jeopardy.Query.FetchObjectByID(uuid, returnMetadata: MetadataOptions.Vector); + // Assert.NotNull(result2); + // Assert.Equal(300, result2.Vectors["default"].Dimensions); + + + // TODO[g-despot] Not implemented + // START UpdateNamedVector + // Coming soon + // END UpdateNamedVector + + var reviews = client.Collections.Use("WineReviewNV"); + var reviewResponse = await reviews.Query.FetchObjects(limit: 1); + var reviewUuid = reviewResponse.Objects.First().ID.Value; + + var titleVector = Enumerable.Repeat(0.12345f, 300).ToArray(); + var reviewBodyVector = Enumerable.Repeat(0.23456f, 300).ToArray(); + var titleCountryVector = Enumerable.Repeat(0.34567f, 300).ToArray(); + + // await reviews.Data.Update(reviewUuid, + // data: new + // { + // title = "A delicious wine", + // review_body = "This mystery wine is a delight to the senses.", + // country = "Mordor" + // }, + // // highlight-start + // vectors: new Dictionary + // { + // { "title", titleVector }, + // { "review_body", reviewBodyVector }, + // { "title_country", titleCountryVector } + // } + // // highlight-end + // ); + + + // START Replace + // highlight-start + await jeopardy.Data.Replace( + // highlight-end + uuid, + data: new { answer = "Replaced" } + // The other properties will be deleted + ); + // END Replace + + var result3 = await jeopardy.Query.FetchObjectByID(uuid); + Assert.NotNull(result3); + var props3 = result3.Properties as IDictionary; + Assert.Equal("Replaced", props3["answer"].ToString()); + Assert.DoesNotContain("question", props3.Keys); + + // START DelProps + + await DelProps(client, uuid, "JeopardyQuestion", new[] { "answer" }); + // END DelProps + + var result4 = await jeopardy.Query.FetchObjectByID(uuid); + Assert.NotNull(result4); + var props4 = result4.Properties as IDictionary; + Assert.False(props4.ContainsKey("answer")); + } +} \ No newline at end of file diff --git a/_includes/code/csharp/ModelProvidersTest.cs b/_includes/code/csharp/ModelProvidersTest.cs new file mode 100644 index 00000000..e69de29b diff --git a/_includes/code/csharp/QuickstartLocalTest.cs b/_includes/code/csharp/QuickstartLocalTest.cs new file mode 100644 index 00000000..c121d3de --- /dev/null +++ b/_includes/code/csharp/QuickstartLocalTest.cs @@ -0,0 +1,113 @@ +using Weaviate.Client; +using Weaviate.Client.Models; +using System; +using System.Threading.Tasks; +using System.Text.Json; +using System.Linq; +using System.Collections.Generic; +using System.Net.Http; +using Xunit; + +namespace WeaviateProject.Examples; + +[Collection("Sequential")] // Ensures tests in this class run one after another +public class QuickstartLocalTest +{ + [Fact] + public async Task TestConnectionIsReady() + { + // START InstantiationExample + using var client = Connect.Local(); + + // highlight-start + // GetMeta returns server info. A successful call indicates readiness. + var meta = await client.GetMeta(); + Console.WriteLine(meta); + // highlight-end + + // The 'using' statement handles freeing up resources automatically. + // END InstantiationExample + } + + [Fact] + public async Task FullQuickstartWorkflowTest() + { + using var client = Connect.Local(); + string collectionName = "Question"; + + // Clean up previous runs if they exist + if (await client.Collections.Exists(collectionName)) + { + await client.Collections.Delete(collectionName); + } + + // START CreateCollection + // highlight-start + var questions = await client.Collections.Create(new Collection + { + Name = collectionName, + Properties = new() + { + Property.Text("answer"), + Property.Text("question"), + Property.Text("category") + }, + VectorConfig = new VectorConfig("default", new Vectorizer.Text2VecContextionary()), // Configure the text2vec-contextionary integration + GenerativeConfig = new Generative.CohereConfig() // Configure the Cohere generative AI integration + }); + // highlight-end + // END CreateCollection + + // START Import + // Get JSON data using HttpClient + using var httpClient = new HttpClient(); + var jsonData = await httpClient.GetStringAsync("https://raw.githubusercontent.com/weaviate-tutorials/quickstart/main/data/jeopardy_tiny.json"); + + // highlight-start + var questionsToInsert = new List(); + + // Parse and prepare objects using System.Text.Json + var jsonObjects = JsonSerializer.Deserialize>>(jsonData); + foreach (var jsonObj in jsonObjects) + { + questionsToInsert.Add(new + { + answer = jsonObj["Answer"].GetString(), + question = jsonObj["Question"].GetString(), + category = jsonObj["Category"].GetString() + }); + } + + // Call InsertMany with the list of objects converted to an array + var insertResponse = await questions.Data.InsertMany(questionsToInsert.ToArray()); + // highlight-end + // END Import + + // TODO[g-despot] Error handling missing + // Check for errors + // if (insertResponse.HasErrors) + // { + // Console.WriteLine($"Number of failed imports: {insertResponse.Errors.Count}"); + // Console.WriteLine($"First failed object error: {insertResponse.Errors.First()}"); + // } + // else + // { + // Console.WriteLine($"Successfully inserted {insertResponse.Results.Count} objects."); + // } + + // START NearText + // highlight-start + var response = await questions.Query.NearText("biology", limit: 2); + // highlight-end + + foreach (var obj in response.Objects) + { + Console.WriteLine(JsonSerializer.Serialize(obj.Properties)); + } + // END NearText + } + + // START RAG + // Coming soon + // END RAG +} \ No newline at end of file diff --git a/_includes/code/csharp/QuickstartTest.cs b/_includes/code/csharp/QuickstartTest.cs new file mode 100644 index 00000000..12031c4f --- /dev/null +++ b/_includes/code/csharp/QuickstartTest.cs @@ -0,0 +1,124 @@ +using Weaviate.Client; +using Weaviate.Client.Models; +using System; +using System.Threading.Tasks; +using System.Text.Json; +using System.Collections.Generic; +using System.Net.Http; +using Xunit; + +namespace WeaviateProject.Examples; + +[Collection("Sequential")] // Ensures tests in this class run one after another +public class QuickstartTest +{ + // TODO[g-despot] Replace meta with readiness + [Fact] + public static async Task TestConnectionIsReady() + { + // START InstantiationExample + // Best practice: store your credentials in environment variables + string weaviateUrl = Environment.GetEnvironmentVariable("WEAVIATE_URL"); + string weaviateApiKey = Environment.GetEnvironmentVariable("WEAVIATE_API_KEY"); + + WeaviateClient client = Connect.Cloud( + weaviateUrl, + weaviateApiKey + ); + + // highlight-start + // GetMeta returns server info. A successful call indicates readiness. + var meta = await client.GetMeta(); + Console.WriteLine(meta); + // highlight-end + // END InstantiationExample + } + + [Fact] + public static async Task FullQuickstartWorkflowTest() + { + // Best practice: store your credentials in environment variables + string weaviateUrl = Environment.GetEnvironmentVariable("WEAVIATE_URL"); + string weaviateApiKey = Environment.GetEnvironmentVariable("WEAVIATE_API_KEY"); + string collectionName = "Question"; + + WeaviateClient client = Connect.Cloud( + weaviateUrl, + weaviateApiKey + ); + if (await client.Collections.Exists(collectionName)) + { + await client.Collections.Delete(collectionName); + } + // START CreateCollection + // highlight-start + var questions = await client.Collections.Create(new Collection + { + Name = collectionName, + Properties = new() + { + Property.Text("answer"), + Property.Text("question"), + Property.Text("category") + }, + VectorConfig = new VectorConfig("default", new Vectorizer.Text2VecWeaviate()), // Configure the Weaviate Embeddings integration + GenerativeConfig = new Generative.CohereConfig() // Configure the Cohere generative AI integration + }); + // highlight-end + // END CreateCollection + + // START Import + // Get JSON data using HttpClient + using var httpClient = new HttpClient(); + var jsonData = await httpClient.GetStringAsync("https://raw.githubusercontent.com/weaviate-tutorials/quickstart/main/data/jeopardy_tiny.json"); + + // highlight-start + var questionsToInsert = new List(); + + // Parse and prepare objects using System.Text.Json + var jsonObjects = JsonSerializer.Deserialize>>(jsonData); + foreach (var jsonObj in jsonObjects) + { + questionsToInsert.Add(new + { + answer = jsonObj["Answer"].GetString(), + question = jsonObj["Question"].GetString(), + category = jsonObj["Category"].GetString() + }); + } + + // Call InsertMany with the list of objects converted to an array + var insertResponse = await questions.Data.InsertMany(questionsToInsert.ToArray()); + // highlight-end + // END Import + + // TODO[g-despot] Error handling missing + // Check for errors + // if (insertResponse.HasErrors) + // { + // Console.WriteLine($"Number of failed imports: {insertResponse.Errors.Count}"); + // Console.WriteLine($"First failed object error: {insertResponse.Errors.First()}"); + // } + // else + // { + // Console.WriteLine($"Successfully inserted {insertResponse.Results.Count} objects."); + // } + + // START NearText + // highlight-start + var response = await questions.Query.NearText("biology", limit: 2); + // highlight-end + + foreach (var obj in response.Objects) + { + Console.WriteLine(JsonSerializer.Serialize(obj.Properties)); + } + // END NearText + + await client.Collections.Delete(collectionName); + } + + // START RAG + // Coming soon + // END RAG +} \ No newline at end of file diff --git a/_includes/code/csharp/SearchAggregateTest.cs b/_includes/code/csharp/SearchAggregateTest.cs new file mode 100644 index 00000000..34d3f88a --- /dev/null +++ b/_includes/code/csharp/SearchAggregateTest.cs @@ -0,0 +1,194 @@ +using Xunit; +using Weaviate.Client; +using Weaviate.Client.Models; +using System; +using System.Threading.Tasks; +using System.Text.Json; + +namespace WeaviateProject.Tests; + +public class SearchAggregateTest : IDisposable +{ + private static readonly WeaviateClient client; + + // Static constructor for one-time setup (like @BeforeAll) + static SearchAggregateTest() + { + // START INSTANTIATION-COMMON + // Best practice: store your credentials in environment variables + string weaviateUrl = Environment.GetEnvironmentVariable("WEAVIATE_URL"); + string weaviateApiKey = Environment.GetEnvironmentVariable("WEAVIATE_API_KEY"); + + client = Connect.Cloud( + weaviateUrl, + weaviateApiKey + ); + // END INSTANTIATION-COMMON + } + + // Dispose is called once after all tests in the class are finished (like @AfterAll) + public void Dispose() + { + // The C# client manages connections automatically and does not require an explicit 'close' method. + GC.SuppressFinalize(this); + } + + [Fact] + public async Task TestMetaCount() + { + // START MetaCount + var jeopardy = client.Collections.Use("JeopardyQuestion"); + var response = await jeopardy.Aggregate.OverAll( + // highlight-start + totalCount: true + // highlight-end + ); + + Console.WriteLine(response.TotalCount); + // END MetaCount + } + + [Fact] + public async Task TestTextProp() + { + // START TextProp + var jeopardy = client.Collections.Use("JeopardyQuestion"); + var response = await jeopardy.Aggregate.OverAll( + // highlight-start + metrics: Metrics.ForProperty("answer") + .Text( + topOccurrencesCount: true, + topOccurrencesValue: true, + minOccurrences: 5 // Corresponds to topOccurrencesCutoff + ) + // highlight-end + ); + + var answerMetrics = response.Properties["answer"] as Aggregate.Text; + if (answerMetrics != null) + { + Console.WriteLine(JsonSerializer.Serialize(answerMetrics.TopOccurrences)); + } + // END TextProp + } + + [Fact] + public async Task TestIntProp() + { + // START IntProp + var jeopardy = client.Collections.Use("JeopardyQuestion"); + var response = await jeopardy.Aggregate.OverAll( + // highlight-start + // Use .Number for floats (NUMBER datatype in Weaviate) + metrics: Metrics.ForProperty("points") + .Integer( + sum: true, + maximum: true, + minimum: true + ) + // highlight-end + ); + + var pointsMetrics = response.Properties["points"] as Aggregate.Integer; + if (pointsMetrics != null) + { + Console.WriteLine($"Sum: {pointsMetrics.Sum}"); + Console.WriteLine($"Max: {pointsMetrics.Maximum}"); + Console.WriteLine($"Min: {pointsMetrics.Minimum}"); + } + // END IntProp + } + + [Fact] + public async Task TestGroupBy() + { + // START groupBy + var jeopardy = client.Collections.Use("JeopardyQuestion"); + var response = await jeopardy.Aggregate.OverAll( + // highlight-start + groupBy: new Aggregate.GroupBy("round") + // highlight-end + ); + + // print rounds names and the count for each + foreach (var group in response.Groups) + { + Console.WriteLine($"Value: {group.GroupedBy.Value} Count: {group.TotalCount}"); + } + // END groupBy + } + + //TODO[g-despot] Why doesn query need to be list? + [Fact] + public async Task TestNearTextWithLimit() + { + // START nearTextWithLimit + var jeopardy = client.Collections.Use("JeopardyQuestion"); + var response = await jeopardy.Aggregate.NearText( + ["animals in space"], + // highlight-start + limit: 10, + // highlight-end + metrics: Metrics.ForProperty("points").Number(sum: true) + ); + + var pointsMetrics = response.Properties["points"] as Aggregate.Number; + Console.WriteLine(JsonSerializer.Serialize(pointsMetrics)); + // END nearTextWithLimit + } + + [Fact] + public async Task TestHybrid() + { + // START HybridExample + var jeopardy = client.Collections.Use("JeopardyQuestion"); + var response = await jeopardy.Aggregate.Hybrid( + "animals in space", + // Additional parameters are available, such as `bm25Operator`, `filters`, etc. + // highlight-start + objectLimit: 10, + // highlight-end + metrics: Metrics.ForProperty("points").Number(sum: true) + ); + + var pointsMetrics = response.Properties["points"] as Aggregate.Number; + Console.WriteLine(JsonSerializer.Serialize(pointsMetrics)); + // END HybridExample + } + + [Fact] + public async Task TestNearTextWithDistance() + { + // Note: The C# client supports the 'distance' parameter for nearText aggregations. + // START nearTextWithDistance + var jeopardy = client.Collections.Use("JeopardyQuestion"); + var response = await jeopardy.Aggregate.NearText( + ["animals in space"], + // highlight-start + distance: 0.19, + // highlight-end + metrics: Metrics.ForProperty("points").Number(sum: true) + ); + + var pointsMetrics = response.Properties["points"] as Aggregate.Number; + Console.WriteLine(JsonSerializer.Serialize(pointsMetrics)); + // END nearTextWithDistance + } + + [Fact] + public async Task TestWhereFilter() + { + // Note: The C# client supports the 'filter' parameter for OverAll aggregations. + // START whereFilter + var jeopardy = client.Collections.Use("JeopardyQuestion"); + var response = await jeopardy.Aggregate.OverAll( + // highlight-start + filter: Filter.Property("round").Equal("Final Jeopardy!"), + // highlight-end + totalCount: true + ); + + Console.WriteLine(response.TotalCount); + // END whereFilter + } +} \ No newline at end of file diff --git a/_includes/code/csharp/SearchBasicTest.cs b/_includes/code/csharp/SearchBasicTest.cs new file mode 100644 index 00000000..b943a6d6 --- /dev/null +++ b/_includes/code/csharp/SearchBasicTest.cs @@ -0,0 +1,261 @@ +using Xunit; +using Weaviate.Client; +using Weaviate.Client.Models; +using System; +using System.Threading.Tasks; +using System.Collections.Generic; +using System.Text.Json; +using System.Linq; + +// Note: This code assumes the existence of a Weaviate instance populated +// with 'JeopardyQuestion' and 'WineReviewMT' collections as per the Python examples. +public class SearchBasicTest : IAsyncLifetime +{ + private WeaviateClient client; + + public Task InitializeAsync() + { + // ================================ + // ===== INSTANTIATION-COMMON ===== + // ================================ + + // Best practice: store your credentials in environment variables + var weaviateUrl = Environment.GetEnvironmentVariable("WEAVIATE_URL"); + var weaviateApiKey = Environment.GetEnvironmentVariable("WEAVIATE_API_KEY"); + var openaiApiKey = Environment.GetEnvironmentVariable("OPENAI_APIKEY"); + + // The Connect.Cloud helper method is a straightforward way to connect. + // We add the OpenAI API key to the headers for the text2vec-openai module. + client = Connect.Cloud( + weaviateUrl, + weaviateApiKey + ); + + return Task.CompletedTask; + } + + public Task DisposeAsync() + { + // The C# client manages its connections automatically and does not require an explicit 'close' method. + return Task.CompletedTask; + } + + [Fact] + public async Task BasicGet() + { + // ============================== + // ===== BASIC GET EXAMPLES ===== + // ============================== + + // BasicGetPython + var jeopardy = client.Collections.Use("JeopardyQuestion"); + // highlight-start + var response = await jeopardy.Query.FetchObjects(); + // highlight-end + + foreach (var o in response.Objects) + { + Console.WriteLine(JsonSerializer.Serialize(o.Properties)); + } + // END BasicGetPython + + Assert.Equal("JeopardyQuestion", response.Objects.First().Collection); + Assert.True(response.Objects.First().Properties.ContainsKey("question")); + } + + [Fact] + public async Task GetWithLimit() + { + // ==================================== + // ===== BASIC GET LIMIT EXAMPLES ===== + // ==================================== + + // GetWithLimitPython + var jeopardy = client.Collections.Use>("JeopardyQuestion"); + var response = await jeopardy.Query.FetchObjects( + // highlight-start + limit: 1 + // highlight-end + ); + + foreach (var o in response.Objects) + { + Console.WriteLine(JsonSerializer.Serialize(o.Properties)); + } + // END GetWithLimitPython + + Assert.Equal("JeopardyQuestion", response.Objects.First().Collection); + Assert.True(response.Objects.First().Properties.ContainsKey("question")); + Assert.Single(response.Objects); + } + + [Fact] + public async Task GetProperties() + { + // ========================================== + // ===== GET OBJECT PROPERTIES EXAMPLES ===== + // ========================================== + + // GetPropertiesPython + var jeopardy = client.Collections.Use>("JeopardyQuestion"); + var response = await jeopardy.Query.FetchObjects( + // highlight-start + limit: 1, + returnProperties: new[] { "question", "answer", "points" } + // highlight-end + ); + + foreach (var o in response.Objects) + { + Console.WriteLine(JsonSerializer.Serialize(o.Properties)); + } + // END GetPropertiesPython + + Assert.Equal("JeopardyQuestion", response.Objects.First().Collection); + foreach (var propName in new[] { "question", "answer", "points" }) + { + Assert.True(response.Objects.First().Properties.ContainsKey(propName)); + } + } + + [Fact] + public async Task GetObjectVector() + { + // ====================================== + // ===== GET OBJECT VECTOR EXAMPLES ===== + // ====================================== + + // GetObjectVectorPython + var jeopardy = client.Collections.Use>("JeopardyQuestion"); + var response = await jeopardy.Query.FetchObjects( + // highlight-start + returnMetadata: (MetadataOptions.Vector, ["default"]), + // highlight-end + limit: 1 + ); + + // Note: The C# client returns a dictionary of named vectors. + // We assume the default vector name is 'default'. + //TODO[g-despot]: Why is vector not returned? + Console.WriteLine("Vector for 'default':"); + Console.WriteLine(JsonSerializer.Serialize(response.Objects.First())); + // END GetObjectVectorPython + + Assert.Equal("JeopardyQuestion", response.Objects.First().Collection); + Assert.IsType(response.Objects.First().Vectors["default"]); + } + + [Fact] + public async Task GetObjectId() + { + // ================================== + // ===== GET OBJECT ID EXAMPLES ===== + // ================================== + + // GetObjectIdPython + var jeopardy = client.Collections.Use>("JeopardyQuestion"); + var response = await jeopardy.Query.FetchObjects( + // Object IDs are included by default with the Weaviate C# client! :) + limit: 1 + ); + + foreach (var o in response.Objects) + { + Console.WriteLine(o.ID); + } + // END GetObjectIdPython + + Assert.Equal("JeopardyQuestion", response.Objects.First().Collection); + Assert.IsType(response.Objects.First().ID); + } + + [Fact] + public async Task GetWithCrossRefs() + { + // ============================== + // ===== GET WITH CROSS-REF EXAMPLES ===== + // ============================== + // GetWithCrossRefsPython + var jeopardy = client.Collections.Use>("JeopardyQuestion"); + var response = await jeopardy.Query.FetchObjects( + // highlight-start + returnReferences: [ + new QueryReference( + linkOn: "hasCategory", + fields: ["title"] + ) + ], + // highlight-end + limit: 2 + ); + + foreach (var o in response.Objects) + { + Console.WriteLine(o.Properties["question"]); + // print referenced objects + // Note: References are grouped by property name ('hasCategory') + foreach (var refObj in o.References["hasCategory"]) + { + Console.WriteLine(JsonSerializer.Serialize(refObj.Properties)); + } + } + // END GetWithCrossRefsPython + + Assert.Equal("JeopardyQuestion", response.Objects.First().Collection); + Assert.True(response.Objects.First().References["hasCategory"].Count > 0); + } + + [Fact] + public async Task GetWithMetadata() + { + // ==================================== + // ===== GET WITH METADATA EXAMPLE ===== + // ==================================== + + // GetWithMetadataPython + var jeopardy = client.Collections.Use>("JeopardyQuestion"); + var response = await jeopardy.Query.FetchObjects( + limit: 1, + // highlight-start + returnMetadata: MetadataOptions.CreationTime + // highlight-end + ); + + foreach (var o in response.Objects) + { + Console.WriteLine(JsonSerializer.Serialize(o.Properties)); // View the returned properties + Console.WriteLine(o.Metadata.CreationTime); // View the returned creation time + } + // END GetWithMetadataPython + + Assert.Equal("JeopardyQuestion", response.Objects.First().Collection); + Assert.NotNull(response.Objects.First().Metadata.CreationTime); + } + + [Fact] + public async Task MultiTenancyGet() + { + // ========================= + // ===== MULTI-TENANCY ===== + // ========================= + + // MultiTenancy + var mtCollection = client.Collections.Use>("WineReviewMT"); + + // In the C# client, the tenant is specified directly in the query method + // rather than creating a separate tenant-specific collection object. + // highlight-start + var response = await mtCollection.Query.FetchObjects( + tenant: "tenantA", + // highlight-end + returnProperties: new[] { "review_body", "title" }, + limit: 1 + ); + + Console.WriteLine(JsonSerializer.Serialize(response.Objects.First().Properties)); + // END MultiTenancy + + Assert.True(response.Objects.Count() > 0); + Assert.Equal("WineReviewMT", response.Objects.First().Collection); + } +} \ No newline at end of file diff --git a/_includes/code/csharp/SearchFiltersTest.cs b/_includes/code/csharp/SearchFiltersTest.cs new file mode 100644 index 00000000..e69de29b diff --git a/_includes/code/csharp/SearchHybridTest.cs b/_includes/code/csharp/SearchHybridTest.cs new file mode 100644 index 00000000..2fd6ddc6 --- /dev/null +++ b/_includes/code/csharp/SearchHybridTest.cs @@ -0,0 +1,419 @@ +using Xunit; +using Weaviate.Client; +using Weaviate.Client.Models; +using System; +using System.Threading.Tasks; +using System.Text.Json; +using System.Linq; +using System.Collections.Generic; + +namespace WeaviateProject.Tests; + +public class SearchHybridTest : IDisposable +{ + private static readonly WeaviateClient client; + + // Static constructor for one-time setup (like @BeforeAll) + static SearchHybridTest() + { + // START INSTANTIATION-COMMON + // Best practice: store your credentials in environment variables + string weaviateUrl = Environment.GetEnvironmentVariable("WEAVIATE_URL"); + string weaviateApiKey = Environment.GetEnvironmentVariable("WEAVIATE_API_KEY"); + string openaiApiKey = Environment.GetEnvironmentVariable("OPENAI_APIKEY"); + + // The C# client uses a configuration object. + var config = new ClientConfiguration + { + GrpcAddress = weaviateUrl, + // Headers = new() + // { + // { "Authorization", $"Bearer {weaviateApiKey}" }, + // { "X-OpenAI-Api-Key", openaiApiKey } + // } + }; + client = new WeaviateClient(config); + // END INSTANTIATION-COMMON + } + + // Dispose is called once after all tests in the class are finished (like @AfterAll) + public void Dispose() + { + // The C# client manages connections automatically and does not require an explicit 'close' method. + GC.SuppressFinalize(this); + } + + [Fact] + public async Task NamedVectorHybrid() + { + // START NamedVectorHybrid + var reviews = client.Collections.Use("WineReviewNV"); + // highlight-start + var response = await reviews.Query.Hybrid( + "A French Riesling", + targetVector: ["title_country"], + limit: 3 + ); + // highlight-end + + foreach (var o in response.Objects) + { + Console.WriteLine(JsonSerializer.Serialize(o.Properties)); + } + // END NamedVectorHybrid + + Assert.Equal("WineReviewNV", response.Objects.First().Collection); + } + + [Fact] + public async Task TestHybridBasic() + { + // START HybridBasic + var jeopardy = client.Collections.Use("JeopardyQuestion"); + var response = await jeopardy.Query.Hybrid( + // highlight-start + "food", + limit: 3 + // highlight-end + ); + + foreach (var o in response.Objects) + { + Console.WriteLine(JsonSerializer.Serialize(o.Properties)); + } + // END HybridBasic + + Assert.Equal("JeopardyQuestion", response.Objects.First().Collection); + } + + [Fact] + public async Task TestHybridWithScore() + { + // START HybridWithScore + var jeopardy = client.Collections.Use("JeopardyQuestion"); + var response = await jeopardy.Query.Hybrid( + "food", + alpha: 0.5f, + // highlight-start + returnMetadata: MetadataOptions.Score | MetadataOptions.ExplainScore, + // highlight-end + limit: 3 + ); + + foreach (var o in response.Objects) + { + Console.WriteLine(JsonSerializer.Serialize(o.Properties)); + // highlight-start + Console.WriteLine($"Score: {o.Metadata.Score}, Explain Score: {o.Metadata.ExplainScore}"); + // highlight-end + } + // END HybridWithScore + + Assert.Equal("JeopardyQuestion", response.Objects.First().Collection); + Assert.NotNull(response.Objects.First().Metadata.Score); + Assert.NotNull(response.Objects.First().Metadata.ExplainScore); + } + + [Fact] + public async Task TestLimit() + { + // START limit + var jeopardy = client.Collections.Use("JeopardyQuestion"); + var response = await jeopardy.Query.Hybrid( + "food", + // highlight-start + limit: 3, + offset: 1 + // highlight-end + ); + + foreach (var o in response.Objects) + { + Console.WriteLine(JsonSerializer.Serialize(o.Properties)); + } + // END limit + + Assert.Equal("JeopardyQuestion", response.Objects.First().Collection); + Assert.Equal(3, response.Objects.Count()); + } + + [Fact] + public async Task TestAutocut() + { + // START autocut + var jeopardy = client.Collections.Use("JeopardyQuestion"); + var response = await jeopardy.Query.Hybrid( + "food", + // highlight-start + fusionType: HybridFusion.RelativeScore, + autoLimit: 1 + // highlight-end + ); + + foreach (var o in response.Objects) + { + Console.WriteLine(JsonSerializer.Serialize(o.Properties)); + } + // END autocut + + Assert.True(response.Objects.Any()); + Assert.Equal("JeopardyQuestion", response.Objects.First().Collection); + } + + [Fact] + public async Task TestHybridWithAlpha() + { + // START HybridWithAlpha + var jeopardy = client.Collections.Use("JeopardyQuestion"); + var response = await jeopardy.Query.Hybrid( + "food", + // highlight-start + alpha: 0.25f, + // highlight-end + limit: 3 + ); + + foreach (var o in response.Objects) + { + Console.WriteLine(JsonSerializer.Serialize(o.Properties)); + } + // END HybridWithAlpha + + Assert.Equal("JeopardyQuestion", response.Objects.First().Collection); + } + + [Fact] + public async Task TestHybridWithFusionType() + { + // START HybridWithFusionType + var jeopardy = client.Collections.Use("JeopardyQuestion"); + var response = await jeopardy.Query.Hybrid( + "food", + // highlight-start + fusionType: HybridFusion.RelativeScore, + // highlight-end + limit: 3 + ); + + foreach (var o in response.Objects) + { + Console.WriteLine(JsonSerializer.Serialize(o.Properties)); + } + // END HybridWithFusionType + + Assert.Equal("JeopardyQuestion", response.Objects.First().Collection); + } + + [Fact] + public async Task HybridWithBM25OperatorOr() + { + // START HybridWithBM25OperatorOrWithMin + var jeopardy = client.Collections.Use("JeopardyQuestion"); + var response = await jeopardy.Query.Hybrid( + // highlight-start + "Australian mammal cute", + bm25Operator: new BM25Operator.Or(MinimumMatch: 2), + // highlight-end + limit: 3 + ); + + foreach (var o in response.Objects) + { + Console.WriteLine(JsonSerializer.Serialize(o.Properties)); + } + // END HybridWithBM25OperatorOrWithMin + + Assert.Equal("JeopardyQuestion", response.Objects.First().Collection); + } + + [Fact] + public async Task HybridWithBM25OperatorAnd() + { + + // START HybridWithBM25OperatorAnd + var jeopardy = client.Collections.Use("JeopardyQuestion"); + var response = await jeopardy.Query.Hybrid( + // highlight-start + "Australian mammal cute", + bm25Operator: new BM25Operator.And(), // Each result must include all tokens + // highlight-end + limit: 3 + ); + + foreach (var o in response.Objects) + { + Console.WriteLine(JsonSerializer.Serialize(o.Properties)); + } + // END HybridWithBM25OperatorAnd + + Assert.Equal("JeopardyQuestion", response.Objects.First().Collection); + } + + [Fact] + public async Task TestHybridWithProperties() + { + // START HybridWithProperties + var jeopardy = client.Collections.Use("JeopardyQuestion"); + var response = await jeopardy.Query.Hybrid( + "food", + // highlight-start + queryProperties: ["question"], + // highlight-end + alpha: 0.25f, + limit: 3 + ); + + foreach (var o in response.Objects) + { + Console.WriteLine(JsonSerializer.Serialize(o.Properties)); + } + // END HybridWithProperties + + Assert.Equal("JeopardyQuestion", response.Objects.First().Collection); + } + + [Fact] + public async Task TestHybridWithPropertyWeighting() + { + // START HybridWithPropertyWeighting + var jeopardy = client.Collections.Use("JeopardyQuestion"); + var response = await jeopardy.Query.Hybrid( + "food", + // highlight-start + queryProperties: ["question^2", "answer"], + // highlight-end + alpha: 0.25f, + limit: 3 + ); + + foreach (var o in response.Objects) + { + Console.WriteLine(JsonSerializer.Serialize(o.Properties)); + } + // END HybridWithPropertyWeighting + + Assert.Equal("JeopardyQuestion", response.Objects.First().Collection); + } + + // TODO[g-despot] Why is name required in VectorData.Create? + [Fact] + public async Task TestHybridWithVector() + { + // START HybridWithVector + var queryVector = Enumerable.Repeat(-0.02f, 1536).ToArray(); + + var jeopardy = client.Collections.Use("JeopardyQuestion"); + var response = await jeopardy.Query.Hybrid( + "food", + // highlight-start + vectors: Vectors.Create("default", queryVector), + // highlight-end + alpha: 0.25f, + limit: 3 + ); + + foreach (var o in response.Objects) + { + Console.WriteLine(JsonSerializer.Serialize(o.Properties)); + } + // END HybridWithVector + + Assert.Equal("JeopardyQuestion", response.Objects.First().Collection); + } + + [Fact] + public async Task TestHybridWithFilter() + { + // START HybridWithFilter + var jeopardy = client.Collections.Use("JeopardyQuestion"); + var response = await jeopardy.Query.Hybrid( + "food", + // highlight-start + filters: Filter.Property("round").Equal("Double Jeopardy!"), + // highlight-end + limit: 3 + ); + + foreach (var o in response.Objects) + { + Console.WriteLine(JsonSerializer.Serialize(o.Properties)); + } + // END HybridWithFilter + + Assert.Equal("JeopardyQuestion", response.Objects.First().Collection); + Assert.Equal("Double Jeopardy!", (response.Objects.First().Properties as IDictionary)["round"].ToString()); + } + + [Fact] + public async Task TestVectorParameters() + { + // START VectorParameters + var jeopardy = client.Collections.Use("JeopardyQuestion"); + // This query is complex and depends on a previous nearText query to get a vector. + // We simulate this by fetching a vector first. + var nearTextResponse = await jeopardy.Query.NearText( + "large animal", + moveAway: new Move(force: 0.5f, concepts: ["mammal", "terrestrial"]), + limit: 1, + returnMetadata: MetadataOptions.Vector + ); + var nearTextVector = nearTextResponse.Objects.First().Vectors["default"]; + + var response = await jeopardy.Query.Hybrid( + "California", + // highlight-start + maxVectorDistance: 0.4f, + vectors: nearTextVector, + // highlight-end + alpha: 0.75f, + limit: 5 + ); + // END VectorParameters + + Assert.True(response.Objects.Any() && response.Objects.Count() <= 5); + } + + [Fact] + public async Task TestVectorSimilarity() + { + // START VectorSimilarity + var jeopardy = client.Collections.Use("JeopardyQuestion"); + var response = await jeopardy.Query.Hybrid( + "California", + // highlight-start + maxVectorDistance: 0.4f, // Maximum threshold for the vector search component + // highlight-end + alpha: 0.75f, + limit: 5 + ); + // END VectorSimilarity + + Assert.True(response.Objects.Any() && response.Objects.Count() <= 5); + } + + [Fact] + public async Task TestHybridGroupBy() + { + // START HybridGroupByPy4 + // Query + var jeopardy = client.Collections.Use("JeopardyQuestion"); + var response = await jeopardy.Query.Hybrid( + "California", + alpha: 0.75f, + groupBy: new GroupByRequest + { + PropertyName = "round", // group by this property + NumberOfGroups = 2, // maximum number of groups + ObjectsPerGroup = 3, // maximum objects per group + } + ); + + foreach (var group in response.Groups.Values) + { + Console.WriteLine($"{group.Name} {JsonSerializer.Serialize(group.Objects)}"); + } + // END HybridGroupByPy4 + + Assert.True(response.Groups.Count > 0 && response.Groups.Count <= 2); + } +} \ No newline at end of file diff --git a/_includes/code/csharp/SearchImageTest.cs b/_includes/code/csharp/SearchImageTest.cs new file mode 100644 index 00000000..07043ddc --- /dev/null +++ b/_includes/code/csharp/SearchImageTest.cs @@ -0,0 +1,156 @@ +using Xunit; +using Weaviate.Client; +using Weaviate.Client.Models; +using System; +using System.Threading.Tasks; +using System.Text.Json; +using System.Linq; +using System.IO; +using System.Net.Http; +using System.Collections.Generic; + +namespace WeaviateProject.Tests; + +public class SearchImageTest : IAsyncLifetime +{ + private static WeaviateClient client; + private const string QUERY_IMAGE_PATH = "images/search-image.jpg"; + + // START helper base64 functions + private static async Task UrlToBase64(string url) + { + using var httpClient = new HttpClient(); + var imageBytes = await httpClient.GetByteArrayAsync(url); + return Convert.ToBase64String(imageBytes); + } + + private static async Task FileToByteArray(string path) + { + return await File.ReadAllBytesAsync(path); + } + // END helper base64 functions + + // Runs once before any tests in the class (like @BeforeAll) + public async Task InitializeAsync() + { + client = new WeaviateClient(new ClientConfiguration { RestAddress = "localhost", RestPort = 8280 , GrpcPort = 50251}); + + if (await client.Collections.Exists("Dog")) + { + await client.Collections.Delete("Dog"); + } + + await client.Collections.Create(new Collection + { + Name = "Dog", + Properties = new() + { + Property.Blob("image"), + Property.Text("breed"), + Property.Text("description") + }, + VectorConfig = new VectorConfig( + "default", + new Vectorizer.Multi2VecClip { ImageFields = new[] { "image" }, TextFields = new[] { "breed", "description" } } + ) + }); + + // Prepare and ingest sample dog images + var dogs = client.Collections.Use("Dog"); + var sampleImages = new[] + { + new { url = "https://images.unsplash.com/photo-1489924034176-2e678c29d4c6?q=80&w=2342&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", breed = "Husky", description = "Siberian Husky with distinctive blue eyes, pointed ears, and thick white and grey fur coat, typical of arctic sled dogs" }, + new { url = "https://images.unsplash.com/photo-1633722715463-d30f4f325e24?w=900&auto=format&fit=crop&q=60&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MXx8R29sZGVuJTIwUmV0cmlldmVyfGVufDB8fDB8fHwy", breed = "Golden Retriever", description = "Golden Retriever with beautiful long golden fur, friendly expression, sitting and posing for the camera, known for being excellent family pets" }, + new { url = "https://images.unsplash.com/photo-1612979148245-d8c79c50935d?w=900&auto=format&fit=crop&q=60&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxzZWFyY2h8OXx8ZG9nJTIwZ2VybWFuJTIwc2hlcGFyZHxlbnwwfHwwfHx8Mg%3D%3D", breed = "German Shepherd", description = "The German Shepherd, also known in Britain as an Alsatian, is a German breed of working dog of medium to large size. It was originally bred as a herding dog, for herding sheep. " } + }; + + Console.WriteLine("Inserting sample data..."); + foreach (var image in sampleImages) + { + string base64Image = await UrlToBase64(image.url); + await dogs.Data.Insert(new { image = base64Image, breed = image.breed, description = image.description }); + Console.WriteLine($"Inserted: {image.breed}"); + } + Console.WriteLine("Data insertion complete!"); + + // Download the specific image to be used for searches + var queryImageUrl = "https://images.unsplash.com/photo-1590419690008-905895e8fe0d?q=80&w=1336&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"; + using var httpClient = new HttpClient(); + var imageStream = await httpClient.GetStreamAsync(queryImageUrl); + + Directory.CreateDirectory("images"); + using var fileStream = new FileStream(QUERY_IMAGE_PATH, FileMode.Create, FileAccess.Write); + await imageStream.CopyToAsync(fileStream); + } + + // Runs once after all tests in the class (like @AfterAll) + public async Task DisposeAsync() + { + if (client != null) + { + if (await client.Collections.Exists("Dog")) + { + await client.Collections.Delete("Dog"); + } + } + if (File.Exists(QUERY_IMAGE_PATH)) File.Delete(QUERY_IMAGE_PATH); + if (Directory.Exists("images")) Directory.Delete("images"); + } + + [Fact] + public async Task TestSearchWithBase64() + { + // START search with base64 + // highlight-start + // The C# client's NearImage method takes a byte array directly. + var imageBytes = await FileToByteArray(QUERY_IMAGE_PATH); + // highlight-end + + // Get the collection containing images + var dogs = client.Collections.Use("Dog"); + + // Perform query + // highlight-start + var response = await dogs.Query.NearImage( + imageBytes, + // highlight-end + returnProperties: ["breed"], + limit: 1 + // targetVector: "vector_name" // required when using multiple named vectors + ); + + if (response.Objects.Any()) + { + Console.WriteLine(JsonSerializer.Serialize(response.Objects.First())); + } + // END search with base64 + } + + // START ImageFileSearch + // Coming soon + // END ImageFileSearch + + [Fact] + public async Task TestDistance() + { + // START Distance + var dogs = client.Collections.Use("Dog"); + var imageBytes = await FileToByteArray(QUERY_IMAGE_PATH); + + var response = await dogs.Query.NearImage( + imageBytes, + // highlight-start + distance: 0.8f, // Maximum accepted distance + returnMetadata: MetadataOptions.Distance, // return distance from the source image + // highlight-end + returnProperties: "breed", + limit: 5 + ); + + foreach (var item in response.Objects) + { + Console.WriteLine(JsonSerializer.Serialize(item)); + } + // END Distance + } +} \ No newline at end of file diff --git a/_includes/code/csharp/SearchKeywordTest.cs b/_includes/code/csharp/SearchKeywordTest.cs new file mode 100644 index 00000000..4aa2ed4b --- /dev/null +++ b/_includes/code/csharp/SearchKeywordTest.cs @@ -0,0 +1,269 @@ +using Xunit; +using Weaviate.Client; +using Weaviate.Client.Models; +using System; +using System.Threading.Tasks; +using System.Text.Json; +using System.Linq; + +namespace WeaviateProject.Tests; + +public class SearchKeywordTest : IDisposable +{ + private static readonly WeaviateClient client; + + // Static constructor for one-time setup (like @BeforeAll) + static SearchKeywordTest() + { + // START INSTANTIATION-COMMON + // Best practice: store your credentials in environment variables + string weaviateUrl = Environment.GetEnvironmentVariable("WEAVIATE_URL"); + string weaviateApiKey = Environment.GetEnvironmentVariable("WEAVIATE_API_KEY"); + string openaiApiKey = Environment.GetEnvironmentVariable("OPENAI_APIKEY"); + + // The C# client uses a configuration object. + var config = new ClientConfiguration + { + // For Weaviate Cloud, the URL is the full gRPC address + GrpcAddress = weaviateUrl, + // Headers are added to the configuration + // Headers = new() + // { + // { "Authorization", $"Bearer {weaviateApiKey}" }, + // { "X-OpenAI-Api-Key", openaiApiKey } + // } + }; + client = new WeaviateClient(config); + // END INSTANTIATION-COMMON + } + + // Dispose is called once after all tests in the class are finished (like @AfterAll) + public void Dispose() + { + // The C# client manages connections automatically and does not require an explicit 'close' method. + GC.SuppressFinalize(this); + } + + [Fact] + public async Task TestBM25Basic() + { + // START BM25Basic + var jeopardy = client.Collections.Use("JeopardyQuestion"); + var response = await jeopardy.Query.BM25( + // highlight-start + "food", + // highlight-end + limit: 3 + ); + + foreach (var o in response.Objects) + { + Console.WriteLine(JsonSerializer.Serialize(o.Properties)); + } + // END BM25Basic + + Assert.Equal("JeopardyQuestion", response.Objects.First().Collection); + Assert.Contains("food", JsonSerializer.Serialize(response.Objects.First().Properties).ToLower()); + } + + // START BM25OperatorOrWithMin + // Coming soon + // END BM25OperatorOrWithMin + + // START BM25OperatorAnd + // Coming soon + // END BM25OperatorAnd + + [Fact] + public async Task TestBM25WithScore() + { + // START BM25WithScore + var jeopardy = client.Collections.Use("JeopardyQuestion"); + var response = await jeopardy.Query.BM25( + "food", + returnMetadata: MetadataOptions.Score, + limit: 3 + ); + + foreach (var o in response.Objects) + { + Console.WriteLine(JsonSerializer.Serialize(o.Properties)); + // highlight-start + Console.WriteLine(o.Metadata.Score); + // highlight-end + } + // END BM25WithScore + Assert.Equal("JeopardyQuestion", response.Objects.First().Collection); + Assert.Contains("food", JsonSerializer.Serialize(response.Objects.First().Properties).ToLower()); + Assert.NotNull(response.Objects.First().Metadata.Score); + } + + [Fact] + public async Task TestLimit() + { + // START limit + var jeopardy = client.Collections.Use("JeopardyQuestion"); + var response = await jeopardy.Query.BM25( + "safety", + // highlight-start + limit: 3, + offset: 1 + // highlight-end + ); + + foreach (var o in response.Objects) + { + Console.WriteLine(JsonSerializer.Serialize(o.Properties)); + } + // END limit + + Assert.Equal("JeopardyQuestion", response.Objects.First().Collection); + Assert.Contains("safety", JsonSerializer.Serialize(response.Objects.First().Properties).ToLower()); + Assert.Equal(3, response.Objects.Count()); + } + + [Fact] + public async Task TestAutocut() + { + // START autocut + var jeopardy = client.Collections.Use("JeopardyQuestion"); + var response = await jeopardy.Query.BM25( + "safety", + // highlight-start + autoCut: 1 + // highlight-end + ); + + foreach (var o in response.Objects) + { + Console.WriteLine(JsonSerializer.Serialize(o.Properties)); + } + // END autocut + + Assert.Equal("JeopardyQuestion", response.Objects.First().Collection); + Assert.Contains("safety", JsonSerializer.Serialize(response.Objects.First().Properties).ToLower()); + } + + [Fact] + public async Task TestBM25WithProperties() + { + // START BM25WithProperties + var jeopardy = client.Collections.Use("JeopardyQuestion"); + var response = await jeopardy.Query.BM25( + "safety", + // highlight-start + searchFields: ["question"], + // highlight-end + returnMetadata: MetadataOptions.Score, + limit: 3 + ); + + foreach (var o in response.Objects) + { + Console.WriteLine(JsonSerializer.Serialize(o.Properties)); + Console.WriteLine(o.Metadata.Score); + } + // END BM25WithProperties + + Assert.Equal("JeopardyQuestion", response.Objects.First().Collection); + Assert.Contains("safety", response.Objects.First().Properties["question"].ToString().ToLower()); + } + + [Fact] + public async Task TestBM25WithBoostedProperties() + { + // START BM25WithBoostedProperties + var jeopardy = client.Collections.Use("JeopardyQuestion"); + var response = await jeopardy.Query.BM25( + "food", + // highlight-start + searchFields: ["question^2", "answer"], + // highlight-end + limit: 3 + ); + + foreach (var o in response.Objects) + { + Console.WriteLine(JsonSerializer.Serialize(o.Properties)); + } + // END BM25WithBoostedProperties + + Assert.Equal("JeopardyQuestion", response.Objects.First().Collection); + Assert.Contains("food", JsonSerializer.Serialize(response.Objects.First().Properties).ToLower()); + } + + [Fact] + public async Task TestMultipleKeywords() + { + // START MultipleKeywords + var jeopardy = client.Collections.Use("JeopardyQuestion"); + var response = await jeopardy.Query.BM25( + // highlight-start + "food wine", // search for food or wine + // highlight-end + searchFields: ["question"], + limit: 5 + ); + + foreach (var o in response.Objects) + { + Console.WriteLine(o.Properties["question"]); + } + // END MultipleKeywords + + Assert.Equal("JeopardyQuestion", response.Objects.First().Collection); + var propertiesJson = JsonSerializer.Serialize(response.Objects.First().Properties).ToLower(); + Assert.True(propertiesJson.Contains("food") || propertiesJson.Contains("wine")); + } + + [Fact] + public async Task TestBM25WithFilter() + { + // START BM25WithFilter + var jeopardy = client.Collections.Use("JeopardyQuestion"); + var response = await jeopardy.Query.BM25( + "food", + // highlight-start + filters: Filter.Property("round").Equal("Double Jeopardy!"), + // highlight-end + returnProperties: ["answer", "question", "round"], // return these properties + limit: 3 + ); + + foreach (var o in response.Objects) + { + Console.WriteLine(JsonSerializer.Serialize(o.Properties)); + } + // END BM25WithFilter + + Assert.Equal("JeopardyQuestion", response.Objects.First().Collection); + Assert.Contains("food", JsonSerializer.Serialize(response.Objects.First().Properties).ToLower()); + Assert.Equal("Double Jeopardy!", response.Objects.First().Properties["round"].ToString()); + } + + [Fact] + public async Task TestBM25GroupBy() + { + // START BM25GroupByPy4 + var jeopardy = client.Collections.Use("JeopardyQuestion"); + + var response = await jeopardy.Query.BM25( + "California", + groupBy: new GroupByRequest + { + PropertyName = "round", // group by this property + NumberOfGroups = 2, // maximum number of groups + ObjectsPerGroup = 3, // maximum objects per group + } + ); + + foreach (var group in response.Groups.Values) + { + Console.WriteLine($"{group.Name} {JsonSerializer.Serialize(group.Objects)}"); + } + // END BM25GroupByPy4 + + Assert.True(response.Groups.Count > 0); + Assert.True(response.Groups.Count <= 2); + } +} \ No newline at end of file diff --git a/_includes/code/csharp/SearchSimilarityTest.cs b/_includes/code/csharp/SearchSimilarityTest.cs new file mode 100644 index 00000000..f409b41c --- /dev/null +++ b/_includes/code/csharp/SearchSimilarityTest.cs @@ -0,0 +1,302 @@ +using Xunit; +using Weaviate.Client; +using Weaviate.Client.Models; +using System; +using System.Threading.Tasks; +using System.Collections.Generic; +using System.Text.Json; +using System.Linq; + +public class SearchSimilarityTest : IAsyncLifetime +{ + private WeaviateClient client; + + // InitializeAsync is used for asynchronous setup before tests in the class run. + public async Task InitializeAsync() + { + // START INSTANTIATION-COMMON + // Best practice: store your credentials in environment variables + var weaviateUrl = Environment.GetEnvironmentVariable("WEAVIATE_URL"); + var weaviateApiKey = Environment.GetEnvironmentVariable("WEAVIATE_API_KEY"); + var openaiApiKey = Environment.GetEnvironmentVariable("OPENAI_APIKEY"); + var cohereApiKey = Environment.GetEnvironmentVariable("COHERE_APIKEY"); + + client = Connect.Cloud( + weaviateUrl, + weaviateApiKey + // additionalHeaders: new Dictionary + // { + // { "X-OpenAI-Api-Key", openaiApiKey }, + // { "X-Cohere-Api-Key", cohereApiKey } + // } + ); + // END INSTANTIATION-COMMON + } + + // DisposeAsync is used for asynchronous teardown after all tests in the class have run. + public async Task DisposeAsync() + { + await client.Collections.DeleteAll(); + // The C# client, using HttpClient, manages its connections automatically and does not require an explicit 'close' method. + } + + [Fact] + public async Task NamedVectorNearText() + { + // START NamedVectorNearText + var reviews = client.Collections.Use("WineReviewNV"); + var response = await reviews.Query.NearText( + "a sweet German white wine", + limit: 2, + // highlight-start + targetVector: ["title_country"], // Specify the target vector for named vector collections + // highlight-end + returnMetadata: MetadataOptions.Distance + ); + + foreach (var o in response.Objects) + { + Console.WriteLine(JsonSerializer.Serialize(o.Properties)); + Console.WriteLine(o.Metadata.Distance); + } + // END NamedVectorNearText + + Assert.Equal("WineReviewNV", response.Objects.First().Collection); + Assert.Equal(2, response.Objects.Count()); + Assert.True(response.Objects.First().Properties.ContainsKey("title")); + Assert.NotNull(response.Objects.First().Metadata.Distance); + } + + [Fact] + public async Task GetNearText() + { + // START GetNearText + var jeopardy = client.Collections.Use("JeopardyQuestion"); + var response = await jeopardy.Query.NearText( + // highlight-start + "animals in movies", + // highlight-end + limit: 2, + returnMetadata: MetadataOptions.Distance + ); + + foreach (var o in response.Objects) + { + Console.WriteLine(JsonSerializer.Serialize(o.Properties)); + Console.WriteLine(o.Metadata.Distance); + } + // END GetNearText + } + + [Fact] + public async Task GetNearObject() + { + var jeopardy = client.Collections.Use("JeopardyQuestion"); + var initialResponse = await jeopardy.Query.FetchObjects(limit: 1); + if (!initialResponse.Objects.Any()) return; // Skip test if no data + Guid uuid = (Guid)initialResponse.Objects.First().ID; + + // START GetNearObject + // highlight-start + var response = await jeopardy.Query.NearObject( + uuid, // A UUID of an object + // highlight-end + limit: 2, + returnMetadata: MetadataOptions.Distance + ); + + foreach (var o in response.Objects) + { + Console.WriteLine(JsonSerializer.Serialize(o.Properties)); + Console.WriteLine(o.Metadata.Distance); + } + // END GetNearObject + + Assert.Equal("JeopardyQuestion", response.Objects.First().Collection); + Assert.Equal(2, response.Objects.Count()); + Assert.True(response.Objects.First().Properties.ContainsKey("question")); + Assert.NotNull(response.Objects.First().Metadata.Distance); + } + + [Fact] + public async Task GetNearVector() + { + var jeopardy = client.Collections.Use("JeopardyQuestion"); + var initialResponse = await jeopardy.Query.FetchObjects(limit: 1, returnMetadata: MetadataOptions.Vector); + if (!initialResponse.Objects.Any()) return; // Skip test if no data + var queryVector = initialResponse.Objects.First().Vectors["default"]; + + // START GetNearVector + // highlight-start + var response = await jeopardy.Query.NearVector( + queryVector, // your query vector goes here + // highlight-end + limit: 2, + returnMetadata: MetadataOptions.Distance + ); + + foreach (var o in response.Objects) + { + Console.WriteLine(JsonSerializer.Serialize(o.Properties)); + Console.WriteLine(o.Metadata.Distance); + } + // END GetNearVector + + Assert.Equal("JeopardyQuestion", response.Objects.First().Collection); + Assert.Equal(2, response.Objects.Count()); + Assert.True(response.Objects.First().Properties.ContainsKey("question")); + Assert.NotNull(response.Objects.First().Metadata.Distance); + } + + [Fact] + public async Task GetLimitOffset() + { + // START GetLimitOffset + var jeopardy = client.Collections.Use("JeopardyQuestion"); + var response = await jeopardy.Query.NearText( + "animals in movies", + // highlight-start + limit: 2, // return 2 objects + offset: 1, // With an offset of 1 + // highlight-end + returnMetadata: MetadataOptions.Distance + ); + + foreach (var o in response.Objects) + { + Console.WriteLine(JsonSerializer.Serialize(o.Properties)); + Console.WriteLine(o.Metadata.Distance); + } + // END GetLimitOffset + } + + [Fact] + public async Task GetWithDistance() + { + // START GetWithDistance + var jeopardy = client.Collections.Use("JeopardyQuestion"); + var response = await jeopardy.Query.NearText( + "animals in movies", + // highlight-start + distance: 0.25f, // max accepted distance + // highlight-end + returnMetadata: MetadataOptions.Distance + ); + + foreach (var o in response.Objects) + { + Console.WriteLine(JsonSerializer.Serialize(o.Properties)); + Console.WriteLine(o.Metadata.Distance); + } + // END GetWithDistance + + Assert.Equal("JeopardyQuestion", response.Objects.First().Collection); + Assert.True(response.Objects.First().Properties.ContainsKey("question")); + Assert.NotNull(response.Objects.First().Metadata.Distance); + foreach (var o in response.Objects) + { + Assert.True(o.Metadata.Distance < 0.25f); + } + } + + [Fact] + public async Task GetWithAutocut() + { + // START Autocut + var jeopardy = client.Collections.Use("JeopardyQuestion"); + var response = await jeopardy.Query.NearText( + "animals in movies", + // highlight-start + autoCut: 1, // number of close groups + // highlight-end + returnMetadata: MetadataOptions.Distance + ); + + foreach (var o in response.Objects) + { + Console.WriteLine(JsonSerializer.Serialize(o.Properties)); + Console.WriteLine(o.Metadata.Distance); + } + // END Autocut + + Assert.True(response.Objects.Count() > 0); + Assert.Equal("JeopardyQuestion", response.Objects.First().Collection); + Assert.True(response.Objects.First().Properties.ContainsKey("question")); + Assert.NotNull(response.Objects.First().Metadata.Distance); + } + + [Fact] + public async Task GetWithGroupBy() + { + // START GetWithGroupby + var jeopardy = client.Collections.Use("JeopardyQuestion"); + + // highlight-start + var response = await jeopardy.Query.NearText( + "animals in movies", // find object based on this query + limit: 10, // maximum total objects + returnMetadata: MetadataOptions.Distance, + groupBy: new GroupByRequest + { + PropertyName = "round", // group by this property + NumberOfGroups = 2, // maximum number of groups + ObjectsPerGroup = 2, // maximum objects per group + } + ); + // highlight-end + + foreach (var o in response.Objects) + { + Console.WriteLine(o.ID); + Console.WriteLine(o.BelongsToGroup); + Console.WriteLine(o.Metadata.Distance); + } + + foreach (var group in response.Groups.Values) + { + Console.WriteLine($"=========={group.Name}=========="); + Console.WriteLine(group.Objects.Count()); + foreach (var o in group.Objects) + { + Console.WriteLine(JsonSerializer.Serialize(o.Properties)); + Console.WriteLine(JsonSerializer.Serialize(o.Metadata)); + } + } + // END GetWithGroupby + + Assert.True(response.Objects.Count() > 0); + Assert.Equal("JeopardyQuestion", response.Objects.First().Collection); + Assert.True(response.Objects.First().Properties.ContainsKey("question")); + Assert.NotNull(response.Objects.First().Metadata.Distance); + Assert.True(response.Groups.Count > 0); + Assert.True(response.Groups.Count <= 2); + } + + [Fact] + public async Task GetWithWhere() + { + // START GetWithFilter + var jeopardy = client.Collections.Use("JeopardyQuestion"); + var response = await jeopardy.Query.NearText( + "animals in movies", + // highlight-start + filters: Filter.Property("round").Equal("Double Jeopardy!"), + // highlight-end + limit: 2, + returnMetadata: MetadataOptions.Distance + ); + + foreach (var o in response.Objects) + { + Console.WriteLine(JsonSerializer.Serialize(o.Properties)); + Console.WriteLine(o.Metadata.Distance); + } + // END GetWithFilter + + Assert.True(response.Objects.Count() > 0); + Assert.Equal("JeopardyQuestion", response.Objects.First().Collection); + Assert.Equal("Double Jeopardy!", response.Objects.First().Properties["round"].ToString()); + Assert.True(response.Objects.First().Properties.ContainsKey("question")); + Assert.NotNull(response.Objects.First().Metadata.Distance); + } +} \ No newline at end of file diff --git a/_includes/code/csharp/StarterGuidesCollectionsTest.cs b/_includes/code/csharp/StarterGuidesCollectionsTest.cs new file mode 100644 index 00000000..fd91feb6 --- /dev/null +++ b/_includes/code/csharp/StarterGuidesCollectionsTest.cs @@ -0,0 +1,139 @@ +using Xunit; +using Weaviate.Client; +using Weaviate.Client.Models; +using System; +using System.Threading.Tasks; + +namespace WeaviateProject.Tests; + +public class StarterGuidesCollectionsTest : IAsyncLifetime +{ + private WeaviateClient client; + + // Runs before each test + public Task InitializeAsync() + { + // START-ANY + // Note: The C# client doesn't support setting headers like 'X-OpenAI-Api-Key' via the constructor for local connections. + // This must be configured in Weaviate's environment variables. + client = new WeaviateClient(new ClientConfiguration { RestAddress = "localhost", RestPort = 8080 }); + // END-ANY + return Task.CompletedTask; + } + + // Runs after each test + public async Task DisposeAsync() + { + // Clean up any collections created during the tests + if (await client.Collections.Exists("Question")) + { + await client.Collections.Delete("Question"); + } + } + + [Fact] + public async Task TestBasicSchema() + { + // START BasicSchema + var questionsCollection = await client.Collections.Create(new Collection + { + Name = "Question", + VectorConfig = new VectorConfig("default", new Vectorizer.Text2VecWeaviate()), // Set the vectorizer + GenerativeConfig = new Generative.CohereConfig(), // Set the generative module + Properties = new() + { + Property.Text("question"), + Property.Text("answer"), + Property.Text("category") + } + }); + + Console.WriteLine(questionsCollection); + // END BasicSchema + } + + // TODO[g-despot] Missing vectorizePropertyName + [Fact] + public async Task TestSchemaWithPropertyOptions() + { + // START SchemaWithPropertyOptions + await client.Collections.Create(new Collection + { + Name = "Question", + VectorConfig = new VectorConfig("default", new Vectorizer.Text2VecWeaviate()), + GenerativeConfig = new Generative.CohereConfig(), + Properties = new() + { + Property.Text( + "question", + tokenization: PropertyTokenization.Lowercase + // vectorizePropertyName: true // Pass as a simple named argument + ), + Property.Text( + "answer", + tokenization: PropertyTokenization.Whitespace + // vectorizePropertyName: false // Pass as a simple named argument + ) + } + }); + // END SchemaWithPropertyOptions + } + + [Fact] + public async Task TestSchemaWithMultiTenancy() + { + // START SchemaWithMultiTenancy + await client.Collections.Create(new Collection + { + Name = "Question", + VectorConfig = new VectorConfig("default", new Vectorizer.Text2VecWeaviate()), + GenerativeConfig = new Generative.CohereConfig(), + Properties = new() + { + Property.Text("question"), + Property.Text("answer") + }, + // highlight-start + MultiTenancyConfig = new MultiTenancyConfig { Enabled = true, AutoTenantCreation = true } // Enable multi-tenancy + // highlight-end + }); + // END SchemaWithMultiTenancy + } + + [Fact] + public async Task TestSchemaWithIndexSettings() + { + // START SchemaWithIndexSettings + await client.Collections.Create(new Collection + { + Name = "Question", + VectorConfig = new VectorConfig( + "default", // Set the name of the vector configuration + new Vectorizer.Text2VecWeaviate(), + // highlight-start + new VectorIndex.HNSW + { + Distance = VectorIndexConfig.VectorDistance.Cosine, // Configure the vector index + Quantizer = new VectorIndex.Quantizers.BQ() // Enable vector compression (quantization) + } + // highlight-end + ), + GenerativeConfig = new Generative.CohereConfig(), + Properties = new() + { + Property.Text("question"), + Property.Text("answer") + }, + // highlight-start + // Configure the inverted index + InvertedIndexConfig = new InvertedIndexConfig + { + IndexNullState = true, + IndexPropertyLength = true, + IndexTimestamps = true + } + // highlight-end + }); + // END SchemaWithIndexSettings + } +} \ No newline at end of file diff --git a/_includes/code/csharp/StarterGuidesCustomVectorsTest.cs b/_includes/code/csharp/StarterGuidesCustomVectorsTest.cs new file mode 100644 index 00000000..9ae430b0 --- /dev/null +++ b/_includes/code/csharp/StarterGuidesCustomVectorsTest.cs @@ -0,0 +1,131 @@ +using Xunit; +using Weaviate.Client; +using Weaviate.Client.Models; +using System; +using System.Threading.Tasks; +using System.Text.Json; +using System.Linq; +using System.Collections.Generic; +using System.Net.Http; +using System.Text.Json.Serialization; + +namespace WeaviateProject.Tests; + +public class StarterGuidesCustomVectorsTest +{ + // Helper class for parsing the JSON data with vectors + private record JeopardyQuestionWithVector + { + [JsonPropertyName("Answer")] + public string Answer { get; init; } + [JsonPropertyName("Question")] + public string Question { get; init; } + [JsonPropertyName("Category")] + public string Category { get; init; } + [JsonPropertyName("vector")] + public float[] Vector { get; init; } + } + + [Fact] + public async Task TestBringYourOwnVectors() + { + using var client = Connect.Local(); + string collectionName = "Question"; + + try + { + // Clean slate + if (await client.Collections.Exists(collectionName)) + { + await client.Collections.Delete(collectionName); + } + + // START CreateCollection + // Create the collection. + await client.Collections.Create(new Collection + { + Name = collectionName, + Properties = new() + { + Property.Text("answer"), + Property.Text("question"), + Property.Text("category") + }, + VectorConfig = new VectorConfig("default", new Vectorizer.SelfProvided()) + }); + // END CreateCollection + + // START ImportData + var fname = "jeopardy_tiny_with_vectors_all-OpenAI-ada-002.json"; + var url = $"https://raw.githubusercontent.com/weaviate-tutorials/quickstart/main/data/{fname}"; + + using var httpClient = new HttpClient(); + var responseBody = await httpClient.GetStringAsync(url); + + var data = JsonSerializer.Deserialize>(responseBody); + + // Get a handle to the collection + var questions = client.Collections.Use(collectionName); + var questionObjs = new List(); + + foreach (var d in data) + { + // highlight-start + var properties = new Dictionary + { + { "answer", d.Answer }, + { "question", d.Question }, + { "category", d.Category } + }; + + questionObjs.Add(new WeaviateObject + { + Properties = properties, + Vectors = Vectors.Create("default", d.Vector) + }); + // highlight-end + } + + var insertManyResponse = await questions.Data.InsertMany(questionObjs.ToArray()); + // END ImportData + // TODO[g-despot] Error handling missing + // Pass the list of objects (converted to an array) to InsertMany + // if (insertManyResponse.HasErrors) + // { + // Console.WriteLine($"Number of failed imports: {insertManyResponse.Errors.Count}"); + // Console.WriteLine($"First failed object error: {insertManyResponse.Errors.First()}"); + // } + + // START NearVector + var queryVector = data[0].Vector; // Use a vector from the dataset for a reliable query + + // Added a small delay to ensure indexing is complete + await Task.Delay(2000); + + var response = await questions.Query.NearVector( + queryVector, + limit: 2, + returnMetadata: MetadataOptions.Certainty + ); + + Console.WriteLine(JsonSerializer.Serialize(response.Objects)); + // END NearVector + + // ===== Test query results ===== + Assert.Equal(2, response.Objects.Count()); + // The first result should be the object we used for the query, with near-perfect certainty + Assert.NotNull(response.Objects.First().Metadata.Certainty); + Assert.True(response.Objects.First().Metadata.Certainty > 0.999); + var props = response.Objects.First().Properties as IDictionary; + Assert.NotNull(props); + Assert.Equal(data[0].Question, props["question"].ToString()); + } + finally + { + if (client != null && await client.Collections.Exists(collectionName)) + { + await client.Collections.Delete(collectionName); + } + } + } +} \ No newline at end of file diff --git a/_includes/code/csharp/WeaviateProject.Tests.csproj b/_includes/code/csharp/WeaviateProject.Tests.csproj new file mode 100644 index 00000000..3cfcc5e2 --- /dev/null +++ b/_includes/code/csharp/WeaviateProject.Tests.csproj @@ -0,0 +1,17 @@ + + + + net8.0 + true + + + + + + + + + + + + diff --git a/_includes/code/csharp/_ManageCollectionsMultiTenancyTest.cs b/_includes/code/csharp/_ManageCollectionsMultiTenancyTest.cs new file mode 100644 index 00000000..f693f447 --- /dev/null +++ b/_includes/code/csharp/_ManageCollectionsMultiTenancyTest.cs @@ -0,0 +1,253 @@ +using Xunit; +using Weaviate.Client; +using Weaviate.Client.Models; +using System; +using System.Threading.Tasks; +using System.Linq; +using System.Collections.Generic; + +namespace WeaviateProject.Tests; + +public class ManageCollectionsMultiTenancyTest : IAsyncLifetime +{ + private WeaviateClient client; + + // Runs before each test (like @BeforeEach) + public Task InitializeAsync() + { + string openaiApiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY"); + if (string.IsNullOrWhiteSpace(openaiApiKey)) + { + throw new ArgumentException("Please set the OPENAI_API_KEY environment variable."); + } + + // Note: The C# client doesn't support setting headers like 'X-OpenAI-Api-Key' via the constructor for local connections. + // This must be configured in Weaviate's environment variables. + client = new WeaviateClient(new ClientConfiguration { RestAddress = "localhost", RestPort = 8080 }); + + return Task.CompletedTask; + } + + // Runs after each test (like @AfterEach) + public async Task DisposeAsync() + { + // Clean up any collections created during the tests + await client.Collections.DeleteAll(); + } + + [Fact] + public async Task TestEnableMultiTenancy() + { + // START EnableMultiTenancy + await client.Collections.Create(new Collection + { + Name = "MultiTenancyCollection", + MultiTenancyConfig = new MultiTenancyConfig { Enabled = true } + }); + // END EnableMultiTenancy + + var config = await client.Collections.Export("MultiTenancyCollection"); + Assert.True(config.MultiTenancyConfig.Enabled); + } + + [Fact] + public async Task TestEnableAutoActivationMultiTenancy() + { + // START EnableAutoActivation + await client.Collections.Create(new Collection + { + Name = "MultiTenancyCollection", + MultiTenancyConfig = new MultiTenancyConfig { Enabled = true, AutoTenantActivation = true } + }); + // END EnableAutoActivation + + var config = await client.Collections.Export("MultiTenancyCollection"); + Assert.True(config.MultiTenancyConfig.AutoTenantActivation); + } + + [Fact] + public async Task TestEnableAutoMT() + { + // START EnableAutoMT + await client.Collections.Create(new Collection + { + Name = "CollectionWithAutoMTEnabled", + MultiTenancyConfig = new MultiTenancyConfig { Enabled = true, AutoTenantCreation = true } + }); + // END EnableAutoMT + + var config = await client.Collections.Export("CollectionWithAutoMTEnabled"); + Assert.True(config.MultiTenancyConfig.AutoTenantCreation); + } + + [Fact] + public async Task TestUpdateAutoMT() + { + string collectionName = "MTCollectionNoAutoMT"; + await client.Collections.Create(new Collection + { + Name = collectionName, + MultiTenancyConfig = new MultiTenancyConfig { Enabled = true, AutoTenantCreation = false } + }); + + // START UpdateAutoMT + var collection = client.Collections.Use(collectionName); + await collection.Config.Update(c => + { + c.MultiTenancyConfig.AutoTenantCreation = true; + }); + // END UpdateAutoMT + + var config = await client.Collections.Export(collectionName); + Assert.True(config.MultiTenancyConfig.AutoTenantCreation); + } + + [Fact] + public async Task TestAddTenantsToClass() + { + string collectionName = "MultiTenancyCollection"; + await client.Collections.Create(new Collection + { + Name = collectionName, + MultiTenancyConfig = new MultiTenancyConfig { Enabled = true } + }); + + var collection = client.Collections.Use(collectionName); + + // START AddTenantsToClass + await collection.Tenants.Add( + new Tenant { Name = "tenantA" }, + new Tenant { Name = "tenantB" } + ); + // END AddTenantsToClass + + var tenants = (await collection.Tenants.List()).ToList(); + Assert.Equal(2, tenants.Count); + Assert.Contains(tenants, t => t.Name == "tenantA"); + Assert.Contains(tenants, t => t.Name == "tenantB"); + } + + [Fact] + public async Task TestListTenants() + { + string collectionName = "MultiTenancyCollection"; + var collection = await client.Collections.Create(new Collection + { + Name = collectionName, + MultiTenancyConfig = new MultiTenancyConfig { Enabled = true } + }); + await collection.Tenants.Add(new Tenant { Name = "tenantA" }, new Tenant { Name = "tenantB" }); + + // START ListTenants + var tenants = await collection.Tenants.List(); + foreach (var t in tenants) Console.WriteLine(t.Name); + // END ListTenants + + Assert.Equal(2, tenants.Count()); + } + + [Fact] + public async Task TestGetTenantsByName() + { + string collectionName = "MultiTenancyCollection"; + var collection = await client.Collections.Create(new Collection + { + Name = collectionName, + MultiTenancyConfig = new MultiTenancyConfig { Enabled = true } + }); + await collection.Tenants.Add(new Tenant { Name = "tenantA" }, new Tenant { Name = "tenantB" }); + + // START GetTenantsByName + var tenantNames = new[] { "tenantA", "tenantB", "nonExistentTenant" }; + var tenants = await collection.Tenants.List(tenantNames); + foreach (var t in tenants) Console.WriteLine(t.Name); + // END GetTenantsByName + + Assert.Equal(2, tenants.Count()); + } + + [Fact] + public async Task TestGetOneTenant() + { + string collectionName = "MultiTenancyCollection"; + var collection = await client.Collections.Create(new Collection + { + Name = collectionName, + MultiTenancyConfig = new MultiTenancyConfig { Enabled = true } + }); + await collection.Tenants.Add(new Tenant { Name = "tenantA" }); + + // START GetOneTenant + string tenantName = "tenantA"; + var tenant = await collection.Tenants.Get(tenantName); + Console.WriteLine(tenant?.Name); + // END GetOneTenant + + Assert.NotNull(tenant); + } + + [Fact] + public async Task TestActivateTenant() + { + string collectionName = "MultiTenancyCollection"; + var collection = await client.Collections.Create(new Collection + { + Name = collectionName, + MultiTenancyConfig = new MultiTenancyConfig { Enabled = true } + }); + await collection.Tenants.Add(new Tenant { Name = "tenantA", Status = TenantActivityStatus.Inactive }); + + // START ActivateTenants + string tenantName = "tenantA"; + await collection.Tenants.Activate(tenantName); + // END ActivateTenants + + var tenant = await collection.Tenants.Get(tenantName); + Assert.Equal(TenantActivityStatus.Active, tenant?.Status); + } + + [Fact] + public async Task TestDeactivateTenant() + { + string collectionName = "MultiTenancyCollection"; + var collection = await client.Collections.Create(new Collection + { + Name = collectionName, + MultiTenancyConfig = new MultiTenancyConfig { Enabled = true, AutoTenantCreation = true } + }); + await collection.Tenants.Add(new Tenant { Name = "tenantA" }); + + // START DeactivateTenants + string tenantName = "tenantA"; + await collection.Tenants.Deactivate(tenantName); + // END DeactivateTenants + + var tenant = await collection.Tenants.Get(tenantName); + Assert.Equal(TenantActivityStatus.Inactive, tenant?.Status); + } + + // START OffloadTenants + // Note: 'Offload' is not a current concept in the client. Use 'Deactivate' for similar functionality. + // Coming soon + // END OffloadTenants + + [Fact] + public async Task TestRemoveTenants() + { + string collectionName = "MultiTenancyCollection"; + var collection = await client.Collections.Create(new Collection + { + Name = collectionName, + MultiTenancyConfig = new MultiTenancyConfig { Enabled = true } + }); + await collection.Tenants.Add(new Tenant { Name = "tenantA" }, new Tenant { Name = "tenantB" }); + + // START RemoveTenants + await collection.Tenants.Delete(new[] { "tenantB", "tenantX" }); + // END RemoveTenants + + var tenants = (await collection.Tenants.List()).ToList(); + Assert.Single(tenants); + Assert.Equal("tenantA", tenants.First().Name); + } +} \ No newline at end of file diff --git a/_includes/code/csharp/_SearchGenerativeTest.cs b/_includes/code/csharp/_SearchGenerativeTest.cs new file mode 100644 index 00000000..4a8a7044 --- /dev/null +++ b/_includes/code/csharp/_SearchGenerativeTest.cs @@ -0,0 +1,304 @@ +// using Xunit; +// using Weaviate.Client; +// using Weaviate.Client.Models; +// using System; +// using System.Threading.Tasks; +// using System.Text.Json; +// using System.Linq; +// using System.Collections.Generic; +// using System.Net.Http; + +// namespace WeaviateProject.Tests; + +// public class GenerativeSearchTest : IDisposable +// { +// private static readonly WeaviateClient client; + +// // Static constructor for one-time setup (like @BeforeAll) +// static GenerativeSearchTest() +// { +// // START INSTANTIATION-COMMON +// // Best practice: store your credentials in environment variables +// string weaviateUrl = Environment.GetEnvironmentVariable("WEAVIATE_URL"); +// string weaviateApiKey = Environment.GetEnvironmentVariable("WEAVIATE_API_KEY"); +// string openaiApiKey = Environment.GetEnvironmentVariable("OPENAI_APIKEY"); +// string anthropicApiKey = Environment.GetEnvironmentVariable("ANTHROPIC_APIKEY"); + +// var config = new ClientConfiguration +// { +// GrpcAddress = weaviateUrl, +// // Headers = new() +// // { +// // { "Authorization", $"Bearer {weaviateApiKey}" }, +// // { "X-OpenAI-Api-Key", openaiApiKey }, +// // { "X-Anthropic-Api-Key", anthropicApiKey } +// // } +// }; +// client = new WeaviateClient(config); +// // END INSTANTIATION-COMMON +// } + +// // Dispose is called once after all tests in the class are finished (like @AfterAll) +// public void Dispose() +// { +// // The C# client manages connections automatically and does not require an explicit 'close' method. +// GC.SuppressFinalize(this); +// } + +// [Fact] +// public async Task TestDynamicRag() +// { +// // START DynamicRag +// var reviews = client.Collections.Use("WineReviewNV"); +// var response = await reviews.Generate.NearText( +// "a sweet German white wine", +// limit: 2, +// targetVector: ["title_country"], +// prompt: new SinglePrompt { Prompt = "Translate this into German: {review_body}" }, +// groupedPrompt: new GroupedPrompt { Task = "Summarize these reviews" } +// // highlight-start +// // provider: new GenerativeProvider.(OpenAI) { Temperature = 0.1f } +// // highlight-end +// ); + +// foreach (var o in response.Objects) +// { +// Console.WriteLine($"Properties: {JsonSerializer.Serialize(o.Properties)}"); +// Console.WriteLine($"Single prompt result: {o.Generative?.Values}"); +// } +// Console.WriteLine($"Grouped task result: {response.Generative?.Values}"); +// // END DynamicRag +// } + +// [Fact] +// public async Task TestNamedVectorNearText() +// { +// // START NamedVectorNearTextPython +// var reviews = client.Collections.Use("WineReviewNV"); +// var response = await reviews.Generate.NearText( +// "a sweet German white wine", +// limit: 2, +// // highlight-start +// targetVector: ["title_country"], // Specify the target vector for named vector collections +// returnMetadata: MetadataOptions.Distance, +// prompt: new SinglePrompt { Prompt = "Translate this into German: {review_body}" }, +// groupedPrompt: new GroupedPrompt { Task = "Summarize these reviews" } +// // highlight-end +// ); + +// foreach (var o in response.Objects) +// { +// Console.WriteLine($"Properties: {JsonSerializer.Serialize(o.Properties)}"); +// Console.WriteLine($"Single prompt result: {o.Generative?.Values}"); +// } +// Console.WriteLine($"Grouped task result: {response.Generative?.Values}"); +// // END NamedVectorNearTextPython +// } + +// [Fact] +// public async Task TestSingleGenerative() +// { +// // START SingleGenerativePython +// // highlight-start +// var prompt = "Convert the following into a question for twitter. Include emojis for fun, but do not include the answer: {question}."; +// // highlight-end + +// var jeopardy = client.Collections.Use("JeopardyQuestion"); +// // highlight-start +// var response = await jeopardy.Generate.NearText( +// // highlight-end +// "World history", +// limit: 2, +// // highlight-start +// prompt: new SinglePrompt { Prompt = prompt } +// ); +// // highlight-end + +// foreach (var o in response.Objects) +// { +// var props = o.Properties as IDictionary; +// Console.WriteLine($"Property 'question': {props?["question"]}"); +// // highlight-start +// Console.WriteLine($"Single prompt result: {o.Generative?.Values}"); +// // highlight-end +// } +// // END SingleGenerativePython +// } + +// [Fact] +// public async Task TestSingleGenerativeProperties() +// { +// // START SingleGenerativePropertiesPython +// // highlight-start +// var prompt = "Convert this quiz question: {question} and answer: {answer} into a trivia tweet."; +// // highlight-end + +// var jeopardy = client.Collections.Use("JeopardyQuestion"); +// var response = await jeopardy.Generate.NearText( +// "World history", +// limit: 2, +// prompt: new SinglePrompt { Prompt = prompt } +// ); + +// // print source properties and generated responses +// foreach (var o in response.Objects) +// { +// Console.WriteLine($"Properties: {JsonSerializer.Serialize(o.Properties)}"); +// Console.WriteLine($"Single prompt result: {o.Generative?.Values}"); +// } +// // END SingleGenerativePropertiesPython +// } + +// [Fact] +// public async Task TestSingleGenerativeParameters() +// { +// // START SingleGenerativeParametersPython +// // highlight-start +// var singlePrompt = new SinglePrompt +// { +// Prompt = "Convert this quiz question: {question} and answer: {answer} into a trivia tweet.", +// // Metadata = true, +// Debug = true +// }; +// // highlight-end + +// var jeopardy = client.Collections.Use("JeopardyQuestion"); +// var response = await jeopardy.Generate.NearText( +// "World history", +// limit: 2, +// // highlight-start +// prompt: singlePrompt +// // highlight-end +// // provider: new GenerativeProvider.OpenAI() +// ); + +// // print source properties and generated responses +// foreach (var o in response.Objects) +// { +// Console.WriteLine($"Properties: {JsonSerializer.Serialize(o.Properties)}"); +// Console.WriteLine($"Single prompt result: {o.Generative?.Values}"); +// //Console.WriteLine($"Debug: {o.Generative?}"); +// //Console.WriteLine($"Metadata: {JsonSerializer.Serialize(o.Generative?.Metadata)}"); +// } +// // END SingleGenerativeParametersPython +// } + +// [Fact] +// public async Task TestGroupedGenerative() +// { +// // START GroupedGenerativePython +// // highlight-start +// var task = "What do these animals have in common, if anything?"; +// // highlight-end + +// var jeopardy = client.Collections.Use("JeopardyQuestion"); +// var response = await jeopardy.Generate.NearText( +// "Cute animals", +// limit: 3, +// // highlight-start +// groupedPrompt: new GroupedPrompt { Task = task } +// ); +// // highlight-end + +// // print the generated response +// Console.WriteLine($"Grouped task result: {response.Generative?.Values}"); +// // END GroupedGenerativePython +// } + +// // TODO[g-despot] Metadata missing +// [Fact] +// public async Task TestGroupedGenerativeParameters() +// { +// // START GroupedGenerativeParametersPython +// // highlight-start +// var groupedTask = new GroupedPrompt +// { +// Task = "What do these animals have in common, if anything?", +// // Metadata = true +// }; +// // highlight-end + +// var jeopardy = client.Collections.Use("JeopardyQuestion"); +// var response = await jeopardy.Generate.NearText( +// "Cute animals", +// limit: 3, +// // highlight-start +// groupedPrompt: groupedTask +// // highlight-end +// // provider: new GenerativeProvider.OpenAI() +// ); + +// // print the generated response +// Console.WriteLine($"Grouped task result: {response.Generative?.Values}"); +// // Console.WriteLine($"Metadata: {JsonSerializer.Serialize(response.Generative?.Metadata)}"); +// // END GroupedGenerativeParametersPython +// } + +// [Fact] +// public async Task TestGroupedGenerativeProperties() +// { +// // START GroupedGenerativeProperties Python +// var task = "What do these animals have in common, if anything?"; + +// var jeopardy = client.Collections.Use("JeopardyQuestion"); +// var response = await jeopardy.Generate.NearText( +// "Australian animals", +// limit: 3, +// groupedPrompt: new GroupedPrompt +// { +// Task = task, +// // highlight-start +// Properties = ["answer", "question"] +// // highlight-end +// } +// ); + +// // print the generated response +// // highlight-start +// foreach (var o in response.Objects) +// { +// Console.WriteLine($"Properties: {JsonSerializer.Serialize(o.Properties)}"); +// } +// Console.WriteLine($"Grouped task result: {response.Generative?.Values}"); +// // highlight-end +// // END GroupedGenerativeProperties Python +// } + +// //TODO[g-despot] Missing image processing +// [Fact] +// public async Task TestWorkingWithImages() +// { +// // START WorkingWithImages +// var srcImgPath = "https://images.unsplash.com/photo-1459262838948-3e2de6c1ec80?w=500&h=500&fit=crop"; +// using var httpClient = new HttpClient(); +// var imageBytes = await httpClient.GetByteArrayAsync(srcImgPath); +// var base64Image = Convert.ToBase64String(imageBytes); + +// var groupedTask = new GroupedPrompt +// { +// // highlight-start +// Task = "Formulate a Jeopardy!-style question about this image", +// // Images = [base64Image] // A list of base64 encoded strings of the image bytes +// // ImageProperties = ["img"] // Properties containing images in Weaviate +// // highlight-end +// }; + +// var jeopardy = client.Collections.Use("JeopardyQuestion"); +// var response = await jeopardy.Generate.NearText( +// "Australian animals", +// limit: 3, +// groupedPrompt: groupedTask +// // highlight-start +// // highlight-end +// // provider: new GenerativeProvider.Anthropic { MaxTokensToSample = 1000 } +// ); + +// // Print the source property and the generated response +// foreach (var o in response.Objects) +// { +// Console.WriteLine($"Properties: {JsonSerializer.Serialize(o.Properties)}"); +// } +// Console.WriteLine($"Grouped task result: {response.Generative?.Result}"); +// // END WorkingWithImages +// } +// } \ No newline at end of file diff --git a/_includes/code/csharp/_SearchMultiTargetTest.cs b/_includes/code/csharp/_SearchMultiTargetTest.cs new file mode 100644 index 00000000..e69de29b diff --git a/_includes/code/howto/manage-data.create.with.geo.mdx b/_includes/code/howto/manage-data.create.with.geo.mdx index ed49e695..5bb29338 100644 --- a/_includes/code/howto/manage-data.create.with.geo.mdx +++ b/_includes/code/howto/manage-data.create.with.geo.mdx @@ -1,9 +1,9 @@ import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; - import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBlock'; - import PyCode from '!!raw-loader!/_includes/code/howto/manage-data.create.py'; +import JavaV6Code from "!!raw-loader!/_includes/code/java-v6/src/test/java/ManageObjectsCreateTest.java"; +import CSharpCode from "!!raw-loader!/_includes/code/csharp/ManageObjectsCreateTest.cs"; @@ -119,4 +119,20 @@ public class App { ``` + + + + + + diff --git a/_includes/code/howto/manage-data.read.check.existence.mdx b/_includes/code/howto/manage-data.read.check.existence.mdx index b652bbd7..6c7a8d97 100644 --- a/_includes/code/howto/manage-data.read.check.existence.mdx +++ b/_includes/code/howto/manage-data.read.check.existence.mdx @@ -1,9 +1,9 @@ import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; - import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBlock'; - import PyCode from '!!raw-loader!/_includes/code/howto/manage-data.create.py'; +import JavaV6Code from "!!raw-loader!/_includes/code/java-v6/src/test/java/ManageObjectsReadTest.java"; +import CSharpCode from "!!raw-loader!/_includes/code/csharp/ManageObjectsReadTest.cs"; @@ -81,6 +81,14 @@ func main() { // before it is considered successful. ``` + + + @@ -122,5 +130,13 @@ public class App { ``` + + + diff --git a/_includes/code/howto/manage-data.shards.inspect.mdx b/_includes/code/howto/manage-data.shards.inspect.mdx index 16d496d0..f387f821 100644 --- a/_includes/code/howto/manage-data.shards.inspect.mdx +++ b/_includes/code/howto/manage-data.shards.inspect.mdx @@ -4,6 +4,8 @@ import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBl import PyCode from '!!raw-loader!/_includes/code/howto/manage-data.collections.py'; import JavaCode from '!!raw-loader!/_includes/code/howto/java/src/test/java/io/weaviate/docs/manage-data.classes.java'; +import JavaV6Code from "!!raw-loader!/_includes/code/java-v6/src/test/java/ManageCollectionsTest.java"; +import CSharpCode from "!!raw-loader!/_includes/code/csharp/ManageCollectionsTest.cs"; @@ -59,7 +61,15 @@ func main() { } ``` - + + + + + + + diff --git a/_includes/code/howto/manage-data.shards.update.mdx b/_includes/code/howto/manage-data.shards.update.mdx index 622109b5..79f09f2b 100644 --- a/_includes/code/howto/manage-data.shards.update.mdx +++ b/_includes/code/howto/manage-data.shards.update.mdx @@ -1,42 +1,51 @@ -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; -import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBlock'; +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; +import FilteredTextBlock from "@site/src/components/Documentation/FilteredTextBlock"; -import PyCode from '!!raw-loader!/_includes/code/howto/manage-data.collections.py'; -import GoCode from '!!raw-loader!/_includes/code/howto/go/docs/manage-data.shards_test.go'; -import JavaCode from '!!raw-loader!/_includes/code/howto/java/src/test/java/io/weaviate/docs/manage-data.classes.java'; +import PyCode from "!!raw-loader!/_includes/code/howto/manage-data.collections.py"; +import GoCode from "!!raw-loader!/_includes/code/howto/go/docs/manage-data.shards_test.go"; +import JavaCode from "!!raw-loader!/_includes/code/howto/java/src/test/java/io/weaviate/docs/manage-data.classes.java"; +import JavaV6Code from "!!raw-loader!/_includes/code/java-v6/src/test/java/ManageCollectionsTest.java"; +import CSharpCode from "!!raw-loader!/_includes/code/csharp/ManageCollectionsTest.cs"; - - - + + + - + ```js -let articles = client.collections.use('Article') +let articles = client.collections.use("Article"); // highlight-start -const shards = await articles.config.updateShards('READY', 'shard-1234') +const shards = await articles.config.updateShards("READY", "shard-1234"); // highlight-end console.log(JSON.stringify(shards, null, 2)); ``` - - - - - + + + + + + + - + + + diff --git a/_includes/code/java-v6/README.md b/_includes/code/java-v6/README.md new file mode 100644 index 00000000..5a1fb88a --- /dev/null +++ b/_includes/code/java-v6/README.md @@ -0,0 +1,5 @@ +mvn test + + +mvn test -Dtest=ConnectionTest + diff --git a/_includes/code/java-v6/pom.xml b/_includes/code/java-v6/pom.xml new file mode 100644 index 00000000..ae3ed45a --- /dev/null +++ b/_includes/code/java-v6/pom.xml @@ -0,0 +1,79 @@ + + 4.0.0 + + io.weaviate.examples + weaviate-java-client-examples + 1.0-SNAPSHOT + jar + + Weaviate Java Client Examples + http://maven.apache.org + + + UTF-8 + 17 + 17 + + + + + + io.weaviate + client6 + 6.0.0-M1 + + + + + org.junit.jupiter + junit-jupiter-api + 5.10.2 + test + + + org.junit.jupiter + junit-jupiter-engine + 5.10.2 + test + + + org.assertj + assertj-core + 3.25.3 + test + + + com.fasterxml.jackson.core + jackson-databind + 2.17.1 + + + org.json + json + 20240303 + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + ${maven.compiler.source} + ${maven.compiler.target} + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.2.5 + + + + diff --git a/_includes/code/java-v6/src/test/java/ConfigureBQTest.java b/_includes/code/java-v6/src/test/java/ConfigureBQTest.java new file mode 100644 index 00000000..aff25b5a --- /dev/null +++ b/_includes/code/java-v6/src/test/java/ConfigureBQTest.java @@ -0,0 +1,87 @@ +import io.weaviate.client6.v1.api.WeaviateClient; +import io.weaviate.client6.v1.api.collections.CollectionHandle; +import io.weaviate.client6.v1.api.collections.Property; +import io.weaviate.client6.v1.api.collections.Quantization; +import io.weaviate.client6.v1.api.collections.VectorConfig; +import io.weaviate.client6.v1.api.collections.vectorindex.Hnsw; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.util.Map; + +class ConfigureBQTest { + + private static WeaviateClient client; + + @BeforeAll + public static void beforeAll() { + // START ConnectCode + String openaiApiKey = System.getenv("OPENAI_API_KEY"); + client = WeaviateClient + .connectToLocal(config -> config.setHeaders(Map.of("X-OpenAI-Api-Key", openaiApiKey))); + // END ConnectCode + } + + @AfterAll + public static void afterAll() throws Exception { + client.close(); + } + + @Test + void testEnableBQ() throws IOException { + String collectionName = "MyCollection"; + if (client.collections.exists(collectionName)) { + client.collections.delete(collectionName); + } + + // START EnableBQ + client.collections.create("MyCollection", + col -> col.vectorConfig(VectorConfig.text2vecContextionary(vc -> vc + // highlight-start + .quantization(Quantization.bq()) + // highlight-end + )).properties(Property.text("title"))); + // END EnableBQ + } + + // TODO[g-despot] Errors on collection update: TYPE_UPDATE_CLASS: bad request :parse class update: invalid update for vector "default": skipDefaultQuantization is immutable: attempted change from "true" to "false" + @Test + void testUpdateSchema() throws IOException { + String collectionName = "MyCollection"; + if (client.collections.exists(collectionName)) { + client.collections.delete(collectionName); + } + client.collections.create(collectionName, + col -> col + .vectorConfig(VectorConfig + .text2vecContextionary(vc -> vc.quantization(Quantization.uncompressed()))) + .properties(Property.text("title"))); + + // START UpdateSchema + CollectionHandle> collection = client.collections.use("MyCollection"); + collection.config.update(collectionName, c -> c.vectorConfig( + VectorConfig.text2vecContextionary(vc -> vc.quantization(Quantization.bq())))); + // END UpdateSchema + // TODO[g-despot]: Verify the update + } + + @Test + void testBQWithOptions() throws IOException { + String collectionName = "MyCollection"; + if (client.collections.exists(collectionName)) { + client.collections.delete(collectionName); + } + + // START BQWithOptions + client.collections.create("MyCollection", + col -> col.vectorConfig(VectorConfig.text2vecContextionary(vc -> vc + // highlight-start + .quantization(Quantization.bq(q -> q.cache(true).rescoreLimit(200))) + .vectorIndex(Hnsw.of(c -> c.vectorCacheMaxObjects(100000))) + // highlight-end + )).properties(Property.text("title"))); + // END BQWithOptions + } +} diff --git a/_includes/code/java-v6/src/test/java/ConfigurePQTest.java b/_includes/code/java-v6/src/test/java/ConfigurePQTest.java new file mode 100644 index 00000000..9d1b20da --- /dev/null +++ b/_includes/code/java-v6/src/test/java/ConfigurePQTest.java @@ -0,0 +1,152 @@ +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.weaviate.client6.v1.api.WeaviateClient; +import io.weaviate.client6.v1.api.collections.CollectionConfig; +import io.weaviate.client6.v1.api.collections.CollectionHandle; +import io.weaviate.client6.v1.api.collections.Quantization; +import io.weaviate.client6.v1.api.collections.VectorConfig; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +import static org.assertj.core.api.Assertions.assertThat; + +class ConfigurePQTest { + + private static WeaviateClient client; + private static List> data; + private static final String COLLECTION_NAME = "Question"; + + @BeforeAll + public static void beforeAll() throws IOException, InterruptedException { + // START DownloadData + HttpClient httpClient = HttpClient.newHttpClient(); + HttpRequest request = HttpRequest.newBuilder().uri(URI.create( + "https://raw.githubusercontent.com/weaviate-tutorials/intro-workshop/main/data/jeopardy_1k.json")) + .build(); + HttpResponse responseHttp = + httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + String responseBody = responseHttp.body(); + + ObjectMapper objectMapper = new ObjectMapper(); + data = objectMapper.readValue(responseBody, new TypeReference<>() {}); + + System.out.printf("Data type: %s, Length: %d\n", data.getClass().getName(), data.size()); + System.out + .println(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(data.get(1))); + // END DownloadData + + // START ConnectCode + String openaiApiKey = System.getenv("OPENAI_API_KEY"); + client = WeaviateClient + .connectToLocal(config -> config.setHeaders(Map.of("X-OpenAI-Api-Key", openaiApiKey))); + + assertThat(client.isReady()).isTrue(); + // END ConnectCode + } + + @AfterAll + public static void afterAll() throws Exception { + client.close(); + } + + @AfterEach + public void cleanup() throws IOException { + if (client.collections.exists(COLLECTION_NAME)) { + client.collections.delete(COLLECTION_NAME); + } + } + + @Test + void testCollectionWithAutoPQ() throws IOException { + // START CollectionWithAutoPQ + client.collections.create("Question", + col -> col.vectorConfig(VectorConfig.text2VecWeaviate("default", + // highlight-start + vc -> vc.quantization(Quantization.pq(pq -> pq.trainingLimit(50000))) // Set the threshold to begin training + // highlight-end + ))); + // END CollectionWithAutoPQ + + // Confirm that the collection has been created with the right settings + var collection = client.collections.use(COLLECTION_NAME); + var config = collection.config.get(); + assertThat(config).isPresent(); + assertThat(config.get().vectors().get("default").quantization()).isNotNull(); + } + + @Test + void testUpdateSchemaWithPQ() throws IOException { + // START InitialSchema + client.collections.create("Question", col -> col.description("A Jeopardy! question") + .vectorConfig(VectorConfig.text2VecWeaviate())); + // END InitialSchema + + var collection = client.collections.use(COLLECTION_NAME); + var initialConfig = collection.config.get(); + assertThat(initialConfig).isPresent(); + // BQ is enabled by default with Weaviate Server v1.25+ + // assertThat(initialConfig.get().getVectorConfig().get("default").getQuantization()).isNull(); + + // START LoadData + List> objectList = data.stream().map(obj -> { + Map properties = new HashMap<>(); + properties.put("question", obj.get("Question")); + properties.put("answer", obj.get("Answer")); + return properties; + }).collect(Collectors.toList()); + + collection.data.insertMany(objectList.toArray(new Map[0])); + // END LoadData + + var aggregateResponse = collection.aggregate.overAll(a -> a.includeTotalCount(true)); + assertThat(aggregateResponse.totalCount()).isEqualTo(1000); + + // START UpdateSchema + collection.config.update("Question", c -> c.vectorConfig(VectorConfig + .text2VecWeaviate(vc -> vc.quantization(Quantization.pq(pq -> pq.trainingLimit(50000)))))); + // END UpdateSchema + + var updatedConfig = collection.config.get(); + assertThat(updatedConfig).isPresent(); + assertThat(updatedConfig.get().vectors().get("default").quantization()) + .isInstanceOf(Quantization.class); + } + + //TODO[g-despot] How to get quantizer parameters from config? + @Test + void testGetSchema() throws IOException { + // Create a collection with PQ enabled to inspect its schema + client.collections.create("Question", col -> col.vectorConfig(VectorConfig + .text2VecWeaviate(vc -> vc.quantization(Quantization.pq(pq -> pq.trainingLimit(50000)))))); + + // START GetSchema + CollectionHandle> jeopardy = client.collections.use("Question"); + Optional configOpt = jeopardy.config.get(); + + System.out.println(configOpt); + // END GetSchema + assertThat(configOpt).isPresent(); + CollectionConfig config = configOpt.get(); + + Quantization pqConfig = config.vectors().get("default").quantization(); + assertThat(pqConfig).isNotNull(); + // print some of the config properties + // System.out.printf("Encoder: %s\n", pqConfig.encoder()); + // System.out.printf("Training: %d\n", pqConfig.getTrainingLimit()); + // System.out.printf("Segments: %d\n", pqConfig.getSegments()); + // System.out.printf("Centroids: %d\n", pqConfig.getCentroids()); + } +} diff --git a/_includes/code/java-v6/src/test/java/ConfigureRQTest.java b/_includes/code/java-v6/src/test/java/ConfigureRQTest.java new file mode 100644 index 00000000..4f87ea68 --- /dev/null +++ b/_includes/code/java-v6/src/test/java/ConfigureRQTest.java @@ -0,0 +1,142 @@ +import io.weaviate.client6.v1.api.WeaviateClient; +import io.weaviate.client6.v1.api.collections.CollectionHandle; +import io.weaviate.client6.v1.api.collections.Property; +import io.weaviate.client6.v1.api.collections.Quantization; +import io.weaviate.client6.v1.api.collections.VectorConfig; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.util.Map; + +class ConfigureRQTest { + + private static WeaviateClient client; + + @BeforeAll + public static void beforeAll() { + // START ConnectCode + String openaiApiKey = System.getenv("OPENAI_API_KEY"); + client = WeaviateClient + .connectToLocal(config -> config.setHeaders(Map.of("X-OpenAI-Api-Key", openaiApiKey))); + // END ConnectCode + } + + @AfterAll + public static void afterAll() throws Exception { + client.close(); + } + + @Test + void testEnableRQ() throws IOException { + String collectionName = "MyCollection"; + if (client.collections.exists(collectionName)) { + client.collections.delete(collectionName); + } + + // START EnableRQ + client.collections.create("MyCollection", + col -> col.vectorConfig(VectorConfig.text2vecContextionary(vc -> vc + // highlight-start + .quantization(Quantization.rq()) + // highlight-end + )).properties(Property.text("title"))); + // END EnableRQ + } + + @Test + void test1BitEnableRQ() throws IOException { + String collectionName = "MyCollection"; + if (client.collections.exists(collectionName)) { + client.collections.delete(collectionName); + } + + // START 1BitEnableRQ + client.collections.create("MyCollection", + col -> col.vectorConfig(VectorConfig.text2vecContextionary(vc -> vc + // highlight-start + .quantization(Quantization.rq(q -> q.bits(1))) + // highlight-end + )).properties(Property.text("title"))); + // END 1BitEnableRQ + } + + @Test + void testUncompressed() throws IOException { + String collectionName = "MyCollection"; + if (client.collections.exists(collectionName)) { + client.collections.delete(collectionName); + } + + // START Uncompressed + client.collections.create("MyCollection", + col -> col.vectorConfig(VectorConfig.text2vecContextionary(vc -> vc + // highlight-start + .quantization(Quantization.uncompressed()) + // highlight-end + )).properties(Property.text("title"))); + // END Uncompressed + } + + @Test + void testRQWithOptions() throws IOException { + String collectionName = "MyCollection"; + if (client.collections.exists(collectionName)) { + client.collections.delete(collectionName); + } + + // START RQWithOptions + client.collections.create("MyCollection", + col -> col.vectorConfig(VectorConfig.text2vecContextionary(vc -> vc + // highlight-start + .quantization(Quantization.rq(q -> q.bits(8) // Optional: Number of bits + .rescoreLimit(20) // Optional: Number of candidates to fetch before rescoring + )) + // highlight-end + )).properties(Property.text("title"))); + // END RQWithOptions + } + + // TODO[g-despot] Errors on collection update: TYPE_UPDATE_CLASS: bad request + // :parse class update: invalid update for vector "default": + // skipDefaultQuantization is immutable: attempted change from "true" to "false" + @Test + void testUpdateSchema() throws IOException { + String collectionName = "MyCollection"; + if (client.collections.exists(collectionName)) { + client.collections.delete(collectionName); + } + client.collections.create(collectionName, + col -> col + .vectorConfig(VectorConfig + .text2vecContextionary(vc -> vc.quantization(Quantization.uncompressed()))) + .properties(Property.text("title"))); + + // START UpdateSchema + CollectionHandle> collection = client.collections.use("MyCollection"); + collection.config.update(collectionName, c -> c.vectorConfig( + VectorConfig.text2vecContextionary(vc -> vc.quantization(Quantization.rq())))); + // END UpdateSchema + // TODO[g-despot]: Verify the update + } + + @Test + void test1BitUpdateSchema() throws IOException { + String collectionName = "MyCollection"; + if (client.collections.exists(collectionName)) { + client.collections.delete(collectionName); + } + client.collections.create(collectionName, + col -> col + .vectorConfig(VectorConfig + .text2vecContextionary(vc -> vc.quantization(Quantization.uncompressed()))) + .properties(Property.text("title"))); + + // START 1BitUpdateSchema + CollectionHandle> collection = client.collections.use("MyCollection"); + collection.config.update(collectionName, c -> c.vectorConfig(VectorConfig + .text2vecContextionary(vc -> vc.quantization(Quantization.rq(q -> q.bits(1)))))); + // END 1BitUpdateSchema + } +} diff --git a/_includes/code/java-v6/src/test/java/ConfigureSQTest.java b/_includes/code/java-v6/src/test/java/ConfigureSQTest.java new file mode 100644 index 00000000..7433f799 --- /dev/null +++ b/_includes/code/java-v6/src/test/java/ConfigureSQTest.java @@ -0,0 +1,90 @@ +import io.weaviate.client6.v1.api.WeaviateClient; +import io.weaviate.client6.v1.api.collections.CollectionHandle; +import io.weaviate.client6.v1.api.collections.Property; +import io.weaviate.client6.v1.api.collections.Quantization; +import io.weaviate.client6.v1.api.collections.VectorConfig; +import io.weaviate.client6.v1.api.collections.vectorindex.Hnsw; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.util.Map; + +class ConfigureSQTest { + + private static WeaviateClient client; + + @BeforeAll + public static void beforeAll() { + // START ConnectCode + String openaiApiKey = System.getenv("OPENAI_API_KEY"); + client = WeaviateClient + .connectToLocal(config -> config.setHeaders(Map.of("X-OpenAI-Api-Key", openaiApiKey))); + // END ConnectCode + } + + @AfterAll + public static void afterAll() throws Exception { + client.close(); + } + + @Test + void testEnableSQ() throws IOException { + String collectionName = "MyCollection"; + if (client.collections.exists(collectionName)) { + client.collections.delete(collectionName); + } + + // START EnableSQ + client.collections.create("MyCollection", + col -> col.vectorConfig(VectorConfig.text2vecContextionary(vc -> vc + // highlight-start + .quantization(Quantization.sq()) + // highlight-end + )).properties(Property.text("title"))); + // END EnableSQ + } + + // TODO[g-despot] Errors on collection update: TYPE_UPDATE_CLASS: bad request + // :parse class update: invalid update for vector "default": + // skipDefaultQuantization is immutable: attempted change from "true" to "false" + @Test + void testUpdateSchema() throws IOException { + String collectionName = "MyCollection"; + if (client.collections.exists(collectionName)) { + client.collections.delete(collectionName); + } + client.collections.create(collectionName, + col -> col + .vectorConfig(VectorConfig + .text2vecContextionary(vc -> vc.quantization(Quantization.uncompressed()))) + .properties(Property.text("title"))); + + // START UpdateSchema + CollectionHandle> collection = client.collections.use("MyCollection"); + collection.config.update(collectionName, c -> c.vectorConfig( + VectorConfig.text2vecContextionary(vc -> vc.quantization(Quantization.sq())))); + // END UpdateSchema + // TODO[g-despot]: Verify the update + } + + @Test + void testSQWithOptions() throws IOException { + String collectionName = "MyCollection"; + if (client.collections.exists(collectionName)) { + client.collections.delete(collectionName); + } + + // START SQWithOptions + client.collections.create("MyCollection", + col -> col.vectorConfig(VectorConfig.text2vecContextionary(vc -> vc + // highlight-start + .quantization( + Quantization.sq(q -> q.cache(true).trainingLimit(50000).rescoreLimit(200))) + .vectorIndex(Hnsw.of(c -> c.vectorCacheMaxObjects(100000))) + // highlight-end + )).properties(Property.text("title"))); + // END SQWithOptions + } +} diff --git a/_includes/code/java-v6/src/test/java/ConnectionTest.java b/_includes/code/java-v6/src/test/java/ConnectionTest.java new file mode 100644 index 00000000..acec3768 --- /dev/null +++ b/_includes/code/java-v6/src/test/java/ConnectionTest.java @@ -0,0 +1,168 @@ +import io.weaviate.client6.v1.api.Authentication; +import io.weaviate.client6.v1.api.WeaviateClient; +import org.junit.jupiter.api.Test; + +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +class ConnectionTest { + + @Test + void testConnectLocalWithCustomUrl() throws Exception { + // START CustomURL + WeaviateClient client = WeaviateClient + .connectToLocal(config -> config.host("127.0.0.1").port(8080)); + + System.out.println(client.isReady()); // Should print: `True` + + client.close(); // Free up resources + // END CustomURL + } + + // TODO[g-despot] Missing timeout options + // @Test + // void testTimeoutLocal() throws Exception { + // WeaviateClient client = WeaviateClient.connectToLocal(config -> config + // .port(8080) + // .grpcPort(50051)); + + // System.out.println(client.isReady()); // Should print: `True` + + // client.close(); // Free up resources + // } + // START TimeoutLocal + // Coming soon + // END TimeoutLocal + // START TimeoutCustom + // Coming soon + // END TimeoutCustom + + void testConnectWCDWithApiKey() throws Exception { + // START APIKeyWCD + // Best practice: store your credentials in environment variables + String weaviateUrl = System.getenv("WEAVIATE_URL"); + String weaviateApiKey = System.getenv("WEAVIATE_API_KEY"); + + WeaviateClient client = WeaviateClient.connectToWeaviateCloud(weaviateUrl, // Replace with your Weaviate Cloud URL + weaviateApiKey // Replace with your Weaviate Cloud key + ); + + System.out.println(client.isReady()); // Should print: `True` + + client.close(); // Free up resources + // END APIKeyWCD + } + + @Test + void testCustomConnection() throws Exception { + // START CustomConnect + // Best practice: store your credentials in environment variables + String httpHost = System.getenv("WEAVIATE_HTTP_HOST"); + String grpcHost = System.getenv("WEAVIATE_GRPC_HOST"); + String weaviateApiKey = System.getenv("WEAVIATE_API_KEY"); + String cohereApiKey = System.getenv("COHERE_API_KEY"); + + WeaviateClient client = + WeaviateClient.connectToCustom(config -> config.scheme("https") // Corresponds to http_secure=True and grpc_secure=True + .httpHost(httpHost) + .httpPort(443) + .grpcHost(grpcHost) + .grpcPort(443) + .authentication(Authentication.apiKey(weaviateApiKey)) + .setHeaders(Map.of("X-Cohere-Api-Key", cohereApiKey))); + + System.out.println(client.isReady()); // Should print: `True` + + client.close(); // Free up resources + // END CustomConnect + } + + @Test + void testCustomApiKeyConnection() throws Exception { + // START ConnectWithApiKeyExample + // Best practice: store your credentials in environment variables + String httpHost = System.getenv("WEAVIATE_HTTP_HOST"); + String grpcHost = System.getenv("WEAVIATE_GRPC_HOST"); + String weaviateApiKey = System.getenv("WEAVIATE_API_KEY"); + String cohereApiKey = System.getenv("COHERE_API_KEY"); + + WeaviateClient client = + WeaviateClient.connectToCustom(config -> config.scheme("https") // Corresponds to http_secure=True and grpc_secure=True + .httpHost(httpHost) + .httpPort(443) + .grpcHost(grpcHost) + .grpcPort(443) + .authentication(Authentication.apiKey(weaviateApiKey)) + .setHeaders(Map.of("X-Cohere-Api-Key", cohereApiKey))); + + System.out.println(client.isReady()); // Should print: `True` + + client.close(); // Free up resources + // // END ConnectWithApiKeyExample + } + + @Test + void testConnectLocalNoAuth() throws Exception { + // START LocalNoAuth + WeaviateClient client = WeaviateClient.connectToLocal(); + + System.out.println(client.isReady()); // Should print: `True` + + client.close(); // Free up resources + // END LocalNoAuth + } + + @Test + void testConnectLocalWithAuth() throws Exception { + // START LocalAuth + // Best practice: store your credentials in environment variables + final String weaviateApiKey = System.getenv("WEAVIATE_LOCAL_API_KEY"); + + // The local() factory doesn't support auth, so we must use custom(). + WeaviateClient client = WeaviateClient.connectToCustom( + config -> config.authentication(Authentication.apiKey(weaviateApiKey))); + + System.out.println(client.isReady()); // Should print: `True` + + client.close(); // Free up resources + // END LocalAuth + } + + @Test + void testConnectLocalWithThirdPartyKeys() throws Exception { + // START LocalThirdPartyAPIKeys + // Best practice: store your credentials in environment variables + final String cohereApiKey = System.getenv("COHERE_API_KEY"); + + WeaviateClient client = WeaviateClient.connectToLocal( + config -> config.setHeaders(Map.of("X-Cohere-Api-Key", cohereApiKey))); + + System.out.println(client.isReady()); // Should print: `True` + + client.close(); // Free up resources + // END LocalThirdPartyAPIKeys + } + + @Test + void testConnectWCDWithThirdPartyKeys() throws Exception { + // START ThirdPartyAPIKeys + // Best practice: store your credentials in environment variables + String weaviateUrl = System.getenv("WEAVIATE_URL"); + String weaviateApiKey = System.getenv("WEAVIATE_API_KEY"); + String cohereApiKey = System.getenv("COHERE_API_KEY"); + + WeaviateClient client = WeaviateClient.connectToWeaviateCloud(weaviateUrl, // Replace with your Weaviate Cloud URL + weaviateApiKey, // Replace with your Weaviate Cloud key + config -> config.setHeaders(Map.of("X-Cohere-Api-Key", cohereApiKey))); + + System.out.println(client.isReady()); // Should print: `True` + + client.close(); // Free up resources + // END ThirdPartyAPIKeys + } + + // START TimeoutWCD + // Coming soon + // END TimeoutWCD +} diff --git a/_includes/code/java-v6/src/test/java/GetStartedTest.java b/_includes/code/java-v6/src/test/java/GetStartedTest.java new file mode 100644 index 00000000..e91f7db1 --- /dev/null +++ b/_includes/code/java-v6/src/test/java/GetStartedTest.java @@ -0,0 +1,102 @@ +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.weaviate.client6.v1.api.WeaviateClient; +import io.weaviate.client6.v1.api.collections.CollectionHandle; +import io.weaviate.client6.v1.api.collections.Property; +import io.weaviate.client6.v1.api.collections.VectorConfig; +import io.weaviate.client6.v1.api.collections.data.InsertManyResponse; +import org.junit.jupiter.api.Test; + +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +class GetStartedTest { + + @Test + void testGetStartedWorkflow() throws Exception { + WeaviateClient client = null; + String collectionName = "Question"; + try { + // START GetStarted + // highlight-start + // Connect to a local Weaviate instance + client = WeaviateClient.connectToLocal(); + // highlight-end + if (client.collections.exists(collectionName)) { + client.collections.delete(collectionName); + } + + // highlight-start + // Create a collection with + client.collections.create( + collectionName, + col -> col.properties(Property.text("answer"), + Property.text("question"), + Property.text("category")) + .vectorConfig(VectorConfig.text2vecContextionary()) // Configure the Contextionary embedding model + ); + CollectionHandle> questions = client.collections.use(collectionName); + // highlight-end + + HttpClient httpClient = HttpClient.newHttpClient(); + HttpRequest request = HttpRequest.newBuilder() + .uri(URI + .create("https://raw.githubusercontent.com/weaviate-tutorials/quickstart/main/data/jeopardy_tiny.json")) + .build(); + HttpResponse responseHttp = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + String responseBody = responseHttp.body(); + + ObjectMapper objectMapper = new ObjectMapper(); + List> data = objectMapper.readValue(responseBody, new TypeReference<>() { + }); + + // highlight-start + // Create a list to hold the objects for insertion + List> questionsToInsert = new ArrayList<>(); + + // Populate the list with the data + for (Map d : data) { + Map properties = new HashMap<>(); + properties.put("answer", d.get("Answer")); + properties.put("question", d.get("Question")); + properties.put("category", d.get("Category")); + questionsToInsert.add(properties); + } + + // Call insertMany with the list of objects + InsertManyResponse insertResponse = questions.data.insertMany(questionsToInsert.toArray(new Map[0])); + + // Check for errors + if (!insertResponse.errors().isEmpty()) { + System.err.println("Errors during insertMany: " + insertResponse.errors()); + } + // highlight-end + + // END GetStarted + Thread.sleep(2000); + // START GetStarted + // highlight-start + // Perform a vector similarity search + var queryResponse = questions.query.nearText("biology", q -> q.limit(2)); + // highlight-end + + for (var obj : queryResponse.objects()) { + System.out.println(obj.properties()); + } + // END GetStarted + } finally { + if (client != null) { + if (client.collections.exists(collectionName)) { + client.collections.delete(collectionName); + } + client.close(); // Free up resources + } + } + } +} \ No newline at end of file diff --git a/_includes/code/java-v6/src/test/java/ManageCollectionsAliasTest.java b/_includes/code/java-v6/src/test/java/ManageCollectionsAliasTest.java new file mode 100644 index 00000000..cf8f7c19 --- /dev/null +++ b/_includes/code/java-v6/src/test/java/ManageCollectionsAliasTest.java @@ -0,0 +1,224 @@ +import io.weaviate.client6.v1.api.WeaviateClient; +import io.weaviate.client6.v1.api.alias.Alias; +import io.weaviate.client6.v1.api.collections.CollectionHandle; +import io.weaviate.client6.v1.api.collections.Property; +import io.weaviate.client6.v1.api.collections.VectorConfig; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; + +class ManageCollectionsAliasTest { + + private static WeaviateClient client; + + @BeforeAll + public static void beforeAll() throws IOException { + // START ConnectToWeaviate + // Connect to local Weaviate instance + client = WeaviateClient.connectToLocal(); + // END ConnectToWeaviate + if (client.alias.list().stream().anyMatch(a -> a.alias().equals("ArticlesAlias"))) + client.alias.delete("ArticlesAlias"); + if (client.alias.list().stream().anyMatch(a -> a.alias().equals("ProductsAlias"))) + client.alias.delete("ProductsAlias"); + client.collections.delete("Articles"); + client.collections.delete("ArticlesV2"); + client.collections.delete("Products_v1"); + client.collections.delete("Products_v2"); + } + + @AfterAll + public static void afterAll() throws Exception { + client.close(); + } + + @AfterEach + public void cleanup() throws IOException { + // Cleanup collections and aliases after each test + if (client.alias.list().stream().anyMatch(a -> a.alias().equals("ArticlesAlias"))) + client.alias.delete("ArticlesAlias"); + if (client.alias.list().stream().anyMatch(a -> a.alias().equals("ProductsAlias"))) + client.alias.delete("ProductsAlias"); + client.collections.delete("Articles"); + client.collections.delete("ArticlesV2"); + client.collections.delete("Products_v1"); + client.collections.delete("Products_v2"); + } + + @Test + void testCreateAlias() throws IOException { + // START CreateAlias + // Create a collection first + client.collections.create("Articles", col -> col.vectorConfig(VectorConfig.selfProvided()) + .properties(Property.text("title"), Property.text("content"))); + + // Create an alias pointing to the collection + client.alias.create("Articles", "ArticlesAlias"); + // END CreateAlias + } + + @Test + void testListAliases() throws IOException { + client.collections.create("Articles"); + client.alias.create("Articles", "ArticlesAlias"); + + // START ListAllAliases + // Get all aliases in the instance + List allAliases = client.alias.list(); + + for (Alias aliasInfo : allAliases) { + System.out.printf("Alias: %s -> Collection: %s\n", aliasInfo.alias(), aliasInfo.collection()); + } + // END ListAllAliases + + // START ListCollectionAliases + // Get all aliases pointing to a specific collection + List collectionAliases = client.alias.list(a -> a.collection("Articles")); + + for (Alias aliasInfo : collectionAliases) { + System.out.printf("Alias pointing to Articles: %s\n", aliasInfo.alias()); + } + // END ListCollectionAliases + } + + @Test + void testGetAlias() throws IOException { + client.collections.create("Articles"); + client.alias.create("Articles", "ArticlesAlias"); + + // START GetAlias + // Get information about a specific alias + Optional aliasInfoOpt = client.alias.get("ArticlesAlias"); + + aliasInfoOpt.ifPresent(aliasInfo -> { + System.out.printf("Alias: %s\n", aliasInfo.alias()); + System.out.printf("Target collection: %s\n", aliasInfo.collection()); + }); + // END GetAlias + } + + // TODO[g-despot] Python alias creation returns bool + @Test + void testUpdateAlias() throws IOException { + client.collections.create("Articles"); + client.alias.create("Articles", "ArticlesAlias"); + + // START UpdateAlias + // Create a new collection for migration + client.collections.create("ArticlesV2", col -> col.vectorConfig(VectorConfig.selfProvided()) + .properties(Property.text("title"), Property.text("content"), Property.text("author") // New field + )); + + // Update the alias to point to the new collection + client.alias.update("ArticlesAlias", "ArticlesV2"); + // END UpdateAlias + } + + @Test + void testUseAlias() throws IOException { + client.collections.create("Articles", col -> col.vectorConfig(VectorConfig.selfProvided()) + .properties(Property.text("title"), Property.text("content"))); + client.alias.create("Articles", "ArticlesAlias"); + + // START UseAlias + // Use the alias just like a collection name + CollectionHandle> articles = client.collections.use("ArticlesAlias"); + + // Insert data using the alias + articles.data.insert(Map.of("title", "Using Aliases in Weaviate", "content", + "Aliases make collection management easier...")); + + // Query using the alias + var results = articles.query.fetchObjects(q -> q.limit(5)); + + for (var obj : results.objects()) { + System.out.printf("Found: %s\n", obj.properties().get("title")); + } + // END UseAlias + + // START DeleteAlias + // Delete an alias (the underlying collection remains) + client.alias.delete("ArticlesAlias"); + // END DeleteAlias + } + + // TODO[g-despot] Python fetchObjects(...) can be empty + @Test + void testMigrationWorkflow() throws IOException { + // START Step1CreateOriginal + // Create original collection with data + client.collections.create("Products_v1", col -> col.vectorConfig(VectorConfig.selfProvided()) + .properties(Property.text("name"), Property.number("price"))); + + var productsV1 = client.collections.use("Products_v1"); + productsV1.data.insertMany(Map.of("name", "Product A", "price", 100.0), + Map.of("name", "Product B", "price", 200.0)); + // END Step1CreateOriginal + + // START Step2CreateAlias + // Create alias pointing to current collection + client.alias.create("Products_v1", "ProductsAlias"); + // END Step2CreateAlias + + // START MigrationUseAlias + // Your application always uses the alias name + CollectionHandle> products = client.collections.use("ProductsAlias"); + + // Insert data through the alias + products.data.insert(Map.of("name", "Product C", "price", 300.0)); + + // Query through the alias + var results = products.query.fetchObjects(q -> q.limit(5)); + for (var obj : results.objects()) { + System.out.printf("Product: %s, Price: $%.2f\n", obj.properties().get("name"), + obj.properties().get("price")); + } + // END MigrationUseAlias + + // START Step3NewCollection + // Create new collection with updated schema + client.collections.create("Products_v2", col -> col.vectorConfig(VectorConfig.selfProvided()) + .properties(Property.text("name"), Property.number("price"), Property.text("category") // New field + )); + // END Step3NewCollection + + // START Step4MigrateData + // Migrate data to new collection + var productsV2 = client.collections.use("Products_v2"); + var oldData = productsV1.query.fetchObjects(c -> c.limit(10)).objects(); + + List> migratedObjects = new ArrayList<>(); + for (var obj : oldData) { + migratedObjects.add(Map.of("name", obj.properties().get("name"), "price", + obj.properties().get("price"), "category", "General" // Default value for new field + )); + } + productsV2.data.insertMany(migratedObjects.toArray(new Map[0])); + // END Step4MigrateData + + // START Step5UpdateAlias + // Switch alias to new collection (instant switch!) + client.alias.update("ProductsAlias", "Products_v2"); + + // All queries using "Products" alias now use the new collection + products = client.collections.use("ProductsAlias"); + var result = products.query.fetchObjects(q -> q.limit(1)); + System.out.println(result.objects().get(0).properties()); // Will include the new "category" field + // END Step5UpdateAlias + assertThat(result.objects().get(0).properties()).containsKey("category"); + + // START Step6Cleanup + // Clean up old collection after verification + client.collections.delete("Products_v1"); + // END Step6Cleanup + } +} diff --git a/_includes/code/java-v6/src/test/java/ManageCollectionsCrossReferencesTest.java b/_includes/code/java-v6/src/test/java/ManageCollectionsCrossReferencesTest.java new file mode 100644 index 00000000..c48f7412 --- /dev/null +++ b/_includes/code/java-v6/src/test/java/ManageCollectionsCrossReferencesTest.java @@ -0,0 +1,420 @@ +import io.weaviate.client6.v1.api.WeaviateClient; +import io.weaviate.client6.v1.api.collections.Property; +import io.weaviate.client6.v1.api.collections.ReferenceProperty; +import io.weaviate.client6.v1.api.collections.data.Reference; +import io.weaviate.client6.v1.api.collections.query.QueryReference; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ManageCollectionsCrossReferencesTest { + + private static WeaviateClient client; + + @BeforeAll + public static void beforeAll() { + // Instantiate the client anonymously + String openaiApiKey = System.getenv("OPENAI_API_KEY"); + client = WeaviateClient.connectToLocal(config -> config + .setHeaders(Map.of("X-OpenAI-Api-Key", openaiApiKey))); + } + + @AfterEach + public void afterEach() throws IOException { + // Clean up collections after each test + try { + client.collections.delete("JeopardyQuestion"); + } catch (Exception e) { + // Collection might not exist + } + try { + client.collections.delete("JeopardyCategory"); + } catch (Exception e) { + // Collection might not exist + } + } + + @Test + void testCrossRefDefinition() throws IOException { + // START CrossRefDefinition + client.collections.create("JeopardyCategory", col -> col + .description("A Jeopardy! category") + .properties( + Property.text("title"))); + + client.collections.create("JeopardyQuestion", col -> col + .description("A Jeopardy! question") + .properties( + Property.text("question"), + Property.text("answer")) + // highlight-start + .references( + ReferenceProperty.to("hasCategory", "JeopardyCategory")) + // highlight-end + ); + // END CrossRefDefinition + + // Verify collections were created properly + var questionConfig = client.collections.getConfig("JeopardyQuestion").get(); + assertThat(questionConfig.references()).hasSize(1); + assertThat(questionConfig.references().get(0).propertyName()).isEqualTo("hasCategory"); + } + + @Test + void testObjectWithCrossRef() throws IOException { + // Setup collections + setupCollections(); + + // Prep data + var categories = client.collections.use("JeopardyCategory"); + Map categoryProperties = Map.of("title", "Weaviate"); + var categoryResult = categories.data.insert(categoryProperties); + var categoryUuid = categoryResult.metadata().uuid(); + + Map properties = Map.of( + "question", "What tooling helps make Weaviate scalable?", + "answer", "Sharding, multi-tenancy, and replication"); + + // START ObjectWithCrossRef + var questions = client.collections.use("JeopardyQuestion"); + + var result = questions.data.insert( + properties, // A map with the properties of the object + opt -> opt + // highlight-start + .reference("hasCategory", Reference.uuids(categoryUuid)) // e.g. {"hasCategory": + // "583876f3-e293-5b5b-9839-03f455f14575"} + // highlight-end + ); + // END ObjectWithCrossRef + + // Test results + var fetchedObj = questions.query.byId( + result.metadata().uuid(), + opt -> opt.returnReferences( + QueryReference.single("hasCategory"))); + + assertThat(fetchedObj).isPresent(); + assertThat(fetchedObj.get().references()).containsKey("hasCategory"); + } + + @Test + void testOneWay() throws IOException { + // Setup collections and get sample IDs + setupCollections(); + var questions = client.collections.use("JeopardyQuestion"); + var categories = client.collections.use("JeopardyCategory"); + + // Insert test data + Map questionData = Map.of( + "question", "This city is known for the Golden Gate Bridge", + "answer", "San Francisco"); + var questionResult = questions.data.insert(questionData); + var questionObjId = questionResult.metadata().uuid(); + + Map categoryData = Map.of("title", "U.S. CITIES"); + var categoryResult = categories.data.insert(categoryData); + var categoryObjId = categoryResult.metadata().uuid(); + + // START OneWayCrossReferences + questions.data.referenceAdd( + questionObjId, + "hasCategory", + // highlight-start + Reference.uuids(categoryObjId) + // highlight-end + ); + // END OneWayCrossReferences + + // Test results + var result = questions.query.byId( + questionObjId, + opt -> opt.returnReferences( + QueryReference.single("hasCategory"))); + + assertThat(result).isPresent(); + assertThat(result.get().references()).containsKey("hasCategory"); + } + + @Test + void testTwoWay() throws IOException { + // Clean up first + client.collections.delete("JeopardyQuestion"); + client.collections.delete("JeopardyCategory"); + + // START TwoWayCategory1CrossReferences + client.collections.create("JeopardyCategory", col -> col + .description("A Jeopardy! category") + .properties( + Property.text("title"))); + // END TwoWayCategory1CrossReferences + + // START TwoWayQuestionCrossReferences + client.collections.create("JeopardyQuestion", col -> col + .description("A Jeopardy! question") + .properties( + Property.text("question"), + Property.text("answer")) + // highlight-start + .references( + ReferenceProperty.to("hasCategory", "JeopardyCategory")) + // highlight-end + ); + // END TwoWayQuestionCrossReferences + + // START TwoWayCategoryCrossReferences + var category = client.collections.use("JeopardyCategory"); + category.config.addReference( + // highlight-start + "hasQuestion", "JeopardyQuestion" + // highlight-end + ); + // END TwoWayCategoryCrossReferences + + // Insert test data + var questions = client.collections.use("JeopardyQuestion"); + var categories = client.collections.use("JeopardyCategory"); + + Map questionData = Map.of( + "question", "This city is known for the Golden Gate Bridge", + "answer", "San Francisco"); + var questionResult = questions.data.insert(questionData); + var questionObjId = questionResult.metadata().uuid(); + + Map categoryData = Map.of("title", "U.S. CITIES"); + var categoryResult = categories.data.insert(categoryData); + var categoryObjId = categoryResult.metadata().uuid(); + + // START TwoWayCrossReferences + // For the "San Francisco" JeopardyQuestion object, add a cross-reference to the + // "U.S. CITIES" JeopardyCategory object + // highlight-start + questions.data.referenceAdd( + questionObjId, + "hasCategory", + Reference.uuids(categoryObjId)); + // highlight-end + + // For the "U.S. CITIES" JeopardyCategory object, add a cross-reference to "San + // Francisco" + // highlight-start + categories.data.referenceAdd( + categoryObjId, + "hasQuestion", + Reference.uuids(questionObjId)); + // highlight-end + // END TwoWayCrossReferences + + // Test results + var result = categories.query.byId( + categoryObjId, + opt -> opt.returnReferences( + QueryReference.single("hasQuestion"))); + + assertThat(result).isPresent(); + assertThat(result.get().references()).containsKey("hasQuestion"); + } + + @Test + void testMultiple() throws IOException { + // Setup collections + setupCollections(); + var questions = client.collections.use("JeopardyQuestion"); + var categories = client.collections.use("JeopardyCategory"); + + // Insert test data + Map questionData = Map.of( + "question", "This city is known for the Golden Gate Bridge", + "answer", "San Francisco"); + var questionResult = questions.data.insert(questionData); + var questionObjId = questionResult.metadata().uuid(); + + Map categoryData1 = Map.of("title", "U.S. CITIES"); + var categoryResult1 = categories.data.insert(categoryData1); + var categoryObjId = categoryResult1.metadata().uuid(); + + Map categoryData2 = Map.of("title", "MUSEUMS"); + var categoryResult2 = categories.data.insert(categoryData2); + var categoryObjIdAlt = categoryResult2.metadata().uuid(); + + // START MultipleCrossReferences + // highlight-start + // Add multiple references - need to add them individually + for (String tempUuid : List.of(categoryObjId, categoryObjIdAlt)) { + questions.data.referenceAdd( + questionObjId, + "hasCategory", + Reference.uuids(tempUuid)); + } + // highlight-end + // END MultipleCrossReferences + + // Test results + var result = questions.query.byId( + questionObjId, + opt -> opt.returnReferences( + QueryReference.single("hasCategory"))); + + assertThat(result).isPresent(); + assertThat(result.get().references()).containsKey("hasCategory"); + + @SuppressWarnings("unchecked") + List refs = result.get().references().get("hasCategory"); + assertThat(refs).hasSize(2); + } + + @Test + void testReadCrossRef() throws IOException { + // Setup collections with data + setupCollections(); + var questions = client.collections.use("JeopardyQuestion"); + var categories = client.collections.use("JeopardyCategory"); + + // Insert category and question with reference + Map categoryData = Map.of("title", "SCIENCE"); + var categoryResult = categories.data.insert(categoryData); + Map questionData = Map.of("question", "What is H2O?", "answer", "Water"); + var questionResult = questions.data.insert( + questionData, + opt -> opt.reference("hasCategory", Reference.objects(categoryResult))); + var questionObjId = questionResult.metadata().uuid(); + + // START ReadCrossRef + // Include the cross-references in a query response + // highlight-start + var response = questions.query.fetchObjects( // Or `hybrid`, `nearText`, etc. + opt -> opt + .limit(2) + .returnReferences( + QueryReference.single("hasCategory", + ref -> ref.returnProperties("title")))); + // highlight-end + + // Or include cross-references in a single-object retrieval + // highlight-start + var obj = questions.query.byId( + questionObjId, + opt -> opt.returnReferences( + QueryReference.single("hasCategory", + ref -> ref.returnProperties("title")))); + // highlight-end + // END ReadCrossRef + + // Test results + assertThat(response.objects()).isNotEmpty(); + assertThat(obj).isPresent(); + assertThat(obj.get().references()).containsKey("hasCategory"); + } + + @Test + void testDelete() throws IOException { + // Setup collections + setupCollections(); + var questions = client.collections.use("JeopardyQuestion"); + var categories = client.collections.use("JeopardyCategory"); + + // Insert test data with reference + Map categoryData = Map.of("title", "MUSEUMS"); + var categoryResult = categories.data.insert(categoryData); + var categoryObjId = categoryResult.metadata().uuid(); + + Map questionData = Map.of( + "question", "This city is known for the Golden Gate Bridge", + "answer", "San Francisco"); + var questionResult = questions.data.insert( + questionData, + opt -> opt.reference("hasCategory", Reference.uuids(categoryObjId))); + var questionObjId = questionResult.metadata().uuid(); + + // START DeleteCrossReference + // From the "San Francisco" JeopardyQuestion object, delete the "MUSEUMS" + // category cross-reference + // highlight-start + questions.data.referenceDelete( + // highlight-end + questionObjId, + "hasCategory", + Reference.uuids(categoryObjId)); + // END DeleteCrossReference + + // Test results + var result = questions.query.byId( + questionObjId, + opt -> opt.returnReferences( + QueryReference.single("hasCategory"))); + + assertThat(result).isPresent(); + @SuppressWarnings("unchecked") + List refs = result.get().references().get("hasCategory"); + assertThat(refs).isEmpty(); + } + + @Test + void testUpdate() throws IOException { + // Setup collections + setupCollections(); + var questions = client.collections.use("JeopardyQuestion"); + var categories = client.collections.use("JeopardyCategory"); + + // Insert test data + Map categoryData1 = Map.of("title", "MUSEUMS"); + var categoryResult1 = categories.data.insert(categoryData1); + var categoryObjId = categoryResult1.metadata().uuid(); + + Map categoryData2 = Map.of("title", "U.S. CITIES"); + categories.data.insert(categoryData2); // Secondary category for testing replacement + + Map questionData = Map.of( + "question", "This city is known for the Golden Gate Bridge", + "answer", "San Francisco"); + var questionResult = questions.data.insert(questionData); + var questionObjId = questionResult.metadata().uuid(); + + // START UpdateCrossReference + // In the "San Francisco" JeopardyQuestion object, set the "hasCategory" + // cross-reference only to "MUSEUMS" + // highlight-start + questions.data.referenceReplace( + // highlight-end + questionObjId, + "hasCategory", + Reference.uuids(categoryObjId)); + // END UpdateCrossReference + + // Test results + var result = questions.query.byId( + questionObjId, + opt -> opt.returnReferences( + QueryReference.single("hasCategory"))); + + assertThat(result).isPresent(); + List refs = result.get().references().get("hasCategory"); + assertThat(refs).hasSize(1); + System.out.println("Reference UUID: " + refs.get(0)); + // var refObj = (ObjectMetadata) refs.get(0); + // assertThat(refObj.uuid()).isEqualTo(categoryObjId); + } + + // Helper method to set up collections + private void setupCollections() throws IOException { + client.collections.create("JeopardyCategory", col -> col + .description("A Jeopardy! category") + .properties( + Property.text("title"))); + + client.collections.create("JeopardyQuestion", col -> col + .description("A Jeopardy! question") + .properties( + Property.text("question"), + Property.text("answer")) + .references( + ReferenceProperty.to("hasCategory", "JeopardyCategory"))); + } +} \ No newline at end of file diff --git a/_includes/code/java-v6/src/test/java/ManageCollectionsMigrateDataTest.java b/_includes/code/java-v6/src/test/java/ManageCollectionsMigrateDataTest.java new file mode 100644 index 00000000..130f6312 --- /dev/null +++ b/_includes/code/java-v6/src/test/java/ManageCollectionsMigrateDataTest.java @@ -0,0 +1,262 @@ +import io.weaviate.client6.v1.api.WeaviateClient; +import io.weaviate.client6.v1.api.collections.CollectionHandle; +import io.weaviate.client6.v1.api.collections.Generative; +import io.weaviate.client6.v1.api.collections.ObjectMetadata; +import io.weaviate.client6.v1.api.collections.Property; +import io.weaviate.client6.v1.api.collections.VectorConfig; +import io.weaviate.client6.v1.api.collections.WeaviateObject; +import io.weaviate.client6.v1.api.collections.data.Reference; +import io.weaviate.client6.v1.api.collections.query.Metadata; +import io.weaviate.client6.v1.api.collections.query.QueryMetadata; +import io.weaviate.client6.v1.api.collections.tenants.Tenant; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +import static org.assertj.core.api.Assertions.assertThat; + +class ManageCollectionsMigrateDataTest { + + private static WeaviateClient clientSrc; + private static WeaviateClient clientTgt; + private static final int DATASET_SIZE = 50; + + @BeforeAll + public static void beforeAll() throws IOException { + String openaiApiKey = System.getenv("OPENAI_APIKEY"); + // Connect to the source Weaviate instance + clientSrc = WeaviateClient + .connectToLocal(config -> config.setHeaders(Map.of("X-OpenAI-Api-Key", openaiApiKey))); + // Connect to the target Weaviate instance + clientTgt = WeaviateClient.connectToLocal(config -> config.port(8090).grpcPort(50061) + .setHeaders(Map.of("X-OpenAI-Api-Key", openaiApiKey))); + + + // Simulate weaviate-datasets by creating and populating source collections + createCollection(clientSrc, "WineReview", false); + createCollection(clientSrc, "WineReviewMT", true); + + var wineReview = clientSrc.collections.use("WineReview"); + List> wineReviewData = new ArrayList<>(); + for (int i = 0; i < DATASET_SIZE; i++) { + wineReviewData.add(Map.of("title", "Review " + i)); + } + wineReview.data.insertMany(wineReviewData.toArray(new Map[0])); + + var wineReviewMT = clientSrc.collections.use("WineReviewMT"); + wineReviewMT.tenants.create(List.of(Tenant.active("tenantA"))); + wineReviewMT.withTenant("tenantA").data.insertMany(wineReviewData.toArray(new Map[0])); + + assertThat(clientSrc.isReady()).isTrue(); + } + + @AfterAll + public static void afterAll() throws Exception { + if (clientSrc != null) { + clientSrc.close(); + } + if (clientTgt != null) { + clientTgt.close(); + } + } + + @AfterEach + public void cleanupTarget() throws IOException { + // Clean up collections on the target client after each test + clientTgt.collections.delete("WineReview"); + clientTgt.collections.delete("WineReviewMT"); + } + + // START CreateCollectionCollectionToCollection // START CreateCollectionCollectionToTenant // START CreateCollectionTenantToCollection // START CreateCollectionTenantToTenant + private static CollectionHandle> createCollection(WeaviateClient clientIn, + String collectionName, boolean enableMt) throws IOException { + // END CreateCollectionCollectionToCollection // END CreateCollectionCollectionToTenant // END CreateCollectionTenantToCollection // END CreateCollectionTenantToTenant + if (clientIn.collections.exists(collectionName)) { + clientIn.collections.delete(collectionName); + } + // START CreateCollectionCollectionToCollection // START CreateCollectionCollectionToTenant // START CreateCollectionTenantToCollection // START CreateCollectionTenantToTenant + clientIn.collections.create(collectionName, col -> col.multiTenancy(c -> c.enabled(enableMt)) + // Additional settings not shown + .vectorConfig(VectorConfig.text2vecContextionary()).generativeModule(Generative.cohere()) + .properties(Property.text("review_body"), Property.text("title"), Property.text("country"), + Property.integer("points"), Property.number("price"))); + + return clientIn.collections.use(collectionName); + } + + // END CreateCollectionCollectionToCollection // END CreateCollectionCollectionToTenant // END CreateCollectionTenantToCollection // END CreateCollectionTenantToTenant + + // START CollectionToCollection // START TenantToCollection // START CollectionToTenant // START TenantToTenant + private void migrateData(CollectionHandle> collectionSrc, + CollectionHandle> collectionTgt) { + System.out.println("Starting data migration..."); + List, Reference, ObjectMetadata>> sourceObjects = + StreamSupport + .stream(collectionSrc.paginate(p -> p.returnMetadata(Metadata.VECTOR)).spliterator(), + false) + .map(( + WeaviateObject, Object, QueryMetadata> obj) -> new WeaviateObject.Builder, Reference, ObjectMetadata>() + .properties(obj.properties()) + .metadata(ObjectMetadata.of(m -> m.uuid(obj.uuid()).vectors(obj.vectors()))) + .build()) + .collect(Collectors.toList()); + + collectionTgt.data.insertMany(sourceObjects); + + System.out.println("Data migration complete."); + } + + // END CollectionToCollection // END TenantToCollection // END CollectionToTenant // END TenantToTenant + + private boolean verifyMigration(CollectionHandle> collectionSrc, + CollectionHandle> collectionTgt, int numSamples) { + List, Object, QueryMetadata>> srcObjects = StreamSupport + .stream(collectionSrc.paginate().spliterator(), false).collect(Collectors.toList()); + + if (srcObjects.isEmpty()) { + System.out.println("No objects in source collection"); + return false; + } + Collections.shuffle(srcObjects); + List, Object, QueryMetadata>> sampledObjects = + srcObjects.subList(0, Math.min(numSamples, srcObjects.size())); + + System.out.printf("Verifying %d random objects...\n", sampledObjects.size()); + for (var srcObj : sampledObjects) { + Optional, Object, QueryMetadata>> tgtObjOpt = + collectionTgt.query.byId(srcObj.uuid()); + if (tgtObjOpt.isEmpty()) { + System.out.printf("Object %s not found in target collection\n", srcObj.uuid()); + return false; + } + if (!srcObj.properties().equals(tgtObjOpt.get().properties())) { + System.out.printf("Properties mismatch for object %s\n", srcObj.uuid()); + return false; + } + } + System.out.println("All sampled objects verified successfully!"); + return true; + } + + // START CreateCollectionCollectionToCollection + void createCollectionToCollection() throws IOException { + createCollection(clientTgt, "WineReview", false); + } + // END CreateCollectionCollectionToCollection + + @Test + // START CollectionToCollection + void testCollectionToCollection() throws IOException { + createCollectionToCollection(); + + var reviewsSrc = clientSrc.collections.use("WineReview"); + var reviewsTgt = clientTgt.collections.use("WineReview"); + migrateData(reviewsSrc, reviewsTgt); + + // END CollectionToCollection + assertThat(reviewsTgt.aggregate.overAll(a -> a.includeTotalCount(true)).totalCount()) + .isEqualTo(DATASET_SIZE); + assertThat(verifyMigration(reviewsSrc, reviewsTgt, 5)).isTrue(); + // START CollectionToCollection + } + // END CollectionToCollection + + // START CreateCollectionTenantToCollection + void createTenantToCollection() throws IOException { + createCollection(clientTgt, "WineReview", false); + } + // END CreateCollectionTenantToCollection + + @Test + // START TenantToCollection + void testTenantToCollection() throws IOException { + createTenantToCollection(); + + var reviewsSrc = clientSrc.collections.use("WineReviewMT"); + var reviewsTgt = clientTgt.collections.use("WineReview"); + var reviewsSrcTenantA = reviewsSrc.withTenant("tenantA"); + migrateData(reviewsSrcTenantA, reviewsTgt); + + // END TenantToCollection + assertThat(reviewsTgt.aggregate.overAll(a -> a.includeTotalCount(true)).totalCount()) + .isEqualTo(DATASET_SIZE); + assertThat(verifyMigration(reviewsSrcTenantA, reviewsTgt, 5)).isTrue(); + // START TenantToCollection + } + // END TenantToCollection + + // START CreateCollectionCollectionToTenant + void createCollectionToTenant() throws IOException { + createCollection(clientTgt, "WineReviewMT", true); + } + // END CreateCollectionCollectionToTenant + + // START CreateTenants // START CreateCollectionTenantToTenant + void createTenants() throws IOException { + var reviewsMtTgt = clientTgt.collections.use("WineReviewMT"); + + var tenantsTgt = List.of(Tenant.active("tenantA"), Tenant.active("tenantB")); + reviewsMtTgt.tenants.create(tenantsTgt); + } + // END CreateTenants // END CreateCollectionTenantToTenant + + @Test + // START CollectionToTenant + void testCollectionToTenant() throws IOException { + createCollectionToTenant(); + createTenants(); + + var reviewsMtTgt = clientTgt.collections.use("WineReviewMT"); + var reviewsSrc = clientSrc.collections.use("WineReview"); + + var reviewsTgtTenantA = reviewsMtTgt.withTenant("tenantA"); + + migrateData(reviewsSrc, reviewsTgtTenantA); + // END CollectionToTenant + + assertThat(reviewsTgtTenantA.aggregate.overAll(a -> a.includeTotalCount(true)).totalCount()) + .isEqualTo(DATASET_SIZE); + assertThat(verifyMigration(reviewsSrc, reviewsTgtTenantA, 5)).isTrue(); + // START CollectionToTenant + } + // END CollectionToTenant + + // START CreateCollectionTenantToTenant + void createTenantToTenant() throws IOException { + createCollection(clientTgt, "WineReviewMT", true); + } + // END CreateCollectionTenantToTenant + + @Test + // START TenantToTenant + void testTenantToTenant() throws IOException { + createTenantToTenant(); + createTenants(); + + var reviewsMtSrc = clientSrc.collections.use("WineReviewMT"); + var reviewsMtTgt = clientTgt.collections.use("WineReviewMT"); + var reviewsSrcTenantA = reviewsMtSrc.withTenant("tenantA"); + var reviewsTgtTenantA = reviewsMtTgt.withTenant("tenantA"); + + migrateData(reviewsSrcTenantA, reviewsTgtTenantA); + // END TenantToTenant + + assertThat(reviewsTgtTenantA.aggregate.overAll(a -> a.includeTotalCount(true)).totalCount()) + .isEqualTo(DATASET_SIZE); + assertThat(verifyMigration(reviewsSrcTenantA, reviewsTgtTenantA, 5)).isTrue(); + // START TenantToTenant + + } + // END TenantToTenant + +} diff --git a/_includes/code/java-v6/src/test/java/ManageCollectionsTest.java b/_includes/code/java-v6/src/test/java/ManageCollectionsTest.java new file mode 100644 index 00000000..f1d6b5e9 --- /dev/null +++ b/_includes/code/java-v6/src/test/java/ManageCollectionsTest.java @@ -0,0 +1,461 @@ +import io.weaviate.client6.v1.api.WeaviateClient; +import io.weaviate.client6.v1.api.collections.CollectionConfig; +import io.weaviate.client6.v1.api.collections.CollectionHandle; +import io.weaviate.client6.v1.api.collections.Property; +import io.weaviate.client6.v1.api.collections.Replication; +import io.weaviate.client6.v1.api.collections.Sharding; +import io.weaviate.client6.v1.api.collections.Tokenization; +import io.weaviate.client6.v1.api.collections.vectorindex.Distance; +import io.weaviate.client6.v1.api.collections.VectorConfig; +import io.weaviate.client6.v1.api.collections.Replication.DeletionStrategy; +import io.weaviate.client6.v1.api.collections.config.Shard; +import io.weaviate.client6.v1.api.collections.config.ShardStatus; +import io.weaviate.client6.v1.api.collections.Reranker; +import io.weaviate.client6.v1.api.collections.Generative; +import io.weaviate.client6.v1.api.collections.vectorindex.Hnsw; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import static org.assertj.core.api.Assertions.assertThat; + +class ManageCollectionsTest { + + private static WeaviateClient client; + + @BeforeAll + public static void beforeAll() throws IOException { + // Instantiate the client with the OpenAI API key + String openaiApiKey = System.getenv("OPENAI_API_KEY"); + assertThat(openaiApiKey).isNotBlank() + .withFailMessage("Please set the OPENAI_API_KEY environment variable."); + + client = WeaviateClient.connectToLocal( + config -> config.setHeaders(Map.of("X-OpenAI-Api-Key", openaiApiKey))); + client.collections.deleteAll(); + } + + @AfterEach + public void afterAll() throws IOException { + // Clean up all collections after each test + client.collections.deleteAll(); + } + + @Test + void testBasicCreateCollection() throws IOException { + // START BasicCreateCollection + client.collections.create("Article"); + // END BasicCreateCollection + + assertThat(client.collections.exists("Article")).isTrue(); + } + + @Test + void testCreateCollectionWithProperties() throws IOException { + // START CreateCollectionWithProperties + client.collections.create("Article", + col -> col.properties(Property.text("title"), Property.text("body"))); + // END CreateCollectionWithProperties + + var config = client.collections.getConfig("Article").get(); + assertThat(config.properties()).hasSize(2); + } + + @Test + void testCreateCollectionWithVectorizer() throws IOException { + // START Vectorizer + client.collections.create("Article", + col -> col.vectorConfig(VectorConfig.text2vecContextionary()) + .properties(Property.text("title"), Property.text("body"))); + // END Vectorizer + + var config = client.collections.getConfig("Article").get(); + assertThat(config.vectors()).containsKey("default"); + System.out.println("first: " + config.vectors().get("default")); + // assertThat(config.vectors().get("default").vectorizerName()).isEqualTo("text2vec-weaviate"); + } + + @Test + void testCreateCollectionWithNamedVectors() throws IOException { + // START BasicNamedVectors + // Weaviate + client.collections + .create("ArticleNV", + col -> col + .vectorConfig( + VectorConfig.text2vecContextionary("title", + c -> c.sourceProperties("title") + .vectorIndex(Hnsw.of())), + VectorConfig.text2vecContextionary("title_country", + c -> c.sourceProperties("title", "country") + .vectorIndex(Hnsw.of())), + VectorConfig.selfProvided("custom_vector", + c -> c.vectorIndex(Hnsw.of()).vectorIndex(Hnsw.of()))) + .properties(Property.text("title"), Property.text("country"))); + // END BasicNamedVectors + + var config = client.collections.getConfig("ArticleNV").get(); + assertThat(config.vectors()).hasSize(3) + .containsKeys("title", "title_country", "custom_vector"); + assertThat(config.properties().get(0).propertyName().contains("title")); + assertThat(config.properties().get(1).propertyName().contains("country")); + } + + // TODO[g-despot]: Add example when AddNamedVectors is implemented + // START AddNamedVectors + // Coming soon + // END AddNamedVectors + + // TODO[g-despot]: Add example when MultiValueVectorCollection is implemented + // START MultiValueVectorCollection + // Coming soon + // END MultiValueVectorCollection + + @Test + void testSetVectorIndexType() throws IOException { + // START SetVectorIndexType + client.collections.create("Article", + col -> col + .vectorConfig(VectorConfig + .text2vecContextionary(vec -> vec.vectorIndex(Hnsw.of()))) + .properties(Property.text("title"), Property.text("body"))); + // END SetVectorIndexType + + var config = client.collections.getConfig("Article").get(); + VectorConfig defaultVector = config.vectors().get("default"); + assertThat(defaultVector.vectorIndex()).isInstanceOf(Hnsw.class); + } + + @Test + void testSetVectorIndexParams() throws IOException { + // START SetVectorIndexParams + client.collections.create("Article", col -> col + .vectorConfig( + VectorConfig.text2vecContextionary(vec -> vec.vectorIndex(Hnsw.of( + hnsw -> hnsw.efConstruction(300).distance(Distance.COSINE))))) + .properties(Property.text("title"))); + // END SetVectorIndexParams + + var config = client.collections.getConfig("Article").get(); + Hnsw hnswConfig = (Hnsw) config.vectors().get("default").vectorIndex(); + assertThat(hnswConfig.efConstruction()).isEqualTo(300); + assertThat(hnswConfig.distance()).isEqualTo(Distance.COSINE); + } + + @Test + void testSetInvertedIndexParams() throws IOException { + // START SetInvertedIndexParams + client.collections.create("Article", col -> col + .properties( + Property.text("title", + p -> p.indexFilterable(true).indexSearchable(true)), + Property.text("chunk", + p -> p.indexFilterable(true).indexSearchable(true)), + Property.integer("chunk_number", p -> p.indexRangeFilters(true))) + .invertedIndex(idx -> idx.bm25(b -> b.b(1).k1(2)) + .indexNulls(true) + .indexPropertyLength(true) + .indexTimestamps(true))); + // END SetInvertedIndexParams + + var config = client.collections.getConfig("Article").get(); + assertThat(config.invertedIndex().bm25().b()).isEqualTo(1); + assertThat(config.invertedIndex().bm25().k1()).isEqualTo(2); + assertThat(config.properties()).hasSize(3); + } + + // TODO[g-despot] IllegalState Not a JSON Object: null + @Test + void testSetReranker() throws IOException { + // START SetReranker + client.collections.create("Article", + col -> col.vectorConfig(VectorConfig.text2vecContextionary()) + .rerankerModules(Reranker.cohere()) + .properties(Property.text("title"))); + // END SetReranker + + var config = client.collections.getConfig("Article").get(); + assertThat(config.rerankerModules()).hasSize(1); + System.out.println("second:" + config.rerankerModules().get(0)); + // assertThat(config.rerankerModules().get(0).name()).isEqualTo("reranker-cohere"); + } + + // TODO[g-despot] Update when more rerankers available + // TODO[g-despot] Why does update need collection name? + // TODO[g-despot] NoSuchElement No value present + @Test + void testUpdateReranker() throws IOException { + // START UpdateReranker + var collection = client.collections.use("Article"); + collection.config.update("Article", + col -> col.rerankerModules(Reranker.cohere())); + // END UpdateReranker + + var config = client.collections.getConfig("Article").get(); + assertThat(config.rerankerModules()).hasSize(1); + System.out.println("second:" + config.rerankerModules().get(0)); + // assertThat(config.rerankerModules().get(0).name()).isEqualTo("reranker-cohere"); + } + + @Test + void testSetGenerative() throws IOException { + // START SetGenerative + client.collections.create("Article", + col -> col.vectorConfig(VectorConfig.text2vecContextionary()) + .generativeModule(Generative.cohere()) + .properties(Property.text("title"))); + // END SetGenerative + + var config = client.collections.getConfig("Article").get(); + System.out.println("third: " + config); + // assertThat(config.generativeModule().name()).isEqualTo("generative-cohere"); + // assertThat(config.generativeModule().model()).isEqualTo("gpt-4o"); + } + + // TODO[g-despot] Update when more generative modules available + // TODO[g-despot] NoSuchElement No value present + @Test + void testUpdateGenerative() throws IOException { + // START UpdateGenerative + var collection = client.collections.use("Article"); + collection.config.update("Article", + col -> col.generativeModule(Generative.cohere())); + // END UpdateGenerative + + var config = client.collections.getConfig("Article").get(); + assertThat(config.generativeModule()).isNotNull(); + System.out.println("third: " + config.generativeModule()); + // assertThat(config.generativeModule().name()).isEqualTo("generative-cohere"); + // assertThat(config.generativeModule().model()).isEqualTo("gpt-4o"); + } + + // TODO[g-despot] Update when more model providers available + // @Test + // void testModuleSettings() throws IOException { + // client.collections.create("Article", + // col -> col.vectorConfig(VectorConfig.text2vecContextionary())); + + // var config = client.collections.getConfig("Article").get(); + // } + // START ModuleSettings + // Coming soon + // END ModuleSettings + + @Test + void testCreateCollectionWithPropertyConfig() throws IOException { + // START PropModuleSettings + client.collections.create("Article", + col -> col.properties( + Property.text("title", + p -> p.description("The title of the article.") + .tokenization(Tokenization.LOWERCASE) + .vectorizePropertyName(false)), + Property.text("body", p -> p.skipVectorization(true) + .tokenization(Tokenization.WHITESPACE)))); + // END PropModuleSettings + + var config = client.collections.getConfig("Article").get(); + assertThat(config.properties()).hasSize(2); + } + + @Test + void testCreateCollectionWithTrigramTokenization() throws IOException { + // START TrigramTokenization + client.collections.create("Article", col -> col + .vectorConfig(VectorConfig.text2vecContextionary()) + .properties( + Property.text("title", p -> p.tokenization(Tokenization.TRIGRAM)))); + // END TrigramTokenization + + var config = client.collections.getConfig("Article").get(); + assertThat(config.properties()).hasSize(1); + } + + @Test + void testDistanceMetric() throws IOException { + // START DistanceMetric + client.collections.create("Article", + col -> col + .vectorConfig(VectorConfig.text2vecContextionary(vec -> vec + .vectorIndex(Hnsw.of(hnsw -> hnsw.distance(Distance.COSINE))))) + .properties(Property.text("title"))); + // END DistanceMetric + + var config = client.collections.getConfig("Article").get(); + Hnsw hnswConfig = (Hnsw) config.vectors().get("default").vectorIndex(); + assertThat(hnswConfig.distance()).isEqualTo(Distance.COSINE); + } + + @Test + void testReplicationSettings() throws IOException { + // START ReplicationSettings + client.collections.create("Article", col -> col + .replication(Replication.of(rep -> rep.replicationFactor(1)))); + // END ReplicationSettings + + var config = client.collections.getConfig("Article").get(); + assertThat(config.replication().replicationFactor()).isEqualTo(1); + } + + @Test + void testAsyncRepair() throws IOException { + // START AsyncRepair + client.collections.create("Article", col -> col.replication( + Replication.of(rep -> rep.replicationFactor(1).asyncEnabled(true)))); + // END AsyncRepair + + var config = client.collections.getConfig("Article").get(); + assertThat(config.replication().asyncEnabled()).isTrue(); + } + + @Test + void testAllReplicationSettings() throws IOException { + // START AllReplicationSettings + client.collections.create("Article", + col -> col.replication(Replication.of(rep -> rep.replicationFactor(1) + .asyncEnabled(true) + .deletionStrategy(DeletionStrategy.TIME_BASED_RESOLUTION)))); + // END AllReplicationSettings + + var config = client.collections.getConfig("Article").get(); + assertThat(config.replication().replicationFactor()).isEqualTo(1); + assertThat(config.replication().asyncEnabled()).isTrue(); + } + + @Test + void testShardingSettings() throws IOException { + // START ShardingSettings + client.collections.create("Article", + col -> col.sharding(Sharding.of(s -> s.virtualPerPhysical(128) + .desiredCount(1) + .desiredVirtualCount(128)))); + // END ShardingSettings + + var config = client.collections.getConfig("Article").get(); + assertThat(config.sharding().virtualPerPhysical()).isEqualTo(128); + // assertThat(config.sharding().desiredCount()).isEqualTo(1); + assertThat(config.sharding().desiredVirtualCount()).isEqualTo(128); + } + + @Test + void testMultiTenancy() throws IOException { + // START Multi-tenancy + client.collections.create("Article", col -> col.multiTenancy( + mt -> mt.autoTenantCreation(true).autoTenantActivation(true))); + // END Multi-tenancy + + var config = client.collections.getConfig("Article").get(); + assertThat(config.multiTenancy().activateAutomatically()).isTrue(); + } + + @Test + void testReadOneCollection() throws IOException { + client.collections.create("Article"); + + // START ReadOneCollection + CollectionHandle> articles = + client.collections.use("Article"); + Optional articlesConfig = articles.config.get(); + + System.out.println(articlesConfig); + // END ReadOneCollection + + assertThat(articlesConfig).isNotNull(); + assertThat(articlesConfig.get().collectionName()).isEqualTo("Article"); + } + + @Test + void testReadAllCollections() throws IOException { + client.collections.create("Article"); + client.collections.create("Publication"); + + // START ReadAllCollections + List response = client.collections.list(); + + System.out.println(response); + // END ReadAllCollections + + assertThat(response).hasSize(2) + .extracting(CollectionConfig::collectionName) + .contains("Article", "Publication"); + } + + @Test + void testUpdateCollection() throws IOException { + client.collections.create("Article", col -> col + .invertedIndex(idx -> idx.bm25(bm25Builder -> bm25Builder.k1(10)))); + + // START UpdateCollection + CollectionHandle> articles = + client.collections.use("Article"); + + articles.config.update("Article", + col -> col.description("An updated collection description.") + .invertedIndex( + idx -> idx.bm25(bm25Builder -> bm25Builder.k1(1.5f)))); + // END UpdateCollection + + var config = articles.config.get().get(); + assertThat(config.description()) + .isEqualTo("An updated collection description."); + assertThat(config.invertedIndex().bm25().k1()).isEqualTo(1.5f); + } + + // TODO[g-despot]: Add example when AddProperty is implemented + // START AddProperty + // Coming soon + // END AddProperty + + @Test + void testDeleteCollection() throws IOException { + String collectionName = "Article"; + client.collections.create(collectionName); + assertThat(client.collections.exists(collectionName)).isTrue(); + + // START DeleteCollection + client.collections.delete(collectionName); + // END DeleteCollection + + assertThat(client.collections.exists(collectionName)).isFalse(); + } + + @Test + void testInspectCollectionShards() throws IOException { + client.collections.create("Article"); + + // START InspectCollectionShards + CollectionHandle> articles = + client.collections.use("Article"); + + List articleShards = articles.config.getShards(); + System.out.println(articleShards); + // END InspectCollectionShards + + assertThat(articleShards).isNotNull().hasSize(1); + } + + @Test + void testUpdateCollectionShards() throws IOException { + client.collections.create("Article"); + String shardName = + client.collections.use("Article").config.getShards().get(0).name(); + + // START UpdateCollectionShards + CollectionHandle> articles = + client.collections.use("Article"); + + List articleShards = + articles.config.updateShards(ShardStatus.READONLY, shardName); + System.out.println(articleShards); + // END UpdateCollectionShards + + assertThat(articleShards).isNotNull().hasSize(1); + assertThat(articleShards.get(0).status()) + .isEqualTo(ShardStatus.READONLY.name()); + } +} diff --git a/_includes/code/java-v6/src/test/java/ManageObjectsCreateTest.java b/_includes/code/java-v6/src/test/java/ManageObjectsCreateTest.java new file mode 100644 index 00000000..7e9d80f0 --- /dev/null +++ b/_includes/code/java-v6/src/test/java/ManageObjectsCreateTest.java @@ -0,0 +1,221 @@ +import io.weaviate.client6.v1.api.WeaviateClient; +import io.weaviate.client6.v1.api.collections.Property; +import io.weaviate.client6.v1.api.collections.VectorConfig; +import io.weaviate.client6.v1.api.collections.Vectors; +import io.weaviate.client6.v1.api.collections.query.Metadata; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; + +class ManageObjectsCreateTest { + + private static WeaviateClient client; + + // A helper method to generate a deterministic UUID from a seed, similar to + // Python's generate_uuid5 + private static UUID generateUuid5(String seed) { + return UUID.nameUUIDFromBytes(seed.getBytes(StandardCharsets.UTF_8)); + } + + @BeforeAll + public static void beforeAll() throws IOException { + // START INSTANTIATION-COMMON + client = WeaviateClient.connectToLocal(); + // END INSTANTIATION-COMMON + + // TODO[g-despot]: Wasn't able to create collection with vectorizer but without + // properties + // START Define the class + client.collections.create("JeopardyQuestion", + col -> col.properties(Property.text("title", p -> p.description("Name of the wine"))) + .vectorConfig(VectorConfig.text2vecContextionary())); + + // TODO[g-despot]: Add source properties + client.collections.create("WineReviewNV", + col -> col + .properties(Property.text("review_body", p -> p.description("Review body")), + Property.text("title", p -> p.description("Name of the wine")), + Property.text("country", p -> p.description("Originating country"))) + .vectorConfig(VectorConfig.text2vecContextionary("title"), + VectorConfig.text2vecContextionary("review_body"), + VectorConfig.text2vecContextionary("title_country"))); + // END Define the class + + // Additional collections for other tests + // TODO[g-despot]: Uncomment once GEO type added + // client.collections.create("Publication", col -> col + // .properties(Property.geo("headquartersGeoLocation"))); + client.collections.create("Author", col -> col.vectorConfig(VectorConfig.selfProvided())); + } + + @AfterAll + public static void afterAll() throws IOException { + client.collections.deleteAll(); + } + + @Test + void testCreateObject() throws IOException { + // START CreateSimpleObject + var jeopardy = client.collections.use("JeopardyQuestion"); + + // highlight-start + var uuid = jeopardy.data.insert(Map.of( + // highlight-end + "question", "This vector DB is OSS & supports automatic property type inference on import", + // "answer": "Weaviate", // properties can be omitted + "newProperty", 123 // will be automatically added as a number property + )).metadata().uuid(); + + System.out.println(uuid); // the return value is the object's UUID + // END CreateSimpleObject + + var result = jeopardy.query.byId(uuid); + assertThat(result).isPresent(); + assertThat(result.get().properties().get("newProperty")).isEqualTo(123.0); // JSON numbers are parsed as Long + } + + @Test + void testCreateObjectWithVector() throws IOException { + // START CreateObjectWithVector + var jeopardy = client.collections.use("JeopardyQuestion"); + var uuid = jeopardy.data.insert( + Map.of("question", + "This vector DB is OSS and supports automatic property type inference on import", + "answer", "Weaviate"), + // highlight-start + meta -> meta.vectors(Vectors.of(new float[300])) // Using a zero vector for demonstration + // highlight-end + ).metadata().uuid(); + + System.out.println(uuid); // the return value is the object's UUID + // END CreateObjectWithVector + + var result = jeopardy.query.byId(uuid); + assertThat(result).isPresent(); + } + + @Test + void testCreateObjectNamedVectors() throws IOException { + // START CreateObjectNamedVectors + var reviews = client.collections.use("WineReviewNV"); // This collection must have named vectors configured + var uuid = reviews.data.insert(Map.of("title", "A delicious Riesling", "review_body", + "This wine is a delicious Riesling which pairs well with seafood.", "country", "Germany"), + // highlight-start + // Specify the named vectors, following the collection definition + meta -> meta.vectors(Vectors.of("title", new float[1536]), + Vectors.of("review_body", new float[1536]), + Vectors.of("title_country", new float[1536])) + // highlight-end + ).metadata().uuid(); + + System.out.println(uuid); // the return value is the object's UUID + // END CreateObjectNamedVectors + + var result = reviews.query.byId(uuid, q -> q.returnMetadata(Metadata.VECTOR)); + assertThat(result).isPresent(); + // assertThat(result.get().metadata().vectors().getVectors()).containsOnlyKeys("title", + // "review_body", + // "title_country"); + } + + @Test + void testCreateObjectWithDeterministicId() throws IOException { + // START CreateObjectWithDeterministicId + // highlight-start + // In Java, you can generate a deterministic UUID from a string or bytes. + // This helper function uses UUID.nameUUIDFromBytes for this purpose. + // highlight-end + + Map dataObject = new HashMap<>(); + dataObject.put("question", + "This vector DB is OSS and supports automatic property type inference on import"); + dataObject.put("answer", "Weaviate"); + + var jeopardy = client.collections.use("JeopardyQuestion"); + var uuid = jeopardy.data.insert(dataObject, + // highlight-start + meta -> meta.uuid(generateUuid5(dataObject.toString()).toString()) + // highlight-end + ).metadata().uuid(); + // END CreateObjectWithDeterministicId + + assertThat(uuid).isEqualTo(generateUuid5(dataObject.toString()).toString()); + jeopardy.data.delete(uuid); // Clean up + } + + @Test + void testCreateObjectWithId() throws IOException { + // START CreateObjectWithId + Map properties = new HashMap<>(); + properties.put("question", + "This vector DB is OSS and supports automatic property type inference on import"); + properties.put("answer", "Weaviate"); + + var jeopardy = client.collections.use("JeopardyQuestion"); + var uuid = jeopardy.data.insert(properties, + // highlight-start + meta -> meta.uuid("12345678-e64f-5d94-90db-c8cfa3fc1234") + // highlight-end + ).metadata().uuid(); + + System.out.println(uuid); // the return value is the object's UUID + // END CreateObjectWithId + + var result = jeopardy.query.byId(uuid); + assertThat(result).isPresent(); + assertThat(result.get().properties().get("question")).isEqualTo(properties.get("question")); + } + + // TODO[g-despot]: Uncomment once GEO type added + // @Test + void testWithGeoCoordinates() throws IOException { + // START WithGeoCoordinates + var publications = client.collections.use("Publication"); + + var uuid = publications.data.insert( + Map.of("headquartersGeoLocation", Map.of("latitude", 52.3932696, "longitude", 4.8374263))) + .metadata().uuid(); + // END WithGeoCoordinates + + assertThat(publications.data.exists(uuid)).isTrue(); + publications.data.delete(uuid); + } + + @Test + void testCheckForAnObject() throws IOException { + // START CheckForAnObject + // generate uuid based on the key properties used during data insert + String objectUuid = generateUuid5("Author to fetch").toString(); + // END CheckForAnObject + + var authors = client.collections.use("Author"); + authors.data.insert(Map.of("name", "Author to fetch"), + meta -> meta.uuid(objectUuid).vectors(Vectors.of(new float[1536]))); + + // START CheckForAnObject + // highlight-start + boolean authorExists = authors.data.exists(objectUuid); + // highlight-end + + System.out.println("Author exist: " + authorExists); + // END CheckForAnObject + + assertThat(authorExists).isTrue(); + authors.data.delete(objectUuid); + assertThat(authors.data.exists(objectUuid)).isFalse(); + } + + // START ValidateObject + // Coming soon + // END ValidateObject +} diff --git a/_includes/code/java-v6/src/test/java/ManageObjectsDeleteTest.java b/_includes/code/java-v6/src/test/java/ManageObjectsDeleteTest.java new file mode 100644 index 00000000..18680c0e --- /dev/null +++ b/_includes/code/java-v6/src/test/java/ManageObjectsDeleteTest.java @@ -0,0 +1,150 @@ +import io.weaviate.client6.v1.api.WeaviateClient; +import io.weaviate.client6.v1.api.collections.CollectionHandle; +import io.weaviate.client6.v1.api.collections.Property; +import io.weaviate.client6.v1.api.collections.WeaviateObject; +import io.weaviate.client6.v1.api.collections.query.QueryResponse; +import io.weaviate.client6.v1.api.collections.query.Where; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import static org.assertj.core.api.Assertions.assertThat; + +class ManageObjectsDeleteTest { + + private static WeaviateClient client; + private static final String COLLECTION_NAME = "EphemeralObject"; + + @BeforeAll + public static void beforeAll() throws IOException { + String openaiApiKey = System.getenv("OPENAI_API_KEY"); + client = WeaviateClient.connectToLocal( + config -> config.setHeaders(Map.of("X-OpenAI-Api-Key", openaiApiKey))); + } + + @AfterAll + public static void afterAll() throws Exception { + client.close(); + } + + @BeforeEach + public void setup() throws IOException { + if (client.collections.exists(COLLECTION_NAME)) { + client.collections.delete(COLLECTION_NAME); + } + client.collections.create(COLLECTION_NAME, col -> col.properties(Property.text("name"))); + } + + @AfterEach + public void cleanup() throws IOException { + if (client.collections.exists(COLLECTION_NAME)) { + client.collections.delete(COLLECTION_NAME); + } + } + + @Test + void testDeleteObject() throws IOException { + CollectionHandle> collection = client.collections.use(COLLECTION_NAME); + String uuidToDelete = collection.data.insert(Map.of("name", "EphemeralObjectA")).uuid(); + assertThat(collection.query.byId(uuidToDelete)).isPresent(); + + // START DeleteObject + collection.data.delete(uuidToDelete); + // END DeleteObject + + assertThat(collection.query.byId(uuidToDelete)).isNotPresent(); + } + + @Test + void testBatchDelete() { + CollectionHandle> collection = client.collections.use(COLLECTION_NAME); + List> objects = IntStream.range(0, 5) + .mapToObj(i -> Map.of("name", "EphemeralObject_" + i)) + .collect(Collectors.toList()); + collection.data.insertMany(objects.toArray(new Map[0])); + assertThat(collection.aggregate.overAll(a -> a.includeTotalCount(true)).totalCount()).isEqualTo(5); + + // START DeleteBatch + collection.data.deleteMany( + // highlight-start + Where.property("name").like("EphemeralObject*") + // highlight-end + ); + // END DeleteBatch + + assertThat(collection.aggregate.overAll(a -> a.includeTotalCount(true)).totalCount()).isZero(); + } + + @Test + void testDeleteContains() { + // START DeleteContains + CollectionHandle> collection = client.collections.use(COLLECTION_NAME); + collection.data.insertMany( + Map.of("name", "asia"), + Map.of("name", "europe")); + + collection.data.deleteMany( + // highlight-start + Where.property("name").containsAny("europe", "asia") + // highlight-end + ); + // END DeleteContains + } + + @Test + void testDryRun() { + CollectionHandle> collection = client.collections.use(COLLECTION_NAME); + List> objects = IntStream.range(0, 5) + .mapToObj(i -> Map.of("name", "EphemeralObject_" + i)) + .collect(Collectors.toList()); + collection.data.insertMany(objects.toArray(new Map[0])); + + // START DryRun + var result = collection.data.deleteMany( + Where.property("name").like("EphemeralObject*"), + // highlight-start + c -> c.dryRun(true).verbose(true) + // highlight-end + ); + + System.out.println(result); + // END DryRun + + assertThat(result.matches()).isEqualTo(5); + assertThat(collection.aggregate.overAll(a -> a.includeTotalCount(true)).totalCount()).isEqualTo(5); + } + + // TODO[g-despot]: containsAny should take list not single string + @Test + void testBatchDeleteWithIDs() { + CollectionHandle> collection = client.collections.use(COLLECTION_NAME); + List> objects = IntStream.range(0, 5) + .mapToObj(i -> Map.of("name", "EphemeralObject_" + i)) + .collect(Collectors.toList()); + collection.data.insertMany(objects.toArray(new Map[0])); + assertThat(collection.aggregate.overAll(a -> a.includeTotalCount(true)).totalCount()).isEqualTo(5); + + // START DeleteByIDBatch + QueryResponse> queryResponse = collection.query.fetchObjects(q -> q.limit(3)); + List ids = queryResponse.objects().stream() + .map(WeaviateObject::uuid) + .collect(Collectors.toList()); + + collection.data.deleteMany( + // highlight-start + Where.uuid().containsAny(ids.toString()) // Delete the 3 objects + // highlight-end + ); + // END DeleteByIDBatch + + assertThat(collection.aggregate.overAll(a -> a.includeTotalCount(true)).totalCount()).isEqualTo(2); + } +} \ No newline at end of file diff --git a/_includes/code/java-v6/src/test/java/ManageObjectsImportTest.java b/_includes/code/java-v6/src/test/java/ManageObjectsImportTest.java new file mode 100644 index 00000000..1c327f63 --- /dev/null +++ b/_includes/code/java-v6/src/test/java/ManageObjectsImportTest.java @@ -0,0 +1,370 @@ +import io.weaviate.client6.v1.api.WeaviateClient; +import io.weaviate.client6.v1.api.collections.CollectionHandle; +import io.weaviate.client6.v1.api.collections.ObjectMetadata; +import io.weaviate.client6.v1.api.collections.Property; +import io.weaviate.client6.v1.api.collections.ReferenceProperty; +import io.weaviate.client6.v1.api.collections.VectorConfig; +import io.weaviate.client6.v1.api.collections.WeaviateObject; +import io.weaviate.client6.v1.api.collections.Vectors; +import io.weaviate.client6.v1.api.collections.data.BatchReference; +import io.weaviate.client6.v1.api.collections.data.InsertManyResponse; +import io.weaviate.client6.v1.api.collections.data.Reference; +import io.weaviate.client6.v1.api.collections.query.QueryReference; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.google.gson.Gson; +import com.google.gson.stream.JsonReader; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; + +class ManageObjectsImportTest { + + private static WeaviateClient client; + + // A helper method to generate a deterministic UUID from a seed + private static UUID generateUuid5(String seed) { + return UUID.nameUUIDFromBytes(seed.getBytes(StandardCharsets.UTF_8)); + } + + @BeforeAll + public static void beforeAll() throws IOException { + // START INSTANTIATION-COMMON + String openaiApiKey = System.getenv("OPENAI_API_KEY"); + assertThat(openaiApiKey).isNotBlank() + .withFailMessage("Please set the OPENAI_API_KEY environment variable."); + + client = WeaviateClient + .connectToLocal(config -> config.setHeaders(Map.of("X-OpenAI-Api-Key", openaiApiKey))); + // END INSTANTIATION-COMMON + + // Download data file for streaming tests + try (InputStream in = new URL( + "https://raw.githubusercontent.com/weaviate-tutorials/edu-datasets/main/jeopardy_1k.json") + .openStream()) { + Files.copy(in, Paths.get("jeopardy_1k.json")); + } + } + + @AfterAll + public static void afterAll() throws IOException { + client.collections.deleteAll(); + Files.deleteIfExists(Paths.get("jeopardy_1k.json")); + } + + @BeforeEach + public void beforeEach() throws IOException { + client.collections.deleteAll(); + } + + @Test + void testBasicBatchImport() throws IOException { + // Define and create the class + client.collections.create("MyCollection", col -> col.vectorConfig(VectorConfig.selfProvided())); + + // START BasicBatchImportExample + List> dataRows = new ArrayList<>(); + for (int i = 0; i < 5; i++) { + dataRows.add(Map.of("title", "Object " + (i + 1))); + } + + var collection = client.collections.use("MyCollection"); + + // The Java client uses insertMany for batching. + // There is no direct equivalent of the Python client's stateful batch manager. + // You collect objects and send them in a single request. + // highlight-start + var response = collection.data.insertMany(dataRows.toArray(new Map[0])); + // highlight-end + + if (!response.errors().isEmpty()) { + System.err.println("Number of failed imports: " + response.errors().size()); + System.err.println("First failed object: " + response.errors().get(0)); + } + // END BasicBatchImportExample + + var result = collection.aggregate.overAll(agg -> agg.includeTotalCount(true)); + assertThat(result.totalCount()).isEqualTo(5); + + client.collections.delete("MyCollection"); + } + + // TODO[g-despot]: Implement once server-side batching is available + // START ServerSideBatchImportExample + // Coming soon + // END ServerSideBatchImportExample + + @Test + // TODO[g-despot]: Somewhere it's string somewhere it's UUID, can we supply it as a string directly without ObjectMetadata? + void testBatchImportWithID() throws IOException { + client.collections.create("MyCollection", col -> col.vectorConfig(VectorConfig.selfProvided())); + + // START BatchImportWithIDExample + List, Reference, ObjectMetadata>> dataObjects = + new ArrayList<>(); + for (int i = 0; i < 5; i++) { + Map dataRow = Map.of("title", "Object " + (i + 1)); + UUID objUuid = generateUuid5(dataRow.toString()); + dataObjects.add(WeaviateObject.of( + obj -> obj.properties(dataRow).metadata(ObjectMetadata.of(meta -> meta.uuid(objUuid))))); + } + + var collection = client.collections.use("MyCollection"); + + // highlight-start + var response = collection.data.insertMany(dataObjects); + // highlight-end + + if (!response.errors().isEmpty()) { + System.err.println("Number of failed imports: " + response.errors().size()); + System.err.println("First failed object: " + response.errors().get(0)); + } + // END BatchImportWithIDExample + + var result = collection.aggregate.overAll(agg -> agg.includeTotalCount(true)); + assertThat(result.totalCount()).isEqualTo(5); + String lastUuid = dataObjects.get(4).metadata().uuid(); + assertThat(collection.data.exists(lastUuid)).isTrue(); + + client.collections.delete("MyCollection"); + } + + @Test + void testBatchImportWithVector() throws IOException { + client.collections.create("MyCollection", col -> col.vectorConfig(VectorConfig.selfProvided())); + + // START BatchImportWithVectorExample + List, Reference, ObjectMetadata>> dataObjects = + new ArrayList<>(); + float[] vector = new float[10]; // Using a small vector for demonstration + Arrays.fill(vector, 0.1f); + + for (int i = 0; i < 5; i++) { + Map dataRow = Map.of("title", "Object " + (i + 1)); + UUID objUuid = generateUuid5(dataRow.toString()); + dataObjects.add(WeaviateObject.of(obj -> obj.properties(dataRow) + .metadata(ObjectMetadata.of(meta -> meta.uuid(objUuid).vectors(Vectors.of(vector)))))); + } + + var collection = client.collections.use("MyCollection"); + + // highlight-start + var response = collection.data.insertMany(dataObjects); + // highlight-end + + if (!response.errors().isEmpty()) { + System.err.println("Number of failed imports: " + response.errors().size()); + System.err.println("First failed object: " + response.errors().get(0)); + } + // END BatchImportWithVectorExample + + var result = collection.aggregate.overAll(agg -> agg.includeTotalCount(true)); + assertThat(result.totalCount()).isEqualTo(5); + + client.collections.delete("MyCollection"); + } + + @Test + void testBatchImportWithCrossReference() throws IOException { + client.collections.create("Publication", col -> col.properties(Property.text("title"))); + client.collections.create("Author", col -> col.properties(Property.text("name")) + .references(new ReferenceProperty("writesFor", List.of("Publication")))); + + var authors = client.collections.use("Author"); + var publications = client.collections.use("Publication"); + + var from = authors.data.insert(Map.of("name", "Jane Austen")); + var fromUuid = from.metadata().uuid(); + var targetUuid = publications.data.insert(Map.of("title", "Ye Olde Times")).metadata().uuid(); + + // START BatchImportWithRefExample + var collection = client.collections.use("Author"); + + var response = + collection.data.referenceAddMany(BatchReference.uuids(from, "writesFor", targetUuid)); + + if (!response.errors().isEmpty()) { + System.err.println("Number of failed imports: " + response.errors().size()); + System.err.println("First failed object: " + response.errors().get(0)); + } + // END BatchImportWithRefExample + + var result = collection.query.byId(fromUuid, + q -> q.returnReferences(QueryReference.single("writesFor"))); + + assertThat(result).isPresent(); + assertThat(result.get().references().get("writesFor")).isNotNull(); + } + + @Test + void testImportWithNamedVectors() throws IOException { + // Define and create the class + client.collections.create("MyCollection", + col -> col + .vectorConfig(VectorConfig.selfProvided("title"), VectorConfig.selfProvided("body")) + .properties(Property.text("title"), Property.text("body"))); + // START BatchImportWithNamedVectors + // Prepare the data and vectors + List> dataRows = new ArrayList<>(); + List titleVectors = new ArrayList<>(); + List bodyVectors = new ArrayList<>(); + + for (int i = 0; i < 5; i++) { + dataRows.add(Map.of("title", "Object " + (i + 1), "body", "Body " + (i + 1))); + + float[] titleVector = new float[1536]; + Arrays.fill(titleVector, 0.12f); + titleVectors.add(titleVector); + + float[] bodyVector = new float[1536]; + Arrays.fill(bodyVector, 0.34f); + bodyVectors.add(bodyVector); + } + + CollectionHandle> collection = client.collections.use("MyCollection"); + + List, Reference, ObjectMetadata>> objectsToInsert = + new ArrayList<>(); + for (int i = 0; i < dataRows.size(); i++) { + int index = i; + objectsToInsert.add( + // highlight-start + // Use the Builder with the EXACT matching generic types + new WeaviateObject.Builder, Reference, ObjectMetadata>() + .properties(dataRows.get(index)) + .metadata(ObjectMetadata + .of(meta -> meta.vectors(Vectors.of("title", titleVectors.get(index)), + Vectors.of("body", bodyVectors.get(index))))) + .build()); + // highlight-end + + } + + // Insert the data using insertMany with the List + // highlight-start + InsertManyResponse response = collection.data.insertMany(objectsToInsert); + // highlight-end + + // Check for errors + if (!response.errors().isEmpty()) { + System.err.printf("Number of failed imports: %d\n", response.errors().size()); + System.err.printf("First failed object error: %s\n", response.errors().get(0)); + } + // END BatchImportWithNamedVectors + } + + @Test + void testJsonStreaming() throws IOException { + client.collections.create("JeopardyQuestion"); + + // START JSON streaming + int batchSize = 100; + List> batch = new ArrayList<>(batchSize); + var collection = client.collections.use("JeopardyQuestion"); + Gson gson = new Gson(); + + System.out.println("JSON streaming, to avoid running out of memory on large files..."); + try (JsonReader reader = new JsonReader(new FileReader("jeopardy_1k.json"))) { + reader.beginArray(); + while (reader.hasNext()) { + Map obj = gson.fromJson(reader, Map.class); + Map properties = new HashMap<>(); + properties.put("question", obj.get("Question")); + properties.put("answer", obj.get("Answer")); + batch.add(properties); + + if (batch.size() == batchSize) { + collection.data.insertMany(batch.toArray(new Map[0])); + System.out.println("Imported " + batch.size() + " articles..."); + batch.clear(); + } + } + reader.endArray(); + } + + if (!batch.isEmpty()) { + collection.data.insertMany(batch.toArray(new Map[0])); + System.out.println("Imported remaining " + batch.size() + " articles..."); + } + + System.out.println("Finished importing articles."); + // END JSON streaming + + var result = collection.aggregate.overAll(agg -> agg.includeTotalCount(true)); + assertThat(result.totalCount()).isEqualTo(1000); + + client.collections.delete("JeopardyQuestion"); + } + + @Test + void testCsvStreaming() throws IOException { + // Create a CSV file from the JSON for the test + try (JsonReader reader = new JsonReader(new FileReader("jeopardy_1k.json")); + java.io.FileWriter writer = new java.io.FileWriter("jeopardy_1k.csv")) { + Gson gson = new Gson(); + reader.beginArray(); + writer.write("Question,Answer\n"); + while (reader.hasNext()) { + Map obj = gson.fromJson(reader, Map.class); + writer.write("\"" + obj.get("Question") + "\",\"" + obj.get("Answer") + "\"\n"); + } + reader.endArray(); + } + + client.collections.create("JeopardyQuestion"); + + // START CSV streaming + int batchSize = 100; + List> batch = new ArrayList<>(batchSize); + var collection = client.collections.use("JeopardyQuestion"); + + System.out.println("CSV streaming to not load all records in RAM at once..."); + try (BufferedReader csvReader = new BufferedReader(new FileReader("jeopardy_1k.csv"))) { + String line = csvReader.readLine(); // skip header + while ((line = csvReader.readLine()) != null) { + String[] data = line.split("\",\""); + Map properties = new HashMap<>(); + properties.put("question", data[0].substring(1)); + properties.put("answer", data[1].substring(0, data[1].length() - 1)); + batch.add(properties); + + if (batch.size() == batchSize) { + collection.data.insertMany(batch.toArray(new Map[0])); + System.out.println("Imported " + batch.size() + " articles..."); + batch.clear(); + } + } + } + + if (!batch.isEmpty()) { + collection.data.insertMany(batch.toArray(new Map[0])); + System.out.println("Imported remaining " + batch.size() + " articles..."); + } + + System.out.println("Finished importing articles."); + // END CSV streaming + + var result = collection.aggregate.overAll(agg -> agg.includeTotalCount(true)); + assertThat(result.totalCount()).isEqualTo(1000); + + Files.deleteIfExists(Paths.get("jeopardy_1k.csv")); + } +} diff --git a/_includes/code/java-v6/src/test/java/ManageObjectsReadAllTest.java b/_includes/code/java-v6/src/test/java/ManageObjectsReadAllTest.java new file mode 100644 index 00000000..8ce5dd56 --- /dev/null +++ b/_includes/code/java-v6/src/test/java/ManageObjectsReadAllTest.java @@ -0,0 +1,113 @@ +import io.weaviate.client6.v1.api.WeaviateClient; +import io.weaviate.client6.v1.api.collections.CollectionHandle; +import io.weaviate.client6.v1.api.collections.WeaviateObject; +import io.weaviate.client6.v1.api.collections.query.Metadata; +import io.weaviate.client6.v1.api.collections.query.QueryMetadata; +import io.weaviate.client6.v1.api.collections.tenants.Tenant; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +class ManageObjectsReadAllTest { + + private static WeaviateClient client; + + @BeforeAll + public static void beforeAll() throws IOException { + // Instantiate the client + String openaiApiKey = System.getenv("OPENAI_API_KEY"); + client = WeaviateClient + .connectToLocal(config -> config.setHeaders(Map.of("X-OpenAI-Api-Key", openaiApiKey))); + + // Simulate weaviate-datasets by creating and populating collections + // Create WineReview collection + if (client.collections.exists("WineReview")) { + client.collections.delete("WineReview"); + } + + // TODO[g-despot] Collection create doesn't return handle + client.collections.create("WineReview"); + var wineReview = client.collections.use("WineReview"); + wineReview.data.insertMany(Map.of("title", "Review A"), Map.of("title", "Review B")); + + // Create WineReviewMT collection + if (client.collections.exists("WineReviewMT")) { + client.collections.delete("WineReviewMT"); + } + client.collections.create("WineReviewMT", + col -> col.multiTenancy(c -> c.autoTenantCreation(true))); + var wineReviewMT = client.collections.use("WineReviewMT"); + + // Create and populate tenants + List tenants = List.of(Tenant.active("tenantA"), Tenant.active("tenantB")); + wineReviewMT.tenants.create(tenants); + wineReviewMT.withTenant("tenantA").data.insert(Map.of("title", "Tenant A Review 1")); + wineReviewMT.withTenant("tenantB").data.insert(Map.of("title", "Tenant B Review 1")); + } + + @AfterAll + public static void afterAll() throws Exception { + client.collections.delete("WineReview"); + client.collections.delete("WineReviewMT"); + client.close(); + } + + @Test + void testReadAllProps() { + // START ReadAllProps + CollectionHandle> collection = client.collections.use("WineReview"); + + // highlight-start + for (WeaviateObject, Object, QueryMetadata> item : collection.paginate()) { + // highlight-end + System.out.printf("%s %s\n", item.uuid(), item.properties()); + } + // END ReadAllProps + } + + // TODO[g-despot] Vector shoudn't be in metadata + @Test + void testReadAllVectors() { + // START ReadAllVectors + CollectionHandle> collection = client.collections.use("WineReview"); + + for (WeaviateObject, Object, QueryMetadata> item : collection.paginate( + // highlight-start + i -> i.returnMetadata(Metadata.VECTOR) // If using named vectors, you can specify ones to include + // highlight-end + )) { + System.out.println(item.properties()); + // highlight-start + System.out.println(item.vectors()); + // highlight-end + } + // END ReadAllVectors + } + + @Test + void testReadAllTenants() { + // START ReadAllTenants + CollectionHandle> multiCollection = client.collections.use("WineReviewMT"); + + // Get a list of tenants + // highlight-start + var tenants = multiCollection.tenants.get(); + // highlight-end + + // Iterate through tenants + for (Tenant tenant : tenants) { + // Iterate through objects within each tenant + // highlight-start + for (WeaviateObject, Object, QueryMetadata> item : multiCollection + .withTenant(tenant.name()).paginate()) { + // highlight-end + System.out.printf("%s: %s\n", tenant.name(), item.properties()); + } + } + // END ReadAllTenants + } +} diff --git a/_includes/code/java-v6/src/test/java/ManageObjectsReadTest.java b/_includes/code/java-v6/src/test/java/ManageObjectsReadTest.java new file mode 100644 index 00000000..17ab5f6e --- /dev/null +++ b/_includes/code/java-v6/src/test/java/ManageObjectsReadTest.java @@ -0,0 +1,106 @@ +import io.weaviate.client6.v1.api.WeaviateClient; +import io.weaviate.client6.v1.api.collections.CollectionHandle; +import io.weaviate.client6.v1.api.collections.query.Metadata; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +class ManageObjectsReadTest { + + private static WeaviateClient client; + + @BeforeAll + public static void beforeAll() throws IOException { + // START INSTANTIATION-COMMON + // Best practice: store your credentials in environment variables + String weaviateUrl = System.getenv("WEAVIATE_URL"); + String weaviateApiKey = System.getenv("WEAVIATE_API_KEY"); + String openaiApiKey = System.getenv("OPENAI_APIKEY"); + + client = WeaviateClient.connectToWeaviateCloud(weaviateUrl, weaviateApiKey, + config -> config.setHeaders(Map.of("X-OpenAI-Api-Key", openaiApiKey))); + // END INSTANTIATION-COMMON + } + + @AfterAll + public static void afterAll() throws Exception { + client.close(); + } + + @Test + void testReadObject() { + // START ReadSimpleObject + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + + // highlight-start + var dataObjectOpt = jeopardy.query.byId("00ff6900-e64f-5d94-90db-c8cfa3fc851b"); + // highlight-end + + dataObjectOpt.ifPresent(dataObject -> System.out.println(dataObject.properties())); + // END ReadSimpleObject + } + + @Test + void testReadObjectWithVector() { + // START ReadObjectWithVector + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + + var dataObjectOpt = jeopardy.query.byId("00ff6900-e64f-5d94-90db-c8cfa3fc851b", + // highlight-start + q -> q.returnMetadata(Metadata.VECTOR) + // highlight-end + ); + + dataObjectOpt.ifPresent(dataObject -> System.out + .println(Arrays.toString(dataObject.metadata().vectors().getSingle("default")))); + // END ReadObjectWithVector + } + + @Test + // TODO[g-despot] Should be able to specify which vectors to return + void testReadObjectNamedVectors() { + // START ReadObjectNamedVectors + CollectionHandle> reviews = client.collections.use("WineReviewNV"); // Collection with named + + // END ReadObjectNamedVectors // vectors + + var someObjResponse = reviews.query.fetchObjects(q -> q.limit(1)); + if (someObjResponse.objects().isEmpty()) { + return; // Skip if no data + } + String objUuid = someObjResponse.objects().get(0).uuid(); + List vectorNames = List.of("title", "review_body"); + + // START ReadObjectNamedVectors + var dataObjectOpt = reviews.query.byId(objUuid, // Object UUID + // highlight-start + q -> q.returnMetadata(Metadata.VECTOR) // Specify to include vectors + // highlight-end + ); + + // The vectors are returned in the `vectors` property as a dictionary + dataObjectOpt.ifPresent(dataObject -> { + for (String n : vectorNames) { + float[] vector = dataObject.metadata().vectors().getSingle(n); + if (vector != null) { + System.out.printf("Vector '%s': %s...\n", n, Arrays.toString(Arrays.copyOf(vector, 5))); + } + } + }); + // END ReadObjectNamedVectors + } + + @Test + void testCheckObject() { + // START CheckForAnObject + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + boolean exists = jeopardy.data.exists("00ff6900-e64f-5d94-90db-c8cfa3fc851b"); + System.out.println(exists); + // END CheckForAnObject + } +} diff --git a/_includes/code/java-v6/src/test/java/ManageObjectsUpdateTest.java b/_includes/code/java-v6/src/test/java/ManageObjectsUpdateTest.java new file mode 100644 index 00000000..df760e90 --- /dev/null +++ b/_includes/code/java-v6/src/test/java/ManageObjectsUpdateTest.java @@ -0,0 +1,187 @@ +import io.weaviate.client6.v1.api.WeaviateClient; +import io.weaviate.client6.v1.api.collections.CollectionHandle; +import io.weaviate.client6.v1.api.collections.Property; +import io.weaviate.client6.v1.api.collections.VectorConfig; +import io.weaviate.client6.v1.api.collections.WeaviateObject; +import io.weaviate.client6.v1.api.collections.Vectors; +import io.weaviate.client6.v1.api.collections.query.Metadata; +import io.weaviate.client6.v1.api.collections.query.QueryMetadata; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; + +class ManageObjectsUpdateTest { + + private static WeaviateClient client; + + @BeforeAll + public static void beforeAll() throws IOException { + // START INSTANTIATION-COMMON + // Instantiate the client with the OpenAI API key + String openaiApiKey = System.getenv("OPENAI_API_KEY"); + client = WeaviateClient + .connectToLocal(config -> config.setHeaders(Map.of("X-OpenAI-Api-Key", openaiApiKey))); + // END INSTANTIATION-COMMON + + // Simulate weaviate-datasets and set up collections + if (client.collections.exists("WineReviewNV")) { + client.collections.delete("WineReviewNV"); + } + client.collections.create("WineReviewNV", + col -> col + .properties(Property.text("review_body", p -> p.description("Review body")), + Property.text("title", p -> p.description("Name of the wine")), + Property.text("country", p -> p.description("Originating country"))) + .vectorConfig(VectorConfig.text2vecContextionary("title"), + VectorConfig.text2vecContextionary("review_body"), + VectorConfig.text2vecContextionary("title_country", + vc -> vc.sourceProperties("title", "country")))); + + // highlight-start + // ===== Add three mock objects to the WineReviewNV collection ===== + var reviews = client.collections.use("WineReviewNV"); + reviews.data.insertMany( + Map.of("title", "Mock Wine A", "review_body", "A fine mock vintage.", "country", + "Mocktugal"), + Map.of("title", "Mock Wine B", "review_body", "Notes of mockberry.", "country", + "Mockstralia"), + Map.of("title", "Mock Wine C", "review_body", "Pairs well with mock turtle soup.", + "country", "Republic of Mockdova")); + // highlight-end + + // START Define the class + if (client.collections.exists("JeopardyQuestion")) { + client.collections.delete("JeopardyQuestion"); + } + client.collections.create("JeopardyQuestion", + col -> col.description("A Jeopardy! question") + .properties(Property.text("question", p -> p.description("The question")), + Property.text("answer", p -> p.description("The answer")), + Property.number("points", p -> p.description("The points the question is worth"))) + .vectorConfig(VectorConfig.text2vecContextionary())); + // END Define the class + } + + @AfterAll + public static void afterAll() throws Exception { + client.collections.delete("WineReviewNV"); + client.collections.delete("JeopardyQuestion"); + client.close(); + } + + // START DelProps + private static void delProps(WeaviateClient client, String uuidToUpdate, String collectionName, + List propNames) throws IOException { + CollectionHandle> collection = client.collections.use(collectionName); + + // fetch the object to update + Optional, Object, QueryMetadata>> objectDataOpt = + collection.query.byId(uuidToUpdate); + if (objectDataOpt.isEmpty()) { + return; + } + Map propertiesToUpdate = new HashMap<>(objectDataOpt.get().properties()); + + // remove unwanted properties + for (String propName : propNames) { + propertiesToUpdate.remove(propName); + } + + // replace the properties + collection.data.replace(uuidToUpdate, r -> r.properties(propertiesToUpdate)); + } + // END DelProps + + @Test + void testUpdateAndReplaceFlow() throws IOException { + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + + String uuid = jeopardy.data + .insert(Map.of("question", "Test question", "answer", "Test answer", "points", -1.0 // JSON numbers are doubles + )).uuid(); + + // START UpdateProps + jeopardy.data.update(uuid, + // highlight-start + u -> u.properties(Map.of("points", 100.0)) + // highlight-end + ); + // END UpdateProps + + Optional, Object, QueryMetadata>> result1 = + jeopardy.query.byId(uuid); + assertThat(result1).isPresent(); + assertThat(result1.get().properties().get("points")).isEqualTo(100.0); + + // START UpdateVector + float[] vector = new float[300]; + Arrays.fill(vector, 0.12345f); + + jeopardy.data.update(uuid, u -> u.properties(Map.of("points", 100.0)) + // highlight-start + .vectors(Vectors.of(vector)) + // highlight-end + ); + // END UpdateVector + + Optional, Object, QueryMetadata>> result2 = + jeopardy.query.byId(uuid, q -> q.returnMetadata(Metadata.VECTOR)); + assertThat(result2).isPresent(); + assertThat(result2.get().metadata().vectors().getSingle("default")).hasSize(300); + + // START UpdateNamedVector + CollectionHandle> reviews = client.collections.use("WineReviewNV"); + String reviewUuid = reviews.query.fetchObjects(q -> q.limit(3)).objects().get(0).uuid(); + float[] titleVector = new float[300]; + float[] reviewBodyVector = new float[300]; + float[] titleCountryVector = new float[300]; + Arrays.fill(titleVector, 0.12345f); + Arrays.fill(reviewBodyVector, 0.12345f); + Arrays.fill(titleCountryVector, 0.12345f); + + reviews.data.update(reviewUuid, + u -> u + .properties(Map.of("title", "A delicious wine", "review_body", + "This mystery wine is a delight to the senses.", "country", "Mordor")) + // highlight-start + .vectors(Vectors.of("title", titleVector), Vectors.of("review_body", reviewBodyVector), + Vectors.of("title_country", titleCountryVector)) + // highlight-end + ); + // END UpdateNamedVector + + // START Replace + // highlight-start + jeopardy.data.replace( + // highlight-end + uuid, r -> r.properties(Map.of("answer", "Replaced" + // The other properties will be deleted + ))); + // END Replace + + Optional, Object, QueryMetadata>> result3 = + jeopardy.query.byId(uuid); + assertThat(result3).isPresent(); + assertThat(result3.get().properties().get("answer")).isEqualTo("Replaced"); + + // START DelProps + + delProps(client, uuid, "JeopardyQuestion", List.of("answer")); + // END DelProps + + Optional, Object, QueryMetadata>> result4 = + jeopardy.query.byId(uuid); + assertThat(result4).isPresent(); + assertThat(result4.get().properties().get("answer")).isNull(); + } +} diff --git a/_includes/code/java-v6/src/test/java/ModelProvidersTest.java b/_includes/code/java-v6/src/test/java/ModelProvidersTest.java new file mode 100644 index 00000000..66abfc06 --- /dev/null +++ b/_includes/code/java-v6/src/test/java/ModelProvidersTest.java @@ -0,0 +1,180 @@ +import io.weaviate.client6.v1.api.WeaviateClient; +import io.weaviate.client6.v1.api.collections.CollectionHandle; +import io.weaviate.client6.v1.api.collections.Property; +import io.weaviate.client6.v1.api.collections.VectorConfig; +import io.weaviate.client6.v1.api.collections.data.InsertManyResponse; +import io.weaviate.client6.v1.api.collections.query.Metadata; +import io.weaviate.client6.v1.api.collections.query.QueryResponse; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +class ModelProvidersTest { + + private static WeaviateClient client; + + @BeforeAll + public static void beforeAll() throws IOException { + String weaviateUrl = System.getenv("WEAVIATE_URL"); + String weaviateApiKey = System.getenv("WEAVIATE_API_KEY"); + + client = WeaviateClient.connectToWeaviateCloud(weaviateUrl, // Replace with your Weaviate Cloud + // URL + weaviateApiKey // Replace with your Weaviate Cloud key + ); + + client.collections.delete("DemoCollection"); + } + + @AfterAll + public static void afterAll() throws IOException { + client.collections.delete("DemoCollection"); + } + + @Test + void testWeaviateInstantiation() throws Exception { + // START WeaviateInstantiation + // Best practice: store your credentials in environment variables + String weaviateUrl = System.getenv("WEAVIATE_URL"); + String weaviateApiKey = System.getenv("WEAVIATE_API_KEY"); + + // highlight-start + WeaviateClient client = WeaviateClient.connectToWeaviateCloud(weaviateUrl, // Replace with your + // Weaviate Cloud URL + weaviateApiKey // Replace with your Weaviate Cloud key + ); + + System.out.println(client.isReady()); // Should print: `True` + // highlight-end + + client.close(); // Free up resources + // END WeaviateInstantiation + } + + @Test + void testWeaviateVectorizer() throws IOException { + client.collections.delete("DemoCollection"); + // START BasicVectorizerWeaviate + client.collections.create("DemoCollection", + col -> col + .vectorConfig( + VectorConfig.text2VecWeaviate("title_vector", c -> c.sourceProperties("title"))) + .properties(Property.text("title"), Property.text("description"))); + // END BasicVectorizerWeaviate + + var config = client.collections.getConfig("DemoCollection").get(); + assertThat(config.vectors()).containsKey("title_vector"); + System.out.println("first: " + config.vectors().get("title_vector")); + assertThat(config.vectors().get("title_vector").getClass().getSimpleName()) + .isEqualTo("Text2VecWeaviateVectorizer"); + client.collections.delete("DemoCollection"); + } + + @Test + void testWeaviateVectorizerModel() throws IOException { + // START VectorizerWeaviateCustomModel + client.collections + .create("DemoCollection", + col -> col + .vectorConfig(VectorConfig.text2VecWeaviate("title_vector", + c -> c.sourceProperties("title") + .model("Snowflake/snowflake-arctic-embed-l-v2.0"))) + .properties(Property.text("title"), Property.text("description"))); + // END VectorizerWeaviateCustomModel + + var config = client.collections.getConfig("DemoCollection").get(); + assertThat(config.vectors()).containsKey("title_vector"); + System.out.println("first: " + config.vectors().get("title_vector")); + assertThat(config.vectors().get("title_vector").getClass().getSimpleName()) + .isEqualTo("Text2VecWeaviateVectorizer"); + client.collections.delete("DemoCollection"); + } + + @Test + void testWeaviateVectorizerParameters() throws IOException { + // START SnowflakeArcticEmbedMV15 + client.collections.create("DemoCollection", + col -> col.vectorConfig(VectorConfig.text2VecWeaviate("title_vector", + c -> c.sourceProperties("title").model("Snowflake/snowflake-arctic-embed-m-v1.5") + // .inferenceUrl(null) + // .dimensions(0) + )).properties(Property.text("title"), Property.text("description"))); + // END SnowflakeArcticEmbedMV15 + + var config = client.collections.getConfig("DemoCollection").get(); + assertThat(config.vectors()).containsKey("title_vector"); + System.out.println("first: " + config.vectors().get("title_vector")); + assertThat(config.vectors().get("title_vector").getClass().getSimpleName()) + .isEqualTo("Text2VecWeaviateVectorizer"); + } + + @Test + void testInsertData() { + // START BatchImportExample + // Define the source objects + List> sourceObjects = List.of(Map.of("title", "The Shawshank Redemption", + "description", + "A wrongfully imprisoned man forms an inspiring friendship while finding hope and redemption in the darkest of places."), + Map.of("title", "The Godfather", "description", + "A powerful mafia family struggles to balance loyalty, power, and betrayal in this iconic crime saga."), + Map.of("title", "The Dark Knight", "description", + "Batman faces his greatest challenge as he battles the chaos unleashed by the Joker in Gotham City."), + Map.of("title", "Jingle All the Way", "description", + "A desperate father goes to hilarious lengths to secure the season's hottest toy for his son on Christmas Eve."), + Map.of("title", "A Christmas Carol", "description", + "A miserly old man is transformed after being visited by three ghosts on Christmas Eve in this timeless tale of redemption.")); + + // Get a handle to the collection + CollectionHandle> collection = client.collections.use("DemoCollection"); + + // Insert the data using insertMany + InsertManyResponse response = collection.data.insertMany(sourceObjects.toArray(new Map[0])); + + // Check for errors + if (!response.errors().isEmpty()) { + System.err.printf("Number of failed imports: %d\n", response.errors().size()); + System.err.printf("First failed object error: %s\n", response.errors().get(0)); + } else { + System.out.printf("Successfully inserted %d objects.\n", response.uuids().size()); + } + // END BatchImportExample + } + + @Test + void testNearText() { + // START NearTextExample + CollectionHandle> collection = client.collections.use("DemoCollection"); + + // highlight-start + var response = collection.query.nearText("A holiday film", // The model provider integration will automatically vectorize the query + q -> q.limit(2).returnMetadata(Metadata.DISTANCE)); + // highlight-end + + for (var o : response.objects()) { + System.out.println(o.properties().get("title")); + } + // END NearTextExample + } + + @Test + void testHybrid() { + // START HybridExample + CollectionHandle> collection = client.collections.use("DemoCollection"); + + // highlight-start + QueryResponse> response = collection.query.hybrid("A holiday film", // The model provider integration will automatically vectorize the query + q -> q.limit(2).returnMetadata(Metadata.DISTANCE)); + // highlight-end + + for (var o : response.objects()) { + System.out.println(o.properties().get("title")); + } + // END HybridExample + } +} diff --git a/_includes/code/java-v6/src/test/java/QuickstartLocalTest.java b/_includes/code/java-v6/src/test/java/QuickstartLocalTest.java new file mode 100644 index 00000000..46f5e03a --- /dev/null +++ b/_includes/code/java-v6/src/test/java/QuickstartLocalTest.java @@ -0,0 +1,156 @@ +import io.weaviate.client6.v1.api.WeaviateClient; +import io.weaviate.client6.v1.api.collections.CollectionHandle; +import io.weaviate.client6.v1.api.collections.Generative; +import io.weaviate.client6.v1.api.collections.Property; +import io.weaviate.client6.v1.api.collections.VectorConfig; +import io.weaviate.client6.v1.api.collections.data.InsertManyResponse; + +import org.json.JSONArray; +import org.json.JSONObject; +import org.junit.jupiter.api.Test; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +class QuickstartLocalTest { + + @Test + void testConnectionIsReady() throws Exception { + // START InstantiationExample + WeaviateClient client = WeaviateClient.connectToLocal(); + // highlight-start + System.out.println(client.isReady()); // Should print: `True` + // highlight-end + + client.close(); // Free up resources + + // END InstantiationExample + } + + @Test + void testCreateCollection() throws Exception { + String collectionName = "Question"; + // START CreateCollection + WeaviateClient client = WeaviateClient.connectToLocal(); + + // highlight-start + client.collections.create( + collectionName, + col -> col + .vectorConfig(VectorConfig.text2vecContextionary()) // Configure the Weaviate Embeddings integration + .generativeModule(Generative.cohere()) // Configure the Cohere generative AI integration + .properties(Property.text("answer"), Property.text("question"), Property.text("category"))); + CollectionHandle> questions = client.collections.use(collectionName); + // highlight-end + // END CreateCollection + client.collections.delete(collectionName); + // START CreateCollection + + client.close(); // Free up resources + + // END CreateCollection + } + + @Test + void testImportDataWorkflow() throws Exception { + // START Import + WeaviateClient client = WeaviateClient.connectToLocal(); + + // Create the collection + String collectionName = "Question"; + client.collections.create(collectionName, col -> col + .properties( + Property.text("answer"), + Property.text("question"), + Property.text("category")) + .vectorConfig(VectorConfig.text2vecContextionary())); // Configure the Weaviate Embeddings integration; + + // Get JSON data using HttpURLConnection + URL url = new URL("https://raw.githubusercontent.com/weaviate-tutorials/quickstart/main/data/jeopardy_tiny.json"); + String jsonData = new BufferedReader( + new InputStreamReader(((HttpURLConnection) url.openConnection()).getInputStream())) + .lines().reduce("", String::concat); + + // highlight-start + CollectionHandle> questions = client.collections.use(collectionName); + List> questionsToInsert = new ArrayList<>(); + + // Parse and prepare objects using org.json + new JSONArray(jsonData).forEach(item -> { + JSONObject json = (JSONObject) item; + Map properties = new HashMap<>(); + properties.put("answer", json.getString("Answer")); + properties.put("question", json.getString("Question")); + properties.put("category", json.getString("Category")); + questionsToInsert.add(properties); + }); + + // Call insertMany with the list of objects + InsertManyResponse insertResponse = questions.data.insertMany(questionsToInsert.toArray(new Map[0])); + // highlight-end + + // Check for errors + if (!insertResponse.errors().isEmpty()) { + System.err.printf("Number of failed imports: %d\n", insertResponse.errors().size()); + System.err.printf("First failed object error: %s\n", insertResponse.errors().get(0)); + } else { + System.out.printf("Successfully inserted %d objects.\n", insertResponse.uuids().size()); + } + // END Import + // client.collections.delete(collectionName); + // START Import + + client.close(); // Free up resources + // END Import + } + + @Test + void testNearTextQuery() throws Exception { + // START NearText + WeaviateClient client = WeaviateClient.connectToLocal(); + + String collectionName = "Question"; + var questions = client.collections.use(collectionName); + + // highlight-start + var response = questions.query.nearText("biology", q -> q.limit(2)); + // highlight-end + + for (var obj : response.objects()) { + System.out.println(obj.properties()); + } + // END NearText + client.collections.delete(collectionName); + // START NearText + + client.close(); // Free up resources + // END NearText + } + + // @Test + // void testRagQuery() { + // WeaviateClient client = WeaviateClient.connectToLocal(); + + // var questions = client.collections.use("Question"); + + // // highlight-start + // var response = questions.generate.nearText( + // q -> q + // .query("biology") + // .limit(2), + // g -> g.groupedTask("Write a tweet with emojis about these facts.")); + // // highlight-end + + // System.out.println(response.generative().text()); // Inspect the generated + // text + // } + // START RAG + // Coming soon + // END RAG +} \ No newline at end of file diff --git a/_includes/code/java-v6/src/test/java/QuickstartTest.java b/_includes/code/java-v6/src/test/java/QuickstartTest.java new file mode 100644 index 00000000..0b4d6415 --- /dev/null +++ b/_includes/code/java-v6/src/test/java/QuickstartTest.java @@ -0,0 +1,190 @@ +import io.weaviate.client6.v1.api.WeaviateClient; +import io.weaviate.client6.v1.api.collections.CollectionHandle; +import io.weaviate.client6.v1.api.collections.Generative; +import io.weaviate.client6.v1.api.collections.Property; +import io.weaviate.client6.v1.api.collections.VectorConfig; +import io.weaviate.client6.v1.api.collections.data.InsertManyResponse; + +import org.json.JSONArray; +import org.json.JSONObject; +import org.junit.jupiter.api.Test; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +class QuickstartTest { + + @Test + void testConnectionIsReady() throws Exception { + // START InstantiationExample + // Best practice: store your credentials in environment variables + String weaviateUrl = System.getenv("WEAVIATE_URL"); + String weaviateApiKey = System.getenv("WEAVIATE_API_KEY"); + + WeaviateClient client = WeaviateClient.connectToWeaviateCloud( + weaviateUrl, // Replace with your Weaviate Cloud URL + weaviateApiKey // Replace with your Weaviate Cloud key + ); + // highlight-start + System.out.println(client.isReady()); // Should print: `True` + // highlight-end + + client.close(); // Free up resources + // END InstantiationExample + } + + @Test + void testCreateCollection() throws Exception { + // START CreateCollection + // Best practice: store your credentials in environment variables + String weaviateUrl = System.getenv("WEAVIATE_URL"); + String weaviateApiKey = System.getenv("WEAVIATE_API_KEY"); + + WeaviateClient client = WeaviateClient.connectToWeaviateCloud( + weaviateUrl, // Replace with your Weaviate Cloud URL + weaviateApiKey // Replace with your Weaviate Cloud key + ); + + String collectionName = "Question"; + // highlight-start + client.collections.create( + collectionName, + col -> col + .vectorConfig(VectorConfig.text2VecWeaviate()) // Configure the Weaviate Embeddings integration + .generativeModule(Generative.cohere()) // Configure the Cohere generative AI integration + ); + CollectionHandle> questions = client.collections.use(collectionName); + // highlight-end + // END CreateCollection + client.collections.delete(collectionName); + // START CreateCollection + + client.close(); // Free up resources + + // END CreateCollection + } + + @Test + void testImportDataWorkflow() throws Exception { + // START Import + // Best practice: store your credentials in environment variables + String weaviateUrl = System.getenv("WEAVIATE_URL"); + String weaviateApiKey = System.getenv("WEAVIATE_API_KEY"); + + WeaviateClient client = WeaviateClient.connectToWeaviateCloud( + weaviateUrl, // Replace with your Weaviate Cloud URL + weaviateApiKey // Replace with your Weaviate Cloud key + ); + + // Create the collection + String collectionName = "Question"; + client.collections.create(collectionName, col -> col + .properties( + Property.text("answer"), + Property.text("question"), + Property.text("category")) + .vectorConfig(VectorConfig.text2VecWeaviate())); // Configure the Weaviate Embeddings integration; + + // Get JSON data using HttpURLConnection + URL url = new URL("https://raw.githubusercontent.com/weaviate-tutorials/quickstart/main/data/jeopardy_tiny.json"); + String jsonData = new BufferedReader( + new InputStreamReader(((HttpURLConnection) url.openConnection()).getInputStream())) + .lines().reduce("", String::concat); + + // highlight-start + CollectionHandle> questions = client.collections.use(collectionName); + List> questionsToInsert = new ArrayList<>(); + + // Parse and prepare objects using org.json + new JSONArray(jsonData).forEach(item -> { + JSONObject json = (JSONObject) item; + Map properties = new HashMap<>(); + properties.put("answer", json.getString("Answer")); + properties.put("question", json.getString("Question")); + properties.put("category", json.getString("Category")); + questionsToInsert.add(properties); + }); + + // Call insertMany with the list of objects + InsertManyResponse insertResponse = questions.data.insertMany(questionsToInsert.toArray(new Map[0])); + // highlight-end + + // Check for errors + if (!insertResponse.errors().isEmpty()) { + System.err.printf("Number of failed imports: %d\n", insertResponse.errors().size()); + System.err.printf("First failed object error: %s\n", insertResponse.errors().get(0)); + } else { + System.out.printf("Successfully inserted %d objects.\n", insertResponse.uuids().size()); + } + // END Import + // client.collections.delete(collectionName); + // START Import + + client.close(); // Free up resources + // END Import + } + + @Test + void testNearTextQuery() throws Exception { + // START NearText + // Best practice: store your credentials in environment variables + String weaviateUrl = System.getenv("WEAVIATE_URL"); + String weaviateApiKey = System.getenv("WEAVIATE_API_KEY"); + + WeaviateClient client = WeaviateClient.connectToWeaviateCloud( + weaviateUrl, // Replace with your Weaviate Cloud URL + weaviateApiKey // Replace with your Weaviate Cloud key + ); + + String collectionName = "Question"; + var questions = client.collections.use(collectionName); + + // highlight-start + var response = questions.query.nearText("biology", q -> q.limit(2)); + // highlight-end + + for (var obj : response.objects()) { + System.out.println(obj.properties()); + } + // END NearText + client.collections.delete(collectionName); + // START NearText + + client.close(); // Free up resources + // END NearText + } + + // @Test + // void testRagQuery() { + // // Best practice: store your credentials in environment variables + // String weaviateUrl = System.getenv("WEAVIATE_URL"); + // String weaviateApiKey = System.getenv("WEAVIATE_API_KEY"); + + // WeaviateClient client = WeaviateClient.connectToWeaviateCloud( + // weaviateUrl, // Replace with your Weaviate Cloud URL + // weaviateApiKey // Replace with your Weaviate Cloud key + // ); + + // var questions = client.collections.use("Question"); + + // // highlight-start + // var response = questions.generate.nearText( + // q -> q + // .query("biology") + // .limit(2), + // g -> g.groupedTask("Write a tweet with emojis about these facts.")); + // // highlight-end + + // System.out.println(response.generative().text()); // Inspect the generated + // text + // } + // START RAG + // Coming soon + // END RAG +} \ No newline at end of file diff --git a/_includes/code/java-v6/src/test/java/SearchAggregateTest.java b/_includes/code/java-v6/src/test/java/SearchAggregateTest.java new file mode 100644 index 00000000..2a14d280 --- /dev/null +++ b/_includes/code/java-v6/src/test/java/SearchAggregateTest.java @@ -0,0 +1,178 @@ +import io.weaviate.client6.v1.api.WeaviateClient; +import io.weaviate.client6.v1.api.collections.CollectionHandle; +import io.weaviate.client6.v1.api.collections.aggregate.Aggregate; +import io.weaviate.client6.v1.api.collections.aggregate.GroupBy; +import io.weaviate.client6.v1.api.collections.query.Where; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.util.Map; + +class SearchAggregateTest { + + private static WeaviateClient client; + + @BeforeAll + public static void beforeAll() throws IOException { + // START INSTANTIATION-COMMON + // Best practice: store your credentials in environment variables + String weaviateUrl = System.getenv("WEAVIATE_URL"); + String weaviateApiKey = System.getenv("WEAVIATE_API_KEY"); + String openaiApiKey = System.getenv("OPENAI_APIKEY"); + + client = WeaviateClient.connectToWeaviateCloud( + weaviateUrl, + weaviateApiKey, + config -> config.setHeaders(Map.of("X-OpenAI-Api-Key", openaiApiKey))); + // END INSTANTIATION-COMMON + } + + @AfterAll + public static void afterAll() throws Exception { + client.close(); + } + + @Test + void testMetaCount() { + // START MetaCount + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.aggregate.overAll( + // highlight-start + a -> a.includeTotalCount(true) + // highlight-end + ); + + System.out.println(response.totalCount()); + // END MetaCount + } + + @Test + void testTextProp() { + // START TextProp + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.aggregate.overAll( + // TODO[g-despot] Count, value and min occurences? + // highlight-start + a -> a.metrics( + Aggregate.text("answer", m -> m + .topOccurences() + .topOccurencesCutoff(5) // Threshold minimum count + )) + // highlight-end + ); + // TODOÏ€[g-despot] How to get topOccurences here + System.out.println(response.properties().get("answer")); + // END TextProp + } + + @Test + void testIntProp() { + // START IntProp + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.aggregate.overAll( + // highlight-start + // Use .number for floats (NUMBER datatype in Weaviate) + a -> a.metrics( + Aggregate.integer("points", m -> m + .sum() + .max() + .min())) + // highlight-end + ); + + // TODOÏ€[g-despot] How to get sum, min and max here + System.out.println(response.properties().get("points")); + System.out.println(response.properties().get("points")); + System.out.println(response.properties().get("points")); + // END IntProp + } + + @Test + void testGroupBy() { + // START groupBy + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.aggregate.overAll( + // TODO[g-despot] Why is metrics needed here? + // highlight-start + c -> c.metrics(), + GroupBy.property("round") + // highlight-end + ); + + // print rounds names and the count for each + for (var group : response.groups()) { + System.out.printf("Value: %s Count: %d\n", group.groupedBy().value(), group.totalCount()); + } + // END groupBy + } + + @Test + void testNearTextWithLimit() { + // START nearTextWithLimit + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.aggregate.nearText( + "animals in space", + a -> a + // highlight-start + .objectLimit(10) + // highlight-end + .metrics(Aggregate.number("points", m -> m.sum()))); + + System.out.println(response.properties().get("points")); + // END nearTextWithLimit + } + + @Test + void testHybrid() { + // START HybridExample + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.aggregate.hybrid( + "animals in space", + a -> a + // TODO what about bm25Operator? + // .bm25Operator(...) // Additional parameters available, such as + // `bm25_operator`, `filter` etc. + // highlight-start + .objectLimit(10) + // highlight-end + .metrics(Aggregate.number("points", m -> m.sum()))); + + System.out.println(response.properties().get("points")); + // END HybridExample + } + + @Test + void testNearTextWithDistance() { + // START nearTextWithDistance + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.aggregate.nearText( + // TODO[g-despot] Should be distance instead of objectLimit + "animals in space", + a -> a.objectLimit(10) + // highlight-start + //.distance(0.19f) + // highlight-end + .metrics(Aggregate.number("points", m -> m.sum()))); + + System.out.println(response.properties().get("points")); + // END nearTextWithDistance + } + + @Test + void testWhereFilter() { + // START whereFilter + // TODO[g-despot] Why is where not available on overAll()? + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.aggregate.overAll( + a -> a + // highlight-start + // .where(Where.property("round").eq("Final Jeopardy!")) + // highlight-end + .includeTotalCount(true)); + + System.out.println(response.totalCount()); + // END whereFilter + } +} \ No newline at end of file diff --git a/_includes/code/java-v6/src/test/java/SearchBasicTest.java b/_includes/code/java-v6/src/test/java/SearchBasicTest.java new file mode 100644 index 00000000..77c86c31 --- /dev/null +++ b/_includes/code/java-v6/src/test/java/SearchBasicTest.java @@ -0,0 +1,216 @@ +import io.weaviate.client6.v1.api.WeaviateClient; +import io.weaviate.client6.v1.api.collections.CollectionHandle; +import io.weaviate.client6.v1.api.collections.query.ConsistencyLevel; +import io.weaviate.client6.v1.api.collections.query.Metadata; +import io.weaviate.client6.v1.api.collections.query.QueryReference; +import io.weaviate.client6.v1.api.collections.query.Where; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.util.Map; + +class SearchBasicTest { + + private static WeaviateClient client; + + @BeforeAll + public static void beforeAll() throws IOException { + // START INSTANTIATION-COMMON + // Best practice: store your credentials in environment variables + String weaviateUrl = System.getenv("WEAVIATE_URL"); + String weaviateApiKey = System.getenv("WEAVIATE_API_KEY"); + String openaiApiKey = System.getenv("OPENAI_APIKEY"); + + client = WeaviateClient.connectToWeaviateCloud( + weaviateUrl, + weaviateApiKey, + config -> config + .setHeaders(Map.of("X-OpenAI-Api-Key", openaiApiKey))); + // END INSTANTIATION-COMMON + } + + @AfterAll + public static void afterAll() throws Exception { + client.close(); + } + + // TODO[g-despot] Why doesn't standalone fetachObjects work? + @Test + void testBasicGet() { + // START BasicGet + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + // highlight-start + var response = jeopardy.query.fetchObjects(config -> config.limit(1)); + // highlight-end + + for (var o : response.objects()) { + System.out.println(o.properties()); + } + // END BasicGet + } + + @Test + void testGetWithLimit() { + // START GetWithLimit + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.query.fetchObjects( + // highlight-start + q -> q.limit(1) + // highlight-end + ); + + for (var o : response.objects()) { + System.out.println(o.properties()); + } + // END GetWithLimit + } + + @Test + void testGetWithLimitOffset() { + // START GetWithOffset + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.query.fetchObjects( + // highlight-start + q -> q.limit(1).offset(1) + // highlight-end + ); + + for (var o : response.objects()) { + System.out.println(o.properties()); + } + // END GetWithOffset + } + + @Test + void testGetProperties() { + // START GetProperties + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.query.fetchObjects( + // highlight-start + q -> q.limit(1) + .returnProperties("question", "answer", "points") + // highlight-end + ); + + for (var o : response.objects()) { + System.out.println(o.properties()); + } + // END GetProperties + } + + // TODO[g-despot] Vector shoudn't be in metadata + @Test + void testGetObjectVector() { + // START GetObjectVector + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.query.fetchObjects( + q -> q + // highlight-start + .returnMetadata(Metadata.VECTOR) + // highlight-end + .limit(1)); + + if (!response.objects().isEmpty()) { + System.out.println(response.objects().get(0).metadata().vectors()); + } + // END GetObjectVector + } + + @Test + void testGetObjectId() { + // START GetObjectId + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.query.fetchObjects( + // Object IDs are included by default with the v6 client! :) + q -> q.limit(1)); + + for (var o : response.objects()) { + System.out.println(o.uuid()); + } + // END GetObjectId + } + + @Test + void testGetWithCrossRefs() { + // START GetWithCrossRefs + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.query.fetchObjects( + q -> q + // highlight-start + .returnReferences( + QueryReference.single("hasCategory", r -> r.returnProperties("title"))) + // highlight-end + .limit(2)); + + for (var o : response.objects()) { + System.out.println(o.properties().get("question")); + // print referenced objects + if (o.references() != null && o.references().get("hasCategory") != null) { + for (var refObj : o.references().get("hasCategory")) { + System.out.println(refObj); + } + } + } + // END GetWithCrossRefs + } + + @Test + void testGetWithMetadata() { + // START GetWithMetadata + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.query.fetchObjects( + q -> q + .limit(1) + // highlight-start + .returnMetadata(Metadata.CREATION_TIME_UNIX) + // highlight-end + ); + + for (var o : response.objects()) { + System.out.println(o.properties()); // View the returned properties + System.out.println(o.metadata().creationTimeUnix()); // View the returned creation time + } + // END GetWithMetadata + } + + @Test + void testMultiTenancy() { + // START MultiTenancy + // Connect to the collection + CollectionHandle> mtCollection = client.collections.use("WineReviewMT"); + + // Get the specific tenant's version of the collection + // highlight-start + var collectionTenantA = mtCollection.withTenant("tenantA"); + // highlight-end + + // Query tenantA's version + var response = collectionTenantA.query.fetchObjects( + q -> q + .returnProperties("review_body", "title") + .limit(1)); + + if (!response.objects().isEmpty()) { + System.out.println(response.objects().get(0).properties()); + } + // END MultiTenancy + } + + // TODO[g-despot] fetchObjectsById missing + @Test + void testGetWithConsistencyLevel() { + // START QueryWithReplication + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion") + .withConsistencyLevel(ConsistencyLevel.QUORUM); + // highlight-start + var response = jeopardy.query.fetchObjects(c -> c.where(Where.uuid().eq("36ddd591-2dee-4e7e-a3cc-eb86d30a4303"))); + // highlight-end + + for (var o : response.objects()) { + System.out.println(o.properties()); + } + // END QueryWithReplication + } +} \ No newline at end of file diff --git a/_includes/code/java-v6/src/test/java/SearchFiltersTest.java b/_includes/code/java-v6/src/test/java/SearchFiltersTest.java new file mode 100644 index 00000000..73d70f72 --- /dev/null +++ b/_includes/code/java-v6/src/test/java/SearchFiltersTest.java @@ -0,0 +1,474 @@ +import io.weaviate.client6.v1.api.WeaviateClient; +import io.weaviate.client6.v1.api.collections.CollectionHandle; +import io.weaviate.client6.v1.api.collections.DataType; +import io.weaviate.client6.v1.api.collections.Property; +import io.weaviate.client6.v1.api.collections.VectorConfig; +import io.weaviate.client6.v1.api.collections.data.InsertManyResponse; +import io.weaviate.client6.v1.api.collections.query.Metadata; +import io.weaviate.client6.v1.api.collections.query.QueryReference; +import io.weaviate.client6.v1.api.collections.query.Where; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.time.Instant; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +class SearchFilterTest { + + private static WeaviateClient client; + + @BeforeAll + public static void beforeAll() throws IOException { + // START INSTANTIATION-COMMON + // Best practice: store your credentials in environment variables + String weaviateUrl = System.getenv("WEAVIATE_URL"); + String weaviateApiKey = System.getenv("WEAVIATE_API_KEY"); + String openaiApiKey = System.getenv("OPENAI_APIKEY"); + + client = WeaviateClient.connectToWeaviateCloud( + weaviateUrl, + weaviateApiKey, + config -> config.setHeaders(Map.of("X-OpenAI-Api-Key", openaiApiKey))); + // END INSTANTIATION-COMMON + } + + @AfterAll + public static void afterAll() throws Exception { + client.close(); + } + + @Test + void testSingleFilter() { + // START SingleFilter + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.query.fetchObjects( + q -> q + // highlight-start + .where(Where.property("round").eq("Double Jeopardy!")) + // highlight-end + .limit(3)); + + for (var o : response.objects()) { + System.out.println(o.properties()); + } + // END SingleFilter + } + + @Test + void testSingleFilterNearText() { + // START NearTextSingleFilter + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.query.nearText( + "fashion icons", + q -> q + // highlight-start + .where(Where.property("points").gt(200)) + // highlight-end + .limit(3)); + + for (var o : response.objects()) { + System.out.println(o.properties()); + } + // END NearTextSingleFilter + } + + @Test + void testContainsAnyFilter() { + // START ContainsAnyFilter + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + + // highlight-start + List tokenList = List.of("australia", "india"); + // highlight-end + var response = jeopardy.query.fetchObjects( + q -> q + // highlight-start + // Find objects where the `answer` property contains any of the strings in + // `token_list` + // TODO[g-despot] containsAny doesn't accept lists? + .where(Where.property("answer").containsAny(tokenList.toString())) + // highlight-end + .limit(3)); + + for (var o : response.objects()) { + System.out.println(o.properties()); + } + // END ContainsAnyFilter + } + + @Test + void testContainsAllFilter() { + // START ContainsAllFilter + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + + // highlight-start + List tokenList = List.of("blue", "red"); + // highlight-end + + var response = jeopardy.query.fetchObjects( + q -> q + // highlight-start + // Find objects where the `question` property contains all of the strings in + // `token_list` + // TODO[g-despot] containsAll doesn't accept lists? + .where(Where.property("question").containsAll(tokenList.toString())) + // highlight-end + .limit(3)); + + for (var o : response.objects()) { + System.out.println(o.properties()); + } + // END ContainsAllFilter + } + + @Test + void testContainsNoneFilter() { + // START ContainsNoneFilter + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + + // highlight-start + List tokenList = List.of("bird", "animal"); + // highlight-end + + var response = jeopardy.query.fetchObjects( + q -> q + // highlight-start + // Find objects where the `question` property contains none of the strings in + // `token_list` + // TODO[g-despot] containsNone doesn't accept lists? + .where(Where.property("question").containsNone(tokenList.toString())) + // highlight-end + .limit(3)); + + for (var o : response.objects()) { + System.out.println(o.properties()); + } + // END ContainsNoneFilter + } + + @Test + void testLikeFilter() { + // START LikeFilter + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.query.fetchObjects( + q -> q + // highlight-start + .where(Where.property("answer").like("*ala*")) + // highlight-end + .limit(3)); + + for (var o : response.objects()) { + System.out.println(o.properties()); + } + // END LikeFilter + } + + @Test + void testMultipleFiltersAnd() { + // START MultipleFiltersAnd + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.query.fetchObjects( + q -> q + // highlight-start + // Combine filters with Where.and(), Where.or(), and Where.not() + .where(Where.and( + Where.property("round").eq("Double Jeopardy!"), + Where.property("points").lt(600), + Where.not(Where.property("answer").eq("Yucatan")))) + // highlight-end + .limit(3)); + + for (var o : response.objects()) { + System.out.println(o.properties()); + } + // END MultipleFiltersAnd + } + + @Test + void testMultipleFiltersAnyOf() { + // START MultipleFiltersAnyOf + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.query.fetchObjects( + q -> q + // highlight-start + .where(Where.or( + Where.property("points").gte(700), + Where.property("points").lt(500), + Where.property("round").eq("Double Jeopardy!"))) + // highlight-end + .limit(5)); + + for (var o : response.objects()) { + System.out.println(o.properties()); + } + // END MultipleFiltersAnyOf + } + + @Test + void testMultipleFiltersAllOf() { + // START MultipleFiltersAllOf + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.query.fetchObjects( + q -> q + // highlight-start + .where(Where.and( + Where.property("points").gt(300), + Where.property("points").lt(700), + Where.property("round").eq("Double Jeopardy!"))) + // highlight-end + .limit(5)); + + for (var o : response.objects()) { + System.out.println(o.properties()); + } + // END MultipleFiltersAllOf + } + + @Test + void testMultipleFiltersNested() { + // START MultipleFiltersNested + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.query.fetchObjects( + q -> q + // highlight-start + .where(Where.and( + Where.property("answer").like("*bird*"), + Where.or( + Where.property("points").gt(700), + Where.property("points").lt(300)))) + // highlight-end + .limit(3)); + + for (var o : response.objects()) { + System.out.println(o.properties()); + } + // END MultipleFiltersNested + } + + @Test + void testCrossReference() { + // START CrossReference + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.query.fetchObjects( + q -> q + // highlight-start + .where(Where.reference("hasCategory", "JeopardyQuestion", "title").eq("Sport")) + .returnReferences(QueryReference.single("hasCategory", r -> r.returnProperties("title"))) + // highlight-end + .limit(3)); + + for (var o : response.objects()) { + System.out.println(o.properties()); + if (o.references() != null && o.references().get("hasCategory") != null) { + // TODO[g-despot] How to get property title here? + System.out.println(o.references().get("hasCategory").get(0)); + } + } + // END CrossReference + } + + @Test + void testFilterById() { + // START FilterById + CollectionHandle> collection = client.collections.use("Article"); + + String targetId = "00037775-1432-35e5-bc59-443baaef7d80"; + var response = collection.query.fetchObjects( + q -> q.where(Where.uuid().eq(targetId))); + + for (var o : response.objects()) { + System.out.println(o.properties()); // Inspect returned objects + System.out.println(o.uuid()); + } + // END FilterById + } + + // TODO[g-despot] How to filter by creation time? + // @Test + // void testFilterByTimestamp() { + // // START FilterByTimestamp + // // highlight-start + // // Set the timezone for avoidance of doubt + // OffsetDateTime filterTime = OffsetDateTime.of(2020, 1, 1, 0, 0, 0, 0, + // ZoneOffset.UTC); + // // highlight-end + + // CollectionHandle> collection = + // client.collections.use("Article"); + // var response = collection.query.fetchObjects( + // q -> q + // .limit(3) + // // highlight-start + // .where(Where.byCreationTime().gt(filterTime.toInstant())) + // .returnMetadata(Metadata.CREATION_TIME_UNIX) + // // highlight-end + // ); + + // for (var o : response.objects()) { + // System.out.println(o.properties()); // Inspect returned objects + // System.out.println(o.metadata().creationTime()); // Inspect object creation + // time + // } + // // END FilterByTimestamp + // } + + @Test + void testFilterByDateDatatype() throws IOException { + String collectionName = "CollectionWithDate"; + client.collections.delete(collectionName); + try { + client.collections.create(collectionName, col -> col + .properties( + Property.text("title"), + Property.date("some_date")) + .vectorConfig(VectorConfig.selfProvided())); + + // Get a handle to your collection + var collection = client.collections.use(collectionName); + + // 1. Create a list to hold all the objects for insertion. + List> objects = new ArrayList<>(); + + // 2. Populate the list with your data. + for (int year = 2020; year <= 2024; year++) { + for (int month = 1; month <= 12; month += 2) { + for (int day = 1; day <= 20; day += 5) { + Instant date = OffsetDateTime.of(year, month, day, 0, 0, 0, 0, ZoneOffset.UTC).toInstant(); + objects.add(Map.of( + "title", String.format("Object: yr/month/day:%d/%d/%d", year, month, day), + "some_date", date)); + } + } + } + + // 3. Call insertMany with the list of objects. + // Note: We convert the List to an array to match the method's varargs + // signature. + InsertManyResponse insertResponse = collection.data.insertMany(objects.toArray(new Map[0])); + + // 4. (Optional) Check for errors. + if (!insertResponse.errors().isEmpty()) { + throw new RuntimeException("Errors occurred during batch insertion: " + insertResponse.errors()); + } + + System.out.printf("Successfully inserted %d objects.", insertResponse.uuids().size()); + + // START FilterByDateDatatype + // highlight-start + // Set the timezone for avoidance of doubt + Instant filterTime = OffsetDateTime.of(2022, 6, 10, 0, 0, 0, 0, ZoneOffset.UTC).toInstant(); + // The filter threshold could also be an RFC 3339 timestamp, e.g.: + // String filter_time = "2022-06-10T00:00:00.00Z"; + // highlight-end + + var response = collection.query.fetchObjects( + q -> q + .limit(3) + // highlight-start + // This property (`some_date`) is a `DATE` datatype + .where(Where.property("some_date").gt(filterTime)) + // highlight-end + ); + + for (var o : response.objects()) { + System.out.println(o.properties()); // Inspect returned objects + } + // END FilterByDateDatatype + } finally { + if (client.collections.exists(collectionName)) { + client.collections.delete(collectionName); + } + } + } + + @Test + void testFilterByPropertyLength() { + // START FilterByPropertyLength + int lengthThreshold = 20; + + CollectionHandle> collection = client.collections.use("JeopardyQuestion"); + var response = collection.query.fetchObjects( + q -> q + .limit(3) + // highlight-start + .where(Where.property("answer").gt(lengthThreshold)) + // highlight-end + ); + + for (var o : response.objects()) { + System.out.println(o.properties()); // Inspect returned objects + System.out.println(((String) o.properties().get("answer")).length()); // Inspect property length + } + // END FilterByPropertyLength + } + + // TODO[g-despot] How to filter for property null state? + // @Test + // void testFilterByPropertyNullState() { + // // START FilterByPropertyNullState + // CollectionHandle> collection = + // client.collections.use("WineReview"); + // var response = collection.query.fetchObjects( + // q -> q + // .limit(3) + // // highlight-start + // // This requires the `country` property to be configured with + // // `index_null_state=True`` + // .where(Where.property("country").isNull()) // Find objects where the + // `country` property is null + // // highlight-end + // ); + + // for (var o : response.objects()) { + // System.out.println(o.properties()); // Inspect returned objects + // } + // // END FilterByPropertyNullState + // } + + // TODO[g-despot] When geo properties become available + // @Test + // void testFilterByGeolocation() { + // String collectionName = "Publication"; + // WeaviateClient localClient = WeaviateClient.connectToLocal(); + // try { + // localClient.collections.create(collectionName, col -> col + // .properties( + // Property.text("title"), + // Property.geo("headquartersGeoLocation"))); + // var publications = localClient.collections.use(collectionName); + // publications.data.insert(Map.of( + // "headquartersGeoLocation", Map.of( + // "latitude", 52.3932696, + // "longitude", 4.8374263))); + + // // START FilterbyGeolocation + // var response = publications.query.fetchObjects( + // q -> q.where( + // Where.property("headquartersGeoLocation") + // .withinGeoRange( + // GeoCoordinate.of(52.39f, 4.84f), + // 1000.0 // In meters + // ))); + + // for (var o : response.objects()) { + // System.out.println(o.properties()); // Inspect returned objects + // } + // // END FilterbyGeolocation + // } finally { + // if (localClient.collections.exists(collectionName)) { + // localClient.collections.delete(collectionName); + // } + // try { + // localClient.close(); + // } catch (IOException e) { + // // ignore + // } + // } + // } +} \ No newline at end of file diff --git a/_includes/code/java-v6/src/test/java/SearchHybridTest.java b/_includes/code/java-v6/src/test/java/SearchHybridTest.java new file mode 100644 index 00000000..c517cd73 --- /dev/null +++ b/_includes/code/java-v6/src/test/java/SearchHybridTest.java @@ -0,0 +1,313 @@ +import io.weaviate.client6.v1.api.WeaviateClient; +import io.weaviate.client6.v1.api.collections.CollectionHandle; +import io.weaviate.client6.v1.api.collections.query.GroupBy; +import io.weaviate.client6.v1.api.collections.query.Hybrid.FusionType; +import io.weaviate.client6.v1.api.collections.query.Metadata; +import io.weaviate.client6.v1.api.collections.query.NearVector; +import io.weaviate.client6.v1.api.collections.query.Where; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.util.Map; +import static org.assertj.core.api.Assertions.assertThat; + +class SearchHybridTest { + + private static WeaviateClient client; + + @BeforeAll + public static void beforeAll() throws IOException { + // START INSTANTIATION-COMMON + // Best practice: store your credentials in environment variables + String weaviateUrl = System.getenv("WEAVIATE_URL"); + String weaviateApiKey = System.getenv("WEAVIATE_API_KEY"); + String openaiApiKey = System.getenv("OPENAI_APIKEY"); + + client = WeaviateClient.connectToWeaviateCloud(weaviateUrl, weaviateApiKey, + config -> config.setHeaders(Map.of("X-OpenAI-Api-Key", openaiApiKey))); + // END INSTANTIATION-COMMON + } + + @AfterAll + public static void afterAll() throws Exception { + client.close(); + } + + // TODO[g-despot] Why isn't targetVector available? + // @Test + // void testNamedVectorHybrid() { + // CollectionHandle> reviews = client.collections.use("WineReviewNV"); + // var response = reviews.query.hybrid( + // // highlight-start + // "A French Riesling", q -> q + // // .targetVector("title_country") + // .limit(3) + + // // highlight-end + // ); + + // for (var o : response.objects()) { + // System.out.println(o.properties()); + // } + // } + // START NamedVectorHybridPython + // Coming soon + // END NamedVectorHybridPython + + + @Test + void testHybridBasic() { + // START HybridBasicPython + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.query.hybrid( + // highlight-start + "food", q -> q.limit(3) + // highlight-end + ); + + for (var o : response.objects()) { + System.out.println(o.properties()); + } + // END HybridBasicPython + } + + @Test + void testHybridWithScore() { + // START HybridWithScorePython + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.query.hybrid("food", q -> q.alpha(0.5f) + // highlight-start + .returnMetadata(Metadata.SCORE, Metadata.EXPLAIN_SCORE) + // highlight-end + .limit(3)); + + for (var o : response.objects()) { + System.out.println(o.properties()); + // highlight-start + System.out.println(o.metadata().score() + " " + o.metadata().explainScore()); + // highlight-end + } + // END HybridWithScorePython + } + + @Test + void testLimit() { + // START limit Python + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.query.hybrid("food", q -> q + // highlight-start + .limit(3).offset(1) + // highlight-end + ); + + for (var o : response.objects()) { + System.out.println(o.properties()); + } + // END limit Python + } + + @Test + void testAutocut() { + // START autocut Python + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.query.hybrid("food", q -> q + // highlight-start + .fusionType(FusionType.RELATIVE_SCORE).autocut(1) + // highlight-end + ); + + for (var o : response.objects()) { + System.out.println(o.properties()); + } + // END autocut Python + } + + @Test + void testHybridWithAlpha() { + // START HybridWithAlphaPython + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.query.hybrid("food", q -> q + // highlight-start + .alpha(0.25f) + // highlight-end + .limit(3)); + + for (var o : response.objects()) { + System.out.println(o.properties()); + } + // END HybridWithAlphaPython + } + + @Test + void testHybridWithFusionType() { + // START HybridWithFusionTypePython + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.query.hybrid("food", q -> q + // highlight-start + .fusionType(FusionType.RELATIVE_SCORE) + // highlight-end + .limit(3)); + + for (var o : response.objects()) { + System.out.println(o.properties()); + } + // END HybridWithFusionTypePython + } + + // TODO Why isn't bm25Operator available? + @Test + void testHybridWithBM25OperatorOrWithMin() { + // START HybridWithBM25OperatorOrWithMin + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.query.hybrid( + // highlight-start + "Australian mammal cute" + // .bm25Operator(BM25Operator.or(2)) + // highlight-end + // .limit(3) + ); + + for (var o : response.objects()) { + System.out.println(o.properties()); + } + // END HybridWithBM25OperatorOrWithMin + } + + // TODO Why isn't bm25Operator available? + @Test + void testHybridWithBM25OperatorAnd() { + // START HybridWithBM25OperatorAnd + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.query.hybrid( + // highlight-start + "Australian mammal cute" + // .bm25Operator(BM25Operator.and()) // Each result must include all tokens + // (e.g. "australian", "mammal", "cute") + // highlight-end + // .limit(3) + ); + + for (var o : response.objects()) { + System.out.println(o.properties()); + } + // END HybridWithBM25OperatorAnd + } + + @Test + void testHybridWithProperties() { + // START HybridWithPropertiesPython + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.query.hybrid("food", q -> q + // highlight-start + .queryProperties("question") + // highlight-end + .alpha(0.25f).limit(3)); + + for (var o : response.objects()) { + System.out.println(o.properties()); + } + // END HybridWithPropertiesPython + } + + @Test + void testHybridWithPropertyWeighting() { + // START HybridWithPropertyWeightingPython + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.query.hybrid("food", q -> q + // highlight-start + .queryProperties("question^2", "answer") + // highlight-end + .alpha(0.25f).limit(3)); + + for (var o : response.objects()) { + System.out.println(o.properties()); + } + // END HybridWithPropertyWeightingPython + } + + @Test + void testHybridWithVector() { + // START HybridWithVectorPython + float[] queryVector = new float[1536]; // Some vector that is compatible with object vectors + for (int i = 0; i < queryVector.length; i++) { + queryVector[i] = -0.02f; + } + + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.query.hybrid("food", q -> q + // highlight-start + .nearVector(NearVector.of(queryVector)) + // highlight-end + .alpha(0.25f).limit(3)); + + for (var o : response.objects()) { + System.out.println(o.properties()); + } + // END HybridWithVectorPython + } + + @Test + void testHybridWithFilter() { + // START HybridWithFilterPython + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.query.hybrid("food", q -> q + // highlight-start + .where(Where.property("round").eq("Double Jeopardy!")) + // highlight-end + .limit(3)); + + for (var o : response.objects()) { + System.out.println(o.properties()); + } + // END HybridWithFilterPython + } + + @Test + void testVectorParameters() { + // START VectorParametersPython + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.query.hybrid("California", q -> q + // highlight-start + .maxVectorDistance(0.4f) + .nearVector(NearVector.of(jeopardy.query + .nearText("large animal", + c -> c.moveAway(0.5f, from -> from.concepts("mammal", "terrestrial"))) + .objects().get(0).vectors().getDefaultSingle())) + // highlight-end + .alpha(0.75f).limit(5)); + // END VectorParametersPython + assertThat(response.objects().size() <= 5 && response.objects().size() > 0); + } + + @Test + void testVectorSimilarity() { + // START VectorSimilarityPython + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.query.hybrid("California", q -> q + // highlight-start + .maxVectorDistance(0.4f) // Maximum threshold for the vector search component + // highlight-end + .alpha(0.75f).limit(5)); + // END VectorSimilarityPython + assertThat(response.objects().size() <= 5 && response.objects().size() > 0); + } + + @Test + void testHybridGroupBy() { + // START HybridGroupByPy4 + // Query + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.query.hybrid("California", q -> q.alpha(0.75f), + GroupBy.property("round", // group by this property + 2, // maximum number of groups + 3 // maximum objects per group + )); + + response.groups().forEach((groupName, group) -> { + System.out.println(group.name() + " " + group.objects()); + }); + // END HybridGroupByPy4 + } +} diff --git a/_includes/code/java-v6/src/test/java/SearchImageTest.java b/_includes/code/java-v6/src/test/java/SearchImageTest.java new file mode 100644 index 00000000..db9917c3 --- /dev/null +++ b/_includes/code/java-v6/src/test/java/SearchImageTest.java @@ -0,0 +1,181 @@ +import io.weaviate.client6.v1.api.WeaviateClient; +import io.weaviate.client6.v1.api.collections.CollectionHandle; +import io.weaviate.client6.v1.api.collections.Property; +import io.weaviate.client6.v1.api.collections.VectorConfig; +import io.weaviate.client6.v1.api.collections.query.Metadata; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.Base64; +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +class SearchImageTest { + + private static WeaviateClient client; + private static final String QUERY_IMAGE_PATH = "images/search-image.jpg"; + + // START helper base64 functions + private static String urlToBase64(String url) throws IOException, InterruptedException { + HttpClient httpClient = HttpClient.newHttpClient(); + HttpRequest request = HttpRequest.newBuilder().uri(URI.create(url)).build(); + HttpResponse response = + httpClient.send(request, HttpResponse.BodyHandlers.ofByteArray()); + byte[] content = response.body(); + return Base64.getEncoder().encodeToString(content); + } + + private static String fileToBase64(String path) throws IOException { + byte[] content = Files.readAllBytes(Paths.get(path)); + return Base64.getEncoder().encodeToString(content); + } + // END helper base64 functions + + @BeforeAll + public static void beforeAll() throws IOException, InterruptedException { + client = WeaviateClient.connectToLocal(c -> c.port(8280).grpcPort(50251)); + + // Delete the collection if it already exists + if (client.collections.exists("Dog")) { + client.collections.delete("Dog"); + } + + client.collections.create("Dog", + c -> c + .properties(Property.blob("image"), Property.text("breed"), + Property.text("description")) + .vectorConfig(VectorConfig + .multi2vecClip(i -> i.imageFields("image").textFields("breed", "description")))); + + // Prepare and ingest sample dog images + CollectionHandle> dogs = client.collections.use("Dog"); + List> sampleImages = List.of(Map.of("url", + "https://images.unsplash.com/photo-1489924034176-2e678c29d4c6?q=80&w=2342&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", + "breed", "Husky", "description", + "Siberian Husky with distinctive blue eyes, pointed ears, and thick white and grey fur coat, typical of arctic sled dogs"), + Map.of("url", + "https://images.unsplash.com/photo-1633722715463-d30f4f325e24?w=900&auto=format&fit=crop&q=60&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MXx8R29sZGVuJTIwUmV0cmlldmVyfGVufDB8fDB8fHwy", + "breed", "Golden Retriever", "description", + "Golden Retriever with beautiful long golden fur, friendly expression, sitting and posing for the camera, known for being excellent family pets"), + Map.of("url", + "https://images.unsplash.com/photo-1612979148245-d8c79c50935d?w=900&auto=format&fit=crop&q=60&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxzZWFyY2h8OXx8ZG9nJTIwZ2VybWFuJTIwc2hlcGFyZHxlbnwwfHwwfHx8Mg%3D%3D", + "breed", "German Shepherd", "description", + "The German Shepherd, also known in Britain as an Alsatian, is a German breed of working dog of medium to large size. It was originally bred as a herding dog, for herding sheep. ")); + + System.out.println("Inserting sample data..."); + for (var image : sampleImages) { + String base64Image = urlToBase64(image.get("url")); + dogs.data.insert(Map.of("image", base64Image, "breed", image.get("breed"), "description", + image.get("description"))); + System.out.println("Inserted: " + image.get("breed")); + } + System.out.println("Data insertion complete!"); + + // Download the specific image to be used for file-based searches (optional, for future use) + String queryImageUrl = + "https://images.unsplash.com/photo-1590419690008-905895e8fe0d?q=80&w=1336&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"; + HttpClient httpClient = HttpClient.newHttpClient(); + HttpRequest request = HttpRequest.newBuilder().uri(URI.create(queryImageUrl)).build(); + HttpResponse response = + httpClient.send(request, HttpResponse.BodyHandlers.ofInputStream()); + + Path imageDir = Paths.get("images"); + if (!Files.exists(imageDir)) { + Files.createDirectories(imageDir); + } + Files.copy(response.body(), Paths.get(QUERY_IMAGE_PATH), StandardCopyOption.REPLACE_EXISTING); + } + + @AfterAll + public static void afterAll() throws Exception { + if (client != null) { + // Clean up the collection + if (client.collections.exists("Dog")) { + client.collections.delete("Dog"); + } + client.close(); + } + + // Clean up the downloaded image file and directory + Path imagePath = Paths.get(QUERY_IMAGE_PATH); + Path imageDir = Paths.get("images"); + if (Files.exists(imagePath)) { + Files.delete(imagePath); + } + if (Files.exists(imageDir)) { + Files.delete(imageDir); + } + } + + @Test + void testSearchWithBase64() throws IOException { + // START search with base64 + // highlight-start + String base64String = fileToBase64(QUERY_IMAGE_PATH); // This would be a real base64 string + // highlight-end + + // Get the collection containing images + CollectionHandle> dogs = client.collections.use("Dog"); + + // Perform query + // highlight-start + var response = dogs.query.nearImage(base64String, + // highlight-end + q -> q.returnProperties("breed").limit(1) + // targetVector: "vector_name" // required when using multiple named vectors + ); + + if (!response.objects().isEmpty()) { + System.out.println(response.objects().get(0)); + } + // END search with base64 + } + + // TODO[g-despot] Image search with file path needed + // @Test + // void testImageFileSearch() { + // CollectionHandle> dogs = client.collections.use("Dog"); + // var response = dogs.query.nearImage( + // // highlight-start + // QUERY_IMAGE_PATH, + // // highlight-end + // q -> q.returnProperties("breed").limit(1) + // // targetVector: "vector_name" // required when using multiple named vectors + // ); + + // if (!response.objects().isEmpty()) { + // System.out.println(response.objects().get(0)); + // } + // } + // START ImageFileSearch + // Coming soon + // END ImageFileSearch + + void testDistance() { + // START Distance + CollectionHandle> dogs = client.collections.use("Dog"); + var response = dogs.query.nearImage("./images/search-image.jpg", q -> q + // highlight-start + .distance(0.8f) // Maximum accepted distance + .returnMetadata(Metadata.DISTANCE) // return distance from the source image + // highlight-end + .returnProperties("breed").limit(5)); + + for (var item : response.objects()) { + System.out.println(item); + } + } +} diff --git a/_includes/code/java-v6/src/test/java/SearchKeywordTest.java b/_includes/code/java-v6/src/test/java/SearchKeywordTest.java new file mode 100644 index 00000000..c49efe1b --- /dev/null +++ b/_includes/code/java-v6/src/test/java/SearchKeywordTest.java @@ -0,0 +1,225 @@ +import io.weaviate.client6.v1.api.WeaviateClient; +import io.weaviate.client6.v1.api.collections.CollectionHandle; +import io.weaviate.client6.v1.api.collections.Property; +import io.weaviate.client6.v1.api.collections.query.GroupBy; +import io.weaviate.client6.v1.api.collections.query.Metadata; +import io.weaviate.client6.v1.api.collections.query.Where; +// import io.weaviate.client6.v1.internal.grpc.protocol.WeaviateProtoBase.Filters.Operator; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.util.Map; + +class SearchKeywordTest { + + private static WeaviateClient client; + + @BeforeAll + public static void beforeAll() throws IOException { + // START INSTANTIATION-COMMON + // Best practice: store your credentials in environment variables + String weaviateUrl = System.getenv("WEAVIATE_URL"); + String weaviateApiKey = System.getenv("WEAVIATE_API_KEY"); + String openaiApiKey = System.getenv("OPENAI_APIKEY"); + + client = WeaviateClient.connectToWeaviateCloud(weaviateUrl, weaviateApiKey, + config -> config.setHeaders(Map.of("X-OpenAI-Api-Key", openaiApiKey))); + // END INSTANTIATION-COMMON + } + + @AfterAll + public static void afterAll() throws Exception { + client.close(); + } + + @Test + void testBM25Basic() { + // START BM25Basic + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.query.bm25( + // highlight-start + "food", + // highlight-end + q -> q.limit(3)); + + for (var o : response.objects()) { + System.out.println(o.properties()); + } + // END BM25Basic + } + + // TODO[g-espot] Why isn't operator available? + // @Test + // void testBM25OperatorOrWithMin() { + // CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + // var response = jeopardy.query.bm25( + // // highlight-start + // "Australian mammal cute" + // // q -> q.operator(Operator.OPERATOR_OR_VALUE, 1) + // // highlight-end + // // .limit(3)); + // ); + // for (var o : response.objects()) { + // System.out.println(o.properties()); + // } + // } + // START BM25OperatorOrWithMin + // Coming soon + // END BM25OperatorOrWithMin + + + // TODO[g-espot] Why isn't operator available? + // @Test + // void testBM25OperatorAnd() { + // CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + // var response = jeopardy.query.bm25( + // // highlight-start + // "Australian mammal cute" + // // q -> q.operator(Operator.OPERATOR_AND) // Each result must include all tokens + // // (e.g. "australian", "mammal", "cute") + // // highlight-end + // // .limit(3) + // ); + + // for (var o : response.objects()) { + // System.out.println(o.properties()); + // } + // } + // START BM25OperatorAnd + // Coming soon + // END BM25OperatorAnd + + + @Test + void testBM25WithScore() { + // START BM25WithScore + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.query.bm25("food", q -> q.returnMetadata(Metadata.SCORE).limit(3)); + + for (var o : response.objects()) { + System.out.println(o.properties()); + // highlight-start + System.out.println(o.metadata().score()); + // highlight-end + } + // END BM25WithScore + } + + @Test + void testLimit() { + // START limit + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.query.bm25("safety", q -> q + // highlight-start + .limit(3).offset(1) + // highlight-end + ); + + for (var o : response.objects()) { + System.out.println(o.properties()); + } + // END limit + } + + @Test + void testAutocut() { + // START autocut + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.query.bm25("safety", q -> q + // highlight-start + .autocut(1) + // highlight-end + ); + + for (var o : response.objects()) { + System.out.println(o.properties()); + } + // END autocut + } + + @Test + void testBM25WithProperties() { + // START BM25WithProperties + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.query.bm25("safety", q -> q + // highlight-start + .queryProperties("question") + // highlight-end + .returnMetadata(Metadata.SCORE).limit(3)); + + for (var o : response.objects()) { + System.out.println(o.properties()); + System.out.println(o.metadata().score()); + } + // END BM25WithProperties + } + + @Test + void testBM25WithBoostedProperties() { + // START BM25WithBoostedProperties + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.query.bm25("food", q -> q + // highlight-start + .queryProperties("question^2", "answer") + // highlight-end + .limit(3)); + + for (var o : response.objects()) { + System.out.println(o.properties()); + } + // END BM25WithBoostedProperties + } + + @Test + void testMultipleKeywords() { + // START MultipleKeywords + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.query.bm25( + // highlight-start + "food wine", // search for food or wine + // highlight-end + q -> q.queryProperties("question").limit(5)); + + for (var o : response.objects()) { + System.out.println(o.properties().get("question")); + } + // END MultipleKeywords + } + + @Test + void testBM25WithFilter() { + // START BM25WithFilter + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.query.bm25("food", q -> q + // highlight-start + .where(Where.property("round").eq("Double Jeopardy!")) + // highlight-end + .returnProperties("answer", "question", "round") // return these properties + .limit(3)); + + for (var o : response.objects()) { + System.out.println(o.properties()); + } + // END BM25WithFilter + } + + @Test + void testBM25GroupBy() { + // START BM25GroupByPy4 + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + + var response = jeopardy.query.bm25("California", q -> q, // No query options needed for this example + GroupBy.property("round", // group by this property + 2, // maximum number of groups + 3 // maximum objects per group + )); + + response.groups().forEach((groupName, group) -> { + System.out.println(group.name() + " " + group.objects()); + }); + // END BM25GroupByPy4 + } +} diff --git a/_includes/code/java-v6/src/test/java/SearchSimilarityTest.java b/_includes/code/java-v6/src/test/java/SearchSimilarityTest.java new file mode 100644 index 00000000..a6fade15 --- /dev/null +++ b/_includes/code/java-v6/src/test/java/SearchSimilarityTest.java @@ -0,0 +1,218 @@ +import io.weaviate.client6.v1.api.WeaviateClient; +import io.weaviate.client6.v1.api.collections.CollectionHandle; +import io.weaviate.client6.v1.api.collections.query.GroupBy; +import io.weaviate.client6.v1.api.collections.query.Metadata; +import io.weaviate.client6.v1.api.collections.query.Where; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.util.Map; + +class SearchSimilarityTest { + + private static WeaviateClient client; + + @BeforeAll + public static void beforeAll() throws IOException { + // START INSTANTIATION-COMMON + // Best practice: store your credentials in environment variables + String weaviateUrl = System.getenv("WEAVIATE_URL"); + String weaviateApiKey = System.getenv("WEAVIATE_API_KEY"); + String openaiApiKey = System.getenv("OPENAI_APIKEY"); + String cohereApiKey = System.getenv("COHERE_APIKEY"); + + client = WeaviateClient.connectToWeaviateCloud(weaviateUrl, weaviateApiKey, config -> config + .setHeaders(Map.of("X-OpenAI-Api-Key", openaiApiKey, "X-Cohere-Api-Key", cohereApiKey))); + // END INSTANTIATION-COMMON + } + + @AfterAll + public static void afterAll() throws Exception { + client.collections.deleteAll(); + client.close(); + } + + // TODO[g-despot] Why isn't targetVector available? + @Test + void testNamedVectorNearText() { + // START NamedVectorNearText + CollectionHandle> reviews = client.collections.use("WineReviewNV"); + var response = reviews.query.nearText("a sweet German white wine", q -> q.limit(2) + // highlight-start + // .targetVector("title_country") // Specify the target vector for named vector + // collections + // highlight-end + .returnMetadata(Metadata.DISTANCE)); + + for (var o : response.objects()) { + System.out.println(o.properties()); + System.out.println(o.metadata().distance()); + } + // END NamedVectorNearText + } + + @Test + void testGetNearText() { + // START GetNearText + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.query.nearText( + // highlight-start + "animals in movies", + // highlight-end + q -> q.limit(2).returnMetadata(Metadata.DISTANCE)); + + for (var o : response.objects()) { + System.out.println(o.properties()); + System.out.println(o.metadata().distance()); + } + // END GetNearText + } + + @Test + void testGetNearObject() { + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var initialResponse = jeopardy.query.fetchObjects(q -> q.limit(1)); + if (initialResponse.objects().isEmpty()) + return; // Skip test if no data + var uuid = initialResponse.objects().get(0).uuid(); + + // START GetNearObject + // highlight-start + var response = jeopardy.query.nearObject(uuid, // A UUID of an object (e.g. "56b9449e-65db-5df4-887b-0a4773f52aa7") + // highlight-end + q -> q.limit(2).returnMetadata(Metadata.DISTANCE)); + + for (var o : response.objects()) { + System.out.println(o.properties()); + System.out.println(o.metadata().distance()); + } + // END GetNearObject + } + + // TODO[g-despot] Why do some argument accept Vector.of while other float[]? + @Test + void testGetNearVector() { + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var initialResponse = + jeopardy.query.fetchObjects(q -> q.limit(1).returnMetadata(Metadata.VECTOR)); + if (initialResponse.objects().isEmpty()) + return; // Skip test if no data + var queryVector = initialResponse.objects().get(0).metadata().vectors().getSingle("default"); + + // START GetNearVector + // highlight-start + var response = jeopardy.query.nearVector(queryVector, // your query vector goes here + // highlight-end + q -> q.limit(2).returnMetadata(Metadata.DISTANCE)); + + for (var o : response.objects()) { + System.out.println(o.properties()); + System.out.println(o.metadata().distance()); + } + // END GetNearVector + } + + @Test + void testGetLimitOffset() { + // START GetLimitOffset + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.query.nearText("animals in movies", q -> q + // highlight-start + .limit(2) // return 2 objects + .offset(1) // With an offset of 1 + // highlight-end + .returnMetadata(Metadata.DISTANCE)); + + for (var o : response.objects()) { + System.out.println(o.properties()); + System.out.println(o.metadata().distance()); + } + // END GetLimitOffset + } + + @Test + void testGetWithDistance() { + // START GetWithDistance + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.query.nearText("animals in movies", q -> q + // highlight-start + .distance(0.25f) // max accepted distance + // highlight-end + .returnMetadata(Metadata.DISTANCE)); + + for (var o : response.objects()) { + System.out.println(o.properties()); + System.out.println(o.metadata().distance()); + } + // END GetWithDistance + } + + // TODO[g-despot] Should autocut be autolimit? + @Test + void testAutocut() { + // START Autocut + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.query.nearText("animals in movies", q -> q + // highlight-start + .autocut(1) // number of close groups + // highlight-end + .returnMetadata(Metadata.DISTANCE)); + + for (var o : response.objects()) { + System.out.println(o.properties()); + System.out.println(o.metadata().distance()); + } + // END Autocut + } + + @Test + // TODO[g-despot] Why isn't UUID available on top-level? + void testGetWithGroupby() { + // START GetWithGroupby + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + // highlight-start + var response = jeopardy.query.nearText("animals in movies", // find object based on this query + q -> q.limit(10) // maximum total objects + .returnMetadata(Metadata.DISTANCE), + GroupBy.property("round", // group by this property + 2, // maximum number of groups + 2 // maximum objects per group + )); + // highlight-end + + for (var o : response.objects()) { + System.out.println(o.metadata().uuid()); + System.out.println(o.belongsToGroup()); + System.out.println(o.metadata().distance()); + } + + response.groups().forEach((groupName, group) -> { + System.out.println("=" + "=".repeat(10) + group.name() + "=" + "=".repeat(10)); + System.out.println(group.numberOfObjects()); + for (var o : group.objects()) { + System.out.println(o.properties()); + System.out.println(o.metadata()); + } + }); + // END GetWithGroupby + } + + @Test + void testGetWithWhere() { + // START GetWithFilter + CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); + var response = jeopardy.query.nearText("animals in movies", q -> q + // highlight-start + .where(Where.property("round").eq("Double Jeopardy!")) + // highlight-end + .limit(2).returnMetadata(Metadata.DISTANCE)); + + for (var o : response.objects()) { + System.out.println(o.properties()); + System.out.println(o.metadata().distance()); + } + // END GetWithFilter + } +} diff --git a/_includes/code/java-v6/src/test/java/StarterGuidesCollectionsTest.java b/_includes/code/java-v6/src/test/java/StarterGuidesCollectionsTest.java new file mode 100644 index 00000000..8334fe4f --- /dev/null +++ b/_includes/code/java-v6/src/test/java/StarterGuidesCollectionsTest.java @@ -0,0 +1,109 @@ +import io.weaviate.client6.v1.api.WeaviateClient; +import io.weaviate.client6.v1.api.collections.CollectionConfig; +import io.weaviate.client6.v1.api.collections.Generative; +import io.weaviate.client6.v1.api.collections.Property; +import io.weaviate.client6.v1.api.collections.Quantization; +import io.weaviate.client6.v1.api.collections.Tokenization; +import io.weaviate.client6.v1.api.collections.VectorConfig; +import io.weaviate.client6.v1.api.collections.vectorindex.Distance; +import io.weaviate.client6.v1.api.collections.vectorindex.Hnsw; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.util.Map; + +class StarterGuidesCollectionsTest { + + private static WeaviateClient client; + + @BeforeAll + public static void beforeAll() { + // START-ANY + String openaiApiKey = System.getenv("OPENAI_APIKEY"); + client = WeaviateClient + .connectToLocal(config -> config.setHeaders(Map.of("X-OpenAI-Api-Key", openaiApiKey))); + + // END-ANY + } + + @AfterAll + public static void afterAll() throws Exception { + client.close(); + } + + @AfterEach + public static void afterEach() throws Exception { + client.collections.delete("Question"); + } + + @Test + void testBasicSchema() throws IOException { + // START BasicSchema + CollectionConfig questionsConfig = client.collections.create("Question", + col -> col.vectorConfig(VectorConfig.text2VecWeaviate()) // Set the vectorizer to use the OpenAI API for vector-related operations + .generativeModule(Generative.cohere()) // Set the generative module to use the Cohere API for RAG + .properties(Property.text("question"), Property.text("answer"), + Property.text("category"))); + + System.out.println(questionsConfig); + // END BasicSchema + + } + + @Test + void testSchemaWithPropertyOptions() throws IOException { + // START SchemaWithPropertyOptions + client.collections.create("Question", + col -> col.vectorConfig(VectorConfig.text2VecWeaviate()) + .generativeModule(Generative.cohere()).properties(Property.text("question", p -> p + // highlight-start + .vectorizePropertyName(true) // Include the property name ("question") when vectorizing + .tokenization(Tokenization.LOWERCASE) // Use "lowercase" tokenization + // highlight-end + ), Property.text("answer", p -> p + // highlight-start + .vectorizePropertyName(false) // Skip the property name ("answer") when vectorizing + .tokenization(Tokenization.WHITESPACE) // Use "whitespace" tokenization + // highlight-end + ))); + // END SchemaWithPropertyOptions + } + + @Test + void testSchemaWithMultiTenancy() throws IOException { + // START SchemaWithMultiTenancy + client.collections.create("Question", + col -> col.vectorConfig(VectorConfig.text2VecWeaviate()) + .generativeModule(Generative.cohere()) + .properties(Property.text("question"), Property.text("answer")) + // highlight-start + .multiTenancy(c -> c.autoTenantCreation(true)) // Enable multi-tenancy + // highlight-end + ); + // END SchemaWithMultiTenancy + + } + + @Test + void testSchemaWithIndexSettings() throws IOException { + // START SchemaWithIndexSettings + client.collections.create("Question", + col -> col.vectorConfig(VectorConfig.text2VecWeaviate("default", // Set the name of the vector configuration + // highlight-start + vc -> vc.vectorIndex(Hnsw.of(hnsw -> hnsw.distance(Distance.COSINE))) // Configure the vector index + .quantization(Quantization.rq()) // Enable vector compression (quantization) + // highlight-end + )).generativeModule(Generative.cohere()) + .properties(Property.text("question"), Property.text("answer")) + // highlight-start + // Configure the inverted index + .invertedIndex( + iic -> iic.indexNulls(true).indexPropertyLength(true).indexTimestamps(true)) + // highlight-end + ); + // END SchemaWithIndexSettings + } +} diff --git a/_includes/code/java-v6/src/test/java/StarterGuidesCustomVectorsTest.java b/_includes/code/java-v6/src/test/java/StarterGuidesCustomVectorsTest.java new file mode 100644 index 00000000..1160a193 --- /dev/null +++ b/_includes/code/java-v6/src/test/java/StarterGuidesCustomVectorsTest.java @@ -0,0 +1,132 @@ +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.weaviate.client6.v1.api.WeaviateClient; +import io.weaviate.client6.v1.api.collections.CollectionHandle; +import io.weaviate.client6.v1.api.collections.ObjectMetadata; +import io.weaviate.client6.v1.api.collections.Property; +import io.weaviate.client6.v1.api.collections.VectorConfig; +import io.weaviate.client6.v1.api.collections.Vectors; +import io.weaviate.client6.v1.api.collections.WeaviateObject; +import io.weaviate.client6.v1.api.collections.data.Reference; +import io.weaviate.client6.v1.api.collections.query.Metadata; +import org.junit.jupiter.api.Test; + +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import static org.assertj.core.api.Assertions.assertThat; + +class StarterGuidesCustomVectorsTest { + + // Helper class for parsing the JSON data with vectors + private static class JeopardyQuestionWithVector { + @JsonProperty("Answer") + String answer; + @JsonProperty("Question") + String question; + @JsonProperty("Category") + String category; + @JsonProperty("vector") + float[] vector; + } + + //TODO[g-despot] NearVector missing targetVector + //TODO[g-despot] Why is UUID required? + @Test + void testBringYourOwnVectors() throws Exception { + WeaviateClient client = null; + String collectionName = "Question"; + + try { + // Clean slate + client = WeaviateClient.connectToLocal(); + if (client.collections.exists(collectionName)) { + client.collections.delete(collectionName); + } + + // START CreateCollection + // Create the collection. + client.collections.create(collectionName, col -> col + .properties(Property.text("answer"), Property.text("question"), Property.text("category")) + .vectorConfig(VectorConfig.selfProvided())); + // END CreateCollection + // START ImportData + String fname = "jeopardy_tiny_with_vectors_all-OpenAI-ada-002.json"; + String url = + "https://raw.githubusercontent.com/weaviate-tutorials/quickstart/main/data/" + fname; + + HttpClient httpClient = HttpClient.newHttpClient(); + HttpRequest request = HttpRequest.newBuilder().uri(URI.create(url)).build(); + HttpResponse responseHttp = + httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + String responseBody = responseHttp.body(); + + ObjectMapper objectMapper = new ObjectMapper(); + List data = + objectMapper.readValue(responseBody, new TypeReference<>() {}); + + // Get a handle to the collection + CollectionHandle> questions = client.collections.use(collectionName); + // Declare the list with the specific generic signature required by insertMany + List, Reference, ObjectMetadata>> questionObjs = + new ArrayList<>(); + + for (JeopardyQuestionWithVector d : data) { + // highlight-start + Map properties = + Map.of("answer", d.answer, "question", d.question, "category", d.category); + + // Use the explicit Builder to construct the object with a custom vector + questionObjs + .add(new WeaviateObject.Builder, Reference, ObjectMetadata>() + .properties(properties) + .metadata(ObjectMetadata + .of(meta -> meta.uuid(UUID.randomUUID()).vectors(Vectors.of(d.vector)))) + .build()); + // highlight-end + } + + // Pass the correctly typed list directly to insertMany + var insertManyResponse = questions.data.insertMany(questionObjs); + if (!insertManyResponse.errors().isEmpty()) { + System.err.printf("Number of failed imports: %d\n", insertManyResponse.errors().size()); + System.err.printf("First failed object error: %s\n", insertManyResponse.errors().get(0)); + } + // END ImportData + // START NearVector + float[] queryVector = data.get(0).vector; // Use a vector from the dataset for a reliable query + + // Added a small sleep to ensure indexing is complete + Thread.sleep(2000); + + var response = questions.query.nearVector(queryVector, + q -> q.limit(2).returnMetadata(Metadata.CERTAINTY)); + + System.out.println(response); + // END NearVector + // ===== Test query results ===== + assertThat(response.objects()).hasSize(2); + // The first result should be the object we used for the query, with near-perfect certainty + assertThat(response.objects().get(0).metadata().certainty()).isNotNull() + .isGreaterThan(0.999f); + assertThat(response.objects().get(0).properties()).isNotNull().isInstanceOf(Map.class); + assertThat(response.objects().get(0).properties().get("question")) + .isEqualTo(data.get(0).question); + + + } finally { + if (client != null) { + if (client.collections.exists(collectionName)) { + client.collections.delete(collectionName); + } + client.close(); + } + } + } +} diff --git a/_includes/code/java-v6/src/test/java/_ManageCollectionsMultiTenancyTest.java b/_includes/code/java-v6/src/test/java/_ManageCollectionsMultiTenancyTest.java new file mode 100644 index 00000000..e38f91e6 --- /dev/null +++ b/_includes/code/java-v6/src/test/java/_ManageCollectionsMultiTenancyTest.java @@ -0,0 +1,234 @@ +import io.weaviate.client6.v1.api.WeaviateClient; +import io.weaviate.client6.v1.api.collections.CollectionHandle; +import io.weaviate.client6.v1.api.collections.tenants.Tenant; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; + +class _ManageCollectionsMultiTenancyTest { + + private static WeaviateClient client; + + @BeforeAll + public static void beforeAll() { + String openaiApiKey = System.getenv("OPENAI_API_KEY"); + assertThat(openaiApiKey).isNotBlank() + .withFailMessage("Please set the OPENAI_API_KEY environment variable."); + + client = WeaviateClient.connectToLocal(config -> config + .setHeaders(Map.of("X-OpenAI-Api-Key", openaiApiKey))); + } + + @AfterEach + public void afterAll() throws Exception { + client.close(); + } + + //TODO[g.-despot] A lot of strange errors: IllegalState Connection pool shut down + @Test + void testEnableMultiTenancy() throws IOException { + // START EnableMultiTenancy + client.collections.create("MultiTenancyCollection", col -> col + .multiTenancy(mt -> mt.enabled(true))); + // END EnableMultiTenancy + + var config = client.collections.getConfig("MultiTenancyCollection").get(); + assertThat(config.multiTenancy().createAutomatically()).isTrue(); + } + + @Test + void testEnableAutoActivationMultiTenancy() throws IOException { + // START EnableAutoActivation + client.collections.create("MultiTenancyCollection", col -> col + .multiTenancy(mt -> mt.autoTenantActivation(true))); + // END EnableAutoActivation + + var config = client.collections.getConfig("MultiTenancyCollection").get(); + assertThat(config.multiTenancy().activateAutomatically()).isTrue(); + } + + @Test + void testEnableAutoMT() throws IOException { + // START EnableAutoMT + client.collections.create("CollectionWithAutoMTEnabled", col -> col + .multiTenancy(mt -> mt + .autoTenantCreation(true))); + // END EnableAutoMT + + var config = client.collections.getConfig("CollectionWithAutoMTEnabled").get(); + assertThat(config.multiTenancy().createAutomatically()).isTrue(); + } + + @Test + void testUpdateAutoMT() throws IOException { + String collectionName = "MTCollectionNoAutoMT"; + client.collections.create(collectionName, col -> col + .multiTenancy(mt -> mt + .autoTenantActivation(false))); + + // START UpdateAutoMT + CollectionHandle> collection = client.collections.use(collectionName); + collection.config.update(collectionName, col -> col + .multiTenancy(mt -> mt.autoTenantCreation(true))); + // END UpdateAutoMT + + var config = client.collections.getConfig(collectionName).get(); + assertThat(config.multiTenancy().createAutomatically()).isTrue(); + } + + @Test + void testAddTenantsToClass() throws IOException { + String collectionName = "MultiTenancyCollection"; + client.collections.create(collectionName, col -> col + .multiTenancy(mt -> mt.autoTenantCreation(true))); + + CollectionHandle> collection = client.collections.use(collectionName); + + // START AddTenantsToClass + collection.tenants.create( + Tenant.active("tenantA"), + Tenant.active("tenantB")); + // END AddTenantsToClass + + List tenants = collection.tenants.get(); + assertThat(tenants).hasSize(2); + assertThat(tenants.get(0).name()) + .containsAnyOf("tenantA", "tenantB"); + } + + @Test + void testListTenants() throws IOException { + String collectionName = "MultiTenancyCollection"; + client.collections.create(collectionName, col -> col + .multiTenancy(mt -> mt.autoTenantCreation(true))); + + CollectionHandle> collection = client.collections.use(collectionName); + collection.tenants.create( + Tenant.active("tenantA"), + Tenant.active("tenantB")); + + // START ListTenants + List tenants = collection.tenants.get(); + System.out.println(tenants); + // END ListTenants + + assertThat(tenants).hasSize(2); + } + + @Test + void testGetTenantsByName() throws IOException { + String collectionName = "MultiTenancyCollection"; + client.collections.create(collectionName, col -> col + .multiTenancy(mt -> mt.autoTenantCreation(true))); + + CollectionHandle> collection = client.collections.use(collectionName); + collection.tenants.create( + Tenant.active("tenantA"), + Tenant.active("tenantB")); + + // // START GetTenantsByName + List tenantNames = Arrays.asList("tenantA", "tenantB", + "nonExistentTenant"); + List tenants = collection.tenants.get(tenantNames); + System.out.println(tenants); + // // END GetTenantsByName + + assertThat(tenants).hasSize(2); + } + + @Test + void testGetOneTenant() throws IOException { + String collectionName = "MultiTenancyCollection"; + client.collections.create(collectionName, col -> col + .multiTenancy(mt -> mt.autoTenantCreation(true))); + + CollectionHandle> collection = client.collections.use(collectionName); + collection.tenants.create(Tenant.active("tenantA")); + + // // START GetOneTenant + String tenantName = "tenantA"; + Optional tenant = collection.tenants.get(tenantName); + System.out.println(tenant); + // // END GetOneTenant + + assertThat(tenant).isPresent(); + } + + @Test + void testActivateTenant() throws IOException { + String collectionName = "MultiTenancyCollection"; + client.collections.create(collectionName, col -> col + .multiTenancy(mt -> mt.autoTenantCreation(true))); + + // // START ActivateTenants + String tenantName = "tenantA"; + CollectionHandle> collection = client.collections.use(collectionName); + collection.tenants.activate(tenantName); + // // END ActivateTenants + + Optional tenant = collection.tenants.get(tenantName); + assertThat(tenant).isPresent(); + } + + @Test + void testDeactivateTenant() throws IOException { + String collectionName = "MultiTenancyCollection"; + client.collections.create(collectionName, col -> col + .multiTenancy(mt -> mt.autoTenantCreation(true))); + + // // START DeactivateTenants + String tenantName = "tenantA"; + CollectionHandle> collection = client.collections.use(collectionName); + collection.tenants.deactivate(tenantName); + // // END DeactivateTenants + + Optional tenant = collection.tenants.get(tenantName); + assertThat(tenant).isPresent(); + } + + @Test + void testOffloadTenant() throws IOException { + String collectionName = "MultiTenancyCollection"; + client.collections.create(collectionName, col -> col + .multiTenancy(mt -> mt.autoTenantCreation(true))); + + // // START OffloadTenants + String tenantName = "tenantA"; + CollectionHandle> collection = client.collections.use(collectionName); + collection.tenants.offload(tenantName); + // // END OffloadTenants + + Optional tenant = collection.tenants.get(tenantName); + assertThat(tenant).isPresent(); + } + + @Test + void testRemoveTenants() throws IOException { + String collectionName = "MultiTenancyCollection"; + client.collections.create(collectionName, col -> col + .multiTenancy(mt -> mt.autoTenantCreation(true))); + + CollectionHandle> collection = client.collections.use(collectionName); + collection.tenants.create( + Tenant.active("tenantA"), + Tenant.active("tenantB")); + + // // START RemoveTenants + collection.tenants.delete(Arrays.asList("tenantB", "tenantX")); + // // END RemoveTenants + + List tenants = collection.tenants.get(); + assertThat(tenants).hasSize(1); + assertThat(tenants.get(0).name()) + .containsAnyOf("tenantA"); + } +} diff --git a/_includes/code/java-v6/src/test/java/_SearchGenerativeTest.java b/_includes/code/java-v6/src/test/java/_SearchGenerativeTest.java new file mode 100644 index 00000000..1ba3c1fd --- /dev/null +++ b/_includes/code/java-v6/src/test/java/_SearchGenerativeTest.java @@ -0,0 +1,291 @@ +// import io.weaviate.client6.v1.api.WeaviateClient; +// import io.weaviate.client6.v1.api.collections.CollectionHandle; +// import io.weaviate.client6.v1.api.generative.GenerativeProvider; +// import io.weaviate.client6.v1.api.generative.GroupedTask; +// import io.weaviate.client6.v1.api.generative.SinglePrompt; +// import io.weaviate.client6.v1.api.collections.query.Metadata; +// import org.junit.jupiter.api.AfterAll; +// import org.junit.jupiter.api.BeforeAll; +// import org.junit.jupiter.api.Test; + +// import java.io.IOException; +// import java.net.URI; +// import java.net.http.HttpClient; +// import java.net.http.HttpRequest; +// import java.net.http.HttpResponse; +// import java.util.Base64; +// import java.util.List; +// import java.util.Map; + +// class GenerativeSearchTest { + +// private static WeaviateClient client; + +// @BeforeAll +// public static void beforeAll() throws IOException { +// // START INSTANTIATION-COMMON +// // Best practice: store your credentials in environment variables +// String weaviateUrl = System.getenv("WEAVIATE_URL"); +// String weaviateApiKey = System.getenv("WEAVIATE_API_KEY"); +// String openaiApiKey = System.getenv("OPENAI_APIKEY"); +// String anthropicApiKey = System.getenv("ANTHROPIC_APIKEY"); + +// client = WeaviateClient.connectToWeaviateCloud( +// weaviateUrl, +// weaviateApiKey, +// config -> config.setHeaders(Map.of( +// "X-OpenAI-Api-Key", openaiApiKey, +// "X-Anthropic-Api-Key", anthropicApiKey))); +// // END INSTANTIATION-COMMON +// } + +// @AfterAll +// public static void afterAll() throws Exception { +// client.close(); +// } + +// @Test +// void testDynamicRag() { +// // START DynamicRag +// CollectionHandle> reviews = client.collections.use("WineReviewNV"); +// var response = reviews.generate.nearText( +// q -> q +// .query("a sweet German white wine") +// .limit(2) +// .targetVector("title_country"), +// g -> g +// .singlePrompt("Translate this into German: {review_body}") +// .groupedTask("Summarize these reviews") +// // highlight-start +// .provider(GenerativeProvider.openAI(p -> p.temperature(0.1f))) +// // highlight-end +// ); + +// for (var o : response.objects()) { +// System.out.printf("Properties: %s\n", o.properties()); +// System.out.printf("Single prompt result: %s\n", o.generative().text()); +// } +// System.out.printf("Grouped task result: %s\n", response.generative().text()); +// // END DynamicRag +// } + +// @Test +// void testNamedVectorNearText() { +// // START NamedVectorNearTextPython +// CollectionHandle> reviews = client.collections.use("WineReviewNV"); +// var response = reviews.generate.nearText( +// q -> q +// .query("a sweet German white wine") +// .limit(2) +// // highlight-start +// .targetVector("title_country") // Specify the target vector for named vector collections +// .returnMetadata(Metadata.DISTANCE), +// g -> g +// .singlePrompt("Translate this into German: {review_body}") +// .groupedTask("Summarize these reviews") +// // highlight-end +// ); + +// for (var o : response.objects()) { +// System.out.printf("Properties: %s\n", o.properties()); +// System.out.printf("Single prompt result: %s\n", o.generative().text()); +// } +// System.out.printf("Grouped task result: %s\n", response.generative().text()); +// // END NamedVectorNearTextPython +// } + +// @Test +// void testSingleGenerative() { +// // START SingleGenerativePython +// // highlight-start +// String prompt = "Convert the following into a question for twitter. Include emojis for fun, but do not include the answer: {question}."; +// // highlight-end + +// CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); +// // highlight-start +// var response = jeopardy.generate.nearText( +// // highlight-end +// q -> q +// .query("World history") +// .limit(2), +// // highlight-start +// g -> g.singlePrompt(prompt) +// // highlight-end +// ); + +// for (var o : response.objects()) { +// System.out.printf("Property 'question': %s\n", o.properties().get("question")); +// // highlight-start +// System.out.printf("Single prompt result: %s\n", o.generative().text()); +// // highlight-end +// } +// // END SingleGenerativePython +// } + +// @Test +// void testSingleGenerativeProperties() { +// // START SingleGenerativePropertiesPython +// // highlight-start +// String prompt = "Convert this quiz question: {question} and answer: {answer} into a trivia tweet."; +// // highlight-end + +// CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); +// var response = jeopardy.generate.nearText( +// q -> q +// .query("World history") +// .limit(2), +// g -> g.singlePrompt(prompt)); + +// // print source properties and generated responses +// for (var o : response.objects()) { +// System.out.printf("Properties: %s\n", o.properties()); +// System.out.printf("Single prompt result: %s\n", o.generative().text()); +// } +// // END SingleGenerativePropertiesPython +// } + +// @Test +// void testSingleGenerativeParameters() { +// // START SingleGenerativeParametersPython +// // highlight-start +// var prompt = SinglePrompt.builder() +// .prompt("Convert this quiz question: {question} and answer: {answer} into a trivia tweet.") +// .metadata(true) +// .debug(true) +// .build(); +// // highlight-end + +// CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); +// var response = jeopardy.generate.nearText( +// q -> q +// .query("World history") +// .limit(2), +// // highlight-start +// g -> g +// .singlePrompt(prompt) +// // highlight-end +// .provider(GenerativeProvider.openAI())); + +// // print source properties and generated responses +// for (var o : response.objects()) { +// System.out.printf("Properties: %s\n", o.properties()); +// System.out.printf("Single prompt result: %s\n", o.generative().text()); +// System.out.printf("Debug: %s\n", o.generative().debug()); +// System.out.printf("Metadata: %s\n", o.generative().metadata()); +// } +// // END SingleGenerativeParametersPython +// } + +// @Test +// void testGroupedGenerative() { +// // START GroupedGenerativePython +// // highlight-start +// String task = "What do these animals have in common, if anything?"; +// // highlight-end + +// CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); +// var response = jeopardy.generate.nearText( +// q -> q +// .query("Cute animals") +// .limit(3), +// // highlight-start +// g -> g.groupedTask(task) +// // highlight-end +// ); + +// // print the generated response +// System.out.printf("Grouped task result: %s\n", response.generative().text()); +// // END GroupedGenerativePython +// } + +// @Test +// void testGroupedGenerativeParameters() { +// // START GroupedGenerativeParametersPython +// // highlight-start +// var groupedTask = GroupedTask.builder() +// .prompt("What do these animals have in common, if anything?") +// .metadata(true) +// .build(); +// // highlight-end + +// CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); +// var response = jeopardy.generate.nearText( +// q -> q +// .query("Cute animals") +// .limit(3), +// // highlight-start +// g -> g +// .groupedTask(groupedTask) +// // highlight-end +// .provider(GenerativeProvider.openAI())); + +// // print the generated response +// System.out.printf("Grouped task result: %s\n", response.generative().text()); +// System.out.printf("Metadata: %s\n", response.generative().metadata()); +// // END GroupedGenerativeParametersPython +// } + +// @Test +// void testGroupedGenerativeProperties() { +// // START GroupedGenerativeProperties Python +// String task = "What do these animals have in common, if anything?"; + +// CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); +// var response = jeopardy.generate.nearText( +// q -> q +// .query("Australian animals") +// .limit(3), +// g -> g +// .groupedTask(task) +// // highlight-start +// .groupedProperties("answer", "question") +// // highlight-end +// ); + +// // print the generated response +// // highlight-start +// for (var o : response.objects()) { +// System.out.printf("Properties: %s\n", o.properties()); +// } +// System.out.printf("Grouped task result: %s\n", response.generative().text()); +// // highlight-end +// // END GroupedGenerativeProperties Python +// } + +// @Test +// void testWorkingWithImages() throws IOException, InterruptedException { +// // START WorkingWithImages +// String srcImgPath = "https://images.unsplash.com/photo-1459262838948-3e2de6c1ec80?w=500&h=500&fit=crop"; +// HttpClient httpClient = HttpClient.newHttpClient(); +// HttpRequest request = HttpRequest.newBuilder().uri(URI.create(srcImgPath)).build(); +// HttpResponse imageResponse = httpClient.send(request, HttpResponse.BodyHandlers.ofByteArray()); +// String base64Image = Base64.getEncoder().encodeToString(imageResponse.body()); + +// var prompt = GroupedTask.builder() +// // highlight-start +// .prompt("Formulate a Jeopardy!-style question about this image") +// .images(List.of(base64Image)) // A list of base64 encoded strings of the image bytes +// // .imageProperties("img") // Properties containing images in Weaviate +// // highlight-end +// .build(); + +// CollectionHandle> jeopardy = client.collections.use("JeopardyQuestion"); +// var response = jeopardy.generate.nearText( +// q -> q +// .query("Australian animals") +// .limit(3) +// .groupedProperties("answer", "question"), +// // highlight-start +// g -> g +// .groupedTask(prompt) +// // highlight-end +// .provider(GenerativeProvider.anthropic(p -> p.maxTokens(1000)))); + +// // Print the source property and the generated response +// for (var o : response.objects()) { +// System.out.printf("Properties: %s\n", o.properties()); +// } +// System.out.printf("Grouped task result: %s\n", response.generative().text()); +// // END WorkingWithImages +// } +// } \ No newline at end of file diff --git a/_includes/code/java-v6/src/test/java/_SearchMultiTargetTest.java b/_includes/code/java-v6/src/test/java/_SearchMultiTargetTest.java new file mode 100644 index 00000000..4ee643cd --- /dev/null +++ b/_includes/code/java-v6/src/test/java/_SearchMultiTargetTest.java @@ -0,0 +1,276 @@ +// import com.fasterxml.jackson.core.type.TypeReference; +// import com.fasterxml.jackson.databind.ObjectMapper; +// import io.weaviate.client6.v1.api.WeaviateClient; +// import io.weaviate.client6.v1.api.collections.CollectionHandle; +// import io.weaviate.client6.v1.api.collections.Property; +// import io.weaviate.client6.v1.api.collections.VectorConfig; +// import io.weaviate.client6.v1.api.collections.query.Metadata; +// import io.weaviate.client6.v1.api.collections.query.TargetVectors; +// import org.junit.jupiter.api.AfterAll; +// import org.junit.jupiter.api.BeforeAll; +// import org.junit.jupiter.api.Test; + +// import java.io.IOException; +// import java.net.URI; +// import java.net.http.HttpClient; +// import java.net.http.HttpRequest; +// import java.net.http.HttpResponse; +// import java.util.ArrayList; +// import java.util.HashMap; +// import java.util.List; +// import java.util.Map; + +// class MultiTargetSearchTest { + +// private static WeaviateClient client; + +// @BeforeAll +// public static void beforeAll() throws IOException, InterruptedException { +// // START LoadDataNamedVectors +// String openaiApiKey = System.getenv("OPENAI_APIKEY"); +// client = WeaviateClient.connectToLocal( +// config -> config.setHeaders(Map.of("X-OpenAI-Api-Key", openaiApiKey))); + +// // Start with a new collection +// // CAUTION: The next line deletes the collection if it exists +// if (client.collections.exists("JeopardyTiny")) { +// client.collections.delete("JeopardyTiny"); +// } + +// // Define a new schema +// client.collections.create( +// "JeopardyTiny", +// col -> col +// .description("Jeopardy game show questions") +// .vectorConfig( +// VectorConfig.text2VecWeaviate("jeopardy_questions_vector", +// vc -> vc.sourceProperties("question")), +// VectorConfig.text2VecWeaviate("jeopardy_answers_vector", +// vc -> vc.sourceProperties("answer"))) +// .properties( +// Property.text("category"), +// Property.text("question"), +// Property.text("answer"))); + +// // Get the sample data set +// HttpClient httpClient = HttpClient.newHttpClient(); +// HttpRequest request = HttpRequest.newBuilder() +// .uri(URI.create("https://raw.githubusercontent.com/weaviate-tutorials/quickstart/main/data/jeopardy_tiny.json")) +// .build(); +// HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); +// String responseBody = response.body(); + +// ObjectMapper objectMapper = new ObjectMapper(); +// List> data = objectMapper.readValue(responseBody, new TypeReference<>() { +// }); + +// // Prepare the sample data for upload +// List> questionObjects = new ArrayList<>(); +// for (Map row : data) { +// Map questionObject = new HashMap<>(); +// questionObject.put("question", row.get("Question")); +// questionObject.put("answer", row.get("Answer")); +// questionObject.put("category", row.get("Category")); +// questionObjects.add(questionObject); +// } + +// // Upload the sample data +// CollectionHandle> nvjcCollection = client.collections.use("JeopardyTiny"); +// nvjcCollection.batch.withFixedSize(200, batch -> { +// for (Map q : questionObjects) { +// batch.addObject(q); +// } +// }); +// // END LoadDataNamedVectors +// } + +// @AfterAll +// public static void afterAll() throws Exception { +// if (client.collections.exists("JeopardyTiny")) { +// client.collections.delete("JeopardyTiny"); +// } +// client.close(); +// } + +// @Test +// void testMultiBasic() { +// // START MultiBasic +// CollectionHandle> collection = client.collections.use("JeopardyTiny"); + +// var response = collection.query.nearText( +// "a wild animal", +// q -> q +// .limit(2) +// // highlight-start +// // .targetVectors("jeopardy_questions_vector", "jeopardy_answers_vector") // Specify the target vectors +// // highlight-end +// .returnMetadata(Metadata.DISTANCE)); + +// for (var o : response.objects()) { +// System.out.println(o.properties()); +// System.out.println(o.metadata().distance()); +// } +// // END MultiBasic +// } + +// @Test +// void testMultiTargetNearVector() { +// CollectionHandle> collection = client.collections.use("JeopardyTiny"); +// var someResult = collection.query.fetchObjects(q -> q.limit(2).returnMetadata(Metadata.VECTOR)); +// if (someResult.objects().size() < 2) +// return; + +// var v1 = someResult.objects().get(0).metadata().vectors().get("jeopardy_questions_vector"); +// var v2 = someResult.objects().get(1).metadata().vectors().get("jeopardy_answers_vector"); + +// // START MultiTargetNearVector +// var response = collection.query.nearVector( +// // highlight-start +// // Specify the query vectors for each target vector +// Map.of( +// "jeopardy_questions_vector", v1, +// "jeopardy_answers_vector", v2), +// // highlight-end +// q -> q +// .limit(2) +// // .targetVectors("jeopardy_questions_vector", "jeopardy_answers_vector") // Specify the target vectors +// .returnMetadata(Metadata.DISTANCE)); + +// for (var o : response.objects()) { +// System.out.println(o.properties()); +// System.out.println(o.metadata().distance()); +// } +// // END MultiTargetNearVector +// } + +// @Test +// void testMultiTargetMultipleNearVectors() { +// CollectionHandle> collection = client.collections.use("JeopardyTiny"); +// var someResult = collection.query.fetchObjects(q -> q.limit(3).returnMetadata(Metadata.VECTOR)); +// if (someResult.objects().size() < 3) +// return; + +// var v1 = someResult.objects().get(0).metadata().vectors().get("jeopardy_questions_vector"); +// var v2 = someResult.objects().get(1).metadata().vectors().get("jeopardy_answers_vector"); +// var v3 = someResult.objects().get(2).metadata().vectors().get("jeopardy_answers_vector"); + +// // START MultiTargetMultipleNearVectorsV1 +// Map nearVectorV1 = new HashMap<>(); +// nearVectorV1.put("jeopardy_questions_vector", v1); +// nearVectorV1.put("jeopardy_answers_vector", List.of(v2, v3)); + +// var responseV1 = collection.query.nearVector( +// // highlight-start +// // Specify the query vectors for each target vector +// nearVectorV1, +// // highlight-end +// q -> q +// .limit(2) +// // Specify the target vectors as a list +// // .targetVectors("jeopardy_questions_vector", "jeopardy_answers_vector") +// .returnMetadata(Metadata.DISTANCE)); + +// for (var o : responseV1.objects()) { +// System.out.println(o.properties()); +// System.out.println(o.metadata().distance()); +// } +// // END MultiTargetMultipleNearVectorsV1 + +// // START MultiTargetMultipleNearVectorsV2 +// Map nearVectorV2 = new HashMap<>(); +// nearVectorV2.put("jeopardy_questions_vector", v1); +// nearVectorV2.put("jeopardy_answers_vector", List.of(v2, v3)); + +// var responseV2 = collection.query.nearVector( +// // highlight-start +// // Specify the query vectors for each target vector +// nearVectorV2, +// // highlight-end +// q -> q +// .limit(2) +// // Specify the target vectors and weights +// .targetVectors(TargetVectors.manualWeights(Map.of( +// "jeopardy_questions_vector", 10, +// "jeopardy_answers_vector", List.of(30, 30) // Matches the order of the vectors above +// ))) +// .returnMetadata(Metadata.DISTANCE)); + +// for (var o : responseV2.objects()) { +// System.out.println(o.properties()); +// System.out.println(o.metadata().distance()); +// } +// // END MultiTargetMultipleNearVectorsV2 +// } + +// @Test +// void testMultiTargetWithSimpleJoin() { +// // START MultiTargetWithSimpleJoin +// CollectionHandle> collection = client.collections.use("JeopardyTiny"); + +// var response = collection.query.nearText( +// "a wild animal", +// q -> q +// .limit(2) +// // highlight-start +// .targetVectors(TargetVectors.average("jeopardy_questions_vector", "jeopardy_answers_vector")) // Specify the +// // target +// // vectors and +// // the join +// // strategy +// // .sum(), .minimum(), .manualWeights(), .relativeScore() also available +// // highlight-end +// .returnMetadata(Metadata.DISTANCE)); + +// for (var o : response.objects()) { +// System.out.println(o.properties()); +// System.out.println(o.metadata().distance()); +// } +// // END MultiTargetWithSimpleJoin +// } + +// @Test +// void testMultiTargetManualWeights() { +// // START MultiTargetManualWeights +// CollectionHandle> collection = client.collections.use("JeopardyTiny"); + +// var response = collection.query.nearText( +// "a wild animal", +// q -> q +// .limit(2) +// // highlight-start +// .targetVectors(TargetVectors.manualWeights(Map.of( +// "jeopardy_questions_vector", 10, +// "jeopardy_answers_vector", 50))) +// // highlight-end +// .returnMetadata(Metadata.DISTANCE)); + +// for (var o : response.objects()) { +// System.out.println(o.properties()); +// System.out.println(o.metadata().distance()); +// } +// // END MultiTargetManualWeights +// } + +// @Test +// void testMultiTargetRelativeScore() { +// // START MultiTargetRelativeScore +// CollectionHandle> collection = client.collections.use("JeopardyTiny"); + +// var response = collection.query.nearText( +// "a wild animal", +// q -> q +// .limit(2) +// // highlight-start +// .targetVectors(TargetVectors.relativeScore(Map.of( +// "jeopardy_questions_vector", 10, +// "jeopardy_answers_vector", 10))) +// // highlight-end +// .returnMetadata(Metadata.DISTANCE)); + +// for (var o : response.objects()) { +// System.out.println(o.properties()); +// System.out.println(o.metadata().distance()); +// } +// // END MultiTargetRelativeScore +// } +// } \ No newline at end of file diff --git a/_includes/code/quickstart.byov.schema.mdx b/_includes/code/quickstart.byov.schema.mdx index a6a00c84..aca68961 100644 --- a/_includes/code/quickstart.byov.schema.mdx +++ b/_includes/code/quickstart.byov.schema.mdx @@ -1,11 +1,13 @@ -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; -import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBlock'; -import ByovAllPyCode from '!!raw-loader!/_includes/code/quickstart.byov.all.py'; -import ByovAllTsCode from '!!raw-loader!/_includes/code/quickstart.byov.all.ts'; -import ByovAllShCode from '!!raw-loader!/_includes/code/quickstart.byov.all.sh'; +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; +import FilteredTextBlock from "@site/src/components/Documentation/FilteredTextBlock"; +import ByovAllPyCode from "!!raw-loader!/_includes/code/quickstart.byov.all.py"; +import ByovAllTsCode from "!!raw-loader!/_includes/code/quickstart.byov.all.ts"; +import JavaV6Code from "!!raw-loader!/_includes/code/java-v6/src/test/java/StarterGuidesCustomVectorsTest.java"; +import CSharpCode from "!!raw-loader!/_includes/code/csharp/StarterGuidesCustomVectorsTest.cs"; +import ByovAllShCode from "!!raw-loader!/_includes/code/quickstart.byov.all.sh"; @@ -17,12 +19,28 @@ import ByovAllShCode from '!!raw-loader!/_includes/code/quickstart.byov.all.sh'; /> - + + + + + + + -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; -import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBlock'; -import ByovAllPyCode from '!!raw-loader!/_includes/code/quickstart.byov.all.py'; -import ByovAllTsCode from '!!raw-loader!/_includes/code/quickstart.byov.all.ts'; - +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; +import FilteredTextBlock from "@site/src/components/Documentation/FilteredTextBlock"; +import ByovAllPyCode from "!!raw-loader!/_includes/code/quickstart.byov.all.py"; +import ByovAllTsCode from "!!raw-loader!/_includes/code/quickstart.byov.all.ts"; +import JavaV6Code from "!!raw-loader!/_includes/code/java-v6/src/test/java/StarterGuidesCustomVectorsTest.java"; +import CSharpCode from "!!raw-loader!/_includes/code/csharp/StarterGuidesCustomVectorsTest.cs"; @@ -17,12 +18,27 @@ import ByovAllTsCode from '!!raw-loader!/_includes/code/quickstart.byov.all.ts'; /> - - + + + + + + + diff --git a/_includes/code/quickstart/clients.install.mdx b/_includes/code/quickstart/clients.install.mdx index 77f5faac..cf7ef0de 100644 --- a/_includes/code/quickstart/clients.install.mdx +++ b/_includes/code/quickstart/clients.install.mdx @@ -1,12 +1,11 @@ -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; Install the latest, [Python client `v4`](/weaviate/client-libraries/python), by adding `weaviate-client` to your Python environment with `pip`:

- ```bash pip install -U weaviate-client ``` @@ -29,6 +28,19 @@ Add `weaviate-go-client` to your project with `go get`:

go get github.com/weaviate/weaviate-go-client/v5 ``` +
+ + +Add this dependency to your project:

+ +```xml + + io.weaviate + client6 + 6.0.0-M1 + +``` +
@@ -42,5 +54,14 @@ Add this dependency to your project:

``` +
+ + +Add this package to your project:

+ +```xml + +``` +
diff --git a/_includes/code/quickstart/connect.partial.mdx b/_includes/code/quickstart/connect.partial.mdx index b708d706..b196e3b2 100644 --- a/_includes/code/quickstart/connect.partial.mdx +++ b/_includes/code/quickstart/connect.partial.mdx @@ -1,61 +1,76 @@ -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; -import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBlock'; +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; +import FilteredTextBlock from "@site/src/components/Documentation/FilteredTextBlock"; - - - -```python -import weaviate, os -import weaviate.classes as wvc - -# Set these environment variables -URL = os.getenv("WEAVIATE_URL") -APIKEY = os.getenv("WEAVIATE_API_KEY") - -# Connect to Weaviate Cloud -client = weaviate.connect_to_weaviate_cloud( - cluster_url=URL, - auth_credentials=wvc.init.Auth.api_key(APIKEY), -) - -# Check connection -client.is_ready() -``` - - - - -```ts -import weaviate, { WeaviateClient } from 'weaviate-client' +import PyCodeV4 from "!!raw-loader!/_includes/code/connections/connect-python-v4.py"; +import TsCodeV3 from "!!raw-loader!/_includes/code/connections/connect-ts-v3.ts"; +import JavaV6Code from "!!raw-loader!/_includes/code/java-v6/src/test/java/ConnectionTest.java"; +import CSharpCode from "!!raw-loader!/_includes/code/csharp/ConnectionTest.cs"; +import JavaCode from "!!raw-loader!/_includes/code/connections/connect.java"; +import ShellCode from "!!raw-loader!/_includes/code/connections/connect.sh"; +import GoCode from "!!raw-loader!/_includes/code/connections/connect.go"; -const weaviateURL = 'https://WEAVIATE_INSTANCE_URL' // Replace with your Weaviate endpoint -const weaviateKey = 'YOUR-WEAVIATE-API-KEY' // Replace with your Weaviate instance API key +To connect, use the `REST Endpoint` and the `Admin` API key stored as environment variables: -const client: WeaviateClient = await weaviate.connectToWeaviateCloud(weaviateURL, { - authCredentials: new weaviate.ApiKey(weaviateKey), - } -) +import HostnameWarning from "/_includes/wcs/hostname-warning.mdx"; -``` - - - - - -For `curl`, add the API key to the header as shown below: -
- -```bash -echo '{ - "query": "" -}' | curl \ - -X POST \ - -H "Content-Type: application/json" \ - -H "Authorization: Bearer YOUR-WEAVIATE-API-KEY" \ - -d @- \ - https://WEAVIATE_INSTANCE_URL/v1/graphql # Replace WEAVIATE_INSTANCE_URL with your instance URL -``` - -
+ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/_includes/code/quickstart/local.quickstart.create_collection.mdx b/_includes/code/quickstart/local.quickstart.create_collection.mdx index f6c12936..f1afa72b 100644 --- a/_includes/code/quickstart/local.quickstart.create_collection.mdx +++ b/_includes/code/quickstart/local.quickstart.create_collection.mdx @@ -4,6 +4,8 @@ import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBl import PyCode from '!!raw-loader!/_includes/code/python/local.quickstart.create_collection.py'; import TSCode from '!!raw-loader!/_includes/code/typescript/local.quickstart.create_collection.ts'; import GoCode from '!!raw-loader!/_includes/code/howto/go/docs/quickstart_local/2_1_create_collection/main.go'; +import JavaV6Code from "!!raw-loader!/_includes/code/java-v6/src/test/java/QuickstartLocalTest.java"; +import CSharpCode from "!!raw-loader!/_includes/code/csharp/QuickstartLocalTest.cs"; import JavaCode from '!!raw-loader!/_includes/code/howto/java/src/test/java/io/weaviate/docs/quickstart_local/CreateCollection.java'; import VectorsAutoSchemaError from "/_includes/error-note-vectors-autoschema.mdx"; @@ -48,6 +50,15 @@ The collection also contains a configuration for the generative (RAG) integratio />
+ + + + The collection also contains a configuration for the generative (RAG) integration: @@ -62,6 +73,14 @@ The collection also contains a configuration for the generative (RAG) integratio /> + + + diff --git a/_includes/code/quickstart/local.quickstart.import_objects.mdx b/_includes/code/quickstart/local.quickstart.import_objects.mdx index b5218b55..3e016144 100644 --- a/_includes/code/quickstart/local.quickstart.import_objects.mdx +++ b/_includes/code/quickstart/local.quickstart.import_objects.mdx @@ -4,6 +4,8 @@ import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBl import PyCode from '!!raw-loader!/_includes/code/python/local.quickstart.import_objects.py'; import TSCode from '!!raw-loader!/_includes/code/typescript/local.quickstart.import_objects.ts'; import GoCode from '!!raw-loader!/_includes/code/howto/go/docs/quickstart_local/2_2_add_objects/main.go'; +import JavaV6Code from "!!raw-loader!/_includes/code/java-v6/src/test/java/QuickstartLocalTest.java"; +import CSharpCode from "!!raw-loader!/_includes/code/csharp/QuickstartLocalTest.cs"; import JavaCode from '!!raw-loader!/_includes/code/howto/java/src/test/java/io/weaviate/docs/quickstart_local/Import.java'; @@ -44,6 +46,15 @@ During a batch import, any failed objects can be obtained through `batch.failed_ /> + + + + + + + diff --git a/_includes/code/quickstart/local.quickstart.is_ready.mdx b/_includes/code/quickstart/local.quickstart.is_ready.mdx index c7959cfd..5ddd8857 100644 --- a/_includes/code/quickstart/local.quickstart.is_ready.mdx +++ b/_includes/code/quickstart/local.quickstart.is_ready.mdx @@ -4,6 +4,8 @@ import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBl import PyCode from '!!raw-loader!/_includes/code/python/local.quickstart.is_ready.py'; import TSCode from '!!raw-loader!/_includes/code/typescript/local.quickstart.is_ready.ts'; import GoCode from '!!raw-loader!/_includes/code/howto/go/docs/quickstart_local/1_is_ready/main.go'; +import JavaV6Code from "!!raw-loader!/_includes/code/java-v6/src/test/java/QuickstartLocalTest.java"; +import CSharpCode from "!!raw-loader!/_includes/code/csharp/QuickstartLocalTest.cs"; import JavaCode from '!!raw-loader!/_includes/code/howto/java/src/test/java/io/weaviate/docs/quickstart_local/IsReady.java'; @@ -42,6 +44,15 @@ import JavaCode from '!!raw-loader!/_includes/code/howto/java/src/test/java/io/w /> + + + + + + + diff --git a/_includes/code/quickstart/local.quickstart.query.neartext.mdx b/_includes/code/quickstart/local.quickstart.query.neartext.mdx index 0053fef1..ba02d545 100644 --- a/_includes/code/quickstart/local.quickstart.query.neartext.mdx +++ b/_includes/code/quickstart/local.quickstart.query.neartext.mdx @@ -4,6 +4,8 @@ import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBl import PyCode from '!!raw-loader!/_includes/code/python/local.quickstart.query.neartext.py'; import TSCode from '!!raw-loader!/_includes/code/typescript/local.quickstart.query.neartext.ts'; import GoCode from '!!raw-loader!/_includes/code/howto/go/docs/quickstart_local/3_1_neartext/main.go'; +import JavaV6Code from "!!raw-loader!/_includes/code/java-v6/src/test/java/QuickstartLocalTest.java"; +import CSharpCode from "!!raw-loader!/_includes/code/csharp/QuickstartLocalTest.cs"; import JavaCode from '!!raw-loader!/_includes/code/howto/java/src/test/java/io/weaviate/docs/quickstart_local/NearText.java'; @@ -42,6 +44,15 @@ import JavaCode from '!!raw-loader!/_includes/code/howto/java/src/test/java/io/w /> + + + + + + + diff --git a/_includes/code/quickstart/local.quickstart.query.rag.mdx b/_includes/code/quickstart/local.quickstart.query.rag.mdx index 35ddc6e6..ca9fd530 100644 --- a/_includes/code/quickstart/local.quickstart.query.rag.mdx +++ b/_includes/code/quickstart/local.quickstart.query.rag.mdx @@ -4,6 +4,8 @@ import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBl import PyCode from '!!raw-loader!/_includes/code/python/local.quickstart.query.rag.py'; import TSCode from '!!raw-loader!/_includes/code/typescript/local.quickstart.query.rag.ts'; import GoCode from '!!raw-loader!/_includes/code/howto/go/docs/quickstart_local/3_2_rag/main.go'; +import JavaV6Code from "!!raw-loader!/_includes/code/java-v6/src/test/java/QuickstartLocalTest.java"; +import CSharpCode from "!!raw-loader!/_includes/code/csharp/QuickstartLocalTest.cs"; import JavaCode from '!!raw-loader!/_includes/code/howto/java/src/test/java/io/weaviate/docs/quickstart_local/RAG.java'; @@ -46,6 +48,15 @@ We are using the Ollama [generative AI integrations](/weaviate/model-providers/o /> + + + + + + + diff --git a/_includes/code/quickstart/quickstart.create_collection.mdx b/_includes/code/quickstart/quickstart.create_collection.mdx index 5e883c45..c7400ee5 100644 --- a/_includes/code/quickstart/quickstart.create_collection.mdx +++ b/_includes/code/quickstart/quickstart.create_collection.mdx @@ -4,6 +4,8 @@ import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBl import PyCode from '!!raw-loader!/_includes/code/python/quickstart.create_collection.py'; import TSCode from '!!raw-loader!/_includes/code/typescript/quickstart.create_collection.ts'; import GoCode from '!!raw-loader!/_includes/code/howto/go/docs/quickstart/2_1_create_collection/main.go'; +import JavaV6Code from "!!raw-loader!/_includes/code/java-v6/src/test/java/QuickstartTest.java"; +import CSharpCode from "!!raw-loader!/_includes/code/csharp/QuickstartTest.cs"; import JavaCode from '!!raw-loader!/_includes/code/howto/java/src/test/java/io/weaviate/docs/quickstart/CreateCollection.java'; import VectorConfigSyntax from "/_includes/vector-config-syntax.mdx"; import VectorsAutoSchemaError from "/_includes/error-note-vectors-autoschema.mdx"; @@ -51,6 +53,15 @@ The collection also contains a configuration for the generative (RAG) integratio /> + + + + The collection also contains a configuration for the generative (RAG) integration: @@ -65,6 +76,14 @@ The collection also contains a configuration for the generative (RAG) integratio /> + + + diff --git a/_includes/code/quickstart/quickstart.import_objects.mdx b/_includes/code/quickstart/quickstart.import_objects.mdx index 601ebad1..b74e0c1a 100644 --- a/_includes/code/quickstart/quickstart.import_objects.mdx +++ b/_includes/code/quickstart/quickstart.import_objects.mdx @@ -4,6 +4,8 @@ import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBl import PyCode from '!!raw-loader!/_includes/code/python/quickstart.import_objects.py'; import TSCode from '!!raw-loader!/_includes/code/typescript/quickstart.import_objects.ts'; import GoCode from '!!raw-loader!/_includes/code/howto/go/docs/quickstart/2_2_add_objects/main.go'; +import JavaV6Code from "!!raw-loader!/_includes/code/java-v6/src/test/java/QuickstartTest.java"; +import CSharpCode from "!!raw-loader!/_includes/code/csharp/QuickstartTest.cs"; import JavaCode from '!!raw-loader!/_includes/code/howto/java/src/test/java/io/weaviate/docs/quickstart/Import.java'; @@ -45,6 +47,15 @@ During a batch import, any failed objects can be obtained through `batch.failed_ /> + + + + + + + diff --git a/_includes/code/quickstart/quickstart.is_ready.mdx b/_includes/code/quickstart/quickstart.is_ready.mdx index 6f095ff0..af72f1d7 100644 --- a/_includes/code/quickstart/quickstart.is_ready.mdx +++ b/_includes/code/quickstart/quickstart.is_ready.mdx @@ -4,6 +4,8 @@ import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBl import PyCode from '!!raw-loader!/_includes/code/python/quickstart.is_ready.py'; import TSCode from '!!raw-loader!/_includes/code/typescript/quickstart.is_ready.ts'; import GoCode from '!!raw-loader!/_includes/code/howto/go/docs/quickstart/1_is_ready/main.go'; +import JavaV6Code from "!!raw-loader!/_includes/code/java-v6/src/test/java/QuickstartTest.java"; +import CSharpCode from "!!raw-loader!/_includes/code/csharp/QuickstartTest.cs"; import JavaCode from '!!raw-loader!/_includes/code/howto/java/src/test/java/io/weaviate/docs/quickstart/IsReady.java'; import HostnameWarning from '/_includes/wcs/hostname-warning.mdx'; @@ -43,6 +45,15 @@ import HostnameWarning from '/_includes/wcs/hostname-warning.mdx'; + + + + + + + diff --git a/_includes/code/quickstart/quickstart.query.neartext.mdx b/_includes/code/quickstart/quickstart.query.neartext.mdx index 547434b5..89b7f834 100644 --- a/_includes/code/quickstart/quickstart.query.neartext.mdx +++ b/_includes/code/quickstart/quickstart.query.neartext.mdx @@ -4,6 +4,8 @@ import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBl import PyCode from '!!raw-loader!/_includes/code/python/quickstart.query.neartext.py'; import TSCode from '!!raw-loader!/_includes/code/typescript/quickstart.query.neartext.ts'; import GoCode from '!!raw-loader!/_includes/code/howto/go/docs/quickstart/3_1_neartext/main.go'; +import JavaV6Code from "!!raw-loader!/_includes/code/java-v6/src/test/java/QuickstartTest.java"; +import CSharpCode from "!!raw-loader!/_includes/code/csharp/QuickstartTest.cs"; import JavaCode from '!!raw-loader!/_includes/code/howto/java/src/test/java/io/weaviate/docs/quickstart/NearText.java'; @@ -42,6 +44,15 @@ import JavaCode from '!!raw-loader!/_includes/code/howto/java/src/test/java/io/w /> + + + + + + + diff --git a/_includes/code/quickstart/quickstart.query.rag.mdx b/_includes/code/quickstart/quickstart.query.rag.mdx index 45b0f6f7..3a2127d4 100644 --- a/_includes/code/quickstart/quickstart.query.rag.mdx +++ b/_includes/code/quickstart/quickstart.query.rag.mdx @@ -4,6 +4,8 @@ import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBl import PyCode from '!!raw-loader!/_includes/code/python/quickstart.query.rag.py'; import TSCode from '!!raw-loader!/_includes/code/typescript/quickstart.query.rag.ts'; import GoCode from '!!raw-loader!/_includes/code/howto/go/docs/quickstart/3_2_rag/main.go'; +import JavaV6Code from "!!raw-loader!/_includes/code/java-v6/src/test/java/QuickstartTest.java"; +import CSharpCode from "!!raw-loader!/_includes/code/csharp/QuickstartTest.cs"; import JavaCode from '!!raw-loader!/_includes/code/howto/java/src/test/java/io/weaviate/docs/quickstart/RAG.java'; @@ -46,6 +48,15 @@ We are using the OpenAI [generative AI integrations](/weaviate/model-providers/o /> + + + + + + + diff --git a/_includes/code/replication.get.object.by.id.mdx b/_includes/code/replication.get.object.by.id.mdx index f8efeced..be009062 100644 --- a/_includes/code/replication.get.object.by.id.mdx +++ b/_includes/code/replication.get.object.by.id.mdx @@ -1,8 +1,10 @@ import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; +import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBlock'; import PyCode from '!!raw-loader!/_includes/code/howto/search.consistency.py'; import TSCode from '!!raw-loader!/_includes/code/howto/search.consistency.ts'; -import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBlock'; +import JavaV6Code from "!!raw-loader!/\_includes/code/java-v6/src/test/java/SearchBasicTest.java"; +import CSharpCode from "!!raw-loader!/\_includes/code/csharp/SearchBasicTest.cs"; @@ -68,6 +70,14 @@ func main() { ``` + + + ```java @@ -110,6 +120,14 @@ public class App { ``` + + + ```bash diff --git a/_includes/code/schema.things.properties.add.mdx b/_includes/code/schema.things.properties.add.mdx index 348b8e1d..c8a4317a 100644 --- a/_includes/code/schema.things.properties.add.mdx +++ b/_includes/code/schema.things.properties.add.mdx @@ -1,9 +1,11 @@ -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; -import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBlock'; +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; +import FilteredTextBlock from "@site/src/components/Documentation/FilteredTextBlock"; -import PyCode from '!!raw-loader!/_includes/code/howto/manage-data.collections.py'; -import JavaCode from '!!raw-loader!/_includes/code/howto/java/src/test/java/io/weaviate/docs/manage-data.classes.java'; +import PyCode from "!!raw-loader!/_includes/code/howto/manage-data.collections.py"; +import JavaCode from "!!raw-loader!/_includes/code/howto/java/src/test/java/io/weaviate/docs/manage-data.classes.java"; +import JavaV6Code from "!!raw-loader!/_includes/code/java-v6/src/test/java/ManageCollectionsTest.java"; +import CSharpCode from "!!raw-loader!/_includes/code/csharp/ManageCollectionsTest.cs"; @@ -22,9 +24,9 @@ let articles = client.collections.use('Article') // highlight-start articles.config.addProperty({ - name: 'onHomepage', - dataType: 'boolean' -}) + name: "onHomepage", + dataType: "boolean", +}); // highlight-end ``` @@ -67,9 +69,16 @@ func main() { } ``` - - - + + + + + - + + + diff --git a/_includes/code/tutorial.schema.create.mdx b/_includes/code/tutorial.schema.create.mdx index e0a0ec7c..26e1c36f 100644 --- a/_includes/code/tutorial.schema.create.mdx +++ b/_includes/code/tutorial.schema.create.mdx @@ -1,7 +1,9 @@ -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; -import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBlock'; -import PyCode from '!!raw-loader!/_includes/code/starter-guides/schema.py'; +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; +import FilteredTextBlock from "@site/src/components/Documentation/FilteredTextBlock"; +import PyCode from "!!raw-loader!/_includes/code/starter-guides/schema.py"; +import JavaV6Code from "!!raw-loader!/_includes/code/java-v6/src/test/java/StarterGuidesCollectionsTest.java"; +import CSharpCode from "!!raw-loader!/_includes/code/csharp/StarterGuidesCollectionsTest.cs"; @@ -14,6 +16,22 @@ import PyCode from '!!raw-loader!/_includes/code/starter-guides/schema.py'; /> + + + + + + {/* ```go diff --git a/_includes/code/tutorial.schema.index-settings.mdx b/_includes/code/tutorial.schema.index-settings.mdx index bbf2a435..07bef9f4 100644 --- a/_includes/code/tutorial.schema.index-settings.mdx +++ b/_includes/code/tutorial.schema.index-settings.mdx @@ -2,6 +2,8 @@ import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBlock'; import PyCode from '!!raw-loader!/_includes/code/starter-guides/schema.py'; +import JavaV6Code from "!!raw-loader!/_includes/code/java-v6/src/test/java/StarterGuidesCollectionsTest.java"; +import CSharpCode from "!!raw-loader!/_includes/code/csharp/StarterGuidesCollectionsTest.cs"; @@ -14,7 +16,22 @@ import PyCode from '!!raw-loader!/_includes/code/starter-guides/schema.py'; /> - + + + + + + ```js diff --git a/_includes/code/tutorial.schema.multi-tenancy.mdx b/_includes/code/tutorial.schema.multi-tenancy.mdx index 916b46d2..d0d58d45 100644 --- a/_includes/code/tutorial.schema.multi-tenancy.mdx +++ b/_includes/code/tutorial.schema.multi-tenancy.mdx @@ -2,6 +2,8 @@ import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBlock'; import PyCode from '!!raw-loader!/_includes/code/starter-guides/schema.py'; +import JavaV6Code from "!!raw-loader!/_includes/code/java-v6/src/test/java/StarterGuidesCollectionsTest.java"; +import CSharpCode from "!!raw-loader!/_includes/code/csharp/StarterGuidesCollectionsTest.cs"; @@ -14,7 +16,22 @@ import PyCode from '!!raw-loader!/_includes/code/starter-guides/schema.py'; /> - + + + + + + ```js diff --git a/_includes/code/tutorial.schema.properties.options.mdx b/_includes/code/tutorial.schema.properties.options.mdx index ca68fab3..6cf0073c 100644 --- a/_includes/code/tutorial.schema.properties.options.mdx +++ b/_includes/code/tutorial.schema.properties.options.mdx @@ -2,6 +2,8 @@ import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBlock'; import PyCode from '!!raw-loader!/_includes/code/starter-guides/schema.py'; +import JavaV6Code from "!!raw-loader!/_includes/code/java-v6/src/test/java/StarterGuidesCollectionsTest.java"; +import CSharpCode from "!!raw-loader!/_includes/code/csharp/StarterGuidesCollectionsTest.cs"; @@ -14,7 +16,22 @@ import PyCode from '!!raw-loader!/_includes/code/starter-guides/schema.py'; /> - + + + + + + ```js diff --git a/_includes/range-filter-performance-note.mdx b/_includes/range-filter-performance-note.mdx index 704bf792..4bd41e49 100644 --- a/_includes/range-filter-performance-note.mdx +++ b/_includes/range-filter-performance-note.mdx @@ -4,4 +4,4 @@ If you are experiencing slow filter performance, you have several options: - Further restrict your query by adding more conditions to the `where` operator - Add a `limit` parameter to your query -- Configure `indexRangeFilters` for properties that require range-based filtering. You can [set inverted index parameters](/weaviate/manage-collections/collection-operations#set-inverted-index-parameters) when creating your collection. Learn more about [configuring the inverted index](/weaviate/concepts/indexing/inverted-index#configure-inverted-indexes) to optimize filter performance for your specific use case. +- Configure `indexRangeFilters` for properties that require range-based filtering. You can [set inverted index parameters](/weaviate/manage-collections/inverted-index#set-inverted-index-parameters) when creating your collection. Learn more about [configuring the inverted index](/weaviate/concepts/indexing/inverted-index#configure-inverted-indexes) to optimize filter performance for your specific use case. diff --git a/_includes/schema-delete-class.mdx b/_includes/schema-delete-class.mdx index db4dd315..ad367c9a 100644 --- a/_includes/schema-delete-class.mdx +++ b/_includes/schema-delete-class.mdx @@ -3,6 +3,8 @@ import TabItem from '@theme/TabItem'; import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBlock'; import ManageCollectionsCode from '!!raw-loader!/_includes/code/howto/manage-data.collections.py'; import JavaCode from '!!raw-loader!/_includes/code/howto/java/src/test/java/io/weaviate/docs/manage-data.classes.java'; +import JavaV6Code from '!!raw-loader!/_includes/code/java-v6/src/test/java/ManageCollectionsTest.java'; +import CSharpCode from "!!raw-loader!/_includes/code/csharp/ManageCollectionsTest.cs"; You can delete any unwanted collection(s), along with the data that they contain. @@ -70,6 +72,15 @@ if err := client.Schema().ClassDeleter().WithClassName(className).Do(context.Bac /> + + + + ```bash @@ -79,4 +90,12 @@ curl \ ``` + + + diff --git a/_includes/weaviate-embeddings-vectorizer-parameters.mdx b/_includes/weaviate-embeddings-vectorizer-parameters.mdx index 5b4e5fae..1ec7f355 100644 --- a/_includes/weaviate-embeddings-vectorizer-parameters.mdx +++ b/_includes/weaviate-embeddings-vectorizer-parameters.mdx @@ -1,13 +1,12 @@ - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; -import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBlock'; -import PyCode from '!!raw-loader!/docs/weaviate/model-providers/_includes/provider.vectorizer.py'; -import TSCode from '!!raw-loader!/docs/weaviate/model-providers/_includes/provider.vectorizer.ts'; -import GoCode from '!!raw-loader!/_includes/code/howto/go/docs/model-providers/2-usage-text/main.go'; -import JavaCode from '!!raw-loader!/_includes/code/howto/java/src/test/java/io/weaviate/docs/model_providers/UsageWeaviateTextEmbeddings.java'; -import JavaCode2 from '!!raw-loader!/_includes/code/howto/java/src/test/java/io/weaviate/docs/model_providers/UsageWeaviateTextEmbeddingsArcticEmbedLV20.java'; -import JavaImportQueries from '!!raw-loader!/_includes/code/howto/java/src/test/java/io/weaviate/docs/model_providers/ImportAndQueries.java'; +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; +import FilteredTextBlock from "@site/src/components/Documentation/FilteredTextBlock"; +import PyCode from "!!raw-loader!/docs/weaviate/model-providers/_includes/provider.vectorizer.py"; +import TSCode from "!!raw-loader!/docs/weaviate/model-providers/_includes/provider.vectorizer.ts"; +import GoCode from "!!raw-loader!/_includes/code/howto/go/docs/model-providers/2-usage-text/main.go"; +import JavaV6Code from "!!raw-loader!/_includes/code/java-v6/src/test/java/ModelProvidersTest.java"; +import CSharpCode from "!!raw-loader!/_includes/code/csharp/ModelProvidersTest.cs"; +import JavaCode from "!!raw-loader!/_includes/code/howto/java/src/test/java/io/weaviate/docs/model_providers/UsageWeaviateTextEmbeddings.java"; - `model` (optional): The name of the model to use for embedding generation. - `dimensions` (optional): The number of dimensions to use for the generated embeddings. @@ -24,28 +23,44 @@ The following examples show how to configure Weaviate Embeddings-specific option language="py" /> - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/docs.sln b/docs.sln new file mode 100644 index 00000000..c5ead1f3 --- /dev/null +++ b/docs.sln @@ -0,0 +1,32 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.2.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_includes", "_includes", "{DE45C9AA-DBB3-91F8-540C-03A41DB425DA}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "code", "code", "{9186D019-DB3D-BB6C-536B-D636275C82A4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WeaviateProject.Tests", "_includes\code\csharp\WeaviateProject.Tests.csproj", "{E1A0B893-B9C7-B0BB-27A1-C94B4D04BC73}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E1A0B893-B9C7-B0BB-27A1-C94B4D04BC73}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E1A0B893-B9C7-B0BB-27A1-C94B4D04BC73}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E1A0B893-B9C7-B0BB-27A1-C94B4D04BC73}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E1A0B893-B9C7-B0BB-27A1-C94B4D04BC73}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {9186D019-DB3D-BB6C-536B-D636275C82A4} = {DE45C9AA-DBB3-91F8-540C-03A41DB425DA} + {E1A0B893-B9C7-B0BB-27A1-C94B4D04BC73} = {9186D019-DB3D-BB6C-536B-D636275C82A4} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {E51A6D01-9E9C-4B44-BE15-9ECBB531729D} + EndGlobalSection +EndGlobal diff --git a/docs/cloud/manage-clusters/connect.mdx b/docs/cloud/manage-clusters/connect.mdx index dc32e165..b53fb641 100644 --- a/docs/cloud/manage-clusters/connect.mdx +++ b/docs/cloud/manage-clusters/connect.mdx @@ -198,7 +198,7 @@ To authenticate with a Weaviate client library, see the following: - [Python](/weaviate/client-libraries/python/index.mdx) - [TypeScript/JavaScript](../../weaviate/client-libraries/typescript/index.mdx) - [Go](/weaviate/client-libraries/go.md#authentication) -- [Java](/weaviate/client-libraries/java.md#authentication) +- [Java](/weaviate/client-libraries/java/index.mdx#authentication) ## Support & feedback diff --git a/docs/contributor-guide/weaviate-clients/index.md b/docs/contributor-guide/weaviate-clients/index.md index d72ee9d9..c405c164 100644 --- a/docs/contributor-guide/weaviate-clients/index.md +++ b/docs/contributor-guide/weaviate-clients/index.md @@ -8,7 +8,7 @@ There are currently four clients developed for Weaviate's APIs: - **[Python](/weaviate/client-libraries/python/index.mdx)** - **[TypeScript/JavaScript](../../weaviate/client-libraries/typescript/index.mdx)** -- **[Java](/weaviate/client-libraries/java.md)** +- **[Java](/weaviate/client-libraries/java/index.mdx)** - **[Go](/weaviate/client-libraries/go.md)** These clients, and all future clients are and will be developed according to the following guidelines: @@ -79,7 +79,7 @@ Then you can take a look at an existing client which matches your language the closest and get inspried. For example, if you plan to implement a client in C#, it might make sense to look at the -[Java](/weaviate/client-libraries/java.md) and +[Java](/weaviate/client-libraries/java/index.mdx) and [Go](/weaviate/client-libraries/go.md) clients. Then we recommend to start porting one of the existing test suites and start diff --git a/docs/weaviate/api/index.mdx b/docs/weaviate/api/index.mdx index bb522396..23b1b81d 100644 --- a/docs/weaviate/api/index.mdx +++ b/docs/weaviate/api/index.mdx @@ -26,7 +26,7 @@ Weaviate offers official client libraries for these programming languages: - **[Python](../client-libraries/python/index.mdx)** - **[TypeScript/JavaScript](../client-libraries/typescript/index.mdx)** - **[Go](../client-libraries/go.md)** -- **[Java](../client-libraries/java.md)** +- **[Java](../client-libraries/java/index.mdx)** :::tip **TIP:** Using the official client libraries for interacting with Weaviate diff --git a/docs/weaviate/benchmarks/ann.md b/docs/weaviate/benchmarks/ann.md index cc9e6273..457adc95 100644 --- a/docs/weaviate/benchmarks/ann.md +++ b/docs/weaviate/benchmarks/ann.md @@ -269,7 +269,7 @@ Each language has its own performance characteristics. You may get different results if you use a different language to send your queries. For maximum throughput, we recommend using the [Go](/weaviate/client-libraries/go.md) or -[Java](/weaviate/client-libraries/java.md) client libraries. +[Java](/weaviate/client-libraries/java/index.mdx) client libraries. The complete import and test scripts are available [here](https://github.com/weaviate/weaviate-benchmarking). diff --git a/docs/weaviate/client-libraries/_components/client.auth.introduction.mdx b/docs/weaviate/client-libraries/_components/client.auth.introduction.mdx index 122b6218..c8cb3556 100644 --- a/docs/weaviate/client-libraries/_components/client.auth.introduction.mdx +++ b/docs/weaviate/client-libraries/_components/client.auth.introduction.mdx @@ -1,5 +1,5 @@ For more comprehensive information on configuring authentication with Weaviate, refer to the [authentication](/weaviate/configuration/authz-authn) page. -

The {props.clientName} client offers multiple options for authenticating against Weaviate, including multiple OIDC authentication flows.

+The {props.clientName} client offers multiple options for authenticating against Weaviate, including multiple OIDC authentication flows. The suitable authentication options and methods for the client largely depend on the specific configuration of the Weaviate instance. diff --git a/docs/weaviate/client-libraries/csharp.mdx b/docs/weaviate/client-libraries/csharp.mdx new file mode 100644 index 00000000..6117b355 --- /dev/null +++ b/docs/weaviate/client-libraries/csharp.mdx @@ -0,0 +1,133 @@ +--- +title: C# - Beta release +sidebar_label: C# 🚧 +description: "Official C# client library documentation for integrating Weaviate with .NET applications and services." +image: og/docs/client-libraries.jpg +# tags: ['c#', 'csharp', 'client library', 'experimental'] +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; +import FilteredTextBlock from "@site/src/components/Documentation/FilteredTextBlock"; +import CSharpCode from "!!raw-loader!/_includes/code/csharp/GetStartedTest.cs"; +import QuickLinks from "/src/components/QuickLinks"; + +:::caution Preview + +The `C#` client is available as a beta release.
+ +This means that the library is still under development and may change in future releases, including potential breaking changes. +**We do not recommend using this client library in production environments at this time.** + +::: + +export const csharpCardsData = [ + { + title: "weaviate/csharp-client", + link: "https://github.com/weaviate/csharp-client/tree/mvp", + icon: "fa-brands fa-github", + }, + /*{ + title: "Reference manual", + link: "https://javadoc.io/doc/io.weaviate/client/latest/index.html", + icon: "fa-solid fa-book", + },*/ +]; + +:::note C# client (SDK) + +The latest C# client is version `v||site.csharp_client_version||`. + + + +::: + +This page broadly covers the Weaviate C# (beta release). For usage information not specific to the C# client, such as code examples, see the relevant pages in the [How-to manuals & Guides](../guides.mdx). + +## Installation + +```xml + +``` + +
+ Requirements: Weaviate version compatibility & gRPC + +#### Weaviate version compatibility + +The C# client requires Weaviate `1.33.0` or higher. Generally, we encourage you to use the latest version of the C# client and the Weaviate Database. + +#### gRPC + +The C# client uses remote procedure calls (RPCs) under-the-hood. Accordingly, a port for gRPC must be open to your Weaviate server. + +
+ docker-compose.yml example + +If you are running Weaviate with Docker, you can map the default port (`50051`) by adding the following to your `docker-compose.yml` file: + +```yaml +ports: + - 8080:8080 + - 50051:50051 +``` + +
+
+ +## Get started + +import BasicPrereqs from "/_includes/prerequisites-quickstart.md"; + + + +Get started with Weaviate using this C# example. The code walks you through these key steps: + +1. **[Connect to Weaviate](docs/weaviate/connections/index.mdx)**: Establish a connection to a local (or Cloud) Weaviate instance. +1. **[Create a collection](../manage-collections/index.mdx)**: Define the data schema for a `Question` collection, using an Ollama model to vectorize the data. +1. **[Import data](../manage-objects/import.mdx)**: Fetch sample Jeopardy questions and use Weaviate's batch import for efficient ingestion and automatic vector embedding generation. +1. **[Search/query the database](../search/index.mdx)**: Execute a vector search to find questions semantically similar to the query `biology`. + + + + + + + +For more code examples, check out the [How-to manuals & Guides](../guides.mdx) section. + +## Asynchronous usage + +_Coming soon_ + + + +## Releases + +Go to the [GitHub releases page](https://github.com/weaviate/csharp-client/releases) to see the history of the C# client library releases and change logs. + +
+ Click here for a table of Weaviate and corresponding client versions + +import ReleaseHistory from "/_includes/release-history.md"; + + + +
+ +## Code examples & further resources + +import CodeExamples from "/_includes/clients/code-examples.mdx"; + + + +## Questions and feedback + +import DocsFeedback from "/_includes/docs-feedback.mdx"; + + diff --git a/docs/weaviate/client-libraries/index.mdx b/docs/weaviate/client-libraries/index.mdx index 49a3df96..001d44e6 100644 --- a/docs/weaviate/client-libraries/index.mdx +++ b/docs/weaviate/client-libraries/index.mdx @@ -33,6 +33,13 @@ export const clientLibrariesData = [ link: "/weaviate/client-libraries/go", icon: "fab fa-golang", }, + { + title: "Java Client v6 (Beta release)", + description: + "Install and use the beta version of the new Java v6 client library for interacting with Weaviate.", + link: "/weaviate/client-libraries/java/java-v6", + icon: "fab fa-java", + }, { title: "Java Client", description: @@ -40,6 +47,13 @@ export const clientLibrariesData = [ link: "/weaviate/client-libraries/java", icon: "fab fa-java", }, + { + title: "C# Client", + description: + "Install and use the official C# library for interacting with Weaviate.", + link: "/weaviate/client-libraries/java", + icon: "fab fa-microsoft", + }, ];
diff --git a/docs/weaviate/client-libraries/java.md b/docs/weaviate/client-libraries/java/index.mdx similarity index 89% rename from docs/weaviate/client-libraries/java.md rename to docs/weaviate/client-libraries/java/index.mdx index 8917728a..e3a08ba4 100644 --- a/docs/weaviate/client-libraries/java.md +++ b/docs/weaviate/client-libraries/java/index.mdx @@ -1,7 +1,6 @@ --- title: Java -sidebar_position: 50 -description: "Official Java client library documentation for integrating Weaviate with Go applications and services." +description: "Official Java client library documentation for integrating Weaviate with Java applications and services." image: og/docs/client-libraries.jpg # tags: ['java', 'client library'] --- @@ -10,9 +9,9 @@ import QuickLinks from "/src/components/QuickLinks"; export const javaCardsData = [ { - title: "weaviate/java-client", - link: "https://github.com/weaviate/java-client", - icon: "fa-brands fa-github", + title: "weaviate/java-client", + link: "https://github.com/weaviate/java-client", + icon: "fa-brands fa-github", }, ]; @@ -24,13 +23,15 @@ The latest Java client is version `v||site.java_client_version||`. ::: -:::info Breaking changes introduced in v4 -The `package` and `import` paths have been updated from `technology.semi.weaviate` to `io.weaviate`. +:::info New Java client library + +We are working on a revamped Java client library with a completely new API. +Check out the [Java client v6 - Beta release](./java-v6.mdx). -See the [Migration Guide](#from-3xx-to-400) for more info. ::: ## Installation and setup + To get the latest stable version of the Java client library, add this dependency to your project: ```xml @@ -71,13 +72,13 @@ public class App { ## Authentication -import ClientAuthIntro from '/docs/weaviate/client-libraries/_components/client.auth.introduction.mdx' +import ClientAuthIntro from "/docs/weaviate/client-libraries/_components/client.auth.introduction.mdx"; - + ### WCD authentication -import ClientAuthWCD from '/docs/weaviate/client-libraries/_components/client.auth.wcs.mdx' +import ClientAuthWCD from "/docs/weaviate/client-libraries/_components/client.auth.wcs.mdx"; @@ -86,7 +87,7 @@ import ClientAuthWCD from '/docs/weaviate/client-libraries/_components/client.au :::info Added in Weaviate Java client version `4.0.2`. ::: -import ClientAuthApiKey from '/docs/weaviate/client-libraries/_components/client.auth.api.key.mdx' +import ClientAuthApiKey from "/docs/weaviate/client-libraries/_components/client.auth.api.key.mdx"; @@ -100,13 +101,13 @@ WeaviateClient client = WeaviateAuthClient.apiKey(config, "YOUR-WEAVIATE-API-KEY ### OIDC authentication -import ClientAuthOIDCIntro from '/docs/weaviate/client-libraries/_components/client.auth.oidc.introduction.mdx' +import ClientAuthOIDCIntro from "/docs/weaviate/client-libraries/_components/client.auth.oidc.introduction.mdx"; -#### Resource Owner Password Flow +#### Resource Owner Password Flow -import ClientAuthFlowResourceOwnerPassword from '/docs/weaviate/client-libraries/_components/client.auth.flow.resource.owner.password.mdx' +import ClientAuthFlowResourceOwnerPassword from "/docs/weaviate/client-libraries/_components/client.auth.flow.resource.owner.password.mdx"; @@ -123,9 +124,9 @@ WeaviateAuthClient.clientPassword( ); ``` -#### Client Credentials flow +#### Client Credentials flow -import ClientAuthFlowClientCredentials from '/docs/weaviate/client-libraries/_components/client.auth.flow.client.credentials.mdx' +import ClientAuthFlowClientCredentials from "/docs/weaviate/client-libraries/_components/client.auth.flow.client.credentials.mdx"; @@ -141,9 +142,9 @@ WeaviateAuthClient.clientCredentials( ); ``` -#### Refresh Token flow +#### Refresh Token flow -import ClientAuthBearerToken from '/docs/weaviate/client-libraries/_components/client.auth.bearer.token.mdx' +import ClientAuthBearerToken from "/docs/weaviate/client-libraries/_components/client.auth.bearer.token.mdx"; @@ -181,7 +182,7 @@ public class App { ## References -All [RESTful endpoints](/weaviate/api/rest) and [GraphQL functions](../api/graphql/index.md) references covered by the Java client, and explained on those reference pages in the code blocks. +All [RESTful endpoints](/weaviate/api/rest) and [GraphQL functions](../../api/graphql/index.md) references covered by the Java client, and explained on those reference pages in the code blocks. ## Typed GraphQL Responses @@ -234,7 +235,7 @@ Float certainty = pizza.getAdditional().getCertainty(); ### Builder pattern -The Java client functions are designed with a 'Builder pattern'. A pattern is used to build complex query objects. This means that a function (for example to retrieve data from Weaviate with a request similar to a RESTful GET request, or a more complex GraphQL query) is built with single objects to reduce complexity. Some builder objects are optional, others are required to perform specific functions. All is documented on the [RESTful API reference pages](/weaviate/api/rest) and the [GraphQL reference pages](../api/graphql/index.md). +The Java client functions are designed with a 'Builder pattern'. A pattern is used to build complex query objects. This means that a function (for example to retrieve data from Weaviate with a request similar to a RESTful GET request, or a more complex GraphQL query) is built with single objects to reduce complexity. Some builder objects are optional, others are required to perform specific functions. All is documented on the [RESTful API reference pages](/weaviate/api/rest) and the [GraphQL reference pages](../../api/graphql/index.md). The code snippet above shows a simple query similar to `RESTful GET /v1/meta`. The client is initiated by requiring the package and connecting to the running instance. Then, a query is constructed by using the `.metaGetter()` on `.misc()`. The query will be sent with the `.run()` function, this object is thus required for every function you want to build and execute. @@ -245,12 +246,14 @@ The code snippet above shows a simple query similar to `RESTful GET /v1/meta`. T #### Moved from `technology.semi.weaviate` to `io.weaviate` package Before: + ```java package technology.semi.weaviate; import technology.semi.weaviate.client.*; ``` After: + ```java package io.weaviate; import io.weaviate.client.*; @@ -261,6 +264,7 @@ import io.weaviate.client.*; #### Removed @Deprecated method `Aggregate::withFields(Fields fields)` Before: + ```java // import io.weaviate.client.v1.graphql.query.fields.Field; // import io.weaviate.client.v1.graphql.query.fields.Fields; @@ -270,6 +274,7 @@ client.graphQL().aggregate().withFields(fields)... ``` After: + ```java client.graphQL().aggregate().withFields(name, description)... ``` @@ -277,6 +282,7 @@ client.graphQL().aggregate().withFields(name, description)... #### Removed @Deprecated method `Get::withFields(Fields fields)` Before: + ```java // import io.weaviate.client.v1.graphql.query.fields.Field; // import io.weaviate.client.v1.graphql.query.fields.Fields; @@ -286,6 +292,7 @@ client.graphQL().get().withFields(fields)... ``` After: + ```java client.graphQL().get().withFields(name, description)... ``` @@ -293,11 +300,13 @@ client.graphQL().get().withFields(name, description)... #### Removed @Deprecated method `Get::withNearVector(Float[] vector)` Before: + ```java client.graphQL().get().withNearVector(new Float[]{ 0f, 1f, 0.8f })... ``` After: + ```java // import io.weaviate.client.v1.graphql.query.argument.NearVectorArgument; @@ -312,6 +321,7 @@ With `batch delete` feature, unified `filters.WhereFilter` implementation is int ##### GraphQL Before: + ```java // import io.weaviate.client.v1.graphql.query.argument.GeoCoordinatesParameter; // import io.weaviate.client.v1.graphql.query.argument.WhereArgument; @@ -332,6 +342,7 @@ client.graphQL().aggregate().withWhere(where)... ``` After: + ```java // import io.weaviate.client.v1.filters.Operator; // import io.weaviate.client.v1.filters.WhereFilter; @@ -357,6 +368,7 @@ client.graphQL().aggregate().withWhere(where)... ``` Before: + ```java // import io.weaviate.client.v1.graphql.query.argument.WhereArgument; // import io.weaviate.client.v1.graphql.query.argument.WhereOperator; @@ -371,6 +383,7 @@ client.graphQL().aggregate().withWhere(where)... ``` After: + ```java // import io.weaviate.client.v1.filters.Operator; // import io.weaviate.client.v1.filters.WhereFilter; @@ -385,6 +398,7 @@ client.graphQL().aggregate().withWhere(where)... ``` Before: + ```java // import io.weaviate.client.v1.graphql.query.argument.WhereArgument; // import io.weaviate.client.v1.graphql.query.argument.WhereFilter; @@ -410,6 +424,7 @@ client.graphQL().aggregate().withWhere(where)... ``` After: + ```java // import io.weaviate.client.v1.filters.Operator; // import io.weaviate.client.v1.filters.WhereFilter; @@ -436,6 +451,7 @@ client.graphQL().aggregate().withWhere(where)... ##### Classification Before: + ```java // import io.weaviate.client.v1.classifications.model.GeoCoordinates; // import io.weaviate.client.v1.classifications.model.Operator; @@ -461,6 +477,7 @@ client.classifications().scheduler().withTrainingSetWhereFilter(where)... ``` After: + ```java // import io.weaviate.client.v1.filters.Operator; // import io.weaviate.client.v1.filters.WhereFilter; @@ -489,7 +506,7 @@ Go to the [GitHub releases page](https://github.com/weaviate/java-client/release
Click here for a table of Weaviate and corresponding client versions -import ReleaseHistory from '/_includes/release-history.md'; +import ReleaseHistory from "/_includes/release-history.md"; @@ -497,6 +514,6 @@ import ReleaseHistory from '/_includes/release-history.md'; ## Questions and feedback -import DocsFeedback from '/_includes/docs-feedback.mdx'; +import DocsFeedback from "/_includes/docs-feedback.mdx"; - + diff --git a/docs/weaviate/client-libraries/java/java-v6.mdx b/docs/weaviate/client-libraries/java/java-v6.mdx new file mode 100644 index 00000000..5b00e613 --- /dev/null +++ b/docs/weaviate/client-libraries/java/java-v6.mdx @@ -0,0 +1,155 @@ +--- +title: Java v6 - Beta release +sidebar_label: Java v6 🚧 +description: "Official Java v6 client library documentation for integrating Weaviate with Java applications and services." +image: og/docs/client-libraries.jpg +# tags: ['java', 'client library', 'experimental'] +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import FilteredTextBlock from "@site/src/components/Documentation/FilteredTextBlock"; +import JavaV6Code from "!!raw-loader!/_includes/code/java-v6/src/test/java/GetStartedTest.java"; +import QuickLinks from "/src/components/QuickLinks"; + +:::caution Preview + +The `Java v6` client is available as a beta release.
+ +This means that the library is still under development and may change in future releases, including potential breaking changes. +**We do not recommend using this client library in production environments at this time.** + +::: + +export const javaCardsData = [ + { + title: "weaviate/java-client", + link: "https://github.com/weaviate/java-client/tree/v6", + icon: "fa-brands fa-github", + }, + { + title: "Reference manual", + link: "https://javadoc.io/doc/io.weaviate/client/latest/index.html", + icon: "fa-solid fa-book", + }, +]; + +:::note Java v6 client (SDK) + +The latest Java v6 client is version `v||site.java_new_client_version||`. + + + +::: + +This page broadly covers the Weaviate Java client (`v6` beta release). For usage information not specific to the Java client, such as code examples, see the relevant pages in the [How-to manuals & Guides](../../guides.mdx). + +## Installation + +```xml + + io.weaviate + client6 + 6.0.0-M1 + +``` + +
+ Uber JAR🫙 + +If you're building an uber-JAR with something like `maven-assembly-plugin`, use a shaded version with classifier `all`. +This ensures that all dynamically-loaded dependencies of `io.grpc` are resolved correctly. + +```xml + + io.weaviate + client6 + 6.0.0-M1 + all + +``` + +
+ +
+ Requirements: Weaviate version compatibility & gRPC + +#### Weaviate version compatibility + +The `v6` Java client requires Weaviate `1.33.0` or higher. Generally, we encourage you to use the latest version of the Java client and the Weaviate Database. + +#### gRPC + +The `v6` client uses remote procedure calls (RPCs) under-the-hood. Accordingly, a port for gRPC must be open to your Weaviate server. + +
+ docker-compose.yml example + +If you are running Weaviate with Docker, you can map the default port (`50051`) by adding the following to your `docker-compose.yml` file: + +```yaml +ports: + - 8080:8080 + - 50051:50051 +``` + +
+ +
+ +## Get started + +import BasicPrereqs from "/_includes/prerequisites-quickstart.md"; + + + +Get started with Weaviate using this Java example. The code walks you through these key steps: + +1. **[Connect to Weaviate](docs/weaviate/connections/index.mdx)**: Establish a connection to a local (or Cloud) Weaviate instance. +1. **[Create a collection](../../manage-collections/index.mdx)**: Define the data schema for a `Question` collection, using an Ollama model to vectorize the data. +1. **[Import data](../../manage-objects/import.mdx)**: Fetch sample Jeopardy questions and use Weaviate's batch import for efficient ingestion and automatic vector embedding generation. +1. **[Search/query the database](../../search/index.mdx)**: Execute a vector search to find questions semantically similar to the query `biology`. + + + + + + + +For more code examples, check out the [How-to manuals & Guides](../../guides.mdx) section. + +## Asynchronous usage + +_Coming soon_ + + + +## Releases + +Go to the [GitHub releases page](https://github.com/weaviate/java-client/releases) to see the history of the Java client library releases and change logs. + +
+ Click here for a table of Weaviate and corresponding client versions + +import ReleaseHistory from "/_includes/release-history.md"; + + + +
+ +## Code examples & further resources + +import CodeExamples from "/_includes/clients/code-examples.mdx"; + + + +## Questions and feedback + +import DocsFeedback from "/_includes/docs-feedback.mdx"; + + diff --git a/docs/weaviate/concepts/interface.md b/docs/weaviate/concepts/interface.md index 71529047..0be9ed0c 100644 --- a/docs/weaviate/concepts/interface.md +++ b/docs/weaviate/concepts/interface.md @@ -133,7 +133,7 @@ The [Weaviate Console](https://console.weaviate.cloud) is a dashboard to manage ## Weaviate Clients -Weaviate has several client libraries: in [Go](/weaviate/client-libraries/go.md), [Java](/weaviate/client-libraries/java.md), [Python](/weaviate/client-libraries/python/index.mdx) and [TypeScript/JavaScript](/weaviate/client-libraries/typescript/index.mdx). The client libraries in all languages support all API functions. Some clients, e.g. the Python client, have additional functionality, such as full schema management and batching operations. This way, Weaviate is easy to use in custom projects. The APIs are intuitive to use, so it is easy to integrate Weaviate into your existing data landscape. +Weaviate has several client libraries: in [Go](/weaviate/client-libraries/go.md), [Java](/weaviate/client-libraries/java/index.mdx), [Python](/weaviate/client-libraries/python/index.mdx) and [TypeScript/JavaScript](/weaviate/client-libraries/typescript/index.mdx). The client libraries in all languages support all API functions. Some clients, e.g. the Python client, have additional functionality, such as full schema management and batching operations. This way, Weaviate is easy to use in custom projects. The APIs are intuitive to use, so it is easy to integrate Weaviate into your existing data landscape. ## Further resources :::info Related pages diff --git a/docs/weaviate/concepts/search/_best-practices.md b/docs/weaviate/concepts/search/_best-practices.md index 9d4b8c63..2f061d5b 100644 --- a/docs/weaviate/concepts/search/_best-practices.md +++ b/docs/weaviate/concepts/search/_best-practices.md @@ -23,7 +23,7 @@ Weaviate makes use of indexes to speed up filtering operations. [Roaring bitmap indexes (`indexFilterable`)](../filtering.md#indexfilterable) were added in `v1.18` to improve the performance of filtering operations. [Range-based indexes (`indexRangeFilters`)](../filtering.md#indexrangefilters) were added in `v1.26` to speed up range-based numerical filtering for `int`, `number`, or `date` properties. -These indexes can be [enabled or disabled](../../manage-collections/collection-operations.mdx#set-inverted-index-parameters) for each property. +These indexes can be [enabled or disabled](../../manage-collections/inverted-index.mdx#set-inverted-index-parameters) for each property. Enabling these indexes will speed up search, at the cost of slight increase in storage requirements both on disk and memory. diff --git a/docs/weaviate/concepts/search/keyword-search.md b/docs/weaviate/concepts/search/keyword-search.md index 271f741a..2efa6cab 100644 --- a/docs/weaviate/concepts/search/keyword-search.md +++ b/docs/weaviate/concepts/search/keyword-search.md @@ -122,7 +122,7 @@ flowchart LR linkStyle default stroke:#718096,stroke-width:3px,fill:none,background-color:white ``` -Set custom `k1` and `b` values [for a collection](../../manage-collections/collection-operations.mdx#set-inverted-index-parameters). +Set custom `k1` and `b` values [for a collection](../../manage-collections/inverted-index.mdx#set-inverted-index-parameters). diff --git a/docs/weaviate/config-refs/indexing/inverted-index.mdx b/docs/weaviate/config-refs/indexing/inverted-index.mdx index fab7cbdf..254257f7 100644 --- a/docs/weaviate/config-refs/indexing/inverted-index.mdx +++ b/docs/weaviate/config-refs/indexing/inverted-index.mdx @@ -244,7 +244,7 @@ Different tokenization methods suit different use cases. Use `word` (default) fo ## Further resources - [Concepts: Inverted index](../../concepts/indexing/inverted-index.md) -- [How-to: Set inverted index parameters](../../manage-collections/collection-operations.mdx#set-inverted-index-parameters) +- [How-to: Set inverted index parameters](../../manage-collections/inverted-index.mdx#set-inverted-index-parameters) - [Reference: Tokenization options](../collections.mdx#tokenization) - Learn about different tokenization methods and how they affect text indexing ## Questions and feedback diff --git a/docs/weaviate/configuration/compression/bq-compression.md b/docs/weaviate/configuration/compression/bq-compression.md index b1ffc423..40329268 100644 --- a/docs/weaviate/configuration/compression/bq-compression.md +++ b/docs/weaviate/configuration/compression/bq-compression.md @@ -11,6 +11,8 @@ import PyCode from '!!raw-loader!/\_includes/code/howto/configure.bq-compression import TSCode from '!!raw-loader!/\_includes/code/howto/configure.bq-compression.ts'; import TSCodeBQOptions from '!!raw-loader!/\_includes/code/howto/configure.bq-compression.options.ts'; import GoCode from '!!raw-loader!/\_includes/code/howto/go/docs/configure/compression.bq_test.go'; +import JavaV6Code from "!!raw-loader!/\_includes/code/java-v6/src/test/java/ConfigureBQTest.java"; +import CSharpCode from "!!raw-loader!/\_includes/code/csharp/ConfigureBQTest.cs"; import JavaCode from '!!raw-loader!/\_includes/code/howto/java/src/test/java/io/weaviate/docs/bq-compression.java'; import CompressionByDefault from '/\_includes/compression-by-default.mdx'; @@ -41,8 +43,6 @@ BQ can be enabled at collection creation time through the collection definition: language="py" /> - - - - - + + + + + + ## Enable compression for existing collection @@ -97,6 +110,14 @@ BQ can also be enabled for an existing collection by updating the collection def language="go" /> + + + + + + ## BQ parameters @@ -126,8 +155,6 @@ For example: language="py" /> - - - - - + + + + + + ## Additional considerations diff --git a/docs/weaviate/configuration/compression/pq-compression.md b/docs/weaviate/configuration/compression/pq-compression.md index a41d1f48..bf9a4447 100644 --- a/docs/weaviate/configuration/compression/pq-compression.md +++ b/docs/weaviate/configuration/compression/pq-compression.md @@ -11,6 +11,8 @@ import PyCode from '!!raw-loader!/\_includes/code/howto/configure.pq-compression import TSCodeAutoPQ from '!!raw-loader!/\_includes/code/howto/configure.pq-compression.autopq.ts'; import TSCodeManualPQ from '!!raw-loader!/\_includes/code/howto/configure.pq-compression.manual.ts'; import GoCode from '!!raw-loader!/\_includes/code/howto/go/docs/configure/compression.pq_test.go'; +import JavaV6Code from "!!raw-loader!/\_includes/code/java-v6/src/test/java/ConfigurePQTest.java"; +import CSharpCode from "!!raw-loader!/\_includes/code/csharp/ConfigurePQTest.cs"; import JavaCode from '!!raw-loader!/\_includes/code/howto/java/src/test/java/io/weaviate/docs/pq-compression.java'; import CompressionByDefault from '/\_includes/compression-by-default.mdx'; @@ -61,8 +63,6 @@ To configure PQ in a collection, use the [PQ parameters](./pq-compression.md#pq- language="py" /> - - - - + + + + + + ### 3. Load your data @@ -118,8 +132,6 @@ Follow these steps to manually enable PQ. language="py" /> - - - - - + + + + + + ### 2. Load training data @@ -176,8 +201,6 @@ To enable PQ, update your collection definition as shown below. For additional c language="py" /> - - - - - + + + + + + ### 4. Load the rest of your data @@ -264,8 +300,6 @@ To review the current `pq` configuration, you can retrieve it as shown below. language="py" /> - - - - - + + + + + + ### Multiple vector embeddings (named vectors) diff --git a/docs/weaviate/configuration/compression/rq-compression.md b/docs/weaviate/configuration/compression/rq-compression.md index 6d0030b7..ce5d83da 100644 --- a/docs/weaviate/configuration/compression/rq-compression.md +++ b/docs/weaviate/configuration/compression/rq-compression.md @@ -10,7 +10,9 @@ import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBl import PyCode from '!!raw-loader!/\_includes/code/howto/configure-rq/rq-compression-v4.py'; import GoCode from '!!raw-loader!/\_includes/code/howto/go/docs/configure/compression.rq_test.go'; import TSCode from '!!raw-loader!/\_includes/code/howto/configure-rq/rq-compression-v3.ts'; +import Java6Code from '!!raw-loader!/\_includes/code/java-v6/src/test/java/ConfigureRQTest.java'; import JavaCode from '!!raw-loader!/\_includes/code/howto/java/src/test/java/io/weaviate/docs/rq-compression.java'; +import CSharpCode from "!!raw-loader!/\_includes/code/csharp/ConfigureRQTest.cs"; import CompressionByDefault from '/\_includes/compression-by-default.mdx'; @@ -66,6 +68,14 @@ RQ can be enabled at collection creation time through the collection definition: language="go" /> + + + + + + ### Enable compression for existing collection @@ -97,7 +115,23 @@ RQ can also be enabled for an existing collection by updating the collection def language="java" /> - + + + + + + + + + + + + + ### Enable compression for existing collection @@ -171,6 +221,22 @@ RQ can also be enabled for an existing collection by updating the collection def endMarker="# END 1BitUpdateSchema" language="py" /> + + + + + + - - + + @@ -223,6 +289,14 @@ import RQParameters from '/\_includes/configuration/rq-compression-parameters.md language="go" /> + + + + + + + ```json { "class": "Article", @@ -305,7 +352,7 @@ This configuration for text objects defines the following:
- Sample configuration: Nested objects +Sample configuration: Nested objects :::info Added in `v1.22` ::: @@ -313,7 +360,9 @@ This configuration for text objects defines the following: This configuration for nested objects defines the following: - The collection name (`Person`) + - The vectorizer module (`text2vec-huggingface`) + - A set of properties (`last_name`, `address`) - `last_name` has `text` data type @@ -321,6 +370,8 @@ This configuration for nested objects defines the following: - The `address` property has two nested properties (`street` and `city`) + + ```json { "class": "Person", @@ -345,9 +396,9 @@ This configuration for nested objects defines the following:
- Sample configuration: Generative search +Sample configuration: Generative search -This configuration for [retrieval augmented generation](../search/generative.md) defines the following: +This configuration for [retrieval augmented generation](https://www.google.com/search?q=../search/generative.md) defines the following: - The collection name (`Article`) - The default vectorizer module (`text2vec-openai`) @@ -356,6 +407,8 @@ This configuration for [retrieval augmented generation](../search/generative.md) - The tokenization option for the `url` property - The vectorization option (`skip` vectorization) for the `url` property + + ```json { "class": "Article", @@ -396,19 +449,21 @@ This configuration for [retrieval augmented generation](../search/generative.md)
- Sample configuration: Images +Sample configuration: Images This configuration for image search defines the following: - The collection name (`Image`) + - The vectorizer module (`img2vec-neural`) - The `image` property configures collection to store image data. - The vector index distance metric (`cosine`) + - A set of properties (`image`), with the `image` property set as `blob`. -For image searches, see [Image search](../search/image.md). +For image searches, see [Image search](https://www.google.com/search?q=../search/image.md). ```json { @@ -446,27 +501,14 @@ Fetch the database schema to retrieve all of the collection definitions. language="py" /> - - - - - - - - - - - + + + + + + + + + + + + ## Update a collection definition @@ -483,7 +549,7 @@ import RaftRFChangeWarning from "/_includes/1-25-replication-factor.mdx"; -You can update a collection definition to change the [mutable collection settings](../config-refs/collections.mdx#mutability). +You can update a collection definition to change the [mutable collection settings](https://www.google.com/search?q=../config-refs/collections.mdx%23mutability). @@ -494,27 +560,14 @@ You can update a collection definition to change the [mutable collection setting language="py" /> - - - - - - - - - - - + + + + + + + + + + + + ## Delete a collection @@ -534,9 +611,9 @@ import CautionSchemaDeleteClass from "/_includes/schema-delete-class.mdx"; ## Add a property
- - Indexing limitations after data import - + +Indexing limitations after data import + There are no index limitations when you add collection properties before you import data. @@ -559,10 +636,10 @@ import CodeSchemaAddProperties from "/_includes/code/schema.things.properties.ad ## Further resources -- [Manage collections: Vectorizer and vector index](./vector-config.mdx) -- [References: Collection definition](/weaviate/config-refs/collections.mdx) -- [Concepts: Data structure](../concepts/data.md) -- +- [Manage collections: Vectorizer and vector index](https://www.google.com/search?q=./vector-config.mdx) +- [References: Collection definition](https://www.google.com/search?q=/weaviate/config-refs/collections.mdx) +- [Concepts: Data structure](https://www.google.com/search?q=../concepts/data.md) +- API References: REST: Schema diff --git a/docs/weaviate/manage-collections/cross-references.mdx b/docs/weaviate/manage-collections/cross-references.mdx index 99e882fb..f88dcf99 100644 --- a/docs/weaviate/manage-collections/cross-references.mdx +++ b/docs/weaviate/manage-collections/cross-references.mdx @@ -3,19 +3,20 @@ title: Cross-references image: og/docs/howto.jpg --- -import CrossReferencePerformanceNote from '/_includes/cross-reference-performance-note.mdx'; +import CrossReferencePerformanceNote from "/_includes/cross-reference-performance-note.mdx"; -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; -import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBlock'; -import PyCode from '!!raw-loader!/_includes/code/howto/manage-data.cross-refs.py'; -import TSCode from '!!raw-loader!/_includes/code/howto/manage-data.cross-refs.ts'; -import JavaCode from '!!raw-loader!/_includes/code/howto/java/src/test/java/io/weaviate/docs/manage-data.cross-refs.java'; -import GoCode from '!!raw-loader!/_includes/code/howto/go/docs/manage-data.cross-refs_test.go'; -import SkipLink from '/src/components/SkipValidationLink' - +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; +import FilteredTextBlock from "@site/src/components/Documentation/FilteredTextBlock"; +import PyCode from "!!raw-loader!/_includes/code/howto/manage-data.cross-refs.py"; +import TSCode from "!!raw-loader!/_includes/code/howto/manage-data.cross-refs.ts"; +import JavaV6Code from "!!raw-loader!/_includes/code/java-v6/src/test/java/ManageCollectionsCrossReferencesTest.java"; +import CSharpCode from "!!raw-loader!/_includes/code/csharp/ManageCollectionsCrossReferencesTest.cs"; +import JavaCode from "!!raw-loader!/_includes/code/howto/java/src/test/java/io/weaviate/docs/manage-data.cross-refs.java"; +import GoCode from "!!raw-loader!/_includes/code/howto/go/docs/manage-data.cross-refs_test.go"; +import SkipLink from "/src/components/SkipValidationLink"; Use cross-references to establish directional relationships between collections. @@ -25,10 +26,11 @@ Use cross-references to establish directional relationships between collections. Notes: + - Cross-references does not affect object vectors of the source or the target objects. - For multi-tenancy collection, you can establish a cross-reference from a multi-tenancy collection object to: - - A non-multi-tenancy collection object, or - - A multi-tenancy collection object belonging to the same tenant. + - A non-multi-tenancy collection object, or + - A multi-tenancy collection object belonging to the same tenant.
@@ -53,6 +55,22 @@ Include the reference property in the collection definition before adding cross- language="ts" /> + + + + + + ## Add a cross-reference property @@ -76,10 +94,24 @@ It is also possible to add a cross-reference property to an existing collection language="ts" /> - + + + + + + - ## Create an object with a cross-reference Specify a cross-reference when creating an object. @@ -93,8 +125,6 @@ Specify a cross-reference when creating an object. language="py" /> - - - + + + + + + - ## Add a one-way cross-reference Specify the required id and properties for the source and the target. @@ -120,8 +164,6 @@ Specify the required id and properties for the source and the target. language="py" /> - - - - - + + + + - - + + + + - ## Add two-way cross-references This requires adding reference properties in both directions, and adding two cross-references per object pair (`from` A -> `to` B and `from` B -> `to` A). @@ -175,7 +229,22 @@ Create the `JeopardyCategory` collection: language="ts" /> - + + + + + + Create the `JeopardyQuestion` collection including the reference property to `JeopardyCategory`: @@ -197,7 +266,22 @@ Create the `JeopardyQuestion` collection including the reference property to `Je language="ts" /> - + + + + + + Modify `JeopardyCategory` to add the reference to `JeopardyQuestion`: @@ -219,7 +303,22 @@ Modify `JeopardyCategory` to add the reference to `JeopardyQuestion`: language="ts" /> - + + + + + + And add the cross-references: @@ -233,8 +332,6 @@ And add the cross-references: language="py" /> - - - - + + + + + + - - + - ## Add multiple (one-to-many) cross-references Weaviate allows creation of multiple cross-references from one source object. @@ -278,8 +387,6 @@ Weaviate allows creation of multiple cross-references from one source object. language="py" /> - - - - + + + + + + - - + @@ -322,8 +442,6 @@ Cross-references can be read as part of the object. language="py" /> - - + + + + + + ## Delete a cross-reference @@ -347,8 +481,6 @@ Deleting a cross-reference with the same parameters used to define the cross-ref language="py" /> - - - - + + + + + + - - + @@ -388,7 +533,6 @@ If an object is deleted, cross-references to it will be left intact. A [Get quer
- ## Update a cross-reference The targets of a cross-reference can be updated. @@ -402,8 +546,6 @@ The targets of a cross-reference can be updated. language="py" />
- - - - + + + + + + - - +
- ## Related pages - [Connect to Weaviate](/weaviate/connections/index.mdx) -- References: REST - /v1/objects +- + References: REST - /v1/objects + - [Retrieve the cross-reference](../search/basics.md#retrieve-cross-referenced-properties) as a part of a query. ## Questions and feedback -import DocsFeedback from '/_includes/docs-feedback.mdx'; +import DocsFeedback from "/_includes/docs-feedback.mdx"; - + diff --git a/docs/weaviate/manage-collections/generative-reranker-models.mdx b/docs/weaviate/manage-collections/generative-reranker-models.mdx index 58d7fe01..f7d41fba 100644 --- a/docs/weaviate/manage-collections/generative-reranker-models.mdx +++ b/docs/weaviate/manage-collections/generative-reranker-models.mdx @@ -10,6 +10,8 @@ import FilteredTextBlock from "@site/src/components/Documentation/FilteredTextBl import PyCode from "!!raw-loader!/_includes/code/howto/manage-data.collections.py"; import TSCode from "!!raw-loader!/_includes/code/howto/manage-data.collections.ts"; import JavaCode from "!!raw-loader!/_includes/code/howto/java/src/test/java/io/weaviate/docs/manage-data.classes.java"; +import JavaV6Code from "!!raw-loader!/_includes/code/java-v6/src/test/java/ManageCollectionsTest.java"; +import CSharpCode from "!!raw-loader!/_includes/code/csharp/ManageCollectionsTest.cs"; import GoCode from "!!raw-loader!/_includes/code/howto/go/docs/manage-data.classes_test.go"; :::tip Embedding models / Vectorizers @@ -36,18 +38,14 @@ Configure a [`reranker`](../concepts/search/index.md#rerank) model integration f language="py" />
- - - - - - - + + + + + + + + +
## Update the reranker model integration @@ -75,18 +89,14 @@ Update the [`reranker`](../concepts/search/index.md#rerank) model integration fo language="py" />
- - - - - - - + + + + + + + + +
## Specify a generative model integration @@ -115,27 +141,22 @@ Specify a `generative` model integration for a collection (for RAG). language="py" />
- - - - - - - - - - - + + + + + + + + + + + +
## Update the generative model integration @@ -163,18 +200,14 @@ Update a [`generative`](../concepts/search/index.md#retrieval-augmented-generati language="py" /> - - - - - - - + + + + + + + + + import RuntimeGenerative from "/_includes/runtime-generative.mdx"; diff --git a/docs/weaviate/manage-collections/inverted-index.mdx b/docs/weaviate/manage-collections/inverted-index.mdx index 2aa5b21a..1ea41fd0 100644 --- a/docs/weaviate/manage-collections/inverted-index.mdx +++ b/docs/weaviate/manage-collections/inverted-index.mdx @@ -10,6 +10,8 @@ import TabItem from "@theme/TabItem"; import FilteredTextBlock from "@site/src/components/Documentation/FilteredTextBlock"; import PyCode from "!!raw-loader!/_includes/code/howto/manage-data.collections.py"; import TSCode from "!!raw-loader!/_includes/code/howto/manage-data.collections.ts"; +import JavaV6Code from "!!raw-loader!/_includes/code/java-v6/src/test/java/ManageCollectionsTest.java"; +import CSharpCode from "!!raw-loader!/_includes/code/csharp/ManageCollectionsTest.cs"; import JavaCode from "!!raw-loader!/_includes/code/howto/java/src/test/java/io/weaviate/docs/manage-data.classes.java"; import GoCode from "!!raw-loader!/_includes/code/howto/go/docs/manage-data.classes_test.go"; @@ -58,6 +60,22 @@ The inverted index in Weaviate can be enabled through parameters at the property language="ts" /> + + + + + + - + @@ -114,6 +132,22 @@ The inverted index in Weaviate can be configured through various parameters at t language="ts" /> + + + + + + - + @@ -172,6 +206,22 @@ Tokenization determines how text content is broken down into individual terms th language="ts" /> + + + + + + - + diff --git a/docs/weaviate/manage-collections/migrate.mdx b/docs/weaviate/manage-collections/migrate.mdx index d811bba2..ad5ed48b 100644 --- a/docs/weaviate/manage-collections/migrate.mdx +++ b/docs/weaviate/manage-collections/migrate.mdx @@ -5,13 +5,13 @@ image: og/docs/howto.jpg # tags: ['how-to', 'cursor'] --- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; -import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBlock'; -import PyCode from '!!raw-loader!/_includes/code/howto/manage-data.migrate.data.v4.py'; -import TSCode from '!!raw-loader!/_includes/code/howto/manage-data.migrate.data.ts'; - +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; +import FilteredTextBlock from "@site/src/components/Documentation/FilteredTextBlock"; +import PyCode from "!!raw-loader!/_includes/code/howto/manage-data.migrate.data.v4.py"; +import TSCode from "!!raw-loader!/_includes/code/howto/manage-data.migrate.data.ts"; +import JavaV6Code from "!!raw-loader!/_includes/code/java-v6/src/test/java/ManageCollectionsMigrateDataTest.java"; +import CSharpCode from "!!raw-loader!/_includes/code/csharp/ManageCollectionsMigrateDataTest.cs"; Follow these examples to migrate data manually when using a backup is not possible. They cover all permutations between: @@ -21,9 +21,9 @@ Follow these examples to migrate data manually when using a backup is not possib
Additional information - The examples use two different Weaviate instances, exposed through different ports. The same process can be used for two different instances as well. +The examples use two different Weaviate instances, exposed through different ports. The same process can be used for two different instances as well. - Cross-references in Weaviate are properties. As such, you can [retrieve cross-reference](./cross-references.mdx#read-cross-references) as a part of the object. +Cross-references in Weaviate are properties. As such, you can [retrieve cross-reference](./cross-references.mdx#read-cross-references) as a part of the object.
@@ -31,7 +31,8 @@ Follow these examples to migrate data manually when using a backup is not possib What about cross-references? These scripts should migrate cross-references as well. -
+ +
Cross-references are properties. As such, these cursor-based exports will include them. During restoration, restore the cross-referenced (i.e. "to") object first, then the object that contains the cross-reference (i.e. "from" object). @@ -61,11 +62,28 @@ Create a collection (e.g. `WineReview`) at the target instance, matching the col language="ts" />
+ + + + + + #### Step 2: Migrate the data Migrate: + - The `source collection` data in the `client_src` instance - to `target collection` in the `client_tgt` instance @@ -86,6 +104,22 @@ Migrate: language="ts" /> + + + + + + ## Collection → Tenant @@ -111,6 +145,22 @@ Create a collection (e.g. `WineReview`) at the target instance, matching the col language="ts" /> + + + + + + #### Step 2: Create the tenant(s) @@ -134,11 +184,28 @@ Add tenants at the target instance before adding data objects. language="ts" /> + + + + + + #### Step 3: Migrate the data Migrate: + - The `source collection` data in the `client_src` instance - to `target tenant` data from `target collection` in the `client_tgt` instance @@ -159,6 +226,22 @@ Migrate: language="ts" /> + + + + + + ## Tenant → Collection @@ -184,11 +267,28 @@ Create a collection (e.g. `WineReview`) at the target instance, matching the col language="ts" /> + + + + + + #### Step 2: Migrate the data Migrate: + - The `source tenant` data from `source collection` in the `client_src` instance - to `target collection` in the `client_tgt` instance @@ -209,6 +309,22 @@ Migrate: language="ts" /> + + + + + + ## Tenant → Tenant @@ -226,8 +342,7 @@ Create a collection (e.g. `WineReview`) at the target instance, matching the col language="py" /> - - + + + + + + + #### Step 2: Create the tenant(s) @@ -258,11 +389,28 @@ Add tenants at the target instance before adding data objects. language="ts" /> + + + + + + #### Step 3: Migrate the data Migrate: + - The `source tenant` data from `source collection` in the `client_src` instance - to `target tenant` data from `target collection` in the `client_tgt` instance @@ -283,6 +431,22 @@ Migrate: language="ts" /> + + + + + + ## Related pages @@ -293,6 +457,6 @@ Migrate: ## Questions and feedback -import DocsFeedback from '/_includes/docs-feedback.mdx'; +import DocsFeedback from "/_includes/docs-feedback.mdx"; - + diff --git a/docs/weaviate/manage-collections/multi-node-setup.mdx b/docs/weaviate/manage-collections/multi-node-setup.mdx index cf83b650..eb1f3c21 100644 --- a/docs/weaviate/manage-collections/multi-node-setup.mdx +++ b/docs/weaviate/manage-collections/multi-node-setup.mdx @@ -9,7 +9,9 @@ import TabItem from "@theme/TabItem"; import FilteredTextBlock from "@site/src/components/Documentation/FilteredTextBlock"; import PyCode from "!!raw-loader!/_includes/code/howto/manage-data.collections.py"; import TSCode from "!!raw-loader!/_includes/code/howto/manage-data.collections.ts"; -import JavaReplicationCode from '!!raw-loader!/_includes/code/howto/java/src/test/java/io/weaviate/docs/manage-data.replication.java'; +import JavaV6Code from "!!raw-loader!/_includes/code/java-v6/src/test/java/ManageCollectionsTest.java"; +import CSharpCode from "!!raw-loader!/_includes/code/csharp/ManageCollectionsTest.cs"; +import JavaReplicationCode from "!!raw-loader!/_includes/code/howto/java/src/test/java/io/weaviate/docs/manage-data.replication.java"; import GoCode from "!!raw-loader!/_includes/code/howto/go/docs/manage-data.classes_test.go"; ## Replication settings @@ -32,35 +34,46 @@ Configure replication settings, such as [async replication](/deploy/configuratio ]} /> - - - - - - - - - - - - - - + + + + + + + + + + + + + + + ```bash @@ -87,7 +100,7 @@ curl \ http://localhost:8080/v1/schema ``` - +
@@ -116,36 +129,46 @@ Configure sharding per collection. language="py" /> - - - - - - - - - - - - - - - + + + + + + + + + + + + + + +
@@ -177,7 +200,9 @@ import CodeSchemaShardsUpdate from "/_includes/code/howto/manage-data.shards.upd ## Further resources -- API References: REST: Schema +- + API References: REST: Schema + - [References: Configuration: Schema](/weaviate/config-refs/collections.mdx) - [Concepts: Data structure](../concepts/data.md) diff --git a/docs/weaviate/manage-collections/multi-tenancy.mdx b/docs/weaviate/manage-collections/multi-tenancy.mdx index 86ad9a2f..64841aa3 100644 --- a/docs/weaviate/manage-collections/multi-tenancy.mdx +++ b/docs/weaviate/manage-collections/multi-tenancy.mdx @@ -9,6 +9,8 @@ import FilteredTextBlock from "@site/src/components/Documentation/FilteredTextBl import PyCode from "!!raw-loader!/_includes/code/howto/manage-data.multi-tenancy.py"; import TSCode from "!!raw-loader!/_includes/code/howto/manage-data.multi-tenancy.ts"; import JavaCode from "!!raw-loader!/_includes/code/howto/java/src/test/java/io/weaviate/docs/manage-data.multi-tenancy.java"; +import JavaV6Code from "!!raw-loader!/_includes/code/java-v6/src/test/java/_ManageCollectionsMultiTenancyTest.java"; +import CSharpCode from "!!raw-loader!/_includes/code/csharp/_ManageCollectionsMultiTenancyTest.cs"; import GoCode from "!!raw-loader!/_includes/code/howto/go/docs/manage-data.multi-tenancy_test.go"; import GoCodeAuto from "!!raw-loader!/_includes/code/howto/go/docs/manage-data.create_auto-multitenancy.go"; import CurlCode from "!!raw-loader!/_includes/code/howto/manage-data.multi-tenancy-curl.sh"; @@ -54,6 +56,22 @@ Multi-tenancy is disabled by default. To enable multi-tenancy, set `multiTenancy language="ts" /> + + + + + + - + @@ -105,6 +123,22 @@ import AutoTenant from "/_includes/auto-tenant.mdx"; language="bash" /> + + + + + + + + + + + + ## Add new tenants manually @@ -176,26 +226,14 @@ Tenant status is available from Weaviate `1.21` onwards. language="py" /> - - - - - - - - - - + + + + + + + + + + + + ## List all tenants @@ -221,26 +283,14 @@ This example lists the tenants in the `MultiTenancyCollection` collection: language="py" /> - - - - - - - - - - + + + + + + + + + + + + ## Get tenants by name @@ -266,16 +340,30 @@ This example returns `tenantA` and `tenantB` from the `MultiTenancyCollection` c language="py" /> - - - - - + + + + + + + + + ## Get one tenant @@ -293,16 +381,30 @@ This example returns a tenant from the `MultiTenancyCollection` collection: language="py" /> - - - - - + + + + + + + + + ## Delete tenants @@ -322,26 +424,14 @@ Deleting a tenant deletes all associated objects. language="py" /> - - - - - - - - - - + + + + + + + + + + + + ## Manage tenant states @@ -365,7 +479,6 @@ Change a tenant state between `ACTIVE`, `INACTIVE`, and `OFFLOADED`. language="py" /> - - - - - - - - - - - + + + + + + ## Search queries @@ -438,26 +547,14 @@ Multi-tenancy collections require the tenant name (e.g. `tenantA`) with each `Ge language="py" /> - - - - - - - - - - + + + + + + ## Cross-references @@ -490,26 +595,14 @@ Multi-tenancy collections require the tenant name (e.g. `tenantA`) when creating language="py" /> - - - - - - - - - - + + + + + + ## Backups @@ -530,7 +631,9 @@ Backups of [multi-tenant collections](../concepts/data.md#multi-tenancy) will on - [Connect to Weaviate](/weaviate/connections/index.mdx) - [How to: Manage collections](../manage-collections/index.mdx) -- References: REST API: Schema +- + References: REST API: Schema + - [Concepts: Data Structure: Multi-tenancy](../concepts/data.md#multi-tenancy) ## Questions and feedback diff --git a/docs/weaviate/manage-collections/tenant-states.mdx b/docs/weaviate/manage-collections/tenant-states.mdx index af3e93ae..1fa7ccc3 100644 --- a/docs/weaviate/manage-collections/tenant-states.mdx +++ b/docs/weaviate/manage-collections/tenant-states.mdx @@ -9,6 +9,8 @@ import TabItem from '@theme/TabItem'; import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBlock'; import PyCode from '!!raw-loader!/_includes/code/howto/manage-data.multi-tenancy.py'; import TSCode from '!!raw-loader!/_includes/code/howto/manage-data.multi-tenancy.ts'; +import JavaV6Code from "!!raw-loader!/_includes/code/java-v6/src/test/java/_ManageCollectionsMultiTenancyTest.java"; +import CSharpCode from "!!raw-loader!/_includes/code/csharp/_ManageCollectionsMultiTenancyTest.cs"; ![Storage Tiers](./img/storage-tiers.jpg) @@ -104,7 +106,6 @@ To activate an `INACTIVE` tenant from disk, or to onload and activate an `OFFLOA language="py" /> - + + + + + + ## Deactivate tenant @@ -131,7 +148,6 @@ To deactivate an `ACTIVE` tenant, or to onload an `OFFLOADED` tenant from cloud language="py" /> - + + + + + + ## Offload tenant @@ -158,7 +190,6 @@ To offload an `ACTIVE` or `INACTIVE` tenant to cloud, call: language="py" /> - + + + + + + :::caution Requires Offload Module @@ -191,7 +238,6 @@ Enable this to automatically activate `INACTIVE` or `OFFLOADED` tenants if a sea language="py" /> - - + + + + + + ## Questions and feedback diff --git a/docs/weaviate/manage-collections/vector-config.mdx b/docs/weaviate/manage-collections/vector-config.mdx index e23775a5..e24be230 100644 --- a/docs/weaviate/manage-collections/vector-config.mdx +++ b/docs/weaviate/manage-collections/vector-config.mdx @@ -11,7 +11,9 @@ import FilteredTextBlock from "@site/src/components/Documentation/FilteredTextBl import PyCode from "!!raw-loader!/_includes/code/howto/manage-data.collections.py"; import TSCode from "!!raw-loader!/_includes/code/howto/manage-data.collections.ts"; import JavaCode from "!!raw-loader!/_includes/code/howto/java/src/test/java/io/weaviate/docs/manage-data.classes.java"; +import JavaV6Code from "!!raw-loader!/_includes/code/java-v6/src/test/java/ManageCollectionsTest.java"; import GoCode from "!!raw-loader!/_includes/code/howto/go/docs/manage-data.classes_test.go"; +import CSharpCode from "!!raw-loader!/_includes/code/csharp/ManageCollectionsTest.cs"; import VectorConfigSyntax from "/_includes/vector-config-syntax.mdx"; @@ -40,36 +42,46 @@ Collection level settings override default values and general configuration para language="py" /> - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + ## Specify vectorizer settings @@ -89,36 +101,46 @@ To configure how a vectorizer works (i.e. what model to use) with a specific col language="py" /> - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + ## Define named vectors @@ -139,36 +161,46 @@ As such, each named vector configuration can include its own vectorizer and vect language="py" /> - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + ## Add new named vectors @@ -188,20 +220,13 @@ Named vectors can be added to existing collection definitions with named vectors /> - - - -```java -// Java support coming soon -``` - - ```go @@ -209,6 +234,22 @@ Named vectors can be added to existing collection definitions with named vectors ``` + + + + + + :::caution Objects aren't automatically revectorized @@ -231,13 +272,21 @@ Multi-vector embeddings, also known as multi-vectors, represent a single object language="py" /> - - + + + + + + + + :::tip Use quantization and encoding to compress your vectors @@ -266,27 +323,14 @@ The vector index type can be set for each collection at creation time, between ` language="py" /> - - - - - - - - - - - + + + + + + + + + + + +
@@ -321,27 +389,14 @@ Was added in `v1.27` language="py" /> - - - - - - - - - - - + + + + + + + + + + + +
@@ -376,27 +455,14 @@ Configure individual properties in a collection. Each property can have it's own language="py" /> - - - - - - - - - - - + + + + + + + + + + + + ## Specify a distance metric @@ -420,27 +510,14 @@ If you choose to bring your own vectors, you should specify the `distance metric language="py" /> - - - - - - - - - - - + + + + + + + + + + + +
@@ -465,7 +566,9 @@ For details on the configuration parameters, see the following: ## Further resources -- API References: REST: Schema +- + API References: REST: Schema + - [References: Configuration: Schema](/weaviate/config-refs/collections.mdx) - [Concepts: Data structure](../concepts/data.md) diff --git a/docs/weaviate/manage-objects/create.mdx b/docs/weaviate/manage-objects/create.mdx index 56a7eaa7..3df966a1 100644 --- a/docs/weaviate/manage-objects/create.mdx +++ b/docs/weaviate/manage-objects/create.mdx @@ -4,15 +4,16 @@ sidebar_position: 10 image: og/docs/howto.jpg --- -import SkipLink from '/src/components/SkipValidationLink' -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; -import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBlock'; -import PyCode from '!!raw-loader!/_includes/code/howto/manage-data.create.py'; -import TSCode from '!!raw-loader!/_includes/code/howto/manage-data.create.ts'; -import JavaCode from '!!raw-loader!/_includes/code/howto/java/src/test/java/io/weaviate/docs/manage-data.create.java'; -import GoCode from '!!raw-loader!/_includes/code/howto/go/docs/manage-data.create_test.go'; - +import SkipLink from "/src/components/SkipValidationLink"; +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; +import FilteredTextBlock from "@site/src/components/Documentation/FilteredTextBlock"; +import PyCode from "!!raw-loader!/_includes/code/howto/manage-data.create.py"; +import TSCode from "!!raw-loader!/_includes/code/howto/manage-data.create.ts"; +import JavaCode from "!!raw-loader!/_includes/code/howto/java/src/test/java/io/weaviate/docs/manage-data.create.java"; +import JavaV6Code from "!!raw-loader!/_includes/code/java-v6/src/test/java/ManageObjectsCreateTest.java"; +import GoCode from "!!raw-loader!/_includes/code/howto/go/docs/manage-data.create_test.go"; +import CSharpCode from "!!raw-loader!/_includes/code/csharp/ManageObjectsCreateTest.cs"; The examples on this page demonstrate how to create individual objects in Weaviate. @@ -33,8 +34,6 @@ This example creates an object in the `JeopardyQuestion` collection. language="py" /> - - - - - + + + + - - + + + + @@ -73,7 +85,7 @@ To create an object, specify the following: - The object data you want to add - The target collection -- If [multi-tenancy](../concepts/data.md#multi-tenancy) is enabled, [specify the tenant]( ../manage-collections/multi-tenancy.mdx) +- If [multi-tenancy](../concepts/data.md#multi-tenancy) is enabled, [specify the tenant](../manage-collections/multi-tenancy.mdx) By default, [auto-schema](/weaviate/config-refs/collections.mdx#auto-schema) creates new collections and adds new properties. @@ -92,8 +104,6 @@ When you create an object, you can provide a vector. (For specifying multiple, n language="py" /> - - - - - + + + + - - + + + + @@ -139,8 +162,6 @@ When you create an object, you can specify named vectors (if [configured in your language="py" /> - - - + + + + + + ## Create an object with a specified ID @@ -169,8 +205,6 @@ If no ID is provided, Weaviate will generate a random [UUID](https://en.wikipedi language="py" /> - - - - - + + + + - - + + + + @@ -217,8 +264,6 @@ Object IDs are not randomly generated. The same value always generates the same language="py" /> - - - - - - -```java -// This feature is under development -``` - - - ```go @@ -244,9 +279,24 @@ Object IDs are not randomly generated. The same value always generates the same ``` + + + + + + -
Additional information @@ -260,12 +310,13 @@ To generate deterministic IDs, use one of these methods: ## Create an object with cross-references -import CrossReferencePerformanceNote from '/_includes/cross-reference-performance-note.mdx'; +import CrossReferencePerformanceNote from "/_includes/cross-reference-performance-note.mdx"; -import XrefPyCode from '!!raw-loader!/_includes/code/howto/manage-data.cross-refs.py'; -import XrefTSCode from '!!raw-loader!/_includes/code/howto/manage-data.cross-refs'; +import XrefPyCode from "!!raw-loader!/_includes/code/howto/manage-data.cross-refs.py"; +import XrefTSCode from "!!raw-loader!/_includes/code/howto/manage-data.cross-refs"; +import XrefJavaV6Code from "!!raw-loader!/_includes/code/java-v6/src/test/java/ManageCollectionsCrossReferencesTest.java"; You can create an object with cross-references to other objects. @@ -278,8 +329,6 @@ You can create an object with cross-references to other objects. language="py" /> - - - + + + + + + :::tip Additional information @@ -297,15 +361,15 @@ See [How-to: Cross-references](../manage-collections/cross-references.mdx) for m ## Create an object with `geoCoordinates` -import GeoLimitations from '/_includes/geo-limitations.mdx'; +import GeoLimitations from "/_includes/geo-limitations.mdx"; - + If you want to supply a [`geoCoordinates`](/weaviate/config-refs/datatypes.md#geocoordinates) property, you need to specify the `latitude` and `longitude` as floating point decimal degrees: -import CreateWithGeo from '/_includes/code/howto/manage-data.create.with.geo.mdx'; +import CreateWithGeo from "/_includes/code/howto/manage-data.create.with.geo.mdx"; - + ## Validate objects before creation @@ -320,8 +384,6 @@ Before you create an object, you can - - - - - + + + + - - + + + + ## Multiple vector embeddings (named vectors) -import MultiVectorSupport from '/_includes/multi-vector-support.mdx'; +import MultiVectorSupport from "/_includes/multi-vector-support.mdx"; @@ -361,10 +436,12 @@ import MultiVectorSupport from '/_includes/multi-vector-support.mdx'; - [Connect to Weaviate](/weaviate/connections/index.mdx) - [How-to: (Batch) Import items](./import.mdx) -- References: REST - /v1/objects +- + References: REST - /v1/objects + ## Questions and feedback -import DocsFeedback from '/_includes/docs-feedback.mdx'; +import DocsFeedback from "/_includes/docs-feedback.mdx"; - + diff --git a/docs/weaviate/manage-objects/delete.mdx b/docs/weaviate/manage-objects/delete.mdx index 99703fe6..ec695e0e 100644 --- a/docs/weaviate/manage-objects/delete.mdx +++ b/docs/weaviate/manage-objects/delete.mdx @@ -4,36 +4,34 @@ sidebar_position: 35 image: og/docs/howto.jpg --- - - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; -import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBlock'; -import PyCode from '!!raw-loader!/_includes/code/howto/manage-data.delete.py'; -import PyCodeV3 from '!!raw-loader!/_includes/code/howto/manage-data.delete-v3.py'; -import TSCode from '!!raw-loader!/_includes/code/howto/manage-data.delete.ts'; -import JavaCode from '!!raw-loader!/_includes/code/howto/java/src/test/java/io/weaviate/docs/manage-data.delete.java'; -import GoCode from '!!raw-loader!/_includes/code/howto/go/docs/manage-data.delete_test.go'; -import SkipLink from '/src/components/SkipValidationLink' - +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; +import FilteredTextBlock from "@site/src/components/Documentation/FilteredTextBlock"; +import PyCode from "!!raw-loader!/_includes/code/howto/manage-data.delete.py"; +import PyCodeV3 from "!!raw-loader!/_includes/code/howto/manage-data.delete-v3.py"; +import TSCode from "!!raw-loader!/_includes/code/howto/manage-data.delete.ts"; +import JavaCode from "!!raw-loader!/_includes/code/howto/java/src/test/java/io/weaviate/docs/manage-data.delete.java"; +import JavaV6Code from "!!raw-loader!/_includes/code/java-v6/src/test/java/ManageObjectsDeleteTest.java"; +import GoCode from "!!raw-loader!/_includes/code/howto/go/docs/manage-data.delete_test.go"; +import CSharpCode from "!!raw-loader!/_includes/code/csharp/ManageObjectsDeleteTest.cs"; +import SkipLink from "/src/components/SkipValidationLink"; Weaviate allows object deletion by id or by a set of criteria. - -import RestObjectsCRUDClassnameNote from '/_includes/rest-objects-crud-classname-note.md'; +import RestObjectsCRUDClassnameNote from "/_includes/rest-objects-crud-classname-note.md";
Additional information - - To delete objects, you must provide the collection name as well as identifying criteria (e.g. object id or filters). - - For [multi-tenancy](../concepts/data.md#multi-tenancy) collections, you will also need to specify the tenant name when deleting objects. See [Manage data: multi-tenancy operations]( ../manage-collections/multi-tenancy.mdx) for details on how. -
+- To delete objects, you must provide the collection name as well as identifying criteria (e.g. object id or filters). +- For [multi-tenancy](../concepts/data.md#multi-tenancy) collections, you will also need to specify the tenant name when deleting objects. See [Manage data: multi-tenancy operations](../manage-collections/multi-tenancy.mdx) for details on how. +
- +{" "} +
- ## Delete object by id To delete by id, specify the collection name and the object id. @@ -47,7 +45,6 @@ To delete by id, specify the collection name and the object id. language="py" />
- - - + + + + + + + /> - - + - - ## Delete multiple objects To delete objects that match a set of criteria, specify the collection and a [`where` filter](../search/similarity.md). @@ -110,8 +118,6 @@ To delete objects that match a set of criteria, specify the collection and a [`w language="py" /> - - - - + + + + + + - - + @@ -150,7 +169,6 @@ To delete objects that match a set of criteria, specify the collection and a [`w
- ### ContainsAny / ContainsAll / ContainsNone Use `ContainsAny` / `ContainsAll` / `ContainsNone` filters to delete of objects by a set of criteria. @@ -164,8 +182,6 @@ Use `ContainsAny` / `ContainsAll` / `ContainsNone` filters to delete of objects language="py" />
- - + +```go + response, err := client.Batch().ObjectsBatchDeleter(). + WithClassName("EphemeralObject"). + WithOutput("minimal"). + WithWhere(filters.Where(). + WithPath([]string{"name"}). + // highlight-start + WithOperator(filters.ContainsAny). + WithValueText("asia", "europe")). // Note the array syntax + // highlight-end + Do(ctx) +``` + + + + ```java @@ -192,21 +230,13 @@ client.batch().objectsBatchDeleter() ``` - - -```go - response, err := client.Batch().ObjectsBatchDeleter(). - WithClassName("EphemeralObject"). - WithOutput("minimal"). - WithWhere(filters.Where(). - WithPath([]string{"name"}). - // highlight-start - WithOperator(filters.ContainsAny). - WithValueText("asia", "europe")). // Note the array syntax - // highlight-end - Do(ctx) -``` - + + @@ -217,7 +247,6 @@ This feature was added in Weaviate `v1.21`.
- ## Delete multiple objects by id To delete multiple objects by their id values, use a filter (e.g. `ContainsAny`) with `id` based criteria. @@ -242,8 +271,6 @@ The default `QUERY_MAXIMUM_RESULTS` value is 10,000. This may be configurable, e language="py" /> - - + +```go + response, err := client.Batch().ObjectsBatchDeleter(). + WithClassName("EphemeralObject"). + WithOutput("minimal"). + WithWhere(filters.Where(). + WithPath([]string{"id"}). + // highlight-start + WithOperator(filters.ContainsAny). + WithValueText("12c88739-7a4e-49fd-bf53-d6a829ba0261", "3022b8be-a6dd-4ef4-b213-821f65cee53b", "30de68c1-dd53-4bed-86ea-915f34faea63")). // Note the array syntax + // highlight-end + Do(ctx) +``` + + + + ```java @@ -270,21 +319,13 @@ client.batch().objectsBatchDeleter() ``` - - -```go - response, err := client.Batch().ObjectsBatchDeleter(). - WithClassName("EphemeralObject"). - WithOutput("minimal"). - WithWhere(filters.Where(). - WithPath([]string{"id"}). - // highlight-start - WithOperator(filters.ContainsAny). - WithValueText("12c88739-7a4e-49fd-bf53-d6a829ba0261", "3022b8be-a6dd-4ef4-b213-821f65cee53b", "30de68c1-dd53-4bed-86ea-915f34faea63")). // Note the array syntax - // highlight-end - Do(ctx) -``` - + + @@ -306,8 +347,6 @@ Objects must belong to a collection in Weaviate. Accordingly [deleting collectio language="py" /> - - - - + + + + + + - - + -
Example response @@ -352,14 +403,15 @@ It should produce a response like the one below:
- ## Related pages - [Connect to Weaviate](/weaviate/connections/index.mdx) -- References: REST - /v1/objects +- + References: REST - /v1/objects + ## Questions and feedback -import DocsFeedback from '/_includes/docs-feedback.mdx'; +import DocsFeedback from "/_includes/docs-feedback.mdx"; - + diff --git a/docs/weaviate/manage-objects/import.mdx b/docs/weaviate/manage-objects/import.mdx index f30395cb..641fddb5 100644 --- a/docs/weaviate/manage-objects/import.mdx +++ b/docs/weaviate/manage-objects/import.mdx @@ -12,6 +12,8 @@ import PySuppCode from '!!raw-loader!/_includes/code/howto/sample-data.py'; import TSCode from '!!raw-loader!/_includes/code/howto/manage-data.import.ts'; import TsSuppCode from '!!raw-loader!/_includes/code/howto/sample-data.ts'; import JavaCode from '!!raw-loader!/_includes/code/howto/java/src/test/java/io/weaviate/docs/manage-data.import.java'; +import JavaV6Code from '!!raw-loader!/_includes/code/java-v6/src/test/java/ManageObjectsImportTest.java'; +import CSharpCode from '!!raw-loader!/_includes/code/csharp/ManageObjectsImportTest.cs'; import GoCode from '!!raw-loader!/_includes/code/howto/go/docs/manage-data.import_test.go'; import SkipLink from '/src/components/SkipValidationLink' @@ -60,6 +62,22 @@ Find out more about error handling on the Python client [reference page](/weavia language="ts" />
+ + + + + + - + @@ -105,20 +123,36 @@ Here's how to import objects into a collection named `MyCollection` using [serve ``` - + -```java -// Java support coming soon +```go +// Go support coming soon ``` - + + + + -```go -// Go support coming soon +```java +// Java support coming soon ``` + + + ## Use the gRPC API @@ -147,6 +181,11 @@ The TypeScript client v3 uses gRPC by default.
The legacy TypeScript client does not support gRPC. +
+ + +The Java client v6 uses gRPC by default. + @@ -181,6 +220,11 @@ if err != nil { } ``` + + + +The C# uses gRPC by default. + @@ -227,6 +271,22 @@ Weaviate generates an UUID for each object. Object IDs must be unique. If you se language="ts" /> + + + + + + - + @@ -266,6 +326,22 @@ Use the `vector` property to specify a vector for each object. language="ts" /> + + + + + + - + @@ -308,6 +384,22 @@ When you create an object, you can specify named vectors (if [configured in your language="ts" /> + + + + + + ## Import with references @@ -323,6 +415,22 @@ You can batch create links from an object to another other object through cross- language="py" />
+ + + + + + ## Python-specific considerations @@ -362,6 +470,22 @@ To try the example code, download the sample data and create the sample input fi language="py" /> + + + + + +
@@ -386,6 +510,22 @@ To try the example code, download the sample data and create the sample input fi language="ts" /> + + + + + +
@@ -410,6 +550,22 @@ To try the example code, download the sample data and create the sample input fi language="ts" /> + + + + + +
@@ -438,6 +594,22 @@ Note that each provider exposes different configuration options. language="py" /> + + + + + + ## Additional considerations diff --git a/docs/weaviate/manage-objects/read-all-objects.mdx b/docs/weaviate/manage-objects/read-all-objects.mdx index f6ea27bf..1d9e8aca 100644 --- a/docs/weaviate/manage-objects/read-all-objects.mdx +++ b/docs/weaviate/manage-objects/read-all-objects.mdx @@ -5,14 +5,15 @@ image: og/docs/howto.jpg # tags: ['how-to', 'cursor'] --- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; -import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBlock'; -import PyCode from '!!raw-loader!/_includes/code/howto/manage-data.read-all-objects.py'; -import TSCode from '!!raw-loader!/_includes/code/howto/manage-data.read-all-objects.ts'; -import JavaCode from '!!raw-loader!/_includes/code/howto/java/src/test/java/io/weaviate/docs/manage-data.read-all-objects.java'; -import GoCode from '!!raw-loader!/_includes/code/howto/go/docs/manage-data.read-all-objects_test.go'; +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; +import FilteredTextBlock from "@site/src/components/Documentation/FilteredTextBlock"; +import PyCode from "!!raw-loader!/_includes/code/howto/manage-data.read-all-objects.py"; +import TSCode from "!!raw-loader!/_includes/code/howto/manage-data.read-all-objects.ts"; +import JavaCode from "!!raw-loader!/_includes/code/howto/java/src/test/java/io/weaviate/docs/manage-data.read-all-objects.java"; +import JavaV6Code from "!!raw-loader!/_includes/code/java-v6/src/test/java/ManageObjectsReadAllTest.java"; +import CSharpCode from "!!raw-loader!/_includes/code/csharp/ManageObjectsReadAllTest.cs"; +import GoCode from "!!raw-loader!/_includes/code/howto/go/docs/manage-data.read-all-objects_test.go"; Weaviate provides the necessary APIs to iterate through all your data. This is useful when you want to manually copy/migrate your data (and vector embeddings) from one place to another. @@ -27,7 +28,6 @@ The new API clients (currently supported by the Python Client v4), encapsulate t The following code iterates through all objects, providing the properties and id for each object. - - - - - - + + + + - - + + + + @@ -72,7 +83,6 @@ The following code iterates through all objects, providing the properties and id Read through all data including the vectors. (Also applicable where [named vectors](../config-refs/collections.mdx#named-vectors) are used.) - - - - + + + + + + + + + + + + ## Read all objects - Multi-tenant collections @@ -99,11 +138,10 @@ Read through all data including the vectors. (Also applicable where [named vecto Iterate through all tenants and read data for each. :::tip Multi-tenancy -For classes where [multi-tenancy](../concepts/data.md#multi-tenancy) is enabled, you need to specify the tenant name when reading or creating objects. See [Manage data: multi-tenancy operations]( ../manage-collections/multi-tenancy.mdx) for details. +For classes where [multi-tenancy](../concepts/data.md#multi-tenancy) is enabled, you need to specify the tenant name when reading or creating objects. See [Manage data: multi-tenancy operations](../manage-collections/multi-tenancy.mdx) for details. ::: - - - - + + + + + + + + + + + + ## Related pages @@ -130,10 +197,10 @@ For classes where [multi-tenancy](../concepts/data.md#multi-tenancy) is enabled, - [Connect to Weaviate](/weaviate/connections/index.mdx) - [How-to: Read objects](./read.mdx) - [References: GraphQL - Additional Operators](../api/graphql/additional-operators.md#cursor-with-after) -- [Manage data: multi-tenancy operations]( ../manage-collections/multi-tenancy.mdx) +- [Manage data: multi-tenancy operations](../manage-collections/multi-tenancy.mdx) ## Questions and feedback -import DocsFeedback from '/_includes/docs-feedback.mdx'; +import DocsFeedback from "/_includes/docs-feedback.mdx"; - + diff --git a/docs/weaviate/manage-objects/read.mdx b/docs/weaviate/manage-objects/read.mdx index 5f8eaf95..bdcb6057 100644 --- a/docs/weaviate/manage-objects/read.mdx +++ b/docs/weaviate/manage-objects/read.mdx @@ -4,23 +4,24 @@ sidebar_position: 20 image: og/docs/howto.jpg --- -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; -import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBlock'; -import PyCode from '!!raw-loader!/_includes/code/howto/manage-data.read.py'; -import TSCode from '!!raw-loader!/_includes/code/howto/manage-data.read.ts'; -import JavaCode from '!!raw-loader!/_includes/code/howto/java/src/test/java/io/weaviate/docs/manage-data.read.java'; -import GoCode from '!!raw-loader!/_includes/code/howto/go/docs/manage-data.read_test.go'; -import SkipLink from '/src/components/SkipValidationLink' - +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; +import FilteredTextBlock from "@site/src/components/Documentation/FilteredTextBlock"; +import PyCode from "!!raw-loader!/_includes/code/howto/manage-data.read.py"; +import TSCode from "!!raw-loader!/_includes/code/howto/manage-data.read.ts"; +import JavaCode from "!!raw-loader!/_includes/code/howto/java/src/test/java/io/weaviate/docs/manage-data.read.java"; +import JavaV6Code from "!!raw-loader!/_includes/code/java-v6/src/test/java/ManageObjectsReadTest.java"; +import CSharpCode from "!!raw-loader!/_includes/code/csharp/ManageObjectsReadTest.cs"; +import GoCode from "!!raw-loader!/_includes/code/howto/go/docs/manage-data.read_test.go"; +import SkipLink from "/src/components/SkipValidationLink"; Instead of querying your database, you can use an ID to retrieve individual objects. -import RestObjectsCRUDClassnameNote from '/_includes/rest-objects-crud-classname-note.md'; +import RestObjectsCRUDClassnameNote from "/_includes/rest-objects-crud-classname-note.md";
Additional information - +
## Get an object by id @@ -36,8 +37,6 @@ Use an ID to retrieve an object. If the id doesn't exist, Weaviate returns a 404 language="py" /> - - - - - + + + + - - + + + + - ## Retrieve the object's vector Object vectors can be retrieved by specifying its return. @@ -81,8 +92,6 @@ Object vectors can be retrieved by specifying its return. language="py" /> - - - - - + + + + - - + + + + @@ -125,8 +147,6 @@ Where [named vectors](../config-refs/collections.mdx#named-vectors) are used, yo language="py" /> - - - - + + + + + + ## Check object existence To efficiently check if an object with a given [id](../api/graphql/additional-properties.md#id) exists without retrieving it, make a `HEAD` request to the `/v1/objects/` REST endpoint, or use the following client code: -import CheckObjectExistence from '/_includes/code/howto/manage-data.read.check.existence.mdx'; +import CheckObjectExistence from "/_includes/code/howto/manage-data.read.check.existence.mdx"; - + ## Related pages - [Connect to Weaviate](/weaviate/connections/index.mdx) - [How-to: Search](../search/index.mdx) - [How-to: Read all objects](./read-all-objects.mdx) -- References: REST - /v1/objects +- + References: REST - /v1/objects + ## Questions and feedback -import DocsFeedback from '/_includes/docs-feedback.mdx'; +import DocsFeedback from "/_includes/docs-feedback.mdx"; - + diff --git a/docs/weaviate/manage-objects/update.mdx b/docs/weaviate/manage-objects/update.mdx index a86476fd..b8a78623 100644 --- a/docs/weaviate/manage-objects/update.mdx +++ b/docs/weaviate/manage-objects/update.mdx @@ -4,31 +4,32 @@ sidebar_position: 30 image: og/docs/howto.jpg --- - - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; -import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBlock'; -import PyCode from '!!raw-loader!/_includes/code/howto/manage-data.update.py'; -import TSCode from '!!raw-loader!/_includes/code/howto/manage-data.update.ts'; -import JavaCode from '!!raw-loader!/_includes/code/howto/java/src/test/java/io/weaviate/docs/manage-data.update.java'; -import GoCode from '!!raw-loader!/_includes/code/howto/go/docs/manage-data.update_test.go'; -import RestObjectsCRUDClassnameNote from '/_includes/rest-objects-crud-classname-note.md'; -import SkipLink from '/src/components/SkipValidationLink' +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; +import FilteredTextBlock from "@site/src/components/Documentation/FilteredTextBlock"; +import PyCode from "!!raw-loader!/_includes/code/howto/manage-data.update.py"; +import TSCode from "!!raw-loader!/_includes/code/howto/manage-data.update.ts"; +import JavaCode from "!!raw-loader!/_includes/code/howto/java/src/test/java/io/weaviate/docs/manage-data.update.java"; +import JavaV6Code from "!!raw-loader!/_includes/code/java-v6/src/test/java/ManageObjectsUpdateTest.java"; +import CSharpCode from "!!raw-loader!/_includes/code/csharp/ManageObjectsUpdateTest.cs"; +import GoCode from "!!raw-loader!/_includes/code/howto/go/docs/manage-data.update_test.go"; +import RestObjectsCRUDClassnameNote from "/_includes/rest-objects-crud-classname-note.md"; +import SkipLink from "/src/components/SkipValidationLink"; Weaviate allows partial or complete object updates.
Additional information - - Partial updates use `PATCH` requests to the `/v1/objects` REST API endpoint under the hood. - - Complete updates use `PUT` requests to the `/v1/objects` REST API endpoint under the hood. - - Updates that include a `vector` property will recalculate the vector embedding (unless all updated `text` properties are [skipped](../manage-collections/vector-config.mdx#property-level-settings)). - - To update objects, you must provide the collection name, id and properties to update. - - For [multi-tenancy](../concepts/data.md#multi-tenancy) collections, you will also need to specify the tenant name. See [Manage data: multi-tenancy operations]( ../manage-collections/multi-tenancy.mdx) for details on how. -
+- Partial updates use `PATCH` requests to the `/v1/objects` REST API endpoint under the hood. +- Complete updates use `PUT` requests to the `/v1/objects` REST API endpoint under the hood. +- Updates that include a `vector` property will recalculate the vector embedding (unless all updated `text` properties are [skipped](../manage-collections/vector-config.mdx#property-level-settings)). +- To update objects, you must provide the collection name, id and properties to update. +- For [multi-tenancy](../concepts/data.md#multi-tenancy) collections, you will also need to specify the tenant name. See [Manage data: multi-tenancy operations](../manage-collections/multi-tenancy.mdx) for details on how. +
- +{" "} +
@@ -36,9 +37,9 @@ Weaviate allows partial or complete object updates. This operation replaces the entire value of the specified properties only, leaving the unspecified properties. Provide the collection name, the object id, and the properties to update. -If you update the value of a *previously vectorized* property, Weaviate re-vectorizes the object automatically. This also reindexes the updated object. +If you update the value of a _previously vectorized_ property, Weaviate re-vectorizes the object automatically. This also reindexes the updated object. -However, if you add a *new* property to your collection definition, Weaviate only vectorizes the new objects. Weaviate doesn't re-vectorize and re-index existing objects when a new property is defined, only when an existing property is updated. +However, if you add a _new_ property to your collection definition, Weaviate only vectorizes the new objects. Weaviate doesn't re-vectorize and re-index existing objects when a new property is defined, only when an existing property is updated. @@ -57,20 +58,36 @@ However, if you add a *new* property to your collection definition, Weaviate onl language="ts" /> - + + + + - + + + + @@ -88,7 +105,6 @@ The object vector can also be updated similarly to properties. For [named vector language="py" />
- + +> Coming soon + + + + - > Coming soon +> Coming soon - - - - > Coming soon - + + - ## Replace an entire object The entire object can be replaced by providing the collection name, id and the new object. @@ -126,7 +154,6 @@ The entire object can be replaced by providing the collection name, id and the n language="py" /> - - - - + + + + - - + + + + - ## Delete a property Deleting or updating properties in the collection definition is [not yet supported](https://github.com/weaviate/weaviate/issues/2848). @@ -172,7 +211,6 @@ At object level, you can replace the object with a copy that has those propertie language="py" /> - - - + + + + + + - - + - ## Related pages - [Connect to Weaviate](/weaviate/connections/index.mdx) -- References: REST - /v1/objects - +- + References: REST - /v1/objects + ## Questions and feedback -import DocsFeedback from '/_includes/docs-feedback.mdx'; +import DocsFeedback from "/_includes/docs-feedback.mdx"; - + diff --git a/docs/weaviate/model-providers/weaviate/embeddings.md b/docs/weaviate/model-providers/weaviate/embeddings.md index 123e7bf0..65741f4d 100644 --- a/docs/weaviate/model-providers/weaviate/embeddings.md +++ b/docs/weaviate/model-providers/weaviate/embeddings.md @@ -11,22 +11,24 @@ image: og/docs/integrations/provider_integrations_wes.jpg # Weaviate Embeddings -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; -import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBlock'; -import PyConnect from '!!raw-loader!../_includes/provider.connect.weaviate.py'; -import TSConnect from '!!raw-loader!../_includes/provider.connect.weaviate.ts'; -import GoConnect from '!!raw-loader!/_includes/code/howto/go/docs/model-providers/1-connect-weaviate-embeddings/main.go'; -import JavaConnect from '!!raw-loader!/_includes/code/howto/java/src/test/java/io/weaviate/docs/model_providers/ConnectWeaviateEmbeddingsTest.java'; -import PyCode from '!!raw-loader!../_includes/provider.vectorizer.py'; -import TSCode from '!!raw-loader!../_includes/provider.vectorizer.ts'; -import GoCode from '!!raw-loader!/_includes/code/howto/go/docs/model-providers/2-usage-text/main.go'; -import JavaCode from '!!raw-loader!/_includes/code/howto/java/src/test/java/io/weaviate/docs/model_providers/UsageWeaviateTextEmbeddingsArcticEmbedLV20.java'; -import JavaImportQueries from '!!raw-loader!/_includes/code/howto/java/src/test/java/io/weaviate/docs/model_providers/ImportAndQueries.java'; +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; +import FilteredTextBlock from "@site/src/components/Documentation/FilteredTextBlock"; +import PyConnect from "!!raw-loader!../\_includes/provider.connect.weaviate.py"; +import TSConnect from "!!raw-loader!../\_includes/provider.connect.weaviate.ts"; +import GoConnect from "!!raw-loader!/\_includes/code/howto/go/docs/model-providers/1-connect-weaviate-embeddings/main.go"; +import JavaConnect from "!!raw-loader!/\_includes/code/howto/java/src/test/java/io/weaviate/docs/model_providers/ConnectWeaviateEmbeddingsTest.java"; +import PyCode from "!!raw-loader!../\_includes/provider.vectorizer.py"; +import TSCode from "!!raw-loader!../\_includes/provider.vectorizer.ts"; +import GoCode from "!!raw-loader!/\_includes/code/howto/go/docs/model-providers/2-usage-text/main.go"; +import JavaV6Code from "!!raw-loader!/\_includes/code/java-v6/src/test/java/ModelProvidersTest.java"; +import CSharpCode from "!!raw-loader!/\_includes/code/csharp/ModelProvidersTest.cs"; +import JavaCode from "!!raw-loader!/\_includes/code/howto/java/src/test/java/io/weaviate/docs/model_providers/UsageWeaviateTextEmbeddingsArcticEmbedLV20.java"; +import JavaImportQueries from "!!raw-loader!/\_includes/code/howto/java/src/test/java/io/weaviate/docs/model_providers/ImportAndQueries.java"; Weaviate Embeddings' models can be accessed directly from a Weaviate Cloud instance. -[Configure a Weaviate vector index](#configure-the-vectorizer) to use a Weaviate Embeddings model, and Weaviate will generate embeddings for various operations using the specified model and your Weaviate API key. This feature is called the *vectorizer*. +[Configure a Weaviate vector index](#configure-the-vectorizer) to use a Weaviate Embeddings model, and Weaviate will generate embeddings for various operations using the specified model and your Weaviate API key. This feature is called the _vectorizer_. At [import time](#data-import), Weaviate generates text object embeddings and saves them into the index. For [vector](#vector-near-text-search) and [hybrid](#hybrid-search) search operations, Weaviate converts text queries into embeddings. @@ -34,7 +36,7 @@ At [import time](#data-import), Weaviate generates text object embeddings and sa ## Requirements -import Requirements from '/_includes/weaviate-embeddings-requirements.mdx'; +import Requirements from "/\_includes/weaviate-embeddings-requirements.mdx"; @@ -56,7 +58,6 @@ Weaviate Embeddings is integrated with Weaviate Cloud. Your Weaviate Cloud crede language="py" /> - - - + + + - + + + ## Configure the vectorizer @@ -99,7 +113,6 @@ Weaviate Embeddings is integrated with Weaviate Cloud. Your Weaviate Cloud crede language="py" /> - - - + + + - + + + ### Select a model @@ -142,7 +168,6 @@ You can specify one of the [available models](#available-models) for the vectori language="py" />
- - - + + + - + + + You can [specify](#vectorizer-parameters) one of the [available models](#available-models) for Weaviate to use. The [default model](#available-models) is used if no model is specified. -import VectorizationBehavior from '/_includes/vectorization.behavior.mdx'; +import VectorizationBehavior from "/\_includes/vectorization.behavior.mdx";
Vectorization behavior - - - +
### Vectorizer parameters -import WeaviateEmbeddingsVectorizerParameters from '/_includes/weaviate-embeddings-vectorizer-parameters.mdx'; +import WeaviateEmbeddingsVectorizerParameters from "/\_includes/weaviate-embeddings-vectorizer-parameters.mdx"; @@ -203,7 +239,6 @@ After configuring the vectorizer, [import data](../../manage-objects/import.mdx) language="py" /> - - - + + + - + + + :::tip Re-use existing vectors @@ -250,7 +298,6 @@ When you perform a [vector search](../../search/similarity.md#search-with-text), The query below returns the `n` most similar objects from the database, set by `limit`. - - - - + - + + + - + + + ### Hybrid search @@ -309,7 +368,6 @@ The query below returns the `n` best scoring objects from the database, set by ` language="py" /> - - - + + + + + + ## References ### Available models -import WeaviateEmbeddingsModels from '/_includes/weaviate-embeddings-models.mdx'; +import WeaviateEmbeddingsModels from "/\_includes/weaviate-embeddings-models.mdx"; @@ -361,6 +433,6 @@ Once the integrations are configured at the collection, the data management and ## Questions and feedback -import DocsFeedback from '/_includes/docs-feedback.mdx'; +import DocsFeedback from "/\_includes/docs-feedback.mdx"; - + diff --git a/docs/weaviate/quickstart/index.md b/docs/weaviate/quickstart/index.md index 1fec0001..eaa75822 100644 --- a/docs/weaviate/quickstart/index.md +++ b/docs/weaviate/quickstart/index.md @@ -120,7 +120,7 @@ import LatestWeaviateVersion from '/\_includes/latest-weaviate-version.mdx'; ### 1.2 Install a client library -We recommend using a [client library](../client-libraries/index.mdx) to work with Weaviate. Follow the instructions below to install one of the official client libraries, available in [Python](../client-libraries/python/index.mdx), [JavaScript/TypeScript](../client-libraries/typescript/index.mdx), [Go](../client-libraries/go.md), and [Java](../client-libraries/java.md). +We recommend using a [client library](../client-libraries/index.mdx) to work with Weaviate. Follow the instructions below to install one of the official client libraries, available in [Python](../client-libraries/python/index.mdx), [JavaScript/TypeScript](../client-libraries/typescript/index.mdx), [Go](../client-libraries/go.md), and [Java](../client-libraries/java/index.mdx). import CodeClientInstall from '/\_includes/code/quickstart/clients.install.mdx'; diff --git a/docs/weaviate/quickstart/local.md b/docs/weaviate/quickstart/local.md index 6ed3e07a..a95e9e53 100644 --- a/docs/weaviate/quickstart/local.md +++ b/docs/weaviate/quickstart/local.md @@ -130,7 +130,7 @@ docker-compose up -d ### 1.2 Install a client library -We recommend using a [client library](../client-libraries/index.mdx) to work with Weaviate. Follow the instructions below to install one of the official client libraries, available in [Python](../client-libraries/python/index.mdx), [JavaScript/TypeScript](../client-libraries/typescript/index.mdx), [Go](../client-libraries/go.md), and [Java](../client-libraries/java.md). +We recommend using a [client library](../client-libraries/index.mdx) to work with Weaviate. Follow the instructions below to install one of the official client libraries, available in [Python](../client-libraries/python/index.mdx), [JavaScript/TypeScript](../client-libraries/typescript/index.mdx), [Go](../client-libraries/go.md), and [Java](../client-libraries/java/index.mdx). import CodeClientInstall from '/_includes/code/quickstart/clients.install.mdx'; diff --git a/docs/weaviate/search/aggregate.md b/docs/weaviate/search/aggregate.md index 98dee7ec..6acc4e21 100644 --- a/docs/weaviate/search/aggregate.md +++ b/docs/weaviate/search/aggregate.md @@ -8,11 +8,12 @@ image: og/docs/howto.jpg import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBlock'; -import PyCode from '!!raw-loader!/_includes/code/howto/search.aggregate.py'; -import PyCodeV3 from '!!raw-loader!/_includes/code/howto/search.aggregate-v3.py'; -import TSCode from '!!raw-loader!/_includes/code/howto/search.aggregate.ts'; -import GoCode from '!!raw-loader!/_includes/code/howto/go/docs/mainpkg/search-aggregation_test.go'; - +import PyCode from '!!raw-loader!/\_includes/code/howto/search.aggregate.py'; +import PyCodeV3 from '!!raw-loader!/\_includes/code/howto/search.aggregate-v3.py'; +import TSCode from '!!raw-loader!/\_includes/code/howto/search.aggregate.ts'; +import GoCode from '!!raw-loader!/\_includes/code/howto/go/docs/mainpkg/search-aggregation_test.go'; +import JavaV6Code from "!!raw-loader!/\_includes/code/java-v6/src/test/java/SearchAggregateTest.java"; +import CSharpCode from "!!raw-loader!/\_includes/code/csharp/SearchAggregateTest.cs"; `Aggregate` queries process the result set to return calculated results. Use `aggregate` queries for groups of objects or the entire result set. @@ -26,9 +27,9 @@ To run an `Aggregate` query, specify the following: - A target collection to search - One or more aggregated properties, such as: - - A meta property - - An object property - - The `groupedBy` property + - A meta property + - An object property + - The `groupedBy` property - Select at least one sub-property for each selected property @@ -38,7 +39,7 @@ For details, see [Aggregate](/weaviate/api/graphql/aggregate). ## Retrieve the `count` meta property -Return the number of objects matched by the query. +Return the number of objects matched by the query. @@ -49,8 +50,6 @@ Return the number of objects matched by the query. language="py" /> - - - - - + + + + + + Example response - The output is like this: +The output is like this: - +
## Aggregate `text` properties -This example counts occurrence frequencies in the `question` property: +This example counts occurrence frequencies: @@ -106,8 +119,6 @@ This example counts occurrence frequencies in the `question` property: language="py" /> - - - - - + + + + + + Example response - The output is like this: +The output is like this: - + ## Aggregate `int` properties -This example sums the `points` property. +This example shows aggregation with integers. @@ -163,8 +188,6 @@ This example sums the `points` property. language="py" /> - - - - - + + + + + + Example response - The output is like this: +The output is like this: - + ## Aggregate `groupedBy` properties @@ -222,8 +259,6 @@ To retrieve aggregate data for each group, use the `groupedBy` properties. language="py" /> - - - - - + + + + + + -
Example response - The output is like this: +The output is like this: - +
-import GroupbyLimitations from '/_includes/groupby-limitations.mdx'; +import GroupbyLimitations from '/\_includes/groupby-limitations.mdx'; @@ -276,6 +324,7 @@ import GroupbyLimitations from '/_includes/groupby-limitations.mdx'; You can use `Aggregate` with a [similarity search](./similarity.md) operator (one of the `Near` operators). + Use `objectLimit` to specify the maximum number of objects to aggregate. @@ -287,8 +336,6 @@ Use `objectLimit` to specify the maximum number of objects to aggregate. language="py" />
- - - - - + + + + + + Example response - The output is like this: +The output is like this: - + ### Set a similarity `distance` @@ -336,6 +397,7 @@ Use `objectLimit` to specify the maximum number of objects to aggregate. You can use `Aggregate` with a [similarity search](./similarity.md) operator (one of the `Near` operators). + Use `distance` to specify how similar the objects should be. @@ -349,8 +411,6 @@ Use `distance` to specify how similar the objects should be. language="py" /> - - - - - + + + + + + Example response - The output is like this: +The output is like this: - + ## Aggregate with a `hybrid search` @@ -406,8 +480,6 @@ You can use `Aggregate` with a [hybrid search](./hybrid.md) operator. language="py" /> - - - - - + + + + + + Example response - The output is like this: +The output is like this: - + ## Filter results @@ -463,8 +549,6 @@ For more specific results, use a `filter` to narrow your search. language="py" /> - - - - - + + + + + + Example response - The output is like this: +The output is like this: - diff --git a/docs/weaviate/search/basics.md b/docs/weaviate/search/basics.md index 53150cf8..6b5bab37 100644 --- a/docs/weaviate/search/basics.md +++ b/docs/weaviate/search/basics.md @@ -8,11 +8,13 @@ image: og/docs/howto.jpg import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBlock'; -import PyCode from '!!raw-loader!/_includes/code/howto/search.basics.py'; -import PyCodeV3 from '!!raw-loader!/_includes/code/howto/search.basics-v3.py'; -import TSCode from '!!raw-loader!/_includes/code/howto/search.basics.ts'; -import GoCode from '!!raw-loader!/_includes/code/howto/go/docs/mainpkg/search-basic_test.go'; -import JavaCode from '!!raw-loader!/_includes/code/howto/java/src/test/java/io/weaviate/docs/search/BasicSearchTest.java'; +import PyCode from '!!raw-loader!/\_includes/code/howto/search.basics.py'; +import PyCodeV3 from '!!raw-loader!/\_includes/code/howto/search.basics-v3.py'; +import TSCode from '!!raw-loader!/\_includes/code/howto/search.basics.ts'; +import GoCode from '!!raw-loader!/\_includes/code/howto/go/docs/mainpkg/search-basic_test.go'; +import JavaV6Code from "!!raw-loader!/\_includes/code/java-v6/src/test/java/SearchBasicTest.java"; +import JavaCode from '!!raw-loader!/\_includes/code/howto/java/src/test/java/io/weaviate/docs/search/BasicSearchTest.java'; +import CSharpCode from "!!raw-loader!/_includes/code/csharp/SearchBasicTest.cs"; With Weaviate you can query your data using [vector similarity search](./similarity.md), [keyword search](./bm25.md), or a mix of both with [hybrid search](./hybrid.md). You can control what object [properties](#specify-object-properties) and [metadata](#retrieve-metadata-values) to return. @@ -23,8 +25,7 @@ This page provides fundamental search syntax to get you started. You can get objects without specifying any parameters. This returns objects in ascending UUID order. - - + - - - - - - - - - - - - - + + + + + + + + + + - - - - - - + + + + + +
@@ -92,46 +100,63 @@ The output is like this:
Additional information - Specify the information that you want your query to return. You can return object properties, object IDs, and object metadata. +Specify the information that you want your query to return. You can return object properties, object IDs, and object metadata.
-## `limit` returned objects +## Fetch objects by ID -Use `limit` to set a fixed maximum number of objects to return. +Fetch an object by its UUID: - - - - - - - - - + + + + +## `limit` returned objects - - - +Use `limit` to set a fixed maximum number of objects to return. - + + + + + + + + + + + + + + - - - - - - + + + + + +
@@ -171,37 +201,39 @@ The output is like this: To start in the middle of your result set, define an `offset`. Set a `limit` to return objects starting at the offset. - - - - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - - - + + + + + +
@@ -238,44 +275,44 @@ The output is like this: To paginate through the entire database, use a [cursor](../manage-objects/read-all-objects.mdx) instead of offset and limit. - ## Specify object `properties` You can specify which object properties to return. - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - - - + + + + + +
@@ -315,37 +357,39 @@ The output is like this: You can retrieve the object vector. (Also applicable where [named vectors](../config-refs/collections.mdx#named-vectors) are used.) - - - - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - - - + + + + + +
@@ -385,38 +434,39 @@ The output is like this: You can retrieve the object `id` (uuid). - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - - - + + + + + +
@@ -453,7 +508,7 @@ The output is like this: ## Retrieve cross-referenced properties -import CrossReferencePerformanceNote from '/_includes/cross-reference-performance-note.mdx'; +import CrossReferencePerformanceNote from '/\_includes/cross-reference-performance-note.mdx'; @@ -464,46 +519,54 @@ To retrieve properties from cross-referenced objects, specify: - The properties to retrieve - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + +
@@ -525,37 +588,39 @@ The output is like this: You can specify metadata fields to be returned. - - - - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - - - + + + + + + For a comprehensive list of metadata fields, see [GraphQL: Additional properties](../api/graphql/additional-properties.md). - ## Multi-tenancy If [multi-tenancy](../concepts/data.md#multi-tenancy) is enabled, specify the tenant parameter in each query. - - - - - - - - - - - - - - - + + + + + + + + + + + + + - - + + + ## Replication For collections with replication enabled, you can specify the consistency level in your queries. This applies to CRUD queries as well as searches. -import QueryReplication from '/_includes/code/replication.get.object.by.id.mdx'; +import QueryReplication from '/\_includes/code/replication.get.object.by.id.mdx'; @@ -642,6 +720,6 @@ import QueryReplication from '/_includes/code/replication.get.object.by.id.mdx'; ## Questions and feedback -import DocsFeedback from '/_includes/docs-feedback.mdx'; +import DocsFeedback from '/\_includes/docs-feedback.mdx'; diff --git a/docs/weaviate/search/bm25.md b/docs/weaviate/search/bm25.md index 43d2e44d..3827cb4c 100644 --- a/docs/weaviate/search/bm25.md +++ b/docs/weaviate/search/bm25.md @@ -14,6 +14,8 @@ import PyCodeV3 from '!!raw-loader!/\_includes/code/howto/search.bm25-v3.py'; import TSCode from '!!raw-loader!/\_includes/code/howto/search.bm25.ts'; import GoCode from '!!raw-loader!/\_includes/code/howto/go/docs/mainpkg/search-bm25_test.go'; import JavaCode from '!!raw-loader!/\_includes/code/howto/java/src/test/java/io/weaviate/docs/search/KeywordSearchTest.java'; +import JavaV6Code from "!!raw-loader!/\_includes/code/java-v6/src/test/java/SearchKeywordTest.java"; +import CSharpCode from "!!raw-loader!/\_includes/code/csharp/SearchKeywordTest.cs"; import GQLCode from '!!raw-loader!/\_includes/code/howto/search.bm25.gql.py'; `Keyword` search, also called "BM25 (Best match 25)" or "sparse vector" search, returns objects that have the highest BM25F scores. @@ -31,8 +33,6 @@ To use BM25 keyword search, define a search string. language="python" /> - - - - - + - + + + - + + + + + + + + + + + + + + + - - - - - + + + - + + + - - - - - + + + - + + + - - - - - + + + + - - + + + + - @@ -358,8 +433,6 @@ import TknTsCode from '!!raw-loader!/\_includes/code/howto/manage-data.collectio language="py" /> - - - - + + + + + + :::tip Tokenization and fuzzy matching @@ -401,8 +488,6 @@ Optionally, use `offset` to paginate the results. language="py" /> - - - - - + + + - + + + - - - - - + + + - + + + - + + + + + +
@@ -560,8 +682,6 @@ For more specific results, use a [`filter`](../api/graphql/filters.md) to narrow language="python" /> - - - - - + + + - + + + + + + + + + :::tip Best practices - Use trigram tokenization selectively on fields that need fuzzy matching. Filtering behavior will change significantly, as text filtering will be done based on trigram-tokenized text, instead of whole words -- Keep exact-match fields with `word` or `field` tokenization for precision. +- Keep exact-match fields with `word` or `field` tokenization for precision. ::: - ## Further resources - [Connect to Weaviate](../connections/index.mdx) diff --git a/docs/weaviate/search/filters.md b/docs/weaviate/search/filters.md index 0ebf6cb6..0d099167 100644 --- a/docs/weaviate/search/filters.md +++ b/docs/weaviate/search/filters.md @@ -8,12 +8,12 @@ image: og/docs/howto.jpg import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBlock'; -import PyCode from '!!raw-loader!/_includes/code/howto/search.filters.py'; -import PyCodeV3 from '!!raw-loader!/_includes/code/howto/search.filters-v3.py'; -import JavaScriptCode from '!!raw-loader!/_includes/code/howto/search.filters.ts'; -import JavaScriptCodeLegacy from '!!raw-loader!/_includes/code/howto/search.filters-v2.ts'; -import GoCode from '!!raw-loader!/_includes/code/howto/go/docs/mainpkg/search-filters_test.go'; - +import PyCode from '!!raw-loader!/\_includes/code/howto/search.filters.py'; +import PyCodeV3 from '!!raw-loader!/\_includes/code/howto/search.filters-v3.py'; +import JavaScriptCode from '!!raw-loader!/\_includes/code/howto/search.filters.ts'; +import GoCode from '!!raw-loader!/\_includes/code/howto/go/docs/mainpkg/search-filters_test.go'; +import JavaV6Code from "!!raw-loader!/\_includes/code/java-v6/src/test/java/SearchFiltersTest.java"; +import CSharpCode from "!!raw-loader!/\_includes/code/csharp/SearchFiltersTest.cs"; Filters let you include, or exclude, particular objects from your result set based on provided conditions.
For a list of filter operators, see the [API reference page](../api/graphql/filters.md#filter-structure). @@ -31,8 +31,6 @@ Add a `filter` to your query, to limit the result set. language="python" /> - - - - - + + + + + + - The `v4` Python client API provides filtering by `any_of`, or `all_of`, as well as using `&` or `|` operators. -
+The `v4` Python client API provides filtering by `any_of`, or `all_of`, as well as using `&` or `|` operators. +
  • Use any_of or all_of for filtering by any, or all of a list of provided filters.
  • @@ -93,27 +104,27 @@ To filter with two or more conditions, use `And`, `Or` and `Not` to define the r
    - #### Filter with `&` or `|` +#### Filter with `&` or `|` - - #### Filter with `any of` +#### Filter with `any of` - - #### Filter with `all of` +#### Filter with `all of` - - - - Use `Filters.and` and `Filters.or` methods to combine filters in the JS/TS `v3` API. -
    +Use `Filters.and` and `Filters.or` methods to combine filters in the JS/TS `v3` API. +
    - These methods take variadic arguments (e.g. `Filters.and(f1, f2, f3, ...)`). To pass an array (e.g. `fs`) as an argument, provide it like so: `Filters.and(...fs)` which will spread the array into its elements. -
    +These methods take variadic arguments (e.g. `Filters.and(f1, f2, f3, ...)`). To pass an array (e.g. `fs`) as an argument, provide it like so: `Filters.and(...fs)` which will spread the array into its elements. +
    - -
    - - - - + + - - - + + + + + + + + - +
    @@ -186,8 +208,6 @@ You can group and nest filters. language="python" /> - - - - - + + + + + + - - - - - + + + + + + - - - - - + + + + + + - - - - - + + + + + + + + + + + + ```java @@ -446,12 +528,12 @@ The `ContainsNone` operator works on text properties and take an array of values ``` - + @@ -518,8 +600,6 @@ If the object property is a `text`, or `text`-like data type such as object ID, language="python" /> - - - - - + + + + + + - The `*` wildcard operator matches zero or more characters. The `?` operator matches exactly one character. -
    +The `*` wildcard operator matches zero or more characters. The `?` operator matches exactly one character. +
    - Currently, the `Like` filter is not able to match wildcard characters (`?` and `*`) as literal characters ([read more](../api/graphql/filters.md#wildcard-literal-matches-with-like)). +Currently, the `Like` filter is not able to match wildcard characters (`?` and `*`) as literal characters ([read more](../api/graphql/filters.md#wildcard-literal-matches-with-like)).
    ## Filter using cross-references -import CrossReferencePerformanceNote from '/_includes/cross-reference-performance-note.mdx'; +import CrossReferencePerformanceNote from '/\_includes/cross-reference-performance-note.mdx'; @@ -592,8 +685,6 @@ To filter on properties from a cross-referenced object, add the collection name language="python" /> - - - - - + - + + + + + + @@ -660,8 +764,6 @@ import GeoLimitations from '/_includes/geo-limitations.mdx'; language="js" /> - - - + + + + + + + + + + + + ## Filter by metadata @@ -737,7 +870,6 @@ For the full list, see [API references: Filters](../api/graphql/filters.md#speci language="js" /> - - + + + + + + @@ -778,25 +925,30 @@ This filter requires the [property timestamp](../config-refs/indexing/inverted-i language="js" /> - - + - - + + + + - @@ -828,8 +980,6 @@ This filter requires the [property length](../config-refs/indexing/inverted-inde language="js" /> - - - + + + + + + @@ -878,6 +1043,22 @@ This filter requires the [property null state](../config-refs/indexing/inverted- language="gonew" /> + + + + + + @@ -911,6 +1092,6 @@ For a list of filter operators, see [the reference page](../api/graphql/filters. ## Questions and feedback -import DocsFeedback from '/_includes/docs-feedback.mdx'; +import DocsFeedback from '/\_includes/docs-feedback.mdx'; diff --git a/docs/weaviate/search/hybrid.md b/docs/weaviate/search/hybrid.md index c6911d48..12b8fbf3 100644 --- a/docs/weaviate/search/hybrid.md +++ b/docs/weaviate/search/hybrid.md @@ -8,11 +8,13 @@ image: og/docs/howto.jpg import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBlock'; -import PyCode from '!!raw-loader!/_includes/code/howto/search.hybrid.py'; -import PyCodeV3 from '!!raw-loader!/_includes/code/howto/search.hybrid-v3.py'; -import TSCode from '!!raw-loader!/_includes/code/howto/search.hybrid.ts'; -import GoCode from '!!raw-loader!/_includes/code/howto/go/docs/mainpkg/search-hybrid_test.go'; -import GQLCode from '!!raw-loader!/_includes/code/howto/search.hybrid.gql.py'; +import PyCode from '!!raw-loader!/\_includes/code/howto/search.hybrid.py'; +import PyCodeV3 from '!!raw-loader!/\_includes/code/howto/search.hybrid-v3.py'; +import TSCode from '!!raw-loader!/\_includes/code/howto/search.hybrid.ts'; +import GoCode from '!!raw-loader!/\_includes/code/howto/go/docs/mainpkg/search-hybrid_test.go'; +import JavaV6Code from "!!raw-loader!/\_includes/code/java-v6/src/test/java/SearchHybridTest.java"; +import CSharpCode from "!!raw-loader!/\_includes/code/csharp/SearchHybridTest.cs"; +import GQLCode from '!!raw-loader!/\_includes/code/howto/search.hybrid.gql.py'; `Hybrid` search combines the results of a vector search and a keyword (BM25F) search by fusing the two result sets. @@ -23,43 +25,54 @@ The [fusion method](#change-the-fusion-method) and the [relative weights](#balan Combine the results of a vector search and a keyword search. The search uses a single query string. - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + +
    @@ -100,6 +113,22 @@ A hybrid search on a collection that has [named vectors](../config-refs/collecti language="ts" /> + + + + + + - - - - - - - - - - - + + + + + + + - - - - + + + + + + + + +
    @@ -190,43 +230,54 @@ Hybrid search results can favor the keyword component or the vector component. T - An `alpha` of `0` is a pure keyword search. - - - - - - - - - - - + + + + + + + - - - - - + + + + + + + + + +
    @@ -251,43 +302,54 @@ The output is like this: - To use [`autocut`](../api/graphql/additional-operators.md#autocut) with the `hybrid` operator, use `Relative Score Fusion`. - - - - - - - - - - - + + + + + + + - - - - - + + + + + + + + + +
    @@ -333,6 +395,22 @@ With the `or` operator, the search returns objects that contain at least `minimu language="python" /> + + + + + + + + + + + + - - - - - - - - - - - + + + + + + + - - - - - + + + + + + + + + +
    @@ -432,43 +537,54 @@ The output is like this: Specify the relative value of an object's `properties` in the keyword search. Higher values increase the property's contribution to the search score. - - - - - - - - - - - + + + + + + + - - - - - + + + + + + + + + +
    @@ -490,43 +606,54 @@ The output is like this: The vector component of hybrid search can use a query string or a query vector. To specify a query vector instead of a query string, provide a query vector (for the vector search) and a query string (for the keyword search) in your query. - - - - - - - - - - - + + + + + + + - - - - - + + + + + + + + + +
    @@ -568,6 +695,22 @@ You can specify [vector similarity search](/weaviate/search/similarity) paramete language="js" /> + + + + + +
    @@ -600,10 +743,7 @@ The only available search threshold is `max vector distance`, which will set the language="python" /> - - - + + + + + @@ -630,7 +785,6 @@ Define criteria to group search results. language="py" /> - + + + + + +
    @@ -668,8 +838,6 @@ Optionally, use `offset` to paginate the results. language="py" /> - - - - - + + + + + + - - - - - + + + + + + - - - - - - - - - - - + + + + + + + - - - - + + + + + + + + +
    @@ -825,7 +1028,7 @@ The output is like this: ### Tokenization -import TokenizationNote from '/_includes/tokenization.mdx' +import TokenizationNote from '/\_includes/tokenization.mdx' @@ -840,6 +1043,6 @@ import TokenizationNote from '/_includes/tokenization.mdx' ## Questions and feedback -import DocsFeedback from '/_includes/docs-feedback.mdx'; +import DocsFeedback from '/\_includes/docs-feedback.mdx'; diff --git a/docs/weaviate/search/image.md b/docs/weaviate/search/image.md index 26bc7372..b348a003 100644 --- a/docs/weaviate/search/image.md +++ b/docs/weaviate/search/image.md @@ -8,10 +8,12 @@ image: og/docs/howto.jpg import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBlock'; -import PyCode from '!!raw-loader!/_includes/code/howto/search.image.py'; -import TSCode from '!!raw-loader!/_includes/code/howto/search.image.ts'; -import GoCode from '!!raw-loader!/_includes/code/howto/go/docs/mainpkg/search-image_test.go'; -import JavaCode from '!!raw-loader!/_includes/code/howto/java/src/test/java/io/weaviate/docs/search/ImageSearchTest.java'; +import PyCode from '!!raw-loader!/\_includes/code/howto/search.image.py'; +import TSCode from '!!raw-loader!/\_includes/code/howto/search.image.ts'; +import GoCode from '!!raw-loader!/\_includes/code/howto/go/docs/mainpkg/search-image_test.go'; +import JavaV6Code from "!!raw-loader!/\_includes/code/java-v6/src/test/java/SearchImageTest.java"; +import CSharpCode from "!!raw-loader!/\_includes/code/csharp/SearchImageTest.cs"; +import JavaCode from '!!raw-loader!/\_includes/code/howto/java/src/test/java/io/weaviate/docs/search/ImageSearchTest.java'; `Image` search uses an **image as a search input** to perform vector similarity search. @@ -47,21 +49,14 @@ If your query image is stored in a file, you can use the client library to searc language="py" /> - - - - - - - - + + + - + + +
    Example response - - ## By the base64 representation You can search by a base64 representation of an image: @@ -108,8 +116,6 @@ You can search by a base64 representation of an image: language="py" /> - - - - - + + + + + + -
    Example response - - -## Create a base64 representation of an online image. +## Create a base64 representation of an online image You can create a base64 representation of an online image, and use it as input for similarity search [as shown above](#by-the-base64-representation). @@ -174,7 +191,6 @@ You can create a base64 representation of an online image, and use it as input f language="js" /> - - + + + - + + + ## Combination with other operators @@ -207,6 +237,6 @@ See the [`similarity search`](./similarity.md) page for more details. ## Questions and feedback -import DocsFeedback from '/_includes/docs-feedback.mdx'; +import DocsFeedback from '/\_includes/docs-feedback.mdx'; diff --git a/docs/weaviate/search/similarity.md b/docs/weaviate/search/similarity.md index 394bfed3..325ee156 100644 --- a/docs/weaviate/search/similarity.md +++ b/docs/weaviate/search/similarity.md @@ -8,11 +8,13 @@ image: og/docs/howto.jpg import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBlock'; -import PyCode from '!!raw-loader!/_includes/code/howto/search.similarity.py'; -import PyCodeV3 from '!!raw-loader!/_includes/code/howto/search.similarity-v3.py'; -import TSCode from '!!raw-loader!/_includes/code/howto/search.similarity.ts'; -import GoCode from '!!raw-loader!/_includes/code/howto/go/docs/mainpkg/search-similarity_test.go'; -import JavaCode from '!!raw-loader!/_includes/code/howto/java/src/test/java/io/weaviate/docs/search/VectorSearchTest.java'; +import PyCode from '!!raw-loader!/\_includes/code/howto/search.similarity.py'; +import PyCodeV3 from '!!raw-loader!/\_includes/code/howto/search.similarity-v3.py'; +import TSCode from '!!raw-loader!/\_includes/code/howto/search.similarity.ts'; +import GoCode from '!!raw-loader!/\_includes/code/howto/go/docs/mainpkg/search-similarity_test.go'; +import JavaCode from '!!raw-loader!/\_includes/code/howto/java/src/test/java/io/weaviate/docs/search/VectorSearchTest.java'; +import JavaV6Code from "!!raw-loader!/\_includes/code/java-v6/src/test/java/SearchSimilarityTest.java"; +import CSharpCode from "!!raw-loader!/_includes/code/csharp/SearchSimilarityTest.cs"; Vector search returns the objects with most similar vectors to that of the query. @@ -29,8 +31,6 @@ Use the [`Near Text`](../api/graphql/search-operators.md#neartext) operator to f language="python" /> - - - - - + + + - + + +
    @@ -85,10 +97,10 @@ The output is like this: ## Search with image -import ImgSrchPyCode from '!!raw-loader!/_includes/code/howto/search.image.py'; -import ImgSrchPyCodeV3 from '!!raw-loader!/_includes/code/howto/search.image-v3.py'; -import ImgSrchTSCode from '!!raw-loader!/_includes/code/howto/search.image.ts'; - +import ImgSrchPyCode from '!!raw-loader!/\_includes/code/howto/search.image.py'; +import ImgSrchPyCodeV3 from '!!raw-loader!/\_includes/code/howto/search.image-v3.py'; +import ImgSrchTSCode from '!!raw-loader!/\_includes/code/howto/search.image.ts'; +import ImgSrchJavaV6Code from "!!raw-loader!/\_includes/code/java-v6/src/test/java/SearchImageTest.java"; Use the [`Near Image`](../api/graphql/search-operators.md) operator to find objects with the nearest vector to an image.
    This example uses a base64 representation of an image. @@ -102,8 +114,6 @@ This example uses a base64 representation of an image. language="py" /> - - - - + + + + + + See [Image search](./image.md) for more information. - ## Search with an existing object If you have an object ID, use the [`Near Object`](../api/graphql/search-operators.md#nearobject) operator to find similar objects to that object. @@ -140,8 +163,6 @@ If you have an object ID, use the [`Near Object`](../api/graphql/search-operator language="python" /> - - - - - + + + - + + +
    - ## Search with a vector If you have an input vector, use the [`Near Vector`](../api/graphql/search-operators.md#nearvector) operator to find objects with similar vectors @@ -203,8 +235,6 @@ If you have an input vector, use the [`Near Vector`](../api/graphql/search-opera language="python" /> - - - - Sample test vector vector := []float32{0.326901312, 0.172652353, 0.574298978, -0.877372618, 0.208563102, 0.534870921, -0.905765693, -0.240794293, 0.2483627, 0.071935073, -0.470612466, 0.899590301, 0.821722525, 0.771190126, -0.729547086, -0.891606557, 0.304722712, -0.299226525, 0.400798778, -0.438221959, 0.84784485, 0.229913025, 0.072704543, 0.754321192, -0.019145501, -0.894141594, -0.994515521, -0.593096071, -0.42883483, 0.24194537, 0.620309746, 0.632115028, 0.588728611, 0.097637792, 0.778057433, 0.218009849, -0.967106101, 0.53489523, -0.41595204, 0.242416186, -0.947618483, -0.521548494, 0.22066765, 0.656955091, -0.937464798, 0.513341425, 0.578846678, 0.249978376, -0.085722009, -0.03557413, 0.943261393, 0.085512458, -0.125636201, 0.554060472, 0.485368427, -0.645984772, 0.756222985, -0.099291789, -0.590909311, 0.233526122, 0.085346719, -0.879696717, -0.5351979, -0.959582549, 0.160636781, -0.505745761, 0.597447967, 0.637738272, -0.7560195, -0.203242247, -0.14202656, 0.0531654, -0.256164061, -0.788468035, 0.687289393, -0.361320829, -0.454431255, -0.056878361, -0.24120844, -0.559818319, -0.260802008, -0.391211829, 0.941519464, 0.427640945, -0.747279873, 0.156631127, 0.283531662, -0.567472453, -0.056855298, 0.376830341, 0.24340912, 0.203539024, -0.472871161, 0.148073935, -0.205732037, -0.113967997, 0.744806131, -0.716108348, -0.121028453, -0.260367162, 0.799248419, 0.693572742, -0.791924921, -0.23802225, 0.61424365, -0.227275991, 0.288018577, 0.43869821, -0.054773369, 0.235872433, 0.150168526, -0.148419033, -0.42652761, 0.708727207, 0.084139137, -0.72887396, -0.218030612, 0.107339953, -0.518407575, 0.835435492, 0.035034357, -0.941809022, 0.787348994, 0.563871276, 0.766441516, -0.027821565, 0.245867777, 0.667148957, 0.738303557, -0.891110299, -0.275965165, -0.768567633, -0.475590831, 0.814911332, -0.297372689, 0.278844884, 0.95130689, 0.637530377, 0.618917313, 0.175740276, -0.249863627, -0.293828547, 0.320150997, -0.197713784, -0.633765065, -0.810942827, 0.591293734, 0.388968601, 0.523304585, -0.171063703, 0.602972529, -0.450091234, 0.345062519, -0.716491932, 0.435084962, -0.991825804, 0.689999161, -0.137097366, -0.537270475, -0.14424947, -0.62181862, 0.44289108, 0.072616733, 0.114381466, -0.972054206, 0.597329412, 0.562940173, 0.549476569, -0.706469709, 0.978081921, 0.180978079, 0.162027999, 0.788607827, -0.267257907, 0.985984986, -0.563312619, -0.640888755, 0.462486684, 0.369103705, 0.650806096, -0.167334677, 0.607351556, 0.822088516, 0.796317805, -0.503272355, -0.251183198, -0.171193987, 0.022293507, 0.428948271, 0.130966005, -0.736595944, 0.304682365, 0.663292867, -0.198997943, 0.035542683, 0.118594925, -0.509118134, 0.169740121, 0.375104805, -0.379886464, -0.498633816, -0.704396843, 0.030748626, 0.944446866, 0.888355185, -0.652586251, -0.906279254, 0.926259459, -0.214344492, 0.322871291, -0.027617198, 0.20895568, 0.035279297, -0.969237773, 0.403299676, 0.428694059, 0.829344779, 0.691959507, 0.383265745, -0.782718812, 0.775060865, -0.779937498, 0.584385461, -0.459012881, 0.662861143, 0.678415842, -0.127245162, -0.634464935, 0.646265039, -0.192781253, 0.950300755, 0.211855294, -0.503585688, 0.836612346, 0.787168113, 0.865806113, 0.38960291, 0.8664508, -0.572625523, 0.56761092, -0.735380506, -0.095070433, -0.783564692, -0.208375599, 0.739675191, 0.073271624, 0.359469611, 0.227572188, 0.03146414, 0.22938932, -0.447168816, 0.997660781, 0.215311392, -0.431177845, 0.016089255, 0.502448595, -0.705274029, -0.289382977, -0.577193696, 0.966175471, -0.510154942, -0.95823724, 0.24204605, 0.365546465, -0.297344885, 0.236294365, 0.446028631, 0.117976098, 0.094099994, 0.260277337, -0.461409164, -0.375480325, -0.614179681, -0.392757615, 0.100161621, -0.814176208, -0.347271514, 0.592469245, -0.988247355, -0.158397473, 0.921216369, -0.962889718, -0.932866744, 0.414358528, 0.12841629, -0.676515076, 0.940077931, -0.434330301, -0.2041959, 0.139998128, -0.937367769, -0.65941309, -0.716202446, -0.707964147, -0.389402878, 0.758786102, 0.543653384, -0.151055143, 0.406115293, -0.667719031, -0.811399948, 0.221955265, -0.493543772, 0.342954834, 0.327300923, -0.19955993, 0.752914123, -0.170643372, -0.14423466, 0.034084297, -0.855779749, 0.741368546, 0.240861775, -0.341099861, -0.6478463, 0.548267419, 0.409670736, 0.995208265, 0.807107939, -0.585172449, 0.163887551, 0.97695251, 0.575339181, -0.569841278, 0.675494554, -0.471893576, -0.030140821, -0.05243822, 0.050174597, -0.412903213, -0.683965383, 0.334143696, 0.421115564, 0.175047935, 0.530304957, 0.304087579, -0.792279648, 0.685567038, -0.803590175, -0.742988649, 0.559471864, -0.720445164, -0.299579897, 0.856260016, -0.181088629, -0.397816074, 0.767682872, 0.738067303, 0.359374803, -0.385285243, -0.038967135, -0.147880482, 0.83122139, -0.446691037, -0.789851962, -0.110046918, -0.468262552, -0.756854501, -0.445852765, 0.978448405, -0.726514778, 0.667864341, 0.74283952, 0.484586568, 0.51334425, 0.819917424, -0.838528257, 0.436940199, -0.448078512, -0.337453429, -0.172542255, 0.17131926, 0.511645199, 0.684561713, 0.486342731, 0.873551862, -0.731099225, -0.753154103, -0.236784718, -0.65032768, -0.239905204, -0.803154248, -0.640516296, 0.855964698, -0.416501359, 0.630052995} +
    - + + + - + + + - - - - - + + + - + + + - - - - - + + + - + + + - - - - - + + + - + + + - - - - - + + + - + + + - - - - - + + + - + + + - - - - - + + + - + + + diff --git a/docs/weaviate/starter-guides/custom-vectors.mdx b/docs/weaviate/starter-guides/custom-vectors.mdx index 5c291728..e6f78d65 100644 --- a/docs/weaviate/starter-guides/custom-vectors.mdx +++ b/docs/weaviate/starter-guides/custom-vectors.mdx @@ -1,17 +1,19 @@ --- -title: 'Quickstart: Bring your own vectors' +title: "Quickstart: Bring your own vectors" sidebar_label: Bring your own vectors sidebar_position: 2 image: og/docs/tutorials.jpg # tags: ['getting started'] --- -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; -import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBlock'; -import ByovAllPyCode from '!!raw-loader!/_includes/code/quickstart.byov.all.py'; -import ByovAllTsCode from '!!raw-loader!/_includes/code/quickstart.byov.all.ts'; -import ByovAllShCode from '!!raw-loader!/_includes/code/quickstart.byov.all.sh'; +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; +import FilteredTextBlock from "@site/src/components/Documentation/FilteredTextBlock"; +import ByovAllPyCode from "!!raw-loader!/_includes/code/quickstart.byov.all.py"; +import ByovAllTsCode from "!!raw-loader!/_includes/code/quickstart.byov.all.ts"; +import JavaV6Code from "!!raw-loader!/_includes/code/java-v6/src/test/java/StarterGuidesCustomVectorsTest.java"; +import CSharpCode from "!!raw-loader!/_includes/code/csharp/StarterGuidesCustomVectorsTest.cs"; +import ByovAllShCode from "!!raw-loader!/_includes/code/quickstart.byov.all.sh"; Weaviate is a vector database. [Vector databases](https://weaviate.io/blog/what-is-a-vector-database) store data objects and vectors that represent those objects. The vector embedding is also called an "embedding." @@ -40,22 +42,25 @@ The example data is based on a set of ten questions from the "Jeopardy!" televis The sample data The JSON file is based on this data. The vector embeddings are generated with the OpenAI API [`text-embedding-ada-002` model](https://platform.openai.com/docs/guides/embeddings/what-are-embeddings). -
    - Your vectors can come from any source, including your own vectorizer model or another model provider such as Cohere or Hugging Face. -
    - - | Category | Question | Answer | Vector | - | :- |:- |:- | :- | - | SCIENCE | This organ removes excess glucose from the blood & stores it as glycogen | Liver | [ -0.006632288, -0.0042016874, ..., -0.020163147 ] | - | ANIMALS | It's the only living mammal in the order Proboseidea | Elephant | [ -0.0166891, -0.00092290324, ..., -0.032253385 ] | - | ANIMALS | The gavial looks very much like a crocodile except for this bodily feature | the nose or snout | [ -0.015592773, 0.019883318, ..., 0.0033349802 ] | - | ANIMALS | Weighing around a ton, the eland is the largest species of this animal in Africa | Antelope | [ 0.014535263, -0.016103541, ..., -0.025882969 ] | - | ANIMALS | Heaviest of all poisonous snakes is this North American rattlesnake | the diamondback rattler | [ -0.0030859283, 0.015239313, ..., -0.021798335 ] | - | SCIENCE | 2000 news: the Gunnison sage grouse isn't just another northern sage grouse, but a new one of this classification | species | [ -0.0090561025, 0.011155112, ..., -0.023036297 ] | - | SCIENCE | A metal that is "ductile" can be pulled into this while cold & under pressure | wire | [ -0.02735741, 0.01199829, ..., 0.010396339 ] | - | SCIENCE | In 1953 Watson & Crick built a model of the molecular structure of this, the gene-carrying substance | DNA | [ -0.014227471, 0.020493254, ..., -0.0027445166 ] | - | SCIENCE | Changes in the tropospheric layer of this are what gives us weather | the atmosphere | [ 0.009625228, 0.027518686, ..., -0.0068922946 ] | - | SCIENCE | In 70-degree air, a plane traveling at about 1,130 feet per second breaks it | Sound barrier | [ -0.0013459147, 0.0018580769, ..., -0.033439033 ] | + +{" "} +
    +Your vectors can come from any source, including your own vectorizer model or +another model provider such as Cohere or Hugging Face. +
    + +| Category | Question | Answer | Vector | +| :------- | :---------------------------------------------------------------------------------------------------------------- | :---------------------- | :------------------------------------------------- | +| SCIENCE | This organ removes excess glucose from the blood & stores it as glycogen | Liver | [ -0.006632288, -0.0042016874, ..., -0.020163147 ] | +| ANIMALS | It's the only living mammal in the order Proboseidea | Elephant | [ -0.0166891, -0.00092290324, ..., -0.032253385 ] | +| ANIMALS | The gavial looks very much like a crocodile except for this bodily feature | the nose or snout | [ -0.015592773, 0.019883318, ..., 0.0033349802 ] | +| ANIMALS | Weighing around a ton, the eland is the largest species of this animal in Africa | Antelope | [ 0.014535263, -0.016103541, ..., -0.025882969 ] | +| ANIMALS | Heaviest of all poisonous snakes is this North American rattlesnake | the diamondback rattler | [ -0.0030859283, 0.015239313, ..., -0.021798335 ] | +| SCIENCE | 2000 news: the Gunnison sage grouse isn't just another northern sage grouse, but a new one of this classification | species | [ -0.0090561025, 0.011155112, ..., -0.023036297 ] | +| SCIENCE | A metal that is "ductile" can be pulled into this while cold & under pressure | wire | [ -0.02735741, 0.01199829, ..., 0.010396339 ] | +| SCIENCE | In 1953 Watson & Crick built a model of the molecular structure of this, the gene-carrying substance | DNA | [ -0.014227471, 0.020493254, ..., -0.0027445166 ] | +| SCIENCE | Changes in the tropospheric layer of this are what gives us weather | the atmosphere | [ 0.009625228, 0.027518686, ..., -0.0068922946 ] | +| SCIENCE | In 70-degree air, a plane traveling at about 1,130 feet per second breaks it | Sound barrier | [ -0.0013459147, 0.0018580769, ..., -0.033439033 ] |
    @@ -76,12 +81,14 @@ The v4 client requires Weaviate 1.23.7 or higher.

    ```bash pip install -U weaviate-client ``` + ```bash npm install weaviate-client ``` + @@ -96,7 +103,7 @@ If you are using a Sandbox instance, the URL and API keys are listed in the [det To connect to Weaviate, run the code for your language to create a client object. Re-use the `client` object in the later steps to connect to your instance and run the sample code. -import ConnectToWeaviate from '/_includes/code/quickstart/connect.partial.mdx' +import ConnectToWeaviate from "/_includes/code/quickstart/connect.partial.mdx"; @@ -112,11 +119,12 @@ For more on configurations, see [vectorizer settings](../manage-collections/vect ### Define the collection -import CreateSchema from '/_includes/code/quickstart.byov.schema.mdx' +import CreateSchema from "/_includes/code/quickstart.byov.schema.mdx"; ### Optional: Set a compatible vectorizer + If Weaviate has an [integration](/weaviate/model-providers) for the vectorizer that you use to generate your custom vectors, consider adding your vectorizer to the collection definition. If you specify a vectorizer, Weaviate can generate new vectors when it needs them. In this example, the vectors are generated by the OpenAI `ada-002` model. The Weaviate `text2vec-openai` integration can access the `ada-002` model, so you would specify `text2vec-openai` in the collection definition. @@ -127,7 +135,7 @@ At import time, Weaviate uses the vectors you provide even if the collection spe For large datasets, batch import is more efficient than importing individual objects. This batch import code imports the question objects and their vectors. -import ImportQuestionsWithVectors from '/_includes/code/quickstart.import.questions-and-vectors.mdx' +import ImportQuestionsWithVectors from "/_includes/code/quickstart.import.questions-and-vectors.mdx"; @@ -148,30 +156,46 @@ When you create a query vector, use the same vectorizer that you use to create t This [`nearVector`](/weaviate/search/similarity#search-with-a-vector) query supplies a query vector. The query vector is an embedding of the query string, "biology". - - - - - - - - - + + + + + + + + + + + + + + +
    @@ -179,7 +203,7 @@ This [`nearVector`](/weaviate/search/similarity#search-with-a-vector) query supp The response is like this: -import BiologyQuestionsJson from '/_includes/code/quickstart/response.biology.questions.mdx'; +import BiologyQuestionsJson from "/_includes/code/quickstart/response.biology.questions.mdx"; @@ -193,6 +217,6 @@ import BiologyQuestionsJson from '/_includes/code/quickstart/response.biology.qu ## Questions and feedback -import DocsFeedback from '/_includes/docs-feedback.mdx'; +import DocsFeedback from "/_includes/docs-feedback.mdx"; - + diff --git a/docs/weaviate/starter-guides/generative.md b/docs/weaviate/starter-guides/generative.md index 0a74a834..2033e66e 100644 --- a/docs/weaviate/starter-guides/generative.md +++ b/docs/weaviate/starter-guides/generative.md @@ -11,9 +11,7 @@ import TabItem from '@theme/TabItem'; import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBlock'; import PyCode from '!!raw-loader!/_includes/code/starter-guides/generative.py'; import TSCodeEduDemo from '!!raw-loader!/_includes/code/starter-guides/generative_edudemo.ts'; -import TSCodeEduDemoLegacy from '!!raw-loader!/_includes/code/starter-guides/generative_edudemo-v2.ts'; import TSCodeLocal from '!!raw-loader!/_includes/code/starter-guides/generative_local.ts'; -import TSCodeLocalLegacy from '!!raw-loader!/_includes/code/starter-guides/generative_local-v2.ts'; :::info Related pages - [Which Weaviate is right for me?](./which-weaviate.md) diff --git a/docs/weaviate/starter-guides/managing-resources/indexing.mdx b/docs/weaviate/starter-guides/managing-resources/indexing.mdx index 0efb097f..9909182e 100644 --- a/docs/weaviate/starter-guides/managing-resources/indexing.mdx +++ b/docs/weaviate/starter-guides/managing-resources/indexing.mdx @@ -119,7 +119,7 @@ Aspects of these indexes can be configured at the collection and property level. The inverted index is configurable on a collection level. The collection level settings determine BM25 parameters, and what metadata is indexed for filtering. For example, you can configure whether timestamps, null state, or property lengths are indexed. -For details, see [set inverted index parameters](../../manage-collections/collection-operations.mdx#set-inverted-index-parameters) +For details, see [set inverted index parameters](../../manage-collections/inverted-index.mdx#set-inverted-index-parameters) ### Property level configuration @@ -138,7 +138,7 @@ For more information, see these documentation pages and blog posts. To configure indexing, follow the steps on these pages: - [Configure vector indexes](/weaviate/config-refs/indexing/vector-index.mdx) -- [Configure inverted indexes](../../manage-collections/collection-operations.mdx#set-inverted-index-parameters) +- [Configure inverted indexes](../../manage-collections/inverted-index.mdx#set-inverted-index-parameters) For more documentation details, see: diff --git a/docs/weaviate/tutorials/collection-aliases.mdx b/docs/weaviate/tutorials/collection-aliases.mdx index d5f40d59..714f15fc 100644 --- a/docs/weaviate/tutorials/collection-aliases.mdx +++ b/docs/weaviate/tutorials/collection-aliases.mdx @@ -13,6 +13,8 @@ import FilteredTextBlock from "@site/src/components/Documentation/FilteredTextBl import PyCode from "!!raw-loader!/_includes/code/howto/manage-data.aliases.py"; import TSCode from "!!raw-loader!/_includes/code/howto/manage-data.aliases.ts"; import GoCode from "!!raw-loader!/_includes/code/howto/go/docs/manage-data.aliases_test.go"; +import JavaV6Code from "!!raw-loader!/_includes/code/java-v6/src/test/java/ManageCollectionsAliasTest.java"; +import CSharpCode from "!!raw-loader!/_includes/code/csharp/ManageCollectionsAliasTest.cs"; import JavaCode from "!!raw-loader!/_includes/code/howto/java/src/test/java/io/weaviate/docs/manage-data.collection-aliases.java"; In this tutorial, we will explore how to use **collection aliases** in Weaviate to perform zero-downtime migrations. Collection aliases are alternative names for Weaviate collections that allow you to reference a collection by multiple names. This powerful feature enables you to migrate to new collection schemas, update configurations, or reorganize your data without any service interruption. @@ -91,6 +93,22 @@ First, connect to your Weaviate instance using your preferred client library. language="js" /> + + + + + + - + @@ -130,6 +148,22 @@ Let's create our initial products collection and populate it with data. language="js" /> + + + + + + - + @@ -169,6 +203,22 @@ Now create an alias that your application will use. This decouples your applicat language="js" /> + + + + + + - + @@ -208,6 +258,22 @@ Your application code should reference the alias, not the underlying collection. language="js" /> + + + + + + - + @@ -249,6 +315,22 @@ Now let's create a new version of the collection with an additional field (e.g., language="js" /> + + + + + + - + @@ -288,6 +370,22 @@ Copy data from the old collection to the new one, adding default values for new language="js" /> + + + + + + - + @@ -327,6 +425,22 @@ This is the magic moment - update the alias to point to the new collection. This language="js" /> + + + + + + - + @@ -366,6 +480,22 @@ After verifying that everything works correctly with the new collection, you can language="js" /> + + + + + + - + diff --git a/docs/weaviate/tutorials/spark-connector.md b/docs/weaviate/tutorials/spark-connector.md index a5d48a0d..05d64ad7 100644 --- a/docs/weaviate/tutorials/spark-connector.md +++ b/docs/weaviate/tutorials/spark-connector.md @@ -229,7 +229,7 @@ df.limit(1500).withColumnRenamed("id", "uuid").write.format("io.weaviate.spark.W - Arbitrary headers can be supplied with the option prefix `header:`. For example to provide an `OPENAI_APIKEY` header the following can be used `.option("header:OPENAI_APIKEY", ...)`. -- Additionally OIDC options are supported `.option("oidc:username", ...)`, `.option("oidc:password", ...)`, `.option("oidc:clientSecret", ...)`, `.option("oidc:accessToken", ...)`, `.option("oidc:accessTokenLifetime", ...)`, `.option("oidc:refreshToken", ...)`. For more information on these options See the [Java client documentation](../client-libraries/java.md#oidc-authentication). +- Additionally OIDC options are supported `.option("oidc:username", ...)`, `.option("oidc:password", ...)`, `.option("oidc:clientSecret", ...)`, `.option("oidc:accessToken", ...)`, `.option("oidc:accessTokenLifetime", ...)`, `.option("oidc:refreshToken", ...)`. For more information on these options See the [Java client documentation](../client-libraries/java/index.mdx#oidc-authentication). ### Named vectors and multi-vector embeddings diff --git a/docusaurus.config.js b/docusaurus.config.js index 6d86cdd8..f8bdbf2c 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -151,7 +151,7 @@ const config = { prism: { theme: prismThemes.github, darkTheme: prismThemes.dracula, - additionalLanguages: ["java"], + additionalLanguages: ['java', 'csharp'], }, docs: { sidebar: { diff --git a/sidebars.js b/sidebars.js index 446e4244..70f549b3 100644 --- a/sidebars.js +++ b/sidebars.js @@ -639,9 +639,22 @@ const sidebars = { }, ], }, + { + type: "category", + label: "Java", + collapsed: false, + className: "sidebar-item", + link: { + type: "doc", + id: "weaviate/client-libraries/java/index", + }, + items: [ + "weaviate/client-libraries/java/java-v6", + ], + }, { type: "doc", - id: "weaviate/client-libraries/java", + id: "weaviate/client-libraries/csharp", className: "sidebar-item", }, { diff --git a/src/components/Documentation/FilteredTextBlock.js b/src/components/Documentation/FilteredTextBlock.js index 4e58ce44..dd3bced0 100644 --- a/src/components/Documentation/FilteredTextBlock.js +++ b/src/components/Documentation/FilteredTextBlock.js @@ -44,6 +44,7 @@ DOC_SYSTEMS.js = DOC_SYSTEMS.ts; DOC_SYSTEMS.gonew = DOC_SYSTEMS.go; DOC_SYSTEMS.goraw = DOC_SYSTEMS.go; DOC_SYSTEMS.javaraw = DOC_SYSTEMS.java; +DOC_SYSTEMS.csharpraw = DOC_SYSTEMS.csharp; // Custom styles for badges const badgeStyles = { @@ -82,6 +83,14 @@ const FilteredTextBlock = ({ let withinMarkers = false; let format; switch (language) { + case 'csharp': // Add this new case + // remove leading indent of 4 spaces + format = (input) => input.replace(/^ /, ''); + break; + case 'csharpraw': // Add this new case + // remove leading indent of 4 spaces + format = (input) => input.replace(/^ /, ''); + break; case 'java': // remove leading indent of 4 spaces format = (input) => input.replace(/^ /, ''); @@ -146,6 +155,9 @@ const FilteredTextBlock = ({ case 'javaraw': language2 = 'java'; break; + case 'csharpraw': + language2 = 'csharp'; + break; } // Generate GitHub URL if path is provided diff --git a/tests/docker-compose-anon-2.yml b/tests/docker-compose-anon-2.yml index 6bef9105..84348711 100644 --- a/tests/docker-compose-anon-2.yml +++ b/tests/docker-compose-anon-2.yml @@ -18,8 +18,19 @@ services: AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: 'true' PERSISTENCE_DATA_PATH: '/var/lib/weaviate' ASYNC_INDEXING: 'true' - ENABLE_MODULES: 'text2vec-ollama,generative-ollama,backup-filesystem' + ENABLE_MODULES: 'text2vec-ollama,generative-ollama,backup-filesystem,text2vec-contextionary' ENABLE_API_BASED_MODULES: 'true' BACKUP_FILESYSTEM_PATH: '/var/lib/weaviate/backups' CLUSTER_HOSTNAME: 'node1' + CONTEXTIONARY_URL: contextionary:9999 + contextionary: + environment: + OCCURRENCE_WEIGHT_LINEAR_FACTOR: 0.75 + EXTENSIONS_STORAGE_MODE: weaviate + EXTENSIONS_STORAGE_ORIGIN: http://weaviate:8080 + NEIGHBOR_OCCURRENCE_IGNORE_PERCENTILE: 5 + ENABLE_COMPOUND_SPLITTING: 'false' + image: cr.weaviate.io/semitechnologies/contextionary:en0.16.0-v1.2.1 + ports: + - 9999:9999 ... diff --git a/tests/docker-compose-anon.yml b/tests/docker-compose-anon.yml index 59add794..40863200 100644 --- a/tests/docker-compose-anon.yml +++ b/tests/docker-compose-anon.yml @@ -18,11 +18,12 @@ services: AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: 'true' PERSISTENCE_DATA_PATH: '/var/lib/weaviate' ASYNC_INDEXING: 'true' - ENABLE_MODULES: 'text2vec-ollama,generative-ollama,backup-filesystem' + ENABLE_MODULES: 'text2vec-ollama,generative-ollama,backup-filesystem,text2vec-contextionary' ENABLE_API_BASED_MODULES: 'true' BACKUP_FILESYSTEM_PATH: '/var/lib/weaviate/backups' CLUSTER_HOSTNAME: 'node1' OLLAMA_API_ENDPOINT: 'http://ollama:11434' + CONTEXTIONARY_URL: contextionary:9999 ollama: image: ollama/ollama:0.9.6 volumes: @@ -31,7 +32,16 @@ services: - "11434:11434" environment: - OLLAMA_HOST=0.0.0.0 - + contextionary: + environment: + OCCURRENCE_WEIGHT_LINEAR_FACTOR: 0.75 + EXTENSIONS_STORAGE_MODE: weaviate + EXTENSIONS_STORAGE_ORIGIN: http://weaviate:8080 + NEIGHBOR_OCCURRENCE_IGNORE_PERCENTILE: 5 + ENABLE_COMPOUND_SPLITTING: 'false' + image: cr.weaviate.io/semitechnologies/contextionary:en0.16.0-v1.2.1 + ports: + - 9999:9999 volumes: ollama_data: ... diff --git a/tools/add_csharp.py b/tools/add_csharp.py new file mode 100644 index 00000000..75217f6d --- /dev/null +++ b/tools/add_csharp.py @@ -0,0 +1,209 @@ +import os +import re +import argparse +from pathlib import Path + +# Template for the new C# TabItem +CSHARP_TAB_TEMPLATE = """ + + + """ + + +def process_imports(content): + """ + Adds a CSharpCode import if a JavaV6Code import exists and the CSharpCode + import does not. + """ + # If C# import already exists, do nothing. + if re.search(r"import\s+CSharpCode\s+from", content): + return content + + # Find the Java V6 import to base the new import on. + # It captures the full import statement and the file path part. + java_import_match = re.search( + r'(import\s+JavaV6Code\s+from\s+([\'"]).*?java-v6/(.*?)(\2;))', + content, + re.DOTALL, + ) + if not java_import_match: + return content + + full_import_statement = java_import_match.group(0) + java_file_path = java_import_match.group(3) + + # Create the corresponding C# file path by replacing .java with .cs + csharp_file_path = re.sub(r"\.java$", ".cs", java_file_path) + + # Create the new import statement for C# + new_import_statement = ( + full_import_statement.replace("JavaV6Code", "CSharpCode") + .replace("java-v6/", "csharp/") + .replace(java_file_path, csharp_file_path) + ) + + # Insert the new import directly after the Java V6 one for consistency. + return content.replace( + full_import_statement, f"{full_import_statement}\n{new_import_statement}" + ) + + +def add_csharp_tab_to_block(tabs_block_match): + """ + A replacer function for re.sub that processes a single ... block. + """ + tabs_block = tabs_block_match.group(0) + + # Condition 1: Don't add if a C# tab already exists. + # Condition 2: Don't add if there's no Java v6 tab to use as a template. + if 'value="csharp"' in tabs_block or 'value="java6"' not in tabs_block: + return tabs_block + + # Find the Java v6 tab. We assume TabItems are not nested within each other. + java6_tab_match = re.search( + r'(.*?)', tabs_block, re.DOTALL + ) + if not java6_tab_match: + return tabs_block + + java6_tab_text = java6_tab_match.group(1) + + # Extract start and end markers from within the Java v6 tab text. + marker_match = re.search( + r'startMarker="([^"]+)"[\s\S]*?endMarker="([^"]+)"', java6_tab_text, re.DOTALL + ) + if not marker_match: + # Cannot proceed if markers aren't found in the Java v6 tab. + return tabs_block + + start_marker, end_marker = marker_match.groups() + + # Create the new C# tab from the template. + csharp_tab_text = CSHARP_TAB_TEMPLATE.format( + start_marker=start_marker, end_marker=end_marker + ).strip() + + # Determine the correct insertion point. + # We insert after the Java v6 tab, OR after the regular Java tab + # if it immediately follows the Java v6 tab. + + end_of_java6_match = java6_tab_match.end() + rest_of_block_after_java6 = tabs_block[end_of_java6_match:] + + # Check if the next element is a regular Java tab. + if re.match(r'\s*.*?)', + rest_of_block_after_java6, + re.DOTALL, + ) + if java_tab_match: + java_tab_text = java_tab_match.group(1) + # Replace the Java tab with itself plus the new C# tab. + return tabs_block.replace( + java_tab_text, f"{java_tab_text}\n {csharp_tab_text}" + ) + + # If no regular Java tab followed, insert after the Java v6 tab. + # Replace the Java v6 tab with itself plus the new C# tab. + return tabs_block.replace(java6_tab_text, f"{java6_tab_text}\n {csharp_tab_text}") + + +def process_file_content(content): + """ + Runs the import and tab processing on the entire file content. + Returns the new content and a boolean indicating if changes were made. + """ + original_content = content + + # Step 1: Add C# import statement if needed. + content_with_imports = process_imports(original_content) + + # Step 2: Find all blocks and apply the replacer function to each. + tabs_pattern = re.compile(r"]*>.*?", re.DOTALL) + new_content = tabs_pattern.sub(add_csharp_tab_to_block, content_with_imports) + + return new_content, new_content != original_content + + +def process_directory(directory_path, file_extensions, dry_run=True): + """ + Process all files with given extensions in a directory and its subdirectories. + """ + directory = Path(directory_path) + files_processed = 0 + files_modified = 0 + + for ext in file_extensions: + for file_path in directory.glob(f"**/*{ext}"): + files_processed += 1 + + # Read the file content + with open(file_path, "r", encoding="utf-8") as file: + try: + content = file.read() + except UnicodeDecodeError: + print(f"Error reading {file_path}: UnicodeDecodeError") + continue + + # Process the content to add tabs and imports + new_content, was_modified = process_file_content(content) + + # If content was modified + if was_modified: + files_modified += 1 + print(f"Modified: {file_path}") + + # Write the changes if not in dry-run mode + if not dry_run: + with open(file_path, "w", encoding="utf-8") as file: + file.write(new_content) + + return files_processed, files_modified + + +def main(): + parser = argparse.ArgumentParser( + description="Adds C# TabItem blocks after Java v6/Java tabs in documentation files." + ) + parser.add_argument("directory", help="Directory to process") + parser.add_argument( + "--ext", + nargs="+", + default=[".md", ".mdx"], + help="File extensions to process (default: .md .mdx)", + ) + parser.add_argument( + "--apply", + action="store_true", + help="Apply changes (without this flag, runs in dry-run mode)", + ) + + args = parser.parse_args() + + print(f"Processing files in {args.directory}") + print(f"File extensions: {', '.join(args.ext)}") + print( + f"Mode: {'Apply Changes' if args.apply else 'Dry Run (no changes will be made)'}" + ) + + files_processed, files_modified = process_directory( + args.directory, args.ext, dry_run=not args.apply + ) + + print(f"\nSummary:") + print(f"Files processed: {files_processed}") + print(f"Files that would be/were modified: {files_modified}") + + if not args.apply and files_modified > 0: + print("\nRun with --apply to make the changes.") + + +if __name__ == "__main__": + main() diff --git a/versions-config.json b/versions-config.json index 82590d5a..e22dc4f2 100644 --- a/versions-config.json +++ b/versions-config.json @@ -8,7 +8,9 @@ "python_client_version": "4.17.0", "go_client_version": "5.5.0", "java_client_version": "5.5.0", + "java_new_client_version": "6.0.0-M1", "javascript_client_version": "2.14.5", "typescript_client_version": "3.9.0", - "spark_connector_version": "1.4.0" + "spark_connector_version": "1.4.0", + "csharp_client_version": "0.0.1-beta.4" }