Dela via


Självstudie: Lägga till växling i sökresultat med hjälp av .NET SDK

Lär dig hur du implementerar två olika växlingssystem, det första baserat på sidnummer och det andra på oändlig rullning. Båda växlingssystemen används ofta, och valet av rätt beror på vilken användarupplevelse du vill ha med resultatet.

I den här självstudiekursen får du lära du dig att:

  • Utöka din app med numrerad växling
  • Utöka din app med oändlig rullning

Översikt

Den här självstudien lägger över ett växlingssystem till ett tidigare skapat projekt som beskrivs i självstudien Skapa din första sökapp .

Färdiga versioner av koden som du kommer att utveckla i den här självstudien finns i följande projekt:

Förutsättningar

Utöka din app med numrerad växling

Numrerad växling är det växlingssystem som är valfritt för de viktigaste kommersiella webbsökmotorerna och många andra sökwebbplatser. Numrerad växling innehåller vanligtvis alternativet "nästa" och "föregående" utöver ett intervall med faktiska sidnummer. Även alternativet "första sidan" och "sista sidan" kan också vara tillgängligt. Dessa alternativ ger verkligen en användare kontroll över att navigera genom sidbaserade resultat.

I den här självstudien lägger du till ett system som innehåller första, föregående, nästa och sista alternativ, tillsammans med sidnummer som inte startar från 1, utan i stället omger den aktuella sidan som användaren är på (så om användaren till exempel tittar på sidan 10, kanske sidnummer 8, 9, 10, 11 och 12 visas).

Systemet är tillräckligt flexibelt för att tillåta att antalet synliga sidnummer anges i en global variabel.

Systemet behandlar knapparna för sidnummer till vänster och höger som speciella, vilket innebär att de utlöser en ändring av det intervall med sidnummer som visas. Om till exempel sidnummer 8, 9, 10, 11 och 12 visas och användaren klickar på 8, visas intervallet för sidnummer till 6, 7, 8, 9 och 10. Och det finns ett liknande skifte till höger om de valde 12.

Lägga till växlingsfält i modellen

Låt den grundläggande söksideslösningen vara öppen.

  1. Öppna sökdata.cs-modellfilen.

  2. Lägg till globala variabler som stöd för sidnumrering. I MVC deklareras globala variabler i sin egen statiska klass. ResultsPerPage anger antalet resultat per sida. MaxPageRange avgör antalet synliga sidnummer i vyn. PageRangeDelta avgör hur många sidor som ska flyttas åt vänster eller höger när sidnumret till vänster eller längst till höger väljs. Det senare talet är vanligtvis ungefär hälften av MaxPageRange. Lägg till följande kod i namnområdet.

    public static class GlobalVariables
    {
        public static int ResultsPerPage
        {
            get
            {
                return 3;
            }
        }
        public static int MaxPageRange
        {
            get
            {
                return 5;
            }
        }
    
        public static int PageRangeDelta
        {
            get
            {
                return 2;
            }
        }
    }
    

    Tips

    Om du kör det här projektet på en enhet med en mindre skärm, till exempel en bärbar dator, bör du överväga att ändra ResultsPerPage till 2.

  3. Lägg till växlingsegenskaper i klassen SearchData efter egenskapen searchText .

    // The current page being displayed.
    public int currentPage { get; set; }
    
    // The total number of pages of results.
    public int pageCount { get; set; }
    
    // The left-most page number to display.
    public int leftMostPage { get; set; }
    
    // The number of page numbers to display - which can be less than MaxPageRange towards the end of the results.
    public int pageRange { get; set; }
    
    // Used when page numbers, or next or prev buttons, have been selected.
    public string paging { get; set; }
    

