Öğretici: .NET SDK'sını kullanarak çok yönlü gezinti ekleme

Modeller, sonuçları filtrelemek için bir dizi bağlantı sağlayarak kendi kendine yönlendirilen gezintiyi etkinleştirir. Bu öğreticide, sayfanın sol tarafına, sonuçları kırpmak için etiketler ve tıklanabilir metin içeren çok yönlü bir gezinti yapısı yerleştirilir.

Bu öğreticide şunların nasıl yapıldığını öğreneceksiniz:

  • Model özelliklerini IsFacetable olarak ayarlama
  • Uygulamanıza model gezintisi ekleme

Genel Bakış

Modeller, arama dizininizdeki alanları temel alır. model=[string] içeren bir sorgu isteği, model oluşturma alanını sağlar. Her biri ve işareti (&) karakteriyle ayrılmış birden çok model (örneğin &facet=category&facet=amenities, ) dahil etmek yaygın bir durum olabilir. Çok yönlü bir gezinti yapısı uygulamak için hem modelleri hem de filtreleri belirtmeniz gerekir. Filtre, sonuçları daraltmak için tıklama olayında kullanılır. Örneğin, "bütçe" seçeneğine tıklanması, sonuçları bu ölçütlere göre filtreler.

Bu öğretici, Arama sonuçlarına disk belleği ekleme öğreticisinde oluşturulan disk belleği projesini genişletir.

Bu öğreticideki kodun tamamlanmış bir sürümü aşağıdaki projede bulunabilir:

Önkoşullar

Model özelliklerini IsFacetable olarak ayarlama

