Back to "एसईएसई के लिए सरल सर्च्स एचएमएक्स तथा ईएफ को लोड किया जा रहा है. एनईईटी कोर"

This is a viewer only at the moment see the article on how this works.

To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk

This is a preview from the server running through my markdig pipeline

ASP.NET Entity Framework HTMX Postgres

एसईएसई के लिए सरल सर्च्स एचएमएक्स तथा ईएफ को लोड किया जा रहा है. एनईईटी कोर

Tuesday, 17 September 2024

परिचय

यह सिर्फ एक त्वरित लेख है जब यह पूर्ण पाठ खोज श्रृंखला में दूसरों पर निर्भर करता है टाइप हेड नीचे और पूरा पाठ खोज. इस पोस्ट में, मैं आपको दिखा सकता हूँ कि HMMX तथा ईएफ को सीएफ में कैसे लागू कर सकते हैं

मैं पहले से ही एक खोज नहीं है?

खैर, साइट के शीर्ष में मेरे पास एक खोज फंक्शन है जो प्रकार का संकेतक प्रदान करता है (जहाँ आप टाइप टाइप करते हैं वास्तविक समय में आता है). लेकिन मैं छिपाता हूँ कि मोबाइल मोड में और मैं भी खोज परिणाम लिंक करने में समर्थ होना चाहता हूँ (जैसे कि जैसे कि) / ढूंढें/मा( क) समर्पित खोज पृष्ठ पर । यह एक बेहतर उपयोगकर्ता का अनुभव देता है, साथ ही मोबाइल उपकरणों पर काम करता है.

खोज सेवा खोजें

यह मैं परिवर्धित करने के लिए मैं कैसे मेरी खोज की. और मैंने एक पैदा किया; BlogSearchService, यह मेरे दो पूरे पाठ क्वैरी तरीकों पर आधारित है. इन दुखियों को दो तरीकों से विभाजित करने की जरूरत है क्योंकि जिस तरह से डाका पूर्ण पाठ खोज विस्तार के साथ बनाया जाता है, EF.Functions.WebSearchToTsQuery("english", processedQuery) और EF.Functions.ToTsQuery("english", query + ":*").

सबसे पहले उचित खोज की ज़रूरत होती है और दूसरा MAV खोज लेता है.

    private IQueryable<BlogPostEntity> QueryForSpaces(string processedQuery)
    {
        return context.BlogPosts
            .Include(x => x.Categories)
            .Include(x => x.LanguageEntity)
            .AsNoTrackingWithIdentityResolution()
            .Where(x =>
                // Search using the precomputed SearchVector
                (x.SearchVector.Matches(EF.Functions.WebSearchToTsQuery("english",
                     processedQuery)) // Use precomputed SearchVector for title and content
                 || x.Categories.Any(c =>
                     EF.Functions.ToTsVector("english", c.Name)
                         .Matches(EF.Functions.WebSearchToTsQuery("english", processedQuery)))) // Search in categories
                && x.LanguageEntity.Name == "en") // Filter by language
            .OrderByDescending(x =>
                // Rank based on the precomputed SearchVector
                x.SearchVector.Rank(EF.Functions.WebSearchToTsQuery("english",
                    processedQuery)));
    }

    private IQueryable<BlogPostEntity> QueryForWildCard(string query)
    {
        return context.BlogPosts
            .Include(x => x.Categories)
            .Include(x => x.LanguageEntity)
            .AsNoTrackingWithIdentityResolution()
            .Where(x =>
                // Search using the precomputed SearchVector
                (x.SearchVector.Matches(EF.Functions.ToTsQuery("english",
                     query + ":*")) // Use precomputed SearchVector for title and content
                 || x.Categories.Any(c =>
                     EF.Functions.ToTsVector("english", c.Name)
                         .Matches(EF.Functions.ToTsQuery("english", query + ":*")))) // Search in categories
                && x.LanguageEntity.Name == "en") // Filter by language
            .OrderByDescending(x =>
                // Rank based on the precomputed SearchVector
                x.SearchVector.Rank(EF.Functions.ToTsQuery("english",
                    query + ":*"))); // Use precomputed SearchVector for ranking
    }

फिर से ये मेरे पूर्वनिर्धारित उपयोग में लें SearchVector स्तम्भ जो पोस्ट बनाने व अद्यतन करने पर अद्यतन किया गया है. यह मेरी में बनाया गया है DbContext प्रयोग कर रहा है OnModelCreating विधि.

      entity.Property(b => b.SearchVector)
                .HasComputedColumnSql("to_tsvector('english', coalesce(\"Title\", '') || ' ' || coalesce(\"PlainTextContent\", ''))", stored: true);
            
           entity.HasIndex(b => b.SearchVector)
                .HasMethod("GIN");