Lägga till en tabell med växlingsalternativ i vyn

  1. Öppna filen index.cshtml och lägg till följande kod precis före den avslutande </body-taggen> . Den här nya koden visar en tabell med växlingsalternativ: först, tidigare, 1, 2, 3, 4, 5, nästa, sista.

    @if (Model != null && Model.pageCount > 1)
    {
    // If there is more than one page of results, show the paging buttons.
    <table>
        <tr>
            <td>
                @if (Model.currentPage > 0)
                {
                    <p class="pageButton">
                        @Html.ActionLink("|<", "Page", "Home", new { paging = "0" }, null)
                    </p>
                }
                else
                {
                    <p class="pageButtonDisabled">|&lt;</p>
                }
            </td>
    
            <td>
                @if (Model.currentPage > 0)
                {
                    <p class="pageButton">
                        @Html.ActionLink("<", "PageAsync", "Home", new { paging = "prev" }, null)
                    </p>
                }
                else
                {
                    <p class="pageButtonDisabled">&lt;</p>
                }
            </td>
    
            @for (var pn = Model.leftMostPage; pn < Model.leftMostPage + Model.pageRange; pn++)
            {
                <td>
                    @if (Model.currentPage == pn)
                    {
                        // Convert displayed page numbers to 1-based and not 0-based.
                        <p class="pageSelected">@(pn + 1)</p>
                    }
                    else
                    {
                        <p class="pageButton">
                            @Html.ActionLink((pn + 1).ToString(), "PageAsync", "Home", new { paging = @pn }, null)
                        </p>
                    }
                </td>
            }
    
            <td>
                @if (Model.currentPage < Model.pageCount - 1)
                {
                    <p class="pageButton">
                        @Html.ActionLink(">", "PageAsync", "Home", new { paging = "next" }, null)
                    </p>
                }
                else
                {
                    <p class="pageButtonDisabled">&gt;</p>
                }
            </td>
    
            <td>
                @if (Model.currentPage < Model.pageCount - 1)
                {
                    <p class="pageButton">
                        @Html.ActionLink(">|", "PageAsync", "Home", new { paging = Model.pageCount - 1 }, null)
                    </p>
                }
                else
                {
                    <p class="pageButtonDisabled">&gt;|</p>
                }
            </td>
        </tr>
    </table>
    }
    

    Vi använder en HTML-tabell för att justera saker snyggt. Men all åtgärd kommer från -uttrycken@Html.ActionLink, var och en anropar kontrollanten med en ny modell som skapats med olika poster till växlingsegenskapen som vi lade till tidigare.

    Alternativen för första och sista sidan skickar inte strängar som "first" och "last", utan skickar i stället rätt sidnummer.

  2. Lägg till växlingsklasser i listan över HTML-format i filen hotels.css. Klassen pageSelected finns där för att identifiera den aktuella sidan (genom att använda ett fetstilt format på sidnumret) i listan med sidnummer.

    .pageButton {
        border: none;
        color: darkblue;
        font-weight: normal;
        width: 50px;
    }
    
    .pageSelected {
        border: none;
        color: black;
        font-weight: bold;
        width: 50px;
    }
    
    .pageButtonDisabled {
        border: none;
        color: lightgray;
        font-weight: bold;
        width: 50px;
    }
    

