title: More advanced features of the high-level .NET client
nav_order: 12
has_children: false
parent: .NET clients
---
# More advanced features of the high-level .NET client (OpenSearch.Client)
The following example illustrates more advanced features of OpenSearch.Client. For a simple example, see the [Getting started guide]({{site.url}}{{site.baseurl}}/clients/OSC-dot-net/). This example uses the following Student class.
OpenSearch uses dynamic mapping to infer field types of the documents that are indexed. However, to have more control over the schema of your document, you can pass an explicit mapping to OpenSearch. You can define data types for some or all fields of your document in this mapping.
Similarly, OpenSearch.Client uses auto mapping to infer field data types based on the types of the class's properties. To use auto mapping, create a `students` index using the AutoMap's default constructor:
```cs
var createResponse = await osClient.Indices.CreateAsync("students",
If you use auto mapping, Id and GradYear are mapped as integers, Gpa is mapped as a double, and FirstName and LastName are mapped as text with a keyword subfield. If you want to search for FirstName and LastName and allow only case-sensitive full matches, you can suppress analyzing by mapping these fields as keyword only. In Query DSL, you can accomplish this using the following query:
```json
PUT students
{
"mappings" : {
"properties" : {
"firstName" : {
"type" : "keyword"
},
"lastName" : {
"type" : "keyword"
}
}
}
}
```
In OpenSearch.Client, you can use fluid lambda syntax to mark these fields as keywords:
```cs
var createResponse = await osClient.Indices.CreateAsync(index,
In addition to mappings, you can specify settings like the number of primary and replica shards when creating an index. The following query sets the number of primary shards to 1 and the number of replica shards to 2:
```json
PUT students
{
"mappings" : {
"properties" : {
"firstName" : {
"type" : "keyword"
},
"lastName" : {
"type" : "keyword"
}
}
},
"settings": {
"number_of_shards": 1,
"number_of_replicas": 2
}
}
```
In OpenSearch.Client, the equivalent of the above query is the following:
```cs
var createResponse = await osClient.Indices.CreateAsync(index,
In addition to indexing one document using `Index` and `IndexDocument` and indexing multiple documents using `IndexMany`, you can gain more control over document indexing by using `Bulk` or `BulkAll`. Indexing documents individually is inefficient because it creates an HTTP request for every document sent. The BulkAll helper frees you from handling retry, chunking or back off request functionality. It automatically retries if the request fails, backs off if the server is down, and controls how many documents are sent in one HTTP request.
In the following example, `BulkAll` is configured with the index name, number of back off retries, and back off time. Additionally, the maximum degrees of parallelism setting controls the number of parallel HTTP requests containing the data. Finally, the size parameter signals how many documents are sent in one HTTP request.
We recommend setting the size to 100–1000 documents in production.
{: .tip}
`BulkAll` takes a stream of data and returns an Observable that you can use to observe the background operation.
OpenSearch.Client exposes full OpenSearch query capability. In addition to simple searches that use the match query, you can create a more complex Boolean query to search for students who graduated in 2022 and sort them by last name. In the example below, search is limited to 10 documents, and the scroll API is used to control the pagination of results.
```cs
var gradResponse = await osClient.SearchAsync<Student>(s => s
The response contains the Documents property with matching documents from OpenSearch. The data is in the form of deserialized JSON objects of Student type, so you can access their properties in a strongly typed fashion. All serialization and deserialization is handled by OpenSearch.Client.
## Aggregations
OpenSearch.Client includes the full OpenSearch query functionality, including aggregations. In addition to grouping search results into buckets (for example, grouping students by GPA ranges), you can calculate metrics like sum or average. The following query calculates the average GPA of all students in the index.
Setting Size to 0 means OpenSearch will only return the aggregation, not the actual documents.
{: .tip}
```cs
var aggResponse = await osClient.SearchAsync<Student>(s => s