फिर इस दृष्टिकोण की जाँच यह है कि यह केवल अंग्रेज़ी के रूप में है के लिए काम करता है. मुझे इस डाटाबेस के वास्तविक पुनर्वास की ज़रूरत होगी कि यह अन्य भाषाओं के लिए काम करे (जैसे कि प्रत्येक भाषा के लिए मेज़ के रूप में एक मेज़ है) ।

तब मैं इन तरीकों का उपयोग अपने में BlogSearchService खोज क्वेरी पर आधारित परिणाम बताता है.

    public async Task<PostListViewModel> GetPosts(string? query, int page = 1, int pageSize = 10)
    {
        if(string.IsNullOrEmpty(query))
        {
            return new PostListViewModel();
        }
        IQueryable<BlogPostEntity> blogPostQuery = query.Contains(" ") ? QueryForSpaces(query) : QueryForWildCard(query);
        var totalPosts = await blogPostQuery.CountAsync();
        var results = await blogPostQuery
            .Select(x => x.ToListModel())
            .Skip((page - 1) * pageSize)
            .Take(pageSize)
            .ToListAsync();
        
        return new PostListViewModel()
        {
            Posts = results,
            TotalItems = totalPosts,
            Page = page,
            PageSize = pageSize
        };
        
    }

मैं कैसे कॉल करने का विधि है यह निर्धारित करने के लिए कि किस विधि में स्पेसेस हैं के लिए सरल चेक का उपयोग करें.

खोज नियंत्रक

खोज नियंत्रण मैं अपने अधिकांश नियंत्रणों के पैटर्न का पालन करता हूँ जहां यह पता चलता है कि क्या कॉल HMMX से आता है या नहीं किसी भी अधूरा या पूर्ण खाका पृष्ठ भेजने के लिए सक्षम नहीं (जैसे कि यह प्रत्यक्ष नेविगेशन तथा HMX निवेदन के लिए काम करता है).

[Route("search")]
public class SearchController(
    BaseControllerService baseControllerService,
    BlogSearchService searchService,
    ILogger<SearchController> logger)
    : BaseController(baseControllerService, logger)
{
    [HttpGet]
    [Route("{query?}")]
    public async Task<IActionResult> Search([FromRoute] string? query)
    {
        var searchResults = await searchService.GetPosts(query);
        var searchModel = new SearchResultsModel
        {
            Query = query,
            SearchResults = searchResults
        };
        searchModel = await PopulateBaseModel(searchModel);
        searchModel.SearchResults.LinkUrl = Url.Action("SearchResults", "Search");
        if (Request.IsHtmx()) return PartialView("SearchResults", searchModel);
        return View("SearchResults", searchModel);
    }

    [HttpGet]
    [Route("results")]
    public async Task<IActionResult> SearchResults([Required] string query, int page = 1, int pageSize = 10)
    {
        var searchResults = await searchService.GetPosts(query, page, pageSize);
        var searchModel = new SearchResultsModel
        {
            Query = query,
            SearchResults = searchResults
        };
        searchModel = await PopulateBaseModel(searchModel);
        searchModel.SearchResults.LinkUrl = Url.Action("SearchResults", "Search");
        if (Request.IsHtmx()) return PartialView("_SearchResultsPartial", searchModel.SearchResults);
        return View("SearchResults", searchModel);
    }
}

यह पूरे नियंत्रण है, आप देख सकते हैं कि मेरे पास दो क्रियाएँ हैं, एक जो पृष्ठ (वैकल्पिक रूप से इसके परिणाम) बताता है और एक जो केवल HMMX निवेदन के परिणाम बताता है.

 if (Request.IsHtmx()) return PartialView("_SearchResultsPartial", searchModel.SearchResults);

खोज परिणाम आंशिक

आप देख सकते हैं कि यह वैकल्पिक रूप से लौटाता है _SearchResultsPartial परिणाम स्वरूप के लिए निवेदन है कि क्या निवेदन है.

यह एक बहुत ही सरल नियमीय दृश्य है जिसमें pances और परिणाम होते हैं.