Lägg till en sidåtgärd i kontrollanten

  1. Öppna filen HomeController.cs och lägg till åtgärden PageAsync . Den här åtgärden svarar på något av de valda sidalternativen.

    public async Task<ActionResult> PageAsync(SearchData model)
    {
        try
        {
            int page;
    
            switch (model.paging)
            {
                case "prev":
                    page = (int)TempData["page"] - 1;
                    break;
    
                case "next":
                    page = (int)TempData["page"] + 1;
                    break;
    
                default:
                    page = int.Parse(model.paging);
                    break;
            }
    
            // Recover the leftMostPage.
            int leftMostPage = (int)TempData["leftMostPage"];
    
            // Recover the search text and search for the data for the new page.
            model.searchText = TempData["searchfor"].ToString();
    
            await RunQueryAsync(model, page, leftMostPage);
    
            // Ensure Temp data is stored for next call, as TempData only stores for one call.
            TempData["page"] = (object)page;
            TempData["searchfor"] = model.searchText;
            TempData["leftMostPage"] = model.leftMostPage;
        }
    
        catch
        {
            return View("Error", new ErrorViewModel { RequestId = "2" });
        }
        return View("Index", model);
    }
    

    Metoden RunQueryAsync visar nu ett syntaxfel på grund av den tredje parametern, som vi kommer till om en stund.

    Anteckning

    Anropen till TempData lagrar ett värde (ett objekt) i tillfällig lagring, även om den här lagringen endast sparas för ett anrop. Om vi lagrar något i tillfälliga data blir det tillgängligt för nästa anrop till en kontrollantåtgärd, men kommer definitivt att försvinna av anropet efter det. På grund av den här korta livslängden lagrar vi söktexten och växlingsegenskaperna i tillfällig lagring varje anrop till PageAsync.

  2. Uppdatera åtgärden Index(modell) för att lagra tillfälliga variabler och lägg till den vänstra sidparametern i RunQueryAsync-anropet .

    public async Task<ActionResult> Index(SearchData model)
    {
        try
        {
            // Ensure the search string is valid.
            if (model.searchText == null)
            {
                model.searchText = "";
            }
    
            // Make the search call for the first page.
            await RunQueryAsync(model, 0, 0);
    
            // Ensure temporary data is stored for the next call.
            TempData["page"] = 0;
            TempData["leftMostPage"] = 0;
            TempData["searchfor"] = model.searchText;
        }
    
        catch
        {
            return View("Error", new ErrorViewModel { RequestId = "1" });
        }
        return View(model);
    }
    
  3. Metoden RunQueryAsync, som introducerades i föregående lektion, behöver ändras för att lösa syntaxfelet. Vi använder fälten Hoppa över, Storlek och IncludeTotalCount i klassen SearchOptions för att bara begära resultat för en sida, med början i inställningen Hoppa över . Vi måste också beräkna växlingsvariablerna för vår vy. Ersätt hela metoden med följande kod.

    private async Task<ActionResult> RunQueryAsync(SearchData model, int page, int leftMostPage)
    {
        InitSearch();
    
        var options = new SearchOptions
        {
            // Skip past results that have already been returned.
            Skip = page * GlobalVariables.ResultsPerPage,
    
            // Take only the next page worth of results.
            Size = GlobalVariables.ResultsPerPage,
    
            // Include the total number of results.
            IncludeTotalCount = true
        };
    
        // Add fields to include in the search results.
        options.Select.Add("HotelName");
        options.Select.Add("Description");
    
        // For efficiency, the search call should be asynchronous, so use SearchAsync rather than Search.
        model.resultList = await _searchClient.SearchAsync<Hotel>(model.searchText, options).ConfigureAwait(false);
    
        // This variable communicates the total number of pages to the view.
        model.pageCount = ((int)model.resultList.TotalCount + GlobalVariables.ResultsPerPage - 1) / GlobalVariables.ResultsPerPage;
    
        // This variable communicates the page number being displayed to the view.
        model.currentPage = page;
    
        // Calculate the range of page numbers to display.
        if (page == 0)
        {
            leftMostPage = 0;
        }
        else if (page <= leftMostPage)
        {
            // Trigger a switch to a lower page range.
            leftMostPage = Math.Max(page - GlobalVariables.PageRangeDelta, 0);
        }
        else if (page >= leftMostPage + GlobalVariables.MaxPageRange - 1)
        {
            // Trigger a switch to a higher page range.
            leftMostPage = Math.Min(page - GlobalVariables.PageRangeDelta, model.pageCount - GlobalVariables.MaxPageRange);
        }
        model.leftMostPage = leftMostPage;
    
        // Calculate the number of page numbers to display.
        model.pageRange = Math.Min(model.pageCount - leftMostPage, GlobalVariables.MaxPageRange);
    
        return View("Index", model);
    }
    
  4. Gör slutligen en liten ändring i vyn. Variabeln resultList.Results.TotalCount innehåller nu antalet resultat som returneras på en sida (3 i vårt exempel), inte det totala antalet. Eftersom vi anger IncludeTotalCount till true innehåller variabeln resultList.TotalCount nu det totala antalet resultat. Leta därför reda på var antalet resultat visas i vyn och ändra det till följande kod.

    // Show the result count.
    <p class="sampleText">
        @Model.resultList.TotalCount Results
    </p>
    
    var results = Model.resultList.GetResults().ToList();
    
    @for (var i = 0; i < results.Count; i++)
    {
        // Display the hotel name and description.
        @Html.TextAreaFor(m => results[i].Document.HotelName, new { @class = "box1" })
        @Html.TextArea($"desc{1}", results[i].Document.Description, new { @class = "box2" })
    }
    

    Anteckning

    Det finns en mindre prestandaträff när du anger IncludeTotalCount till true, eftersom den här summan måste beräknas av Azure Cognitive Search. Med komplexa datauppsättningar finns det en varning om att det returnerade värdet är en uppskattning. Eftersom hotellsökning corpus är liten, kommer det att vara korrekt.

