Back to "साइट को ठीक करना's खोज:postgreSQL पूर्णM SK2पाठ खोज MSC3और जहाँ यह टूटता है"

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 PostgreSQL RRF Search

साइट को ठीक करना's खोज:postgreSQL पूर्णM SK2पाठ खोज MSC3और जहाँ यह टूटता है

Wednesday, 14 January 2026

खोज उन विशेषताओं में से एक है जो सभी कम मानते हैं। सैद्धांतिक रूप-से सही लेकिन पाठ्य रूप से गलत

यह' also somethign है कि ' ने इस साइट पर बिगाड़ पैदा किया है क्योंकि यह' के शुरूआत से ओपन सर्चQuery, PostgreSQL पूर्ण पाठ खोज के साथ काम किया है, इसके साथ '\s फैशनी भेक्टर सामान लेकिन कभी भी सचमुच उससे खुश नहीं था भ्रमित मुझे विशेष रूप से अब मैं एक खोज उपकरण बना रहा हूँ सुदृढ़आरएजी अच्छी तरह से मैंने सोचा कि मैं अंततः इसे ठीक करना चाहिए क्या यह है या नहीं एक अन्य बात है

यह लेख नहीं है-' शून्य से एक खोज इंजिन का निर्माण करने के बारे में नहीं है।

पूर्ववर्ती कार्यों पर निर्माण: यह अनुच्छेद अर्थात्मक खोज क्रियान्वयन जिसने पारस्परिक रैंक फ्यूजन के साथ हाइब्रिड खोज जोड़ी।

सेमेटिक खोज अवसंरचना दो उद्देश्यों का काम करती है और के लिए पुनर्प्राप्ति परत प्रदान करता है वकील जीपीटी RAG प्रणाली -एक लेखन सहायक जो साइट के उपयोग से नए पदों का प्रारूप करता है। अधिवक्ता GPT का निर्माण एम्बेडिंग और भेक्टर खोज पर

समस्याएं

1. खाली परिणाम अनियमित लेख दिखाए गए

जब एक खोज ने कोई परिणाम नहीं दिखाया, तो सिस्टम सहायक सुझावों के बजाय अनियमित पुराने लेख दिखा रहा था।

द फिक्स: परिवर्धित BlogSearchService.HybridSearchWithPagingAsync() पता लगाने के लिए जब खोज शून्य परिणाम लौटाता है और वापस पिछले पोस्टों को दिखाने के लिए नीचे आने वाले तिथि से क्रमबद्ध करने में गिर जाता है NoMatchFound फ्लैग को BasePagingModel यूआई एक उपयुक्त संदेश प्रदर्शित कर सकता है जैसे " कोई मिलान नहीं मिला

// No match found - return recent posts as suggestions
if (noMatchFound)
{
    Log.Logger.Information("No search results for '{Query}', returning recent posts as suggestions", query);
    return await GetRecentPostsAsSuggestions(targetLanguage, startDate, endDate, page, pageSize);
}

2. एसcronyms Like

PostgreSQL के पूर्वनिर्धारित अंग्रेजी पाठ खोज कॉन्फ़िगरेशन में तकनीकी रूप से एक्रोनिमों को अनुक्रमित करता है, लेकिन सामान्यीकरण और समापन से छोटा होता है।

द फिक्सक्रें ILIKE. यह इसे बदलने के बजाय पूर्ण पाठ खोज को संपूरित करता है।

// Detect if query looks like an acronym or short term
var isAcronymLike = query.Length <= 6 && query.Any(char.IsUpper);

// Add substring search for acronyms
searchQuery = searchQuery.Where(x =>
    EF.Functions.ILike(x.Title, $"%{acronym}%")
    || EF.Functions.ILike(x.PlainTextContent, $"%{acronym}%"));

3. विशेष अक्षरों के साथ तकनीकी शर्तें विच्छेद खोज

के लिए खोजें असफल होंगी क्योंकि PostgreSQL की पाठ खोज पदवर्णक अवधि और हैश प्रतीकों को डिलिमिटर के रूप में व्यवहार करता है।

द फिक्स: एक बनाया गया SearchQueryParser जो सामान्य तकनीकी शब्दों को पहचानता है और उन्हें खोज योग्य संस्करणों से बदलता है