@model Mostlylucid.Models.Blog.PostListViewModel
<div class="pt-2" id="content">
    @if (Model.Posts?.Any() is true)
    {
        <div class="inline-flex w-full items-center justify-center print:!hidden">
            @if (Model.TotalItems > Model.PageSize)
            {
                <pager
                    x-ref="pager"
                    link-url="@Model.LinkUrl"
                    hx-boost="true"
                    hx-target="#content"
                    hx-swap="show:none"
                    page="@Model.Page"
                    page-size="@Model.PageSize"
                    total-items="@Model.TotalItems"
                    hx-headers='{"pagerequest": "true"}'>
                </pager>
            }
            <partial name="_Pager" model="Model"/>

        </div>
        @foreach (var post in Model.Posts)
        {
            <partial name="_ListPost" model="post"/>
        }
    }
</div>

पोस्ट आंशिक दृश्य सूची दें

मैं एक ही उपयोग _ListPost आंशिक दृश्य जहाँ भी मुझे पोस्टों की सूची करनी है.

@model Mostlylucid.Models.Blog.PostListModel

<div class="border-b border-grey-lighter pb-8 mb-8">
 
    <a asp-controller="Blog" asp-action="Show" hx-boost="true"  hx-swap="show:window:top"  hx-target="#contentcontainer" asp-route-slug="@Model.Slug"
       class="block font-body text-lg font-semibold transition-colors hover:text-green text-blue-dark dark:text-white  dark:hover:text-secondary">@Model.Title</a>  
    <div class="flex flex-wrap space-x-2 items-center py-4 print:!hidden">
    @foreach (var category in Model.Categories)
    {
        <partial name="_Category" model="category"/>
    }
    @{ var languageModel = (Model.Slug, Model.Languages, Model.Language); }
        <partial name="_LanguageList" model="languageModel"/>
    </div>
    <div class="block font-body text-black dark:text-white">@Model.Summary</div>
    <div class="flex items-center pt-4">
        <p class="pr-2 font-body font-light text-primary light:text-black dark:text-white">
            @Model.PublishedDate.ToString("f")
        </p>
        <span class="font-body text-grey dark:text-white">//</span>
        <p class="pl-2 font-body font-light text-primary light:text-black dark:text-white">
            @Model.ReadingTime
        </p>
    </div>
</div>

एचएमएमएक्स तथा खोज पृष्ठ

HMAX का मेरा उपयोग यहाँ बहुत सरल है. मैं सिर्फ बटन में हुक करता हूँ (इस बार मैंने तय किया था कि इस बार कुंजी अप / परिवर्तन के लिए इनपुट को नहीं बदल दूँगा) और जब बटन क्लिक किया जाता है तब इस अनुरोध को भेज दीजिए. निवेदन में क्वैरी शामिल करें जो कि निवेदन में शामिल है hx-include और लक्ष्य #content परिणाम को बदलने के लिए पानी.

<div class="flex items-center gap-2 bg-neutral-500 bg-opacity-10 p-2 rounded-lg">
    <button
        hx-get="@Url.Action("SearchResults", "Search")"
        hx-target="#content"
        hx-include="[name='query']"
        hx-swap="outerHTML"
        class="btn btn-outline btn-sm flex items-center gap-2 text-black dark:text-white">
        Search
        <i class="bx bx-search text-lg"></i>
    </button>
    <input
        type="text"
        placeholder="Search..."
        value="@Model.Query"
        name="query"
        class="input input-sm border-0 grow text-black dark:text-white bg-transparent focus:outline-none"/>

</div>

अद्यतन

सो इससे कुछ टिप्पणियों का अनुसरण करें खाल मैं खोज को सक्षम करने के लिए इस प्रकार्य को विस्तार करने का फैसला किया:

  1. एंटर कुंजी प्रेस पर ट्रिगर किया
  2. दो से अधिक अक्षर में प्रवेश करने के लिए ट्रिगर (इसलिए एक ढीला प्रकार का सिर बन जाता है).

भविष्य में मैं पृष्ठ आकार में वापस काम जोड़ने की जरूरत है; यह एक हैक का एक बिमेल है और अधिक आवश्यकताओं का समर्थन करने की जरूरत है.

खोज फ़ॉर्म अद्यतन करें

यह करने के लिए मैंने पहली बार इनपुट को एक रूप में लपेट लिया और आन्नति के लिए प्रयोग किया जब उपयोगकर्ता टाइप कर रहा होता है इस रूप को आत्मसमर्पण करने के लिए। आप देख सकते हैं कि मैं इस्तेमाल कर सकते हैं x-data क्वैरी के लिए प्रतिक्रियाित वेरिएबल बनाने के लिए और फिर मैं प्रश्न की लंबाई की जाँच करता हूँ कि क्या इस फ़ॉर्म को जमा करना है.