Kompilera och köra appen

Välj nu Starta utan felsökning (eller tryck på F5-tangenten).

  1. Sök efter en sträng som returnerar många resultat (till exempel "wifi"). Kan du sida snyggt genom resultaten?

    Numrerad växling via

  2. Prova att klicka på de mest högra och senare sidnumren till vänster. Justeras sidnumren på rätt sätt för att centrera sidan du är på?

  3. Är alternativen "första" och "sista" användbara? Vissa kommersiella sökmotorer använder dessa alternativ, och andra inte.

  4. Gå till den sista sidan med resultat. Den sista sidan är den enda sidan som kan innehålla mindre än ResultsPerPage-resultat .

    Undersöka sista sidan i

  5. Skriv "town" (stad) och klicka på Sök. Inga växlingsalternativ visas om resultatet är färre än en sida.

    Söker efter

Spara det här projektet och fortsätt till nästa avsnitt för en alternativ form av växling.

Utöka din app med oändlig rullning

Oändlig rullning utlöses när en användare rullar en lodrät rullningslist till det sista resultatet som visas. I det här fallet görs ett anrop till söktjänsten för nästa resultatsida. Om det inte finns fler resultat returneras ingenting och den lodräta rullningslisten ändras inte. Om det finns fler resultat läggs de till på den aktuella sidan och rullningslisten ändras för att visa att fler resultat är tillgängliga.

En viktig punkt att notera är att den aktuella sidan inte ersätts, utan snarare utökas för att visa ytterligare resultat. En användare kan alltid rulla tillbaka till de första resultaten av sökningen.

För att implementera oändlig rullning börjar vi med projektet innan något av sidnummerrullningselementen har lagts till. På GitHub är det här lösningen FirstAzureSearchApp .

Lägga till växlingsfält i modellen

  1. Lägg först till en växlingsegenskap i klassen SearchData (i sökdata.cs-modellfilen).

    // Record if the next page is requested.
    public string paging { get; set; }
    

    Den här variabeln är en sträng som innehåller "nästa" om nästa sida med resultat ska skickas eller vara null för den första sidan i en sökning.

  2. I samma fil och i namnområdet lägger du till en global variabelklass med en egenskap. I MVC deklareras globala variabler i sin egen statiska klass. ResultsPerPage anger antalet resultat per sida.

    public static class GlobalVariables
    {
        public static int ResultsPerPage
        {
            get
            {
                return 3;
            }
        }
    }
    

