diff --git a/guides/search.md b/guides/search.md new file mode 100644 index 0000000000..1695dd45aa --- /dev/null +++ b/guides/search.md @@ -0,0 +1,212 @@ +# Search +OpenSearch provides a powerful search API that allows you to search for documents in an index. The search API supports a number of parameters that allow you to customize the search operation. In this guide, we will use the low-level OpenSearch.Net client to explore the search API and its parameters. + +## Setup +Assuming you have OpenSearch running locally on port 9200, you can create a client instance with the following code: + +```csharp +using OpenSearch.Client; +using OpenSearch.Net; + +var node = new Uri("https://localhost:9200"); +var config = new ConnectionSettings(node) + .ServerCertificateValidationCallback(CertificateValidations.AllowAll) + .BasicAuthentication("admin", "admin"); + +var client = new OpenSearchClient(config); +``` + +# Scaffold An Index & Add Documents +```csharp +class Movie +{ + public string Title { get; set; } + public string Director { get; set; } + public int Year { get; set; } +} + +var createIndexResponse = client.Indices.Create("movies", c => c + .Settings(s => s + .NumberOfShards(1) + .NumberOfReplicas(0) + ) + .Map(m => m + .Properties(p => p + .Text(t => t + .Name("title") + ) + .Text(t => t + .Name("director") + ) + .Number(n => n + .Name("year") + ) + ) + ) + ); + + // Create 15 movies + var movies = new List + { + new Movie { Title = "The Shawshank Redemption", Director = "Frank Darabont", Year = 1994 }, + new Movie { Title = "The Avengers", Director = "Joss Whedon", Year = 2012 }, + new Movie { Title = "The Dark Knight", Director = "Christopher Nolan", Year = 2008 }, + new Movie { Title = "Pulp Fiction", Director = "Quentin Tarantino", Year = 1994 }, + new Movie { Title = "Fight Club", Director = "David Fincher", Year = 1999 }, + new Movie { Title = "Forrest Gump", Director = "Robert Zemeckis", Year = 1994 }, + new Movie { Title = "Inception", Director = "Christopher Nolan", Year = 2010 }, + new Movie { Title = "The Matrix", Director = "The Wachowski Brothers", Year = 1999 }, + new Movie { Title = "Interstellar", Director = "Christopher Nolan", Year = 2014 }, + new Movie { Title = "The Lord of the Rings: The Fellowship of the Ring", Director = "Peter Jackson", Year = 2001 }, + new Movie { Title = "The Lord of the Rings: The Two Towers", Director = "Peter Jackson", Year = 2002 }, + new Movie { Title = "The Lord of the Rings: The Return of the King", Director = "Peter Jackson", Year = 2003 }, + new Movie { Title = "The Godfather", Director = "Francis Ford Coppola", Year = 1972 }, + new Movie { Title = "The Godfather: Part II", Director = "Francis Ford Coppola", Year = 1974 }, + new Movie { Title = "The Good, the Bad and the Ugly", Director = "Sergio Leone", Year = 1966 } + }; + + // Index the movies + var indexResponse = await client.IndexManyAsync(movies, "movies"); + Console.WriteLine($"Indexed {indexResponse.Items.Count} movies"); // Indexed 15 movies + + // Refresh the documents to make them searchable + var refreshResponse = await client.Indices.RefreshAsync("movies"); +``` + + +## Search API + +### Basic Search +The search API allows you to search for documents in an index. The following example searches for ALL documents in the `movies` index: + +```csharp +var searchResponse = await client.SearchAsync(s => s + .Index("movies") + .Query(q => q + .MatchAll() + ) + .TrackTotalHits(true) +); +Console.WriteLine($"Found {searchResponse.Total} movies"); // Found 15 movies +``` + +You can also search for documents that match a specific query. The following example searches for documents that match the query `Avengers`: +```csharp +var searchResponse = await client.SearchAsync(s => s + .Index("movies") + .Query(q => q + .Match(m => m + .Field(f => f.Title) + .Query("Avengers") + ) + ) + .TrackTotalHits(true) +); +``` + +OpenSearch query DSL allows you to specify complex queries. Check out the [OpenSearch query DSL documentation](https://opensearch.org/docs/latest/query-dsl/) for more information. + + +### Basic Pagination +The search API allows you to paginate through the search results. The following example searches for documents that match the query `The Lord of the Rings`, sorted by `year` in in ascending order, and returns the first 2 results after skipping the first 5 results: + +```csharp +var searchResponse = await client.SearchAsync(s => s + .Index("movies") + .Query(q => q + .Match(m => m + .Field(f => f.Title) + .Query("The Lord of the Rings") + ) + ) + .Sort(so => so + .Ascending(f => f.Year) + ) + .From(5) + .Size(2) + .TrackTotalHits(true) +); +``` + +### Sorting +With sorting, you can also use the `search_after` parameter to paginate through the search results. Let's say you have already displayed the first page of results, and you want to display the next page. You can use the `search_after` parameter to paginate through the search results. The following example will demonstrate how to get the first 3 pages of results using the search query of the previous example: + +```csharp +var pageOneResponse = await client.SearchAsync(s => s + .Index("movies") + .Query(q => q + .Match(m => m + .Field(f => f.Title) + .Query("The Lord of the Rings") + ) + ) + .Sort(so => so + .Ascending(f => f.Year) + ) + .Size(2) + .TrackTotalHits(true) +); + +var pageTwoResponse = await client.SearchAsync(s => s + .Index("movies") + .Query(q => q + .Match(m => m + .Field(f => f.Title) + .Query("The Lord of the Rings") + ) + ) + .Sort(so => so + .Ascending(f => f.Year) + ) + .Size(2) + .SearchAfter(pageOneResponse.Hits.Last().Sorts) + .TrackTotalHits(true) +); + +var pageThreeResponse = await client.SearchAsync(s => s + .Index("movies") + .Query(q => q + .Match(m => m + .Field(f => f.Title) + .Query("The Lord of the Rings") + ) + ) + .Sort(so => so + .Ascending(f => f.Year) + ) + .Size(2) + .SearchAfter(pageTwoResponse.Hits.Last().Sorts) + .TrackTotalHits(true) +); +``` + +### Pagination with Scroll API +When retrieving large amounts of non-real-time data, you can use the `scroll` parameter to paginate through the search results: + +```csharp +var scrollResponse = await client.SearchAsync(s => s + .Index("movies") + .Query(q => q + .Match(m => m + .Field(f => f.Title) + .Query("Lord of the Rings") + ) + ) + .Sort(so => so + .Ascending(f => f.Year) + ) + .Size(2) + .Scroll("1m") + .TrackTotalHits(true) +); + +var scrollResponseTwo = await client.ScrollAsync("1m", scrollResponse.ScrollId); + +var scrollResponseThree = await client.ScrollAsync("1m", scrollResponseTwo.ScrollId); +``` + + +## Cleanup +```csharp +var deleteIndexResponse = await client.Indices.DeleteAsync("movies"); +``` \ No newline at end of file