<form
    x-data="{ query: '@Model.Query', checkSubmit() { if (this.query.length > 2) { $refs.searchButton.click(); } } }"
    class="flex items-center gap-2 bg-neutral-500 bg-opacity-10 p-2 rounded-lg"
    action="@Url.Action("Search", "Search")"
    hx-push-url="true"
    hx-boost="true"
    hx-target="#content"
    hx-swap="outerHTML show:window:top"
    hx-headers='{"pagerequest": "true"}'>
    <button
        type="submit"
        x-ref="searchButton"
        class="btn btn-outline btn-sm flex items-center gap-2 text-black dark:text-white">
        Search
        <i class="bx bx-search text-lg"></i>
    </button>
    <input
        type="text"
        placeholder="Search..."
        name="query"
        value="@Model.Query"
        x-model="query"
        x-on:input.debounce.200ms="checkSubmit"
        x-on:keydown.enter.prevent="$refs.searchButton.click()"
        class="input input-sm border-0 grow text-black dark:text-white bg-transparent focus:outline-none"
    />
</form>

मैं एक ही नियंत्रण कार्य को फिर से प्रयोग करने के लिए मैं भी सेट pagerequest यह सूचित करने के लिए शीर्षक है कि यह एक plaged अनुरोध है.

मैं यू.js का भी इस्तेमाल करता हूँ x-on:keydown.enter.prevent जब एंटर कुंजी दबाया जाए तो बटन को ट्रिगर करने के लिए क्लिक करें तथा इनपुट पर चले जाने के लिए बहुत से निवेदन रोकने के लिए क्लिक करें.

नियंत्रक अद्यतन

नियंत्रण में मैं खोज '% 1' क्रिया हटा दिया और इसके बजाय मुख्य में अधिक 'रूट' जोड़ें Search आरंभिक खोज तथा समझौता किए गए अनुरोधों को नियंत्रित करने के लिए क्रिया.

यहाँ आप देख सकते हैं कि मैं एक अतिरिक्त पैरामीटर जोड़ें बुलाया गया pagerequest यह निर्धारित करने के लिए कि क्या यह एक पेपलित निवेदन है या नहीं और सूचित करता है कि यह शीर्ष संग्रह से आबाद किया जाना चाहिए.


    [HttpGet]
    [Route("")]
    public async Task<IActionResult> Search(string? query, int page = 1, int pageSize = 10,[FromHeader] bool pagerequest=false)
    {
        var searchResults = await searchService.GetPosts(query, page, pageSize);
        var searchModel = new SearchResultsModel
        {
            Query = query,
            SearchResults = searchResults
        };
        searchModel = await PopulateBaseModel(searchModel);
        var linkUrl = Url.Action("Search", "Search");
        searchModel.SearchResults.LinkUrl = linkUrl;
        if(pagerequest && Request.IsHtmx()) return PartialView("_SearchResultsPartial", searchModel.SearchResults);
        
        if (Request.IsHtmx()) return PartialView("SearchResults", searchModel);
        return View("SearchResults", searchModel);
    }

मैं फिर परिणाम मिलता है और इस शीर्षक का पता लगाने के लिए पता लगाने के लिए कि कौन सा आंशिक दृश्य वापसी के लिए.

मैंने और एक स्लेरेट रिटायर्ड रिटायर्ट ने आगे कहा की तरह काम करने के लिए /search/umami क्वैरी के साथ मुख्य खोज पृष्ठ को पुनर्निदेशित करने के लिए.

   [HttpGet]
    [Route("{query}")]
    public  IActionResult InitialSearch([FromRoute] string query)
    {
        return RedirectToAction("Search", new { query });
    }

ऑन्टियम

तो, बहुत सरल है? यह एचएमएमएक्स तथा EF कोपीएसी में उपयोग कर एक खोज पृष्ठ का सीधे कार्यान्वयन है. Name आप आसानी से इसे फ़िल्टर, छंटाई, या अन्य खोज सेवाओं के साथ भी अधिक विशेषताओं को शामिल कर सकते हैं । कुंजी दूर है कि किस तरह एक नरम उपयोगकर्ता के अनुभव के लिए प्राप्त किया जा सकता है...... बैकेंड तर्क साफ और कुशल बनाए रखने के दौरान.

logo

©2024 Scott Galloway