Lägg till en lodrät rullningslist i vyn

  1. Leta upp avsnittet i filen index.cshtml som visar resultatet (det börjar med @if (Modell != null)).

  2. Ersätt avsnittet med koden nedan. Det nya <div-avsnittet> finns runt det område som ska vara rullningsbart och lägger till både ett overflow-y-attribut och ett anrop till en onscroll-funktion med namnet "scrolled()", så här.

    @if (Model != null)
    {
        // Show the result count.
        <p class="sampleText">
            @Model.resultList.TotalCount Results
        </p>
    
        var results = Model.resultList.GetResults().ToList();
    
        <div id="myDiv" style="width: 800px; height: 450px; overflow-y: scroll;" onscroll="scrolled()">
    
            <!-- Show the hotel data. -->
            @for (var i = 0; i < results.Count; i++)
            {
                // Display the hotel name and description.
                @Html.TextAreaFor(m => results[i].Document.HotelName, new { @class = "box1" })
                @Html.TextArea($"desc{i}", results[i].Document.Description, new { @class = "box2" })
            }
    
  3. Direkt under loopen lägger du till den rullningsade funktionen efter taggen </div>.

    <script>
        function scrolled() {
            if (myDiv.offsetHeight + myDiv.scrollTop >= myDiv.scrollHeight) {
                $.getJSON("/Home/NextAsync", function (data) {
                    var div = document.getElementById('myDiv');
    
                    // Append the returned data to the current list of hotels.
                    for (var i = 0; i < data.length; i += 2) {
                        div.innerHTML += '\n<textarea class="box1">' + data[i] + '</textarea>';
                        div.innerHTML += '\n<textarea class="box2">' + data[i + 1] + '</textarea>';
                    }
                });
            }
        }
    </script>
    

    If-instruktionen i skriptet ovan testar om användaren har rullat längst ned i den lodräta rullningslisten. Om de har gjort det görs ett anrop till startkontrollanten till en åtgärd som heter NextAsync. Ingen annan information behövs av kontrollanten. Den returnerar nästa sida med data. Dessa data formateras sedan med identiska HTML-format som den ursprungliga sidan. Om inga resultat returneras läggs ingenting till och saker och ting förblir som de är.

Hantera åtgärden Nästa

Det finns bara tre åtgärder som måste skickas till kontrollanten: den första körningen av appen, som anropar Index(), den första sökningen av användaren, som anropar Index(modell)och sedan efterföljande anrop för fler resultat via Next(model).

  1. Öppna hemkontrollantfilen och ta bort metoden RunQueryAsync från den ursprungliga självstudien.

  2. Ersätt åtgärden Index(modell) med följande kod. Nu hanteras växlingsfältet när det är null eller inställt på "nästa" och hanterar anropet till Azure Cognitive Search.

    public async Task<ActionResult> Index(SearchData model)
    {
        try
        {
            InitSearch();
    
            int page;
    
            if (model.paging != null && model.paging == "next")
            {
                // Increment the page.
                page = (int)TempData["page"] + 1;
    
                // Recover the search text.
                model.searchText = TempData["searchfor"].ToString();
            }
            else
            {
                // First call. Check for valid text input.
                if (model.searchText == null)
                {
                    model.searchText = "";
                }
                page = 0;
            }
    
            // Setup the search parameters.
            var options = new SearchOptions
            {
                SearchMode = SearchMode.All,
    
                // Skip past results that have already been returned.
                Skip = page * GlobalVariables.ResultsPerPage,
    
                // Take only the next page worth of results.
                Size = GlobalVariables.ResultsPerPage,
    
                // Include the total number of results.
                IncludeTotalCount = true
            };
    
            // Specify which fields to include in results.
            options.Select.Add("HotelName");
            options.Select.Add("Description");
    
            // For efficiency, the search call should be asynchronous, so use SearchAsync rather than Search.
            model.resultList = await _searchClient.SearchAsync<Hotel>(model.searchText, options).ConfigureAwait(false);               
    
            // Ensure TempData is stored for the next call.
            TempData["page"] = page;
            TempData["searchfor"] = model.searchText;
        }
        catch
        {
            return View("Error", new ErrorViewModel { RequestId = "1" });
        }
    
        return View("Index", model);
    }
    

    Precis som med den numrerade växlingsmetoden använder vi sökinställningarna Hoppa över och Storlek för att bara begära de data som vi behöver returneras.

  3. Lägg till åtgärden NextAsync i startkontrollanten. Observera hur den returnerar en lista, där varje hotell lägger till två element i listan: ett hotellnamn och en hotellbeskrivning. Det här formatet är inställt på att matcha den rullningsade funktionens användning av returnerade data i vyn.

    public async Task<ActionResult> NextAsync(SearchData model)
    {
        // Set the next page setting, and call the Index(model) action.
        model.paging = "next";
        await Index(model).ConfigureAwait(false);
    
        // Create an empty list.
        var nextHotels = new List<string>();
    
        // Add a hotel name, then description, to the list.
        await foreach (var searchResult in model.resultList.GetResultsAsync())
        {
            nextHotels.Add(searchResult.Document.HotelName);
            nextHotels.Add(searchResult.Document.Description);
        }
    
        // Rather than return a view, return the list of data.
        return new JsonResult(nextHotels);
    }
    
  4. Om du får ett syntaxfel i liststrängen<> lägger du till följande användningsdirektiv i huvudet på kontrollantfilen.

    using System.Collections.Generic;
    

Kompilera och köra projektet

Välj nu Starta utan felsökning (eller tryck på F5).

  1. Ange en term som ger många resultat (till exempel "pool") och testa sedan den lodräta rullningslisten. Utlöser det en ny sida med resultat?

    Oändlig rullning genom

    Tips

    För att säkerställa att en rullningslist visas på den första sidan måste den första resultatsidan något överskrida höjden på det område som de visas i. I vårt exempel har .box1 en höjd på 30 bildpunkter, .box2 har en höjd på 100 bildpunkter och en nedre marginal på 24 bildpunkter. Varje post använder alltså 154 bildpunkter. Tre poster tar upp 3 x 154 = 462 bildpunkter. För att en lodrät rullningslist ska visas måste en höjd på visningsområdet anges som är mindre än 462 bildpunkter, även 461 fungerar. Det här problemet uppstår bara på den första sidan. Därefter visas en rullningslist. Raden som ska uppdateras är: <div id="myDiv" style="width: 800px; height: 450px; overflow-y: scroll;" onscroll="scrolled()">.

  2. Rulla ned till slutet av resultatet. Observera att all information nu finns på en vysida. Du kan rulla hela vägen tillbaka till toppen utan att utlösa några serveranrop.

Mer avancerade oändliga rullningssystem kan använda mushjulet, eller liknande annan mekanism, för att utlösa inläsningen av en ny resultatsida. Vi kommer inte att ta oändlig bläddring längre i dessa självstudier, men det har en viss charm till det eftersom det undviker extra musklickningar, och du kanske vill undersöka andra alternativ ytterligare.

Lärdomar

Tänk på följande från det här projektet:

  • Numrerad sidindelning är användbart för sökningar där resultatordningen är något godtycklig, vilket innebär att det mycket väl kan finnas något av intresse för användarna på de senare sidorna.
  • Oändlig rullning är användbart när resultatordningen är särskilt viktig. Om resultaten till exempel sorteras på avståndet från mitten av en målstad.
  • Numrerad sidindelning ger bättre navigering. En användare kan till exempel komma ihåg att ett intressant resultat fanns på sidan 6, medan det inte finns någon sådan enkel referens i oändlig rullning.
  • Oändlig rullning har en enkel överklagande, rulla upp och ner utan sidnummer att klicka på.
  • En viktig funktion i oändlig rullning är att resultaten läggs till på en befintlig sida, inte ersätter den sidan, vilket är effektivt.
  • Tillfällig lagring bevaras bara för ett anrop och måste återställas för att överleva ytterligare anrop.

Nästa steg

Sidindelning är grundläggande för en sökupplevelse. När sidindelningen är väl täckt är nästa steg att förbättra användarupplevelsen ytterligare genom att lägga till type-ahead-sökningar.