# Täyden tekstin haku (Pt 3 - OpenSearch ASP:n kanssa

<!--category-- OpenSearch, ASP.NET -->
<datetime class="hidden">2024-08-24T06:40</datetime>

## Esitys

Aikaisemmissa osissa otimme esille koko tekstien hakua koskevan käsitteen ja sen, miten sitä voidaan käyttää tekstien etsimiseen tietokannassa.

Aiemmat osat:

- [Täyden tekstin haku Postgresilla](/blog/textsearchingpt1)
- [hakulaatikko Postgresin kanssa](/blog/textsearchingpt11)
- [OpenSearchin käyttöönotto](/blog/textsearchingpt2)

Tässä osassa käsitellään, miten voitte aloittaa uuden loistavan OpenSearch-esityksen käyttämisen ASP:n kanssa.

[TOC]

## Järjestelmä

Kun OpenSearch-esitys on otettu käyttöön ja käynnissä, voimme aloittaa vuorovaikutuksen sen kanssa. [OpenSearch-klientti](https://opensearch.org/docs/latest/clients/OSC-dot-net/) .NETM SK1
Ensinnäkin otamme käyttöön Clientin ohjelmien laajentamisessa

```csharp
    var openSearchConfig = services.ConfigurePOCO<OpenSearchConfig>(configuration.GetSection(OpenSearchConfig.Section));
        var config = new ConnectionSettings(new Uri(openSearchConfig.Endpoint))
            .EnableHttpCompression()
            .EnableDebugMode()
            .ServerCertificateValidationCallback((sender, certificate, chain, errors) => true)
            .BasicAuthentication(openSearchConfig.Username, openSearchConfig.Password);
        services.AddSingleton<OpenSearchClient>(c => new OpenSearchClient(config));
```

Tämä vahvistaa Clientin loppupisteen ja todisteiden kanssa. Myönnämme myös debug-muodon, jotta voimme nähdä, mitä tapahtuuM SK1 . Koska emme käytä todellisia SSL-todistuksia, lopetamme todistusten validoinnin.

## Indexointitiedot

OpenSearchin keskeinen käsite on indeksi.

Tätä varten me' käytämme [OpenSearch-klientti](https://opensearch.org/docs/latest/clients/OSC-dot-net/) .NET. Voitte asentaa tämän NuGet-järjestelmän kautta

Huomaatte, että siellä on kaksi asiaa: - Opensearch . Net ja Opensearch. Client . Ensinnäkin niillä on alhainen taso, kuten yhteyksien hallinnointi , Toiseksi niissä on korkean tason, kuten indeksointi ja haku

Nyt kun se on otettu käyttöön, voimme alkaa tarkastella indeksointitietoja.

Esimerkkien luominen on puolinkertaista- suoraan eteenpäinM SK1 määrittele vain, miltä indeksin pitäisi näyttää ja sitten luo se.
jäljempänä olevassa koodissa voimme nähdä 'kartta'indeksimallimme (blogin yksinkertaistettu versioM SK3tietokannan mallinMSC4
Tämän mallin kutakin alaa varten määritämme sen tyyppi: (tekstiM SK1 päivämäärä, avainsana jne.

Luettelo on tärkeä, koska siinä määritellään, miten tietoja tallennetaan ja miten niitä voidaan hakea.. Esimerkiksi, field analysoidaan ja tokenoidaan |'text | ' field is analyzed and tokenized |

Myös tässä Categories on itse asiassa string[] mutta avainsanatyyppi ymmärtää, miten käsitellä niitä oikeinM SK1

```csharp
   public async Task CreateIndex(string language)
    {
        var languageName = language.ConvertCodeToLanguageName();
        var indexName = GetBlogIndexName(language);

      var response =  await client.Indices.CreateAsync(indexName, c => c
            .Settings(s => s
                .NumberOfShards(1)
                .NumberOfReplicas(1)
            )
            .Map<BlogIndexModel>(m => m
                .Properties(p => p
                    .Text(t => t
                        .Name(n => n.Title)
                        .Analyzer(languageName)
                    )
                    .Text(t => t
                        .Name(n => n.Content)
                        .Analyzer(languageName)
                    )
                    .Text(t => t
                        .Name(n => n.Language)
                    )
                    .Date(t => t
                        .Name(n => n.LastUpdated)
                    )
                    .Date(t => t
                        .Name(n => n.Published)
                    )
                    .Date(t => t
                        .Name(n => n.LastUpdated)
                    )
                    .Keyword(t => t
                        .Name(n => n.Id)
                    )
                    .Keyword(t=>t
                        .Name(n=>n.Slug)
                    )
                    .Keyword(t=>t
                        .Name(n=>n.Hash)
                    )
                    .Keyword(t => t
                        .Name(n => n.Categories)
                    )
                )
            )
        );
        
        if (!response.IsValid)
        {
           logger.LogError("Failed to create index {IndexName}: {Error}", indexName, response.DebugInformation);
        }
    }
```

## Esimerkkien lisääminen luetteloon

Kun olemme laatineet luettelomme lisätäksemme siihen asioita, meidän on lisättävä asioita tähän luetteloon.

Voitte nähdä, että alun perin kutsumme menetelmäksi`GetExistingPosts` , joka palauttaa kaikki kohdat, jotka ovat jo indeksissä.
Käytämme hashia ja idiä tunnistaaksemme, onko posti jo indeksissä.

```csharp
    public async Task AddPostsToIndex(IEnumerable<BlogIndexModel> posts)
    {
        var existingPosts = await GetExistingPosts();
        var langPosts = posts.GroupBy(p => p.Language);
        langPosts=langPosts.Where(p => p.Key!="uk");
        langPosts = langPosts.Where(p =>
            p.Any(post => !existingPosts.Any(existing => existing.Id == post.Id && existing.Hash == post.Hash)));
        
        foreach (var blogIndexModels in langPosts)
        {
            
            var language = blogIndexModels.Key;
            var indexName = GetBlogIndexName(language);
            if(!await IndexExists(language))
            {
                await CreateIndex(language);
            }
            
            var bulkRequest = new BulkRequest(indexName)
            {
                Operations = new BulkOperationsCollection<IBulkOperation>(blogIndexModels.ToList()
                    .Select(p => new BulkIndexOperation<BlogIndexModel>(p))
                    .ToList()),
                Refresh = Refresh.True,
                ErrorTrace = true,
                RequestConfiguration = new RequestConfiguration
                {
                    MaxRetries = 3
                }
            };

            var bulkResponse = await client.BulkAsync(bulkRequest);
            if (!bulkResponse.IsValid)
            {
                logger.LogError("Failed to add posts to index {IndexName}: {Error}", indexName, bulkResponse.DebugInformation);
            }
            
        }
    }
```

Kun olemme ”'” –filteroineet olemassa olevat postit ja poistaneet puutteellisen analysaattorimme, luomme uuden indeksin (nimien perusteella.<language>") ja sen jälkeen luoda kokonaispyynnön. Tämä kokonaispyynti on luettelo toimista, joita on tehtävä indeksissä
Tämä on tehokkaampaa kuin jokaisen kohdan lisääminen yksi kerrallaan.

Huomaatte, että `BulkRequest` asetamme `Refresh` omaisuus `true`. Tämä merkitsee sitä, että indeksi on päivitetty sen jälkeen kun kokonaispano on saatu päätökseen.

## Indeksin etsintä

Hyvä tapa testata sitä, mitä ”'” on itse asiassa luotu, on mennä OpenSearch Dashboardin Dev-välineisiin ja tehdä hakupyyntö.

```json
GET /mostlylucid-blog-*
{}
```

Tämä kysymys palauttaa meille kaikki indeksit, jotka vastaavat mallia `mostlylucid-blog-*`. (kaikki tähänastiset indikaattorimme

```json
{
  "mostlylucid-blog-ar": {
    "aliases": {},
    "mappings": {
      "properties": {
        "categories": {
          "type": "keyword"
        },
        "content": {
          "type": "text",
          "analyzer": "arabic"
        },
        "hash": {
          "type": "keyword"
        },
        "id": {
          "type": "keyword"
        },
        "language": {
          "type": "text"
        },
        "lastUpdated": {
          "type": "date"
        },
        "published": {
          "type": "date"
        },
        "slug": {
          "type": "keyword"
        },
        "title": {
          "type": "text",
          "analyzer": "arabic"
        }
      }
    },
    "settings": {
      "index": {
        "replication": {
          "type": "DOCUMENT"
..MANY MORE
```

Dev Tools OpenSearch Dashboardissa on erinomainen tapa testata kysymyksiänne ennen kuin panette ne kokeenne.

![Dev-välineet](devtools.png?width=900&quality=25)

## Indeksin etsintä

Nyt voimme alkaa hakea indeksiä. Voimme käyttää `Search` menetelmä, jonka avulla tämä tehdään
Tämä on se, missä OpenSearchin todellinen voima tulee . [useat erilaiset kysymykset](https://opensearch.org/docs/latest/query-dsl/) , jonka avulla voit hakea tietojanne. Kaikista yksinkertaisesta avainsanoista monimutkaiseen

```csharp
    public async Task<List<BlogIndexModel>> GetSearchResults(string language, string query, int page = 1, int pageSize = 10)
    {
        var indexName = GetBlogIndexName(language);
        var searchResponse = await client.SearchAsync<BlogIndexModel>(s => s
                .Index(indexName)  // Match index pattern
                .Query(q => q
                    .Bool(b => b
                        .Must(m => m
                            .MultiMatch(mm => mm
                                .Query(query)
                                .Fields(f => f
                                    .Field(p => p.Title, boost: 2.0) 
                                    .Field(p => p.Categories, boost: 1.5) 
                                    .Field(p => p.Content)
                                )
                                .Type(TextQueryType.BestFields)
                                .Fuzziness(Fuzziness.Auto)
                            )
                        )
                    )
                )
                .Skip((page -1) * pageSize)  // Skip the first n results (adjust as needed)
                .Size(pageSize)  // Limit the number of results (adjust as needed)
        );

        if(!searchResponse.IsValid)
        {
            logger.LogError("Failed to search index {IndexName}: {Error}", indexName, searchResponse.DebugInformation);
            return new List<BlogIndexModel>();
        }
        return searchResponse.Documents.ToList();
    }

```

### Kysymyksen kuvaus

Tämä menetelmä, `GetSearchResults`, on suunniteltu kysymään tietyn OpenSearch-indeksin, jotta blogit saadaan takaisin. `language`, `query`, ja sivuusparameterit `page` ja `pageSize`.Herä on ', mitä se tekee

1. **Indexin valinta**:
   
   - Se palauttaa indeksin nimeä käyttäen `GetBlogIndexName` menetelmä annetun kielen perusteella. indeksi selected dynamically according to the language

2. **hakukysymys**:
   
   - Kysymyksessä käytetään `Bool` kysymyksen `Must` lauseke sen varmistamiseksi, että tulokset vastaavat tiettyjä kriteerejä
   - Sisäpuolella `Must` alakohta, a `MultiMatch` (`Title`, `Categories`, ja `Content`).
     - **Painottaminen**: `Title` alueelle annetaan lisäys `2.0`,, mikä tekee sen tärkeämmäksi hakussa `Categories` on nopeuttanut `1.5`. Tämä tarkoittaa, että asiakirjat, joissa hakukysymys ilmenee otsikossa tai kategorioissa, suoritetaan korkeammalla sijalla.
     - **Kysymyksentyyppi**: Se käyttää `BestFields`,, joka yrittää löytää parhaan vastaamaan kysymykseen kuuluvan alueen
     - **Fuzziness**: `Fuzziness.Auto` parametri antaa approximate matches (e .gM SK2 käsitellä pieniä typoja).

3. **Pageointi**:
   
   - Euroopan unionin `Skip` menetelmä välttää ensimmäisen `n` tulos riippuu sivunumerosta,, joka lasketaan `(page - 1) * pageSize`. Tämä auttaa navigoimaan sivuttujen tulosten läpi
   - Euroopan unionin `Size` menetelmä rajoittaa määrän asiakirjoja, jotka palautetaan `pageSize`.

4. **Ongelmakäsittelyssä**:
   
   - Jos haku epäonnistuu, virhe kirjataan ja tyhjä luettelo palautetaan.

5. **Tulos**:
   
   - Menetelmä palauttaa luettelon `BlogIndexModel` hakukriteerien mukaiset asiakirjat.

Voitte siis nähdä, että voimme olla erittäin joustavia tietojen hakumenettelyssä.

Yksi BIG-etu on se helppous, että voimme tukea useita kieliä. Meillä on erilainen indeksi jokaiselle kielelle ja mahdollistaa haku kyseisessä indeksissä. Tämä tarkoittaa, että me voimme käyttää oikean analyysin jokaiseen kieleen ja saada parhaat tuloksetM SK2

## Uusi haku API

Toisin kuin tämän sarjan edellisissä osissa nähdyssä haku API:ssa, me voimme yksinkertaistaa laajasti hakuprosessia käyttämällä OpenSearch-järjestelmää.Me voimme vain syöttää tekstiä tähän kysymykseen ja saada hyviä tuloksia takaisin.

```csharp
   [HttpGet]
    [Route("osearch/{query}")]
   [ValidateAntiForgeryToken]
    public async Task<JsonHttpResult<List<SearchResults>>> OpenSearch(string query, string language = MarkdownBaseService.EnglishLanguage)
    {
        var results = await indexService.GetSearchResults(language, query);
        
        var host = Request.Host.Value;
        var output = results.Select(x => new SearchResults(x.Title.Trim(), x.Slug, @Url.ActionLink("Show", "Blog", new{ x.Slug}, protocol:"https", host:host) )).ToList();
        return TypedResults.Json(output);
    }
```

Kuten voitte nähdä, meillä on kaikki tiedot, joita tarvitsemme indeksissä tulosten palauttamiseksi. Voimme sen jälkeen käyttää sitä luodaksemme URL:n blogiin lähetettävälle postilleM SK1 Tämä vähentää tietokantamme kuormitusta ja tekee hakuprosessista paljon nopeamman.

## Päätelmissä

Tässä artikkelissa näimme, miten kirjoitetaan C# klientti vuorovaikutukseen OpenSearchin instanssin kanssa.