private static readonly Dictionary<string, string> TechnicalTerms = new(StringComparer.OrdinalIgnoreCase)
{
    ["asp.net"] = "aspnet",
    ["c#"] = "csharp",
    [".net"] = "dotnet",
    ["f#"] = "fsharp",
    ["node.js"] = "nodejs",
    // ... more terms
};

प्रश्नों की तरह "ASP

पोस्टग्रेएसक्यूएल ने " और" को एक रोक शब्द के रूप में व्यवहार किया है तथा इसे खोजों से हटाता है।

द फिक्स: एक गूगल - शैली क्वेरी पारसर को लागू किया है जो stop शब्दों को कुशलता से नियंत्रित करता है और उन्नत खोज ऑपरेटरों का समर्थन करता है

समाधान

Google-Style खोज ऑपरेटर

मैंने एक कार्यान्वित किया SearchQueryParser वर्ग जो : के लिए समर्थन सहित प्रश्नों को पद वर्णन करता है

  1. उद्धरण वाक्यांश: "exact match" - सही वाक्यांश के लिए खोजता है
  2. अपवर्जित शर्तें: -unwanted - इस शब्द को समाहित परिणामों को छोड़ता है
  3. विल्डकार्ड: ASP* मिलाएँ
  4. तकनीकी शर्तेंस्वचालित रूप से ह्यान्डल करता है

पदवर्णक क्वेरी को टोकनाइज करने के लिए एक संकलित रेगेक्स पैटर्न का उपयोग करता है