Model özelliğinin model aramasında bulunması için IsFacetable ile etiketlenmesi gerekir.

  1. Hotel sınıfını inceleyin. Örneğin Kategori ve EtiketlerIsFacetable olarak etiketlenir, ancak HotelName ve Description etiketlenmez.

    public partial class Hotel
    {
        [SimpleField(IsFilterable = true, IsKey = true)]
        public string HotelId { get; set; }
    
        [SearchableField(IsSortable = true)]
        public string HotelName { get; set; }
    
        [SearchableField(AnalyzerName = LexicalAnalyzerName.Values.EnLucene)]
        public string Description { get; set; }
    
        [SearchableField(AnalyzerName = LexicalAnalyzerName.Values.FrLucene)]
        [JsonPropertyName("Description_fr")]
        public string DescriptionFr { get; set; }
    
        [SearchableField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
        public string Category { get; set; }
    
        [SearchableField(IsFilterable = true, IsFacetable = true)]
        public string[] Tags { get; set; }
    
        [SimpleField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
        public bool? ParkingIncluded { get; set; }
    
        [SimpleField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
        public DateTimeOffset? LastRenovationDate { get; set; }
    
        [SimpleField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
        public double? Rating { get; set; }
    
        public Address Address { get; set; }
    
        [SimpleField(IsFilterable = true, IsSortable = true)]
        public GeographyPoint Location { get; set; }
    
        public Room[] Rooms { get; set; }
    }
    
  2. Bu öğreticinin bir parçası olarak hiçbir etiketi değiştirmeyeceğiz, bu nedenle değiştirmeden hotel.cs dosyasını kapatın.

    Not

    Aramada istenen bir alan uygun şekilde etiketlenmediyse model araması hata oluşturur.

Uygulamanıza model gezintisi ekleme

Bu örnekte, kullanıcının sonuçların solunda gösterilen bağlantı listelerinden bir otel kategorisi veya bir olanak seçmesini etkinleştireceğiz. Kullanıcı bir arama metni girerek başlar, ardından bir kategori veya olanak seçerek aramanın sonuçlarını aşamalı olarak daraltıyor.

Model listelerini görünüme geçirmek denetleyicinin görevidir. Arama ilerledikçe kullanıcı seçimlerini korumak için, durumu koruma mekanizması olarak geçici depolamayı kullanırız.

SearchData modeline filtre dizeleri ekleme

  1. Model filtresi dizelerini tutmak için SearchData.cs dosyasını açın ve SearchData sınıfına dize özellikleri ekleyin.

    public string categoryFilter { get; set; }
    public string amenityFilter { get; set; }
    

Model eylem yöntemini ekleme

Ev denetleyicisinin yeni bir eyleme, Modele ve mevcut Dizin ve Sayfa eylemlerine ve RunQueryAsync yöntemine güncelleştirilmesi gerekir.

  1. Index(SearchData modeli) eylem yöntemini değiştirin.

    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, "", "").ConfigureAwait(false);
        }
        catch
        {
            return View("Error", new ErrorViewModel { RequestId = "1" });
        }
    
        return View(model);
    }
    
  2. PageAsync(SearchData modeli) eylem yöntemini değiştirin.

    public async Task<ActionResult> PageAsync(SearchData model)
    {
        try
        {
            int page;
    
            // Calculate the page that should be displayed.
            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 filters.
            string catFilter = TempData["categoryFilter"].ToString();
            string ameFilter = TempData["amenityFilter"].ToString();
    
            // Recover the search text.
            model.searchText = TempData["searchfor"].ToString();
    
            // Search for the new page.
            await RunQueryAsync(model, page, leftMostPage, catFilter, ameFilter);
        }
    
        catch
        {
            return View("Error", new ErrorViewModel { RequestId = "2" });
        }
        return View("Index", model);
    }
    
  3. Kullanıcı bir model bağlantısına tıkladığında etkinleştirilecek bir FacetAsync(SearchData modeli) eylem yöntemi ekleyin. Model bir kategori veya olanak arama filtresi içerir. PageAsync eyleminden sonra ekleyin.

    public async Task<ActionResult> FacetAsync(SearchData model)
    {
        try
        {
            // Filters set by the model override those stored in temporary data.
            string catFilter;
            string ameFilter;
            if (model.categoryFilter != null)
            {
                catFilter = model.categoryFilter;
            } else
            {
                catFilter = TempData["categoryFilter"].ToString();
            }
    
            if (model.amenityFilter != null)
            {
                ameFilter = model.amenityFilter;
            } else
            {
                ameFilter = TempData["amenityFilter"].ToString();
            }
    
            // Recover the search text.
            model.searchText = TempData["searchfor"].ToString();
    
            // Initiate a new search.
            await RunQueryAsync(model, 0, 0, catFilter, ameFilter).ConfigureAwait(false);
        }
        catch
        {
            return View("Error", new ErrorViewModel { RequestId = "2" });
        }
    
        return View("Index", model);
    }
    

Arama filtresini ayarlama

Örneğin bir kullanıcı belirli bir model seçtiğinde Resort ve Spa kategorisine tıklar, ardından sonuçlarda yalnızca bu kategori olarak belirtilen oteller döndürülür. Aramayı bu şekilde daraltmak için bir filtre ayarlamamız gerekir.

  1. RunQueryAsync yöntemini aşağıdaki kodla değiştirin. Öncelikle, bir kategori filtre dizesi ve bir olanak filtre dizesi alır ve SearchOptions'ınFilter parametresini ayarlar.

    private async Task<ActionResult> RunQueryAsync(SearchData model, int page, int leftMostPage, string catFilter, string ameFilter)
    {
        InitSearch();
    
        string facetFilter = "";
    
        if (catFilter.Length > 0 && ameFilter.Length > 0)
        {
            // Both facets apply.
            facetFilter = $"{catFilter} and {ameFilter}"; 
        } else
        {
            // One, or zero, facets apply.
            facetFilter = $"{catFilter}{ameFilter}";
        }
    
        var options = new SearchOptions
        {
            Filter = facetFilter,
    
            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,
        };
    
        // Return information on the text, and number, of facets in the data.
        options.Facets.Add("Category,count:20");
        options.Facets.Add("Tags,count:20");
    
        // Enter Hotel property names into this list, so only these values will be returned.
        options.Select.Add("HotelName");
        options.Select.Add("Description");
        options.Select.Add("Category");
        options.Select.Add("Tags");
    
        // 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);
    
        // Ensure Temp data is stored for the next call.
        TempData["page"] = page;
        TempData["leftMostPage"] = model.leftMostPage;
        TempData["searchfor"] = model.searchText;
        TempData["categoryFilter"] = catFilter;
        TempData["amenityFilter"] = ameFilter;
    
        // Return the new view.
        return View("Index", model);
    }
    

    Kategori ve Etiketler özelliklerinin Döndürülecek öğeleri seçin listesine eklendiğine dikkat edin. Bu ekleme model gezintisinin çalışması için bir gereksinim değildir, ancak filtrelerin düzgün çalıştığını doğrulamak için bu bilgileri kullanırız.

Görünümde bazı önemli değişiklikler yapılması gerekir.

  1. hotels.css dosyasını (wwwroot/css klasöründe) açarak başlayın ve aşağıdaki sınıfları ekleyin.

    .facetlist {
        list-style: none;
    }
    
    .facetchecks {
        width: 250px;
        display: normal;
        color: #666;
        margin: 10px;
        padding: 5px;
    }
    
    .facetheader {
        font-size: 10pt;
        font-weight: bold;
        color: darkgreen;
    }
    
  2. Görünümde, soldaki model listelerini ve sağ taraftaki sonuçları düzgün bir şekilde hizalamak için çıkışı bir tablo halinde düzenleyin. index.cshtml dosyasını açın. HTML <gövde> etiketlerinin tüm içeriğini aşağıdaki kodla değiştirin.

    <body>
        @using (Html.BeginForm("Index", "Home", FormMethod.Post))
        {
            <table>
                <tr>
                    <td></td>
                    <td>
                        <h1 class="sampleTitle">
                            <img src="~/images/azure-logo.png" width="80" />
                            Hotels Search - Facet Navigation
                        </h1>
                    </td>
                </tr>
    
                <tr>
                    <td></td>
                    <td>
                        <!-- Display the search text box, with the search icon to the right of it.-->
                        <div class="searchBoxForm">
                            @Html.TextBoxFor(m => m.searchText, new { @class = "searchBox" }) <input value="" class="searchBoxSubmit" type="submit">
                        </div>
                    </td>
                </tr>
    
                <tr>
                    <td valign="top">
                        <div id="facetplace" class="facetchecks">
    
                            @if (Model != null && Model.resultList != null)
                            {
                                List<string> categories = Model.resultList.Facets["Category"].Select(x => x.Value.ToString()).ToList();
    
                                if (categories.Count > 0)
                                {
                                    <h5 class="facetheader">Category:</h5>
                                    <ul class="facetlist">
                                        @for (var c = 0; c < categories.Count; c++)
                                        {
                                            var facetLink = $"{categories[c]} ({Model.resultList.Facets["Category"][c].Count})";
                                            <li>
                                                @Html.ActionLink(facetLink, "FacetAsync", "Home", new { categoryFilter = $"Category eq '{categories[c]}'" }, null)
                                            </li>
                                        }
                                    </ul>
                                }
    
                                List<string> tags = Model.resultList.Facets["Tags"].Select(x => x.Value.ToString()).ToList();
    
                                if (tags.Count > 0)
                                {
                                    <h5 class="facetheader">Amenities:</h5>
                                    <ul class="facetlist">
                                        @for (var c = 0; c < tags.Count; c++)
                                        {
                                            var facetLink = $"{tags[c]} ({Model.resultList.Facets["Tags"][c].Count})";
                                            <li>
                                                @Html.ActionLink(facetLink, "FacetAsync", "Home", new { amenityFilter = $"Tags/any(t: t eq '{tags[c]}')" }, null)
                                            </li>
                                        }
                                    </ul>
                                }
                            }
                        </div>
                    </td>
                    <td valign="top">
                        <div id="resultsplace">
                            @if (Model != null && Model.resultList != null)
                            {
                                // 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++)
                                {
                                    string amenities = string.Join(", ", results[i].Document.Tags);
    
                                    string fullDescription = results[i].Document.Description;
                                    fullDescription += $"\nCategory: {results[i].Document.Category}";
                                    fullDescription += $"\nAmenities: {amenities}";
    
    
                                    // Display the hotel name and description.
                                    @Html.TextAreaFor(m => results[i].Document.HotelName, new { @class = "box1" })
                                    @Html.TextArea($"desc{i}", fullDescription, new { @class = "box2" })
                                }
                            }
                        </div>
                    </td>
                </tr>
    
                <tr>
                    <td></td>
                    <td valign="top">
                        @if (Model != null && Model.pageCount > 1)
                        {
                            // If there is more than one page of results, show the paging buttons.
                            <table>
                                <tr>
                                    <td class="tdPage">
                                        @if (Model.currentPage > 0)
                                        {
                                            <p class="pageButton">
                                                @Html.ActionLink("|<", "PageAsync", "Home", new { paging = "0" }, null)
                                            </p>
                                        }
                                        else
                                        {
                                            <p class="pageButtonDisabled">|&lt;</p>
                                        }
                                    </td>
    
                                    <td class="tdPage">
                                        @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 class="tdPage">
                                            @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 class="tdPage">
                                        @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 class="tdPage">
                                        @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>
                        }
                    </td>
                </tr>
            </table>
        }
    </body>
    

    Html.ActionLink çağrısının kullanıldığına dikkat edin. Bu çağrı, kullanıcı bir model bağlantısına tıkladığında denetleyiciye geçerli filtre dizelerini iletir.

Uygulamayı çalıştırma ve test edin

Kullanıcıya model gezintisinin avantajı, aramaları aşağıdaki sırada gösterebileceğimiz tek bir tıklamayla daraltabilmesidir.

  1. Uygulamayı çalıştırın, arama metni olarak "airport" yazın. Model listesinin sol tarafta düzgün göründüğünü doğrulayın. Bu modeller, metin verilerinde "havaalanı" bulunan oteller için geçerli olan ve bunların ne sıklıkta gerçekleştiğini sayan modellerdir.

  2. Resort ve Spa kategorisine tıklayın. Tüm sonuçların bu kategoride olduğunu doğrulayın.

    Aramayı

  3. Kontinental kahvaltı olanaklarına tıklayın. Tüm sonuçların seçili olanakla birlikte hala "Resort and Spa" kategorisinde olduğunu doğrulayın.

    Aramayı

  4. Başka bir kategori ve ardından bir olanak seçmeyi deneyin ve daraltma sonuçlarını görüntüleyin. Sonra diğer yolu deneyin, bir olanak, sonra bir kategori. Sayfayı sıfırlamak için boş bir arama gönderin.

    Not

    Model listesinde (kategori gibi) bir seçim yapıldığında, kategori listesindeki önceki seçimleri geçersiz kılar.

Paketler

Bu projeden aşağıdakileri göz önünde bulundurun:

  • Model gezintisine eklenmesi için her modellenebilir alanı IsFacetable özelliğiyle işaretlemek zorunludur.
  • Modeller, sonuçları azaltmak için filtrelerle birleştirilir.
  • Modeller kümülatiftir ve sonuçları daha da daraltmak için her seçim bir öncekinde oluşturulur.

Sonraki adımlar

Sonraki öğreticide sıralama sonuçlarını inceleyeceğiz. Bu noktaya kadar, sonuçlar veritabanında bulundukları sırayla sıralanır.