# إصلاح الموقع

<!--category-- ASP.NET, PostgreSQL, Search, RRF -->
<datetime class="hidden">2026-01-14T12:00</datetime>

البحث هو أحد تلك الصفات التي يضعها الجميع تحت تقديره. *بشكل مفهومي* صحيح ولكن خطأ بصفة نصية. عندما يفشل البحث في تلك الحالات, لا يعتقد المستخدمينM SK2 لا يفكرون MSC3 حالة حافةMST4 MST5 يعتقدون أن الموقع قد أُكسر

إنه "مسك0" أيضاً من المفترض أن "مسک1" يرتكز هذا الموقع منذ بداية "مكس2" و "مكسي3" نظرت إلى [البحث المفتوح](/blog/textsearchingpt2), عملت على البحث النصي الكامل بـPostgreSQL معها 'شيء الحافز فيكتوري لكنه لم يكن أبدا سعيداً به **مختلطة** أنا especially now I'm building a search tool in [***وضوح*RAG**](https://www.lucidrag.com) حسناً ظننت أنه يجب أن أصلحها أخيراً
سواء كان ذلك هو أو لا هو أمر آخر.

هذا المقال لا يتعلق بـ"مسك0" بل ببناء محرك بحث من الصفر. "مسك1" بل "مسک2" يتعلق بحل الحواف الحادة لنظام PostgreSQL الكامل. "ماسك3" البحث النصي في نظام الإنتاج الحقيقي. "موسك4" سأقوم ببحث عن الاختلافات المحددة التي واجهتها. "ميسك6" لماذا حدثت. "مهسك7" وإجراء الإصلاحات العملية اللازمة لجعل البحث يتصرف كما يتوقعه المستخدمون.

**بناء على أعمال سابقة**: هذا المقال يمد [تطبيق البحث الدلالي](/blog/semantic-search-in-action) التي أضافت البحث الهجين مع دمج الرتبة المتقابلة (RRF). تلك المقالة السابقة تغطي أساسا -جمع PostgreSQL كاملةM SK3بحث النص مع بحث الفيكتور QdrantMSC4 هذه المقالة تصلح الحالات الهوائية التي تفتقدها التنفيذ : الأختصارات التي لا تطابق مع | ' لا تتطابق مع ـ , المصطلحات التقنية مع الحروف الخاصة |, و الاسئلة الطبيعية التي يكسرها تحليل Postgre SQL |

إن البنية التحتية للبحث الدلالي تخدم أهدافاً مزدوجة *و* يوفر طبقة استرجاع ل [محامي GPT](/blog/building-a-lawyer-gpt-for-your-blog-part1) نظام RAG -أعمال كتابة يقوم برسم पदों الجديدة باستخدام الموقع' المحتوى المتوفر كقاعدة للمعرفة [بناء جزء "حامل GPT" –](/blog/building-a-lawyer-gpt-for-your-blog-part3) على الدمجات و البحث فيكتوري.

## المشاكل

### 1. نتائج فارغة أظهرت مقالات عشوائية

عندما أعاد البحث لا نتائج , كان النظام يعرض مقالات قديمة عشوائية بدلاً من إقتراحات مفيدة . هذا إنتهى من مبدأ الحد الأدنى من الإثارة للدهشة | - | توقع المستخدمين إما نتائج ذات مغزى أو واضحة |" | لم يعثر على تطابق |" | رسالة مع الملاحظات الأخيرة كإقتراح | . | هذا جعل الأمر يبدو وكأن البحث قد نجح |

**الحل**: تم تعديله `BlogSearchService.HybridSearchWithPagingAsync()` لإكتشاف عندما يعيد البحث نتائج صفر وتعود لإظهار الملاحظات الأخيرة مرتبة بال fecha descending. أضافت `NoMatchFound` علم إلى `BasePagingModel` بحيث يمكن للواجهة أن تظهر رسالة مناسبة مثل "لم يُعثر على تطابق . هل كنتم تقصدون واحدة من هذه

```csharp
// 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. اختصارات مثل "DiSEM SK2 Weren't متطابقة

نظام البحث النصي الإنجليزي تلقائي بـ PostgreSQL ' يحوّل إحصائياً على الحروف المختصرة , لكن العولمة والإستنتاج تجعل الحالة القصيرة | , | حالة |- | المصطلحات الهامة غير موثوق بها في الواقع | МSK4 | عندما تبحث عن | | مSK5 | DiSE | ومSK6 | فإنه ينزل إلى | ما | 7 | dise | و بعد ذلك قاموس اللغة الإنجليزية قد يتخلص منه أو يخصص له وزناً منخفضاً | , | مما يؤدي إلى أن البحث النصية الكاملة ــ - | تضيع المقالات التي تحتوي بوضوح | ام | 11 | DISE ٬ م | 12 | في عنوانها و المحتوى | أم | 13

**الحل**: أضفنا إكتشاف الأحرف الرمزية (مواضيع ≤6 حروف مع حرفات كبيرة) وقضيةM SK4التراجع الغير حساس للأرقام الفرعية باستخدام PostgreSQLMSC5s `ILIKE`. هذا يكمل البحث النصي الكامل- بدلاً من استبداله (انظروا إلى ملاحظات الأداء في الأسفل لسبب قبول هذا

```csharp
// 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. Terms Technical with Special Characters Broke Search

البحث عن "ASP.NETM SK2 أو "CMSC4 سيفشل لأن محرر البحث النصي PostgreSQLMska5 يتعامل مع المراحل ورمزات الهش كمحدودينMske6 ويقسم "AspMsko8NET\" إلى "A SPMsku11 و "net" كعلامة مستقلةMsek14 وهذا يعني أن البحث عن المفهوم الدقيق لن يعمل'Msco16

**الحل**: خلقت `SearchQueryParser` التي تعترف بالمصطلحات التقنية المشتركة وتستبدلها بإصدارات قابلة للبحث

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

### 4. أسئلة مثل "ASPM SK2NET and Alpine" لم تعمل

PostgreSQL يتعامل مع "و" ككلمة توقف وتزيلها من ال検索اتM SK2 تدمج مع مسألة الخصائص الخاصة , سؤال طبيعي مثل |" |ASP | . |NET | and Alpine |

**الحل**: تم تنفيذ محرك بحث غوغل - يتعامل مع كلمات التوقف بذكاء ويدعم مشغلات البحث المتقدمة

## الحلول

### Google-مشغلات البحث فيStyle

لقد قمت بتطبيق `SearchQueryParser` الصنف الذي يحل الاسئلة مع الدعم ل:

1. **العبارات المقتطفة**: `"exact match"` - يبحث عن الجملة بالضبط
2. **شروط غير محصّلة**: `-unwanted` - يبعد عن النتائج التي تحتوي على هذا المصطلح
3. **البطاقات البرية**: `ASP*` - تطابق "ASP", "AspNETM SK4 ♫"ASPNetCoreMSC6 إلخ |.
4. **الشروط التقنية**: يتعامل تلقائيا "ASP.NETM SK3 "CMSC5 | | ".NET |" | إلخ |

يستخدم التحليلي نمط regex المكتمل لترميز الشؤال:

```csharp
[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 tsquery"

يصدر الخوارزميات البيانات الهيكلية التي تم تحويلها إلى PostgreSQL `to_tsquery` لأننا ' ننتج تركيبة مبنية - لا `websearch_to_tsquery` لأننا' لقد قمنا بتحليل المشغلين بأنفسنا- -, مما يعطينا المزيد من التحكم في كيفية دمج الشروط .

```csharp
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`?**
> لأنه لا يزال يفشل في المصطلحات التقنية, الإختلافات, والドメインM SK2 صيغة خاصة MSC3 ولا يقدم أي مقابض للتصنيف الهجين أو التراجعات . عن طريق تحليل أنفسناMST5 يمكننا التعامل مع هذه الحالات المتطرفة قبل أن تصل إلى PostgreSQL

### بحث بناء مُحسن

الـ `BuildSearchQuery` تم إعادة كتابة الطريقة بالكامل لإستخدام بنية الشؤال المنحلة. `baseQuery` بالفعل تصفيه باللغة, نطاق التاريخ, وقابلية الرؤية - نحنM SK3 نضيف البحثMSC4 الشروط المحددة في الأعلىMNK5

```csharp
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" في عنوان أو المحتوى (caseM SK4insensitiveMSC5

### الشروط التقنية

```
ASP.NET
```

✅ يجد مقالات عن ASP.NET ( محولة تلقائياً إلى "aspnetM SK4 للبحث الكاملMSC5text searchMスク6

### البحث في الجملة

```
"semantic search"
```

✅ يجد جملة دقيقة "بحث لغوي" في المقالات

### شروط غير محصّلة

```
ASP.NET -Core
```

✅ يجد مقالات ASP.NET لكن يبعد عنهم تلك التي تشير إلى "CoreM SK3

### البطاقات البرية

```
ASP*
```

✅ تتطابق "ASP", "AspNETM SK4 |" |ASPNetCore | ", | إلخ

### السؤال المعقد

```
"full text search" PostgreSQL -MySQL
```

✅ يجد المقالات التي تحتوي على جملة دقيقة "بحث عن النص الكامل" وذكرات PostgreSQLM SK3 لكن بإستثناء المقالات التى تشير إلى MySQL

### مثال قبل وبعد

**السؤال**: `ASP.NET and Alpine`

**قبل** (broken):

- "ASPM SK1NET" تقسم إلى "AspMSC4 + | | "NET |"
- "and" أزيلت ككلمة توقف
- Result: searches only for M SK1Alpine"

**بعد** (fixed):

- "ASPM SK1NET" حول إلى "aspnetMSC4
- "and" محفوظة بطريقة ذكية كجزء من إهتمام استبيان المستخدم
- "Alpine" بحث بشكل طبيعي
- Result: يجد مقالات حول كلا ASP.NET و Alpine

## تكامل التصنيف لـ RRF

يتم دمج البحث مع السلسلة المتباعدة (RRF) كما تم تنفيذه في [مقال البحث الدلالي السابق](/blog/semantic-search-in-action#hybrid-search-with-reciprocal-rank-fusion). RRF هنا يستخدم ل *التصنيف*, لا يعيد الذاكرة - إنه يجمع بالفعل- نتائج تم استرجاعها من BM

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

الخوارزمية RRF تستخدم `1/(k+rank)` حيث يجمع k=60 النتائج من مصادر متعددة , ثم يطبق مضاعفات :

- **تناسب التصنيف**: +2.0
- **تطابق शीर्षक**: +1.0
- **الشباب**: التدهور النسبي على مدى 1 سنة

لتطبيق RRF الكامل وكيفية عمل البحث الهجين, أنظر [البحث الدلالي في العمل](/blog/semantic-search-in-action). للغوص العميق في المداخلات والتشابه الفيكتوري , см. [بناء جزء "حامل GPT" -](/blog/building-a-lawyer-gpt-for-your-blog-part3). نفس البنية التحتية تمكن كلا من المستخدم- التوجه للبحث والبحث عن RAG من أجل الذكاء الاصطناعي

## العمارة التقنية

### إنبوب الإعتماد

المكونات الجديدة تم تسجيلها كخدمات:

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

`SearchQueryParser` و `SearchRanker` هي مجردtons لأنّها ' لا تملك حالة وثابتة , تار - آمن , ويمكن إستخدامها مرة أخرى في جميع الطلبات `BlogSearchService` تم تحديد نطاقها لأنها تتمكن من الوصول إلى سياق البيانات.

### تدفق التساؤل

1. **يدخل المستخدم بحثاً** → محرك البحث
2. **تحليل الاسئلة** → SearchQueryParser يقسم إلى مكونات مبنية
3. **البحث الدلالي (if enabled)** → قاعدة بيانات векторية Qdrant مع مداخلات ONNX
4. **PostgreSQL كاملة-بحث نصي (متوفر دائماM SK2** → BM25-تطابق كلمات المرور على أساس
5. **الإنصهار RRF** → يجمع كلا مجموعتين النتائج مع التصنيف / تحسن الطابع
6. **لا النتائج** → يعيد ال posts الأخيرة كإقتراحات

نفس عنصر البحث الدلالي يستخدم أيضاً من قبل [محامي GPT](/blog/building-a-lawyer-gpt-for-your-blog-part1) نظام لإستقاط المدونات السابقة ذات الصلة للAI-كتابة المساعدة. شاهد [الجزء 4](/blog/building-a-lawyer-gpt-for-your-blog-part4) detailed on the ingestion pipeline.

### تخطيط قاعدة البيانات

البحث يعتمد على تحليل مسبق `SearchVector` ستجد ستونا في `BlogPosts` جدول:

```sql
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 (index generalized Inverted ) يوفر بحثاً سريعاً كاملاً | - | text search across large text corpora |.

## وجهات النظر في الأداء

### لماذا اللافتة لكلمة اختصارية?

بينما `ILIKE` (caseM SK1insensitive LIKE) is slower than full -text searchMST4 itMSC5s necessary for acronyms becauseMS:

1. الجمل الكاملة -البحث النصي يُطَمِّرَي /يمكّن من الوصول إلى المصطلحات | , | كسر الأوتار الفائقة القصيرة
2. الأختصارات هي عادة قصيرة (≤6 حروف ), محدودة تأثير الأداء
3. يتم إضافة الحالة فقط عند الحاجة (حرف اختصارية مكتشفة)

هذه المقاربة لن تتدرج إلى بحث فرعي عشوائي عبر ملايين الأ行ات -لكن بالنسبة للحرف الرمزية المستهدفة فإنها 'suitable

### تصنيف كثافة الغلاف PostgreSQL (`ts_rank_cd`)

offers two ranking functions for full-text search

- `ts_rank`: التردد الأساسي للأفكار ( كم مرة تظهر الأفكار
- `ts_rank_cd`: تصنيف كثافة الغطاء (كيف تبدو المصطلحات قريبة مع بعضها البعض

نحن نستخدم `ts_rank_cd` لأنها توفر **علاقة بـ BM25-** بأخذ في الإعتبار اقتراب المدى:

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

**لماذا `ts_rank_cd` هو أفضل**

| Metric  | `ts_rank` | `ts_rank_cd` |
|--------|-----------|--------------|
| **الخوارزمية** |تردد المدى | كثافة الغطاء (مقاربة ) S|
| **العديد من -សំណួរ الكلمات** | يحسب المصطلحات منفصلاً | مصطلحات الجائزة تظهر معاً ≥|
| **مثال: "طاولة الدوكرM SK2** | مقالة مع "دوكير" 50 تعطي علامة مرتفعة | | | مقالة بـ " | علبة الدوكير |" | معا تعطي علامات أعلى |
| **الأداء** | سريع | بطيئ نسبياً , لا يزال يرفع من إحصاء الـGIN
| **السلوك** | العد البسيط | تقريبية BM

هذا هو **تحسين الفوز السريع** - أعلى نسبية في التصنيف مع تطبيق صفر-حساب مستوىM SK2 كل التصنيف يحدث في PostgreSQL باستخدام إحصاء GIN موجود على `SearchVector`.

**الإشارات:**

- [documentación PostgreSQL ts_rank_cd](https://www.postgresql.org/docs/current/textsearch-controls.html#TEXTSEARCH-RANKING)
- [EF Core RankCoverDensity](https://learn.microsoft.com/en-us/ef/core/providers/postgres/misc#full-text-search)

### تحسينات الأداء الإضافية

أكثر من إصلاح أداء البحث, تحسينات رئيسية عديدة لتحسين الأداء:

#### 1. تخزين اللغة

اللغات المتاحة نادراً ما تتغير ولكن تم سؤالها في كل طلب بحث

```csharp
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);
```

**التأثير**: يزيل 1 سؤال قاعدة بيانات لكل طلب بحث

#### 2. أسئلة ILIKE المزدحمة

الرمز الأصلي المستعمل `foreach` حلقات تخلق العديد من فقرات أين. الآن مدمجة في تعبيرات واحدة:

```csharp
// 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}%")));
}
```

**التأثير**: SQL أكثر سلاسة, ~5-10% أسرع لكثير من الأسئلة

#### 3. Index Covering for Common Queries

أضفنا مؤشراً جزئياً مع ستloupes INCLUDE لنمط الوصول المكرر:

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

**التأثير**: يُمكن [index-مسح فقط](https://www.postgresql.org/docs/current/indexes-index-only-scans.html) - PostgreSQL لا يحتاج إلى ' الوصول إلى كومة الجدول

#### 4. أزيل ما هو غير ضروري بما في ذلك

EF Core's `Include()` loads full navigation entities. Removed where only navigation properties used in WHERE clauses:

```csharp
// 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")
```

**التأثير**: ~5-10% تخفيض الذاكرةM SK2 نقل أقل من البيانات من قاعدة البيانات.

**الإشارات:**

- [مؤشرات PostgreSQL](https://www.postgresql.org/docs/current/indexes.html)
- [الأداء الأساسي لـ EF](https://learn.microsoft.com/en-us/ef/core/performance/)

### استراتيجية تحسين الأسئلة

يبنى البحث أين ستستخدم الجمل بشكل متزايد [تركيب شجرة التعبير EF Core's](https://learn.microsoft.com/en-us/ef/core/querying/):

```csharp
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(...); }
```

هذا ينتج **سؤال واحد مُحسن من SQL** بدلاً من عدة رحلات دائرية. PostgreSQL' يستطيع مخطط الطلب استخدام الإحصائيات والمؤشرات بشكل فعال عندما يرى فقرة أين كاملة

## ملخص الأداء

التأثير المتراكم لكل التحسينات:

| تحسين | تأثير البطاقة | تأثير تحميل البيانات |
|-------------|----------------|----------------|
| ال caching للغة | الحد الأدنى
| أزيلوا من ضمنهم() | ≥-5-10% | || نقل البيانات أقل | |
| بطاقة ILIKE | -5-10% | | | Cleaner SQL |
| المؤشر المغطى | -20-30% | | | المؤشر | - | فقط المسح ||
| tsM SK1rank_cd | مماثلة | علاقة أفضل |
| إصلاح السجل (مساريات الطريق) | NM SK4A | يمنع البيانات الميتة M|

**التحسن المتوقع الكلي**: **30-50% بحث أسرع** مع انخفاض كبير في ضغط قاعدة البيانات.

## الدروس المستفادة

1. **لا أفترض أن البحث النصي يعالج كل شيء**: حاسوبي القضبان مثل الأختصارات والحرف الخاصة يحتاجون إلى معالجة خاصة

2. **دمج المقاربات المتعددة**: البحث الكلي- البحث النصي (BMM SK3 | | + البحث الدلالي |( |vectors | МSK6 | + | فرعية الخداعات توفر تغطية أفضل من أي طريقة واحدة

3. **تم تشكيل توقعات المستخدم من قبل غوغل**: دعم العبارات المشروحة, استثناءاتM SK2 وطاقات عشوائية يجعل البحث يبدو طبيعيا لأن المستخدمين يعرفون هذه المشغلات

4. **توفر تراجعات مفيدة**: عندما يفشل البحث, لا ' لا يظهر أي شيء - يظهر الملاحظات الأخيرة وجعل الأمر واضحا أنه لم يعثر على تطابق

5. **Parse, don't hack**: محرر الطلب المناسب هو أكثر نظافة وأكثر قابلية للحفاظ من سلسلة من عمليات التلاعب بالسلسلة.

6. **إستفادة من PostgreSQL's بنيت-في التصنيف**: استخدام `ts_rank_cd` بدلاً من `ts_rank` بالنسبة لل BM25-ماثل الجدوىM SK1 تصنيف كثافة الغطاء يأخذ في الإعتبار قريبة المصطلح - الفوز السريع الذي يحسن جودة النتائج مع تطبيق صفر -رمز مستوىMSC4

7. **Profil before optimizing**: bottlenecks "obviousM SK2 bottlenes (FTS parsing) werenMSC5t the real problemMska6 language lookupsMsko7 excessive includesMske8 and missing indexes had bigger impact than expectedMsek9

8. **عمليات قاعدة بيانات بطاقات**: متعددة `foreach` حلقات تخلق فقرات منفصلة عن بعضها تنتج SQL . استخدام `Any()` أو `All()` لتجمع في تعبيرات واحدة.

## تحسينات مستقبلية

تحسينات محتملة للمستقبل, تصنف بالقلق:

**تحسينات البحث**:

- **تطابق مبهم**: مسافة ليفينشتين لتحمل النمط
- **اختصار التمدد**: "رسالة مدونية" ♫→ | | " | مقالة

**تحسينات التحليل**:

- **تصفية التصنيف**: `category:ASP.NET` المشغل
- **نطاق التاريخ**: `after:2025-01-01` المشغل

**تحسينات التصنيف**:

- **توضيح النتائج**: أظهر الأجزاء المتطابقة من النص في النتائج
- **اضغط على - عبر التعقب**: تعلم من سلوك المستخدم لتحسين التصنيف

**قابلة للملاحظة**:

- **تحليل البحث**: تتبع الأسئلة الشائعة والبحث الفاشلة لتحديد الفجوات

## الاستنتاجات

صناعة البناء -بحث الدرجة يتطلب إصلاح الحواف **و** تحسين الأداء. هذا المقال غطى كلاً من: إصلاح PostgreSQL كاملةM SK2حواسيب حافة البحث النصية (مصطلحات إختصاريّة, عبارات تقنية, غوغل- مشغلات نمطية) وتطبيق تحسينات الأداء الرئيسية MSC8 المخزنة, التحميل, تغطية الإحصائيات, `ts_rank_cd`).

تم تحقيق **30-50% بحث أسرع** بينما تقلل ضغط قاعدة البيانات من خلال:

- تصنيف كثافة الغطاء (`ts_rank_cd`) لجعل الموضوع أكثر relevance
- ال caching اللغوي يزيل الاسئلة المتكررة
- تعابير ILIKE المزدحمة لنظام SQL النظيف
- تغطية المؤشرات التي تمكن من مسح المؤشر فقط
- أزيلت أساس EF غير ضروري بما في ذلك

الفكرة الرئيسية: لا يوجد منهج واحد يتعامل مع كل الحالات. PostgreSQL FTS تفوق على تطابق كلمات المرور , البحث الدلالي يتعامل باسئلة نظرية , والتراجعات المستهدفة تأخذ في الإعتبار الحالات المتطرفة MSC4 طبقة التحليل النظيفة تربطها معاً M SK5 تتعامل مع المشغلين والمصطلحات التقنية قبل أن تصل إلى محرك البحث

البنية التحتية للبحث الدلالي تخدم أهدافاً مزدوجة:

- **المستخدم- يوجه البحث**: يجمع كلمات رئيسية ومقارنة الدلالية لأفضل النتائج
- **استرجاع RAG**: تعطي قوة [محامي GPT](/blog/building-a-lawyer-gpt-for-your-blog-part1) مساعد الكتابة

**المقالات الأخرى:**

- [البحث الدلالي في العمل](/blog/semantic-search-in-action) - مؤسسة: بحث هجين مع RRF
- [بناء جزء "حامل GPT" -](/blog/building-a-lawyer-gpt-for-your-blog-part3) - مداخلات & قاعدة بيانات فيكتورية
- [بناء جزء "حامل GPT" -](/blog/building-a-lawyer-gpt-for-your-blog-part4) - أنابيب الإمتصاص

**الوثائق الرسمية:**

- [PostgreSQL Full-Text Search](https://www.postgresql.org/docs/current/textsearch.html)
- [مؤشرات PostgreSQL](https://www.postgresql.org/docs/current/indexes.html)
- [الأداء الأساسي لـ EF](https://learn.microsoft.com/en-us/ef/core/performance/)
- [EF Core Npgsql Provider](https://www.npgsql.org/efcore/index.html)

كل البرمجة المتاحة في [blog's مخزن GitHub](https://github.com/scottgal/mostlylucidweb).