[GeneratedRegex(@"""([^""]+)""|(-)?(\S+)", RegexOptions.Compiled)]
private static partial Regex QueryTokenRegex();

public ParsedQuery Parse(string query)
{
    var matches = QueryTokenRegex().Matches(processedQuery);

    foreach (Match match in matches)
    {
        // Quoted phrase
        if (match.Groups[1].Success)
        {
            var phrase = match.Groups[1].Value.Trim();
            result.Phrases.Add(phrase);
            continue;
        }

        // Excluded term (starts with -)
        if (match.Groups[2].Success)
        {
            var term = match.Groups[3].Value.Trim();
            result.ExcludeTerms.Add(term.ToLowerInvariant());
            continue;
        }

        // Regular term or wildcard
        var token = match.Groups[3].Value.Trim();
        if (token.Contains('*'))
        {
            result.WildcardTerms.Add(token.Replace("*", ""));
        }
        else if (!StopWords.Contains(token))
        {
            result.IncludeTerms.Add(token.ToLowerInvariant());
        }
    }
}

पोस्टग्रेएसक्यूएल त्रुटि बनाना

पदवर्णक संरचनात्मक डेटा आउटपुट करता है जो तब PostgreSQL में परिवर्तित किया जाता है to_tsquery क्योंकि हम निर्मित वाक्य संरचना उत्पन्न कर रहे हैं websearch_to_tsquery क्योंकि हम पहले से ही ऑपरेटरों का विश्लेषण कर चुके हैं

public string BuildTsQuery(ParsedQuery parsed)
{
    var queryParts = new List<string>();

    // Add include terms with AND
    foreach (var term in parsed.IncludeTerms)
    {
        queryParts.Add(term);
    }

    // Add wildcard terms with :* suffix
    foreach (var term in parsed.WildcardTerms)
    {
        queryParts.Add($"{term}:*");
    }

    return queryParts.Count > 0 ? string.Join(" & ", queryParts) : string.Empty;
}

क्यों न सिर्फ उपयोग websearch_to_tsquery? चूंकि यह अभी भी तकनीकी संदर्भों पर असफल है।

उन्नत BuildSearchQuery

BuildSearchQuery विधि को पूर्ण रूप से फिर से लिखा गया है पद वर्णन क्वेरी संरचना का उपयोग करने के लिए baseQuery भाषा के अनुसार पहले से ही फ़िल्टर करता है, तिथि शृंखला

private IOrderedQueryable<BlogPostEntity> BuildSearchQuery(
    string query,
    string language,
    DateTime? startDate,
    DateTime? endDate,
    string order)
{
    var parsed = _queryParser.Parse(query);
    IQueryable<BlogPostEntity> searchQuery = baseQuery;

    // Handle phrases (exact substring matching)
    foreach (var phrase in parsed.Phrases)
    {
        searchQuery = searchQuery.Where(x =>
            EF.Functions.ILike(x.Title, $"%{phrase}%")
            || EF.Functions.ILike(x.PlainTextContent, $"%{phrase}%")
            || x.Categories.Any(c => EF.Functions.ILike(c.Name, $"%{phrase}%")));
    }

    // Build tsquery for include terms and wildcards
    var tsQuery = _queryParser.BuildTsQuery(parsed);

    // Apply full-text search if we have terms
    if (!string.IsNullOrWhiteSpace(tsQuery))
    {
        searchQuery = searchQuery.Where(x =>
            x.SearchVector.Matches(EF.Functions.ToTsQuery("english", tsQuery))
            || x.Categories.Any(c =>
                EF.Functions.ToTsVector("english", c.Name)
                    .Matches(EF.Functions.ToTsQuery("english", tsQuery))));
    }

    // Handle acronyms with case-insensitive substring search
    // This supplements full-text search (additive OR), not replaces it
    var acronymTerms = parsed.IncludeTerms
        .Concat(parsed.WildcardTerms)
        .Where(t => _queryParser.IsAcronymLike(t))
        .ToList();

    foreach (var acronym in acronymTerms)
    {
        searchQuery = searchQuery.Where(x =>
            EF.Functions.ILike(x.Title, $"%{acronym}%")
            || EF.Functions.ILike(x.PlainTextContent, $"%{acronym}%"));
    }

    // Handle excluded terms (must NOT contain these)
    foreach (var excludeTerm in parsed.ExcludeTerms)
    {
        searchQuery = searchQuery.Where(x =>
            !EF.Functions.ILike(x.Title, $"%{excludeTerm}%")
            && !EF.Functions.ILike(x.PlainTextContent, $"%{excludeTerm}%")
            && !x.Categories.Any(c => EF.Functions.ILike(c.Name, $"%{excludeTerm}%")));
    }

    return orderedQuery;
}

खोज उदाहरण

यहाँ उन्नत खोज कार्यात्मकता के कुछ उदाहरण हैं

मूल खोज

DiSE

अब शीर्षक या अंतर्वस्तु में लेखों को "DiSE" के साथ मिलाता है

तकनीकी शर्तें

ASP.NET

ASP के बारे में लेख ढूंढता है

वाक्यांश खोज

"semantic search"

✅ वस्तुओं में सही वाक्यांश खोजता है

अपवर्जित शर्तें

ASP.NET -Core

क्रें

विल्डकार्ड

ASP*

मिलान

जटिल प्रश्न

"full text search" PostgreSQL -MySQL

✅ सही वाक्यांश के साथ आलेखों को खोजता है.

पूर्व और बाद उदाहरण

क्वेरी: ASP.NET and Alpine

पहले टूटा

  • एमएसके0एएसपीएमएसके1नेटएमएसक2में विभाजित होता है
  • आरंभ शब्द के रूप में "and" हटाया
  • परिणाम: केवल खोज के लिए "AlpineM SK2

इसके बाद स्थिर

  • को "ASPM SK1NET" में बदल दिया गया
  • उपयोगकर्ता क्वेरी उद्देश्य के भाग के रूप में intelligently preserved
  • सामान्य रूप से खोजा गया
  • परिणाम: ASP तथा Alpine दोनों के बारे में लेख पाता है

आरआरएफ रैंकिंग इंटीग्रेशन

खोज प्रतिस्परिक रैंक फ्यूजन (RRF) के साथ एकीकृत करता है जैसा कि इस में लागू किया गया पूर्व सेमेटिक खोज लेख. आरआरएफ यहाँ के लिए उपयोग किया जाता है वरीयता, रिकार्ड नहीं करता है।

// Fuse using RRF with category/freshness boosts
var fusedDtos = _ranker.FuseResults(bm25Results, vectorResults, query);

आरआरएफ एल्गोरिथ्म का उपयोग करता है 1/(k+rank) जहां k=60 कई स्रोतों से परिणामों को जोड़ने के लिए M SK1 तब boosts के लिए लागू करता है

  • श्रेणी मिलान: +2.0
  • शीर्षक मिलान: +1.0
  • ताजगी: वर्ष के दौरान एक्सपोनेन्शल ह्रास

पूर्ण आरआरएफ क्रियान्वयन और हाइब्रिड खोज कैसे काम करता है क्रिया में सांकेतिक खोज. embeddings और वेक्टर समानता में गहरे खोज के लिए अधिवक्ता GPT का निर्माण. एक ही मूल संरचना उपयोगकर्ता को दोनों शक्तियां प्रदान करता है

तकनीकी वास्तुकला

निर्भरता इंजेक्शन

नए घटक सेवाओं के रूप में पंजीकृत हैं

services.AddSingleton<SearchQueryParser>();
services.AddSingleton<SearchRanker>();
services.AddScoped<BlogSearchService>();

SearchQueryParser और SearchRanker एकलटन होते हैं क्योंकि वे stateless है BlogSearchService स्कॉप किया गया है क्योंकि यह डाटाबेस संदर्भ पहुँचता है

क्वेरी फ्लो

  1. उपयोगकर्ता खोज क्वेरी प्रविष्ट करता है खोज कंट्रोलर
  2. क्वेरी विश्लेषण → SearchQueryParser संरचनात्मक घटकों में विभाजित करता है
  3. सेमेंटिक खोज (यदि सक्षम है → ONNX सम्मिलित के साथ Qdrant भेक्टर डाटाबेस
  4. पोस्टग्रेएसक्यूएल पूरा -पाठ खोज → BM25-आधारित शब्दकुञ्जी मिलान
  5. आरआरएफ संलयन → दोनों परिणाम सेट को श्रेणी के साथ जोड़ता है
  6. कोई परिणाम नहीं → हाल के पदों को सुझाव के रूप में लौटाता है

एक ही सेमेटिक खोज अवयव भी द्वारा उपयोग किया जाता है वकील जीपीटी AI के लिए प्रासंगिक पिछली ब्लॉग पोस्टों को प्राप्त करने की प्रणाली भाग 4 इंजेक्शन पाइपलाइन के बारे में जानकारी के लिए

डाटाबेस योजना

खोज पूर्वानुमानित पर निर्भर करता है SearchVector स्तंभ में BlogPosts तालिका

ALTER TABLE mostlylucid."BlogPosts"
ADD COLUMN "SearchVector" tsvector
GENERATED ALWAYS AS (
    to_tsvector('english',
        coalesce("Title", '') || ' ' ||
        coalesce("PlainTextContent", '')
    )
) STORED;

CREATE INDEX idx_blog_posts_search_vector
ON mostlylucid."BlogPosts"
USING GIN ("SearchVector");

GIN (Generalized Inverted Index) provides fast full

निष्पादन विचार

संक्षिप्ताक्षरों के लिए इलाइक क्यों है

जब ILIKE @item: inlistbox Adjective type

  1. पूर्ण-पाठ खोज सामान्य बनाता है
  2. संक्षिप्ताक्षर आमतौर पर छोटे हैं (≤6 अक्षरों), निष्पादन प्रभाव को सीमित करने के लिए
  3. शर्त तभी जोड़ी जाती है जब आवश्यक हो (अक्षांशों को पहचाना गया

यह दृष्टिकोण लाखों पंक्तियों के बीच स्वैच्छिक उपस्ट्रिंग खोज पर मापन नहीं करेगा, लेकिन लक्षित लघुकवि के लिए वापसी यह

पोस्टग्रेएसक्यूएल आवरण घनत्व रेटिंग (ts_rank_cd)

पोस्टग्रेएसक्यूएल पूरा के लिए दो रैंकिंग फ़ंक्शन्स प्रदान करता है

  • ts_rankबुनियादी शब्द आवृत्ति
  • ts_rank_cdकवर घनत्व रैंकिंग

हम उपयोग करते हैं ts_rank_cd क्योंकि यह प्रदान करता है BM25-सदृश प्रासंगिकता समय निकटता को ध्यान में रखते हुए

// Order by cover density ranking - rewards term proximity
orderedQuery = searchQuery.OrderByDescending(x =>
    x.SearchVector.RankCoverDensity(EF.Functions.ToTsQuery("english", tsQuery)));

क्यों ts_rank_cd बेहतर है

मीट्रिक ts_rank ts_rank_cd
एल्गोरिथम सावधि आवृत्ति
बहुत से शब्द क्वेरी शर्तों को अलग से गणना करता है पुरस्कार शर्तें एक साथ प्रकट होती हैं
उदाहरण के साथ अनुच्छेद1से अधिक गुण प्राप्त करता है docker " 50 times scores high
निष्पादन तेजी से मार्जिनally slower, अभी भी GIN सूचकांक को लेबरेज करता है
व्यवहार साधारण गणना

यह एक है त्वरित जीत अनुकूलन 0 अनुप्रयोग के साथ बेहतर प्रासंगिकता रैंकिंग SearchVector.

संदर्भ

अतिरिक्त निष्पादन अनुकूलन

खोज कार्यात्मकता को सुधारने के अलावा कई प्रमुख सुधार निष्पादन में सुधार करते हैं

भाषा कैशिंग

उपलब्ध भाषाएँ दुर्लभ रूप से बदलती हैं लेकिन प्रत्येक खोज अनुरोध पर क्वेरी की जाती है

private static readonly TimeSpan LanguageCacheDuration = TimeSpan.FromHours(1);
private static List<string>? _cachedLanguages;
private static DateTime _languageCacheExpiry = DateTime.MinValue;
private static readonly SemaphoreSlim _cacheLock = new(1, 1);

प्रभाव: खोज अनुरोध प्रति डाटाबेस क्वेरी को समाप्त करता है

2. बैच आईलिक क्वेरी

मूल कोड का प्रयोग foreach बहुविध WHERE खंडों को बनाने वाले लूप्स. अब एकल एक्सप्रेशन में बैच किया गया

// BEFORE: Multiple WHERE clauses
foreach (var acronym in acronymTerms)
{
    searchQuery = searchQuery.Where(x =>
        EF.Functions.ILike(x.Title, $"%{acronym}%"));
}

// AFTER: Single batched WHERE
if (acronymTerms.Count > 0)
{
    searchQuery = searchQuery.Where(x =>
        acronymTerms.Any(acronym =>
            EF.Functions.ILike(x.Title, $"%{acronym}%")));
}

प्रभावक्लीनर एसक्यूएल

सामान्य प्रश्नों के लिए 3. आवरण सूचकांक

आंशिक अनुक्रमणिका जोड़ी गई है INCLUDE स्तम्भों के साथ बार-बार अभिगम पैटर्न

CREATE INDEX idx_blog_posts_search_covering
ON mostlylucid."BlogPosts" ("LanguageId", "IsHidden", "ScheduledPublishDate")
INCLUDE ("Id", "Slug", "Title", "PublishedDate")
WHERE "IsHidden" = false;

प्रभावसक्षम करता है सिर्फ स्कैन करता है PostgreSQL को तालिका ढेर पहुँचने की जरूरत नहीं है

4. हटाया अनावश्यक शामिल है

ईएफ कोर Include() पूर्ण नेविगेशन इकाइयों को लोड करता है. जहाँ केवल WHERE खंडों में प्रयुक्त नेविगेशन गुणों को हटाया जाता है

// BEFORE: Loads full LanguageEntity into memory
.Include(x => x.LanguageEntity)
.Where(x => x.LanguageEntity.Name == "en")

// AFTER: EF translates navigation property without loading entity
.Where(x => x.LanguageEntity.Name == "en")

प्रभावमेमोरी कमी

संदर्भ

क्वेरी अनुकूलन रणनीति

खोज बनाता है जहाँ खंडों को क्रमशः उपयोग करते हुए EF Core's एक्सप्रेशन ट्री संरचना:

IQueryable<BlogPostEntity> searchQuery = baseQuery;

// Each filter added conditionally - PostgreSQL optimizes the final query
if (parsed.Phrases.Count > 0) { searchQuery = searchQuery.Where(...); }
if (!string.IsNullOrWhiteSpace(tsQuery)) { searchQuery = searchQuery.Where(...); }
if (acronymTerms.Count > 0) { searchQuery = searchQuery.Where(...); }

यह एक उत्पन्न करता है एकल अनुकूलित एसक्यूएल क्वेरी बहुविध राउंड टाइप के बजाय

निष्पादन सारांश

सभी optimizations का संकलित प्रभाव

अनुकूलन |-------------|----------------|----------------| भाषा कैशिंग हटाएँ शामिल करें बैच इलाइक कवरिंग अनुक्रमणिका | tsM SK1rank_cd | समान | बेहतर प्रासंगिकता कैश फिक्स (मार्ग पैरामीज़

कुल प्रत्याशित सुधार: 30-50% तेज खोज काफी कम डाटाबेस लोड के साथ

सीखा गया पाठ

  1. Don't assume full-text search handles everything: एक्रोनिम और विशेष अक्षरों जैसे किनारे के मामलों को विशेष व्यवहार की आवश्यकता होती है

  2. बहुविध दृष्टिकोणों को संयोजित करेंपाठ खोज (BM

  3. गूगल ने उपयोगकर्ताओं की अपेक्षाओं को आकार दिया: उद्धरण वाक्यांशों को समर्थित करता है।

  4. सहायक अपवर्त उपलब्ध कराएँजब खोज असफल होती है तो

  5. पार्सेएक उचित क्वेरी पार्सर स्ट्रिंग परिचालनों की श्रृंखला से अधिक साफ और रख-रखाव करने योग्य है

  6. PostgreSQL लाईवरेजउपयोग ts_rank_cd के बजाय ts_rank के लिए BM25-सदृश प्रासंगिकता. आवरण घनत्व रैंकिंग शब्द समीपता को मानता है | - | एक त्वरित जीत जो शून्य अनुप्रयोग के साथ परिणाम गुणवत्ता में सुधार करता है

  7. अनुकूलन से पहले प्रोफ़ाइल: त्रुटियों को स्पष्ट करता है।

  8. बैच डाटाबेस ऑपरेशन: बहुविध foreach लूप अलग अलग WHERE खंड बनाने के लिए उपoptimal SQL उत्पन्न करता है Any() या All() एकल एक्सप्रेशन में बैच करने के लिए

भावी संवर्धन

भविष्य के लिए संभावित सुधार

पुनःप्राप्ति सुधार:

  • अस्पष्ट मिलानटाइपो सहिष्णुता के लिए : लेवेन्सटेन दूरी
  • संज्ञा विस्तारब्लॉग पोस्ट

पद वर्णन संवर्धन:

  • श्रेणी फिल्टरिंग: category:ASP.NET प्रचालक
  • तिथि शृंखला: after:2025-01-01 प्रचालक

रैंकिंग परिमार्जन:

  • परिणाम हाइलाइटिंग: परिणाम में पाठ के संगत भाग दिखाएँ
  • ट्रैकिंग के माध्यम से क्लिक करें: रैंकिंग में सुधार के लिए उपयोगकर्ता व्यवहार से सीखें

प्रेक्षणशीलता:

  • खोज विश्लेषण: लोकप्रिय क्वेरी को ट्रैक करें और अंतरों की पहचान करने के लिए असफल खोज

निष्कर्ष

निर्माण उत्पादन-श्रेणी खोज को किनारा केसों को स्थिर करने की आवश्यकता होती है और निष्पादन को अनुकूलित करना. इस लेख में दोनों शामिल हैं। ts_rank_cd).

क्रियान्वयन प्राप्त करता है 30-50% तेज खोज के माध्यम से डाटाबेस लोड को कम करते हुए

  • आवरण घनत्व रैंकिंग (ts_rank_cdबेहतर प्रासंगिकता के लिए )
  • भाषा कैशिंग, बार-बार पूछे जाने वाले प्रश्नों को समाप्त करता है
  • साफ SQL के लिए बैच आईलिक एक्सप्रेशन
  • अनुक्रमणिका सक्षम करने के लिए आंकड़े का आवरण
  • हटाए गए अनावश्यक एफ कोर में शामिल है

प्रमुख अंतर्दृष्टि: कोई एकल दृष्टिकोण सभी केसों को नियंत्रित करता है।

सांकेतिक खोज अवसंरचना दो उद्देश्यों की सेवा करती है

  • उपयोगकर्ता-फ़ॉक्सिंग खोज: बेहतर परिणाम के लिए शब्दकुञ्जी और अर्थात्मक मिलान को संयोजित करता है
  • आरएजी पुनःप्राप्ति: पावर वकील जीपीटी लेखन सहायक

संबंधित लेख:

आधिकारिक दस्तावेज़

सभी कोड उपलब्ध है ब्लॉग's GitHub रिपोजिटरी.

logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.