Skip to content

perf(search): האינדקס בזיכרון נבנה רק בחיפושים שקוראים ממנו - #3351

Merged
amirbiron merged 4 commits into
mainfrom
claude/search-index-utility-5ekh4q
Sep 7, 2026
Merged

amirbiron merged 4 commits into
mainfrom
claude/search-index-utility-5ekh4q

Conversation

@amirbiron

@amirbiron amirbiron commented Sep 7, 2026 •

Copy link
Copy Markdown
Owner

✨ תיאור קצר

AdvancedSearchEngine.search בנה את SearchIndex לפני שבדק איזה סוג חיפוש התבקש. רק TEXT ו-FUNCTION קוראים מהאינדקס — CONTENT, REGEX ו-FUZZY סורקים את ה-DB בעצמם ואינם נוגעים בו. מכיוון ש-CONTENT הוא ברירת המחדל של החיפוש ב-WebApp, המסלול הרגיל שילם על סריקה מלאה של כל קבצי המשתמש שאיש לא קרא את תוצאתה, ומיד אחריה על סריקה מלאה שנייה לחיפוש עצמו. בנוסף נוסף מתג סביבה לכיבוי מלא של האינדקס בזיכרון.

📦 שינויים עיקריים

  • קוד (Backend)
  • בוט טלגרם
  • מסד נתונים/מיגרציות
  • תיעוד (docs/)
  • DevOps/CI/CD

פירוט:

  • search_engine.py — get_index נקרא רק בענפי TEXT ו-FUNCTION, דרך _get_index_for_search ששומרת על מדידת search_index_get הקיימת.
  • search_engine.py — הפרמטר index הוסר מ-_fuzzy_search. הוא התקבל ומעולם לא נקרא בגוף הפונקציה, וזה מה שהסתיר את העובדה שהמסלול אינו נשען על האינדקס. הקורא היחיד הוא ה-dispatch עצמו, ואף טסט אינו קורא למתודה ישירות.
  • search_engine.py + config.py — מתג SEARCH_MEMORY_INDEX_ENABLED (ברירת מחדל true). כבוי: get_index מחזיר אינדקס ריק בלי בנייה ובלי לשמור אותו ב-self.indexes, TEXT ו-FUNCTION מחזירים רשימה ריקה, ו-_safe_search ב-WebApp נופל משם לחיפוש $text במונגו. מה שנעלם בכיבוי: function_index (שמונגו אינו יודע לייצר) וההתאמה החלקית prefix/substring של _text_search (ש-$text אינו תומך בה).
  • services/config_inspector_service.py — הצהרה על המשתנה החדש.
  • docs/ — environment-variables.rst, configuration.rst, ו-workflows/search-flow.rst (כולל הבהרת היחס בין האינדקס בזיכרון לאינדקס search_text_idx של מונגו).

🧪 בדיקות

  • Unit
  • Integration
  • Manual

הטסטים החדשים הורצו על הקוד הישן ונפלו שם לפני שנכתב התיקון — שלוש נפילות (CONTENT, REGEX, FUZZY), ושלושה שעברו כצפוי (TEXT, FUNCTION, ו-fallback ל-SEMANTIC).

tests/test_search_engine_index_built_only_when_used.py:

  • האינדקס אינו נבנה ב-CONTENT/REGEX/FUZZY, וכן נבנה ב-TEXT/FUNCTION ובנפילה-לאחור של סוג לא מוכר.
  • המתג מכבה גם את הענפים שצורכים את האינדקס, מחזיר רשימה ריקה, ואינו משאיר אינדקס ריק ב-self.indexes.
  • טסט אחרון בודק את התוצאה הנצפית ולא רק את הקריאה: שורת הלוג בונה אינדקס חיפוש עבור משתמש <id> עם rebuild_index האמיתי — נעדרת ב-CONTENT, קיימת ב-TEXT. גם הוא הורץ על הקוד הישן ונפל שם.

tests/test_config.py — ברירת המחדל של השדה ב-BotConfig וקריאתו מה-ENV.

הרצות: 168 טסטים עוברים בכל קובצי הטסט שנוגעים ב-search_engine, ב-config וב-Config Inspector. בסוויטה המלאה בסביבת הפיתוח יש 33 נפילות — אותן 33 בדיוק גם על הקוד שלפני השינוי (הושווה ריצה מול ריצה). כולן נובעות מתלויות חסרות בקונטיינר (PyGithub, playwright, גופני PIL, posthog) ומהיעדר רשת, ואף אחת מהן אינה בקובץ שהשינוי נוגע בו.

דיאגרמת ה-mermaid המעודכנת נבדקה בפריסה מול mermaid 11.12.0 שב-node_modules של הריפו, ולא רק נכתבה.

🧪 בדיקות נדרשות ב‑PR

  • 🔍 Code Quality & Security
  • Unit Tests (3.11)
  • Unit Tests (3.12)

📝 סוג שינוי

  • feat: פיצ'ר חדש
  • fix: תיקון באג
  • docs: שינוי תיעוד בלבד
  • refactor: שינוי קוד ללא שינוי התנהגות
  • perf: שיפור ביצועים
  • chore/ci: תשתית/CI
  • breaking change: שינוי שובר תאימות

📚 עיון בתיעוד

עיינתי ב-CodeBot – Project Docs. הקבצים: AI-MAP.md, docs/workflows/search-flow.rst, docs/doc-authoring.rst, docs/testing.rst, docs/environment-variables.rst, docs/configuration.rst. בנוסף amir-bug-patterns: claude-md-snippets/testing.md, CRITICAL-PATTERNS.md K11, bugbot-rules/widened-exception-scope.md, bugbot-rules/line-number-coupling.md.

⚠️ ממצאים בתיעוד שתוקנו במפורש (ולא בשקט)

שתי אי-דיוקים קיימים מראש ב-docs/workflows/search-flow.rst, ששניהם ישבו בתוך הבלוקים שנכתבו מחדש כאן:

  1. העמוד אמר שהאינדקס נבנה מחדש "כל 24 שעות". בקוד should_rebuild מגדיר 30 דקות. הניסוח הוחלף בהפניה ל-should_rebuild במקום לנקוב במספר.
  2. הדיאגרמה ייחסה לענף Fuzzy את fuzz.ratio(). _fuzzy_search משתמש ב-partial_ratio; ratio שייך ל-_function_search.

🔎 ממצאים שלא תוקנו כאן

  • אותו עמוד מכיל סתירה פנימית לגבי הגנת ReDoS: הסקירה והטבלה בראשו מציינות "עם הגנת ReDoS", וסעיף בהמשך קובע שהקוד אינו כולל אותה. בפועל _regex_search ב-search_engine.py אכן אינו בודק ReDoS, בעוד _is_regex_safe ב-webapp/app.py כן מריץ היוריסטיקות לפני הקריאה למנוע. לא נגעתי בזה — זה מחוץ לשינוי, ולא בדקתי את מסלול הבוט.
  • AdvancedSearchEngine.get_search_statistics הוא קוד מת: אין לו אף קורא בריפו. הוא קורא ל-get_index, ולכן עם המתג כבוי יקבל אינדקס ריק. לא נגעתי בו.
  • self.indexes נצבר לפי user_id ואין לו eviction. מחוץ לתחום ה-PR הזה.

🤖 Generated with Claude Code

https://claude.ai/code/session_01RgnHZBJaH4VhdFFgwYZKLr


Generated by Claude Code

Review in cubic

Summary by Sourcery

Optimize search by building the in-process index only when needed, add configurable index controls, and improve search suggestions and their documentation.

New Features:

  • Add configuration switches to enable or disable the in-process search index and optionally preserve eager index building for suggestion support.
  • Improve the global search suggestion UI with theme-aware styling, proper visibility semantics, accessibility roles, and responsive behavior.

Bug Fixes:

  • Prevent text searches from mutating stored index data and degrading relevance scores across repeated searches.
  • Stop searches that do not use the memory index from triggering unnecessary full index builds.

Enhancements:

  • Route memory-index-dependent searches through a dedicated indexed-search path while allowing disabled-index searches to fall back to MongoDB text search.
  • Clarify search indexing behavior and configuration in the user-facing documentation.

Documentation:

  • Document the memory search index controls and clarify the relationship between the in-process index and MongoDB text indexes.

Tests:

  • Add coverage for index build behavior across search types, configuration switches, eager building, fallback behavior, logging, and stable text-search scoring.

‏`AdvancedSearchEngine.search` בנה את `SearchIndex` לפני שבדק איזה סוג חיפוש
התבקש. רק `TEXT` ו-`FUNCTION` קוראים מהאינדקס; `CONTENT`, `REGEX` ו-`FUZZY`
סורקים את ה-DB בעצמם ואינם נוגעים בו. מכיוון ש-`CONTENT` הוא ברירת המחדל של
החיפוש ב-WebApp, המסלול הרגיל שילם על סריקה מלאה של כל קבצי המשתמש (כולל
`code`, במנות של `SEARCH_PAGE_SIZE`, עם קיבוץ גרסאות מלא בכל עמוד) שאיש לא
קרא את תוצאתה — ומיד אחריה סריקה מלאה שנייה לחיפוש עצמו.

מה השתנה:

- ‏`get_index` נקרא רק בענפי `TEXT` ו-`FUNCTION`, דרך `_get_index_for_search`
  ששומרת על מדידת `search_index_get` הקיימת.
- הפרמטר `index` הוסר מ-`_fuzzy_search`; הוא התקבל ומעולם לא נקרא, וזה מה
  שהסתיר את העובדה שהמסלול אינו נשען על האינדקס.
- נוסף `SEARCH_MEMORY_INDEX_ENABLED` (ברירת מחדל `true`) לכיבוי מלא של
  האינדקס בזיכרון. כבוי ⇒ `TEXT` ו-`FUNCTION` מחזירים רשימה ריקה, ו-
  ‏`_safe_search` ב-WebApp נופל לחיפוש `$text` במונגו. מה שנעלם: התאמה
  חלקית (prefix/substring) ו-`function_index`.

התיעוד ב-`docs/workflows/search-flow.rst` עודכן בהתאם, כולל היחס בין האינדקס
בזיכרון לאינדקס `search_text_idx` של מונגו.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RgnHZBJaH4VhdFFgwYZKLr
@qodo-code-review

Copy link
Copy Markdown

ⓘ Qodo reviews are paused because your trial has ended. Ask your workspace admin to add credits to resume reviews. Manage billing

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@sourcery-ai

sourcery-ai Bot commented Sep 7, 2026 •

Copy link
Copy Markdown
Contributor

Reviewer's Guide

השינוי משפר ביצועים בכך שמונע סריקת בניית אינדקס מיותרת עבור CONTENT, REGEX ו-FUZZY, ומוסיף מתג סביבה להשבתת האינדקס לחלוטין עם fallback מתועד ל-MongoDB. הבדיקות מכסות את בחירת ענף החיפוש, התנהגות הכיבוי והתוצאה הנצפית בלוגים.

Sequence diagram for disabled memory index fallback

sequenceDiagram
    participant WebApp
    participant SearchEngine
    participant MongoDB

    WebApp->>SearchEngine: search(user_id, query, TEXT)
    SearchEngine->>SearchEngine: _get_index_for_search(user_id)
    SearchEngine->>SearchEngine: get_index(user_id)
    SearchEngine-->>SearchEngine: SearchIndex()
    SearchEngine-->>WebApp: empty result list
    WebApp->>MongoDB: $text search
    MongoDB-->>WebApp: search results
Loading

Flow diagram for conditional search index usage

flowchart TD
    A["AdvancedSearchEngine.search"] --> B{Search type}
    B -->|TEXT| C[_get_index_for_search]
    B -->|FUNCTION| C
    C --> D[get_index]
    D --> E[_text_search or _function_search]
    B -->|CONTENT| F[_content_search]
    B -->|REGEX| G[_regex_search]
    B -->|FUZZY| H[_fuzzy_search]
    F --> I["Database scan"]
    G --> I
    H --> I
Loading

File-Level Changes

Change Details Files
דחיית בניית האינדקס בזיכרון עד לענפי חיפוש שבאמת צורכים אותו.
  • העברת קבלת האינדקס לענפי TEXT, FUNCTION ול-fallback של חיפוש טקסט.
  • השארת CONTENT, REGEX ו-FUZZY ללא גישה לאינדקס, והסרת פרמטר index שאינו בשימוש מ-_fuzzy_search.
  • שימור מדידת search_index_get באמצעות עטיפה ייעודית.
search_engine.py
הוספת מתג תצורה להשבתת אינדקס החיפוש בזיכרון.
  • הוספת SEARCH_MEMORY_INDEX_ENABLED עם ברירת מחדל פעילה וקריאה ממשתני הסביבה.
  • החזרת אינדקס ריק ללא בנייה או caching כשהמתג כבוי, כך ש-TEXT ו-FUNCTION מחזירים תוצאות ריקות ומאפשרים fallback של WebApp ל-MongoDB $text.
  • רישום המשתנה בשירות בדיקת התצורה ותיעוד ההבדלים ההתנהגותיים.
config.py
services/config_inspector_service.py
docs/environment-variables.rst
docs/configuration.rst
docs/workflows/search-flow.rst
הוספת כיסוי בדיקות להתנהגות בניית האינדקס ולמתג התצורה.
  • בדיקה שהאינדקס נבנה רק עבור סוגי חיפוש שנשענים עליו, כולל fallback מסוג לא מוכר.
  • בדיקה שהמתג מונע בנייה ושמירת אינדקס, וכן בדיקת היעדר/נוכחות שורת הלוג בפועל.
  • בדיקת ברירת המחדל וקריאת הערך מה-ENV.
tests/test_search_engine_index_built_only_when_used.py
tests/test_config.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented Sep 7, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

Next included review available in 1 minute.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Team

Run ID: 78ba659f-6524-46ec-9794-c0932675b24d

📥 Commits

Reviewing files that changed from the base of the PR and between 324ab5d and 20fc40b.

📒 Files selected for processing (12)
  • config.py
  • docs/configuration.rst
  • docs/environment-variables.rst
  • docs/workflows/search-flow.rst
  • search_engine.py
  • services/config_inspector_service.py
  • tests/test_config.py
  • tests/test_search_engine_index_built_only_when_used.py
  • tests/test_search_engine_index_not_mutated_by_search.py
  • webapp/static/css/global_search.css
  • webapp/static/js/global_search.js
  • webapp/templates/files.html

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

🧯 Dangerous deletes guard report

Policy: see .cursorrules — dangerous deletions are blocked unless wrapped safely.

Summary:

  • Flagged findings (blocking): 0
    0
  • Excluded matches (not blocking): 15
  • Total matches (all files): 129

Flagged findings (file:line:snippet):
(none)

Excluded matches (by path pattern)
./webapp/static/js/md_preview.bundle.js.map:4:  "sourcesContent": ["// Markdown-it plugin to render GitHub-style task lists; see\n//\n// https://github.com/blog/1375-task-lists-in-gfm-issues-pulls-comments\n// https://github.com/blog/1825-t … [truncated]
./docs/DOCUMENTATION_GUIDE.md:453:rm -rf _build
./docs/Makefile:24:	rm -rf $(BUILDDIR)
./Dockerfile:42:    rm -rf /var/lib/apt/lists/*
./Dockerfile:121:    rm -rf /var/lib/apt/lists/*
./node_modules/katex/package.json:153:    "build": "rimraf dist/ && mkdirp dist && cp README.md dist && rollup -c --failAfterWarnings && webpack && node update-sri.js package dist/README.md",
./node_modules/katex/src/fonts/Makefile:139:	rm -rf pfa ff otf ttf woff woff2
./node_modules/mermaid/dist/mermaid.js.map:4:  "sourcesContent": ["/**\n* Default values for dimensions\n*/\nconst defaultIconDimensions = Object.freeze({\n\tleft: 0,\n\ttop: 0,\n\twidth: 16,\n\theight: 16\n});\n/**\n* Default values for tr … [truncated]
./node_modules/mermaid/dist/chunks/mermaid.esm/chunk-2M32CCKP.mjs.map:4:  "sourcesContent": ["{\n  \"name\": \"mermaid\",\n  \"version\": \"11.12.0\",\n  \"description\": \"Markdown-ish syntax for generating flowcharts, mindmaps, sequence d … [truncated]
./node_modules/mermaid/dist/chunks/mermaid.esm.min/chunk-4HFYJGYH.mjs.map:4:  "sourcesContent": ["{\n  \"name\": \"mermaid\",\n  \"version\": \"11.12.0\",\n  \"description\": \"Markdown-ish syntax for generating flowcharts, mindmaps, sequen … [truncated]
./node_modules/mermaid/dist/chunks/mermaid.esm.min/chunk-4HFYJGYH.mjs:1:var r={name:"mermaid",version:"11.12.0",description:"Markdown-ish syntax for generating flowcharts, mindmaps, sequence diagrams, class diagrams, gantt charts, git graph … [truncated]
./node_modules/mermaid/dist/chunks/mermaid.core/chunk-KS23V3DP.mjs.map:4:  "sourcesContent": ["{\n  \"name\": \"mermaid\",\n  \"version\": \"11.12.0\",\n  \"description\": \"Markdown-ish syntax for generating flowcharts, mindmaps, sequence  … [truncated]
./node_modules/mermaid/dist/mermaid.min.js:1524:`,"getStyles"),c1e=RQe});var h1e={};dr(h1e,{diagram:()=>NQe});var NQe,f1e=N(()=>{"use strict";$ge();a1e();l1e();u1e();NQe={parser:Fge,db:n1e,renderer:o1e,styles:c1e}});var m1e,g1e=N(()=>{"use  … [truncated]
./node_modules/mermaid/dist/mermaid.min.js.map:4:  "sourcesContent": ["/**\n* Default values for dimensions\n*/\nconst defaultIconDimensions = Object.freeze({\n\tleft: 0,\n\ttop: 0,\n\twidth: 16,\n\theight: 16\n});\n/**\n* Default values fo … [truncated]
./README.md:842:find . -name "__pycache__" -exec rm -rf {} +

@github-actions

github-actions Bot commented Sep 7, 2026 •

Copy link
Copy Markdown
Contributor

⏱️ Performance report

(No performance test durations collected. Mark tests with @pytest.mark.performance.)

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="search_engine.py" line_range="823-824" />
<code_context>
+        ש-``$text`` אינו תומך בה.
+        """
+
+        if not _memory_index_enabled():
+            return SearchIndex()
+
         if user_id not in self.indexes:
</code_context>
<issue_to_address>
**issue (bug_risk):** When `SEARCH_MEMORY_INDEX_ENABLED` is false, `get_index` returns an empty `SearchIndex`, so direct callers of `AdvancedSearchEngine.search` receive no `TEXT` or `FUNCTION` results; only the WebApp-specific `_safe_search` wrapper performs the documented MongoDB fallback. The configuration description therefore promises a fallback that the search-engine API itself does not provide.

**Triggers:** When a bot, internal fallback such as `_fallback_text_search`, or any non-WebApp caller uses `AdvancedSearchEngine.search` while the switch is disabled.

**Suggested fix:** Either implement the MongoDB fallback inside the search engine or narrow the configuration description and make the switch explicitly WebApp-only.
</issue_to_address>

Sourcery assessment

Approval pending. 1 finding to address first.

Blocking findings: search_engine.py:824


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment thread search_engine.py
Comment on lines +823 to +824
if not _memory_index_enabled():
return SearchIndex()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): When SEARCH_MEMORY_INDEX_ENABLED is false, get_index returns an empty SearchIndex, so direct callers of AdvancedSearchEngine.search receive no TEXT or FUNCTION results; only the WebApp-specific _safe_search wrapper performs the documented MongoDB fallback. The configuration description therefore promises a fallback that the search-engine API itself does not provide.

Triggers: When a bot, internal fallback such as _fallback_text_search, or any non-WebApp caller uses AdvancedSearchEngine.search while the switch is disabled.

Suggested fix: Either implement the MongoDB fallback inside the search engine or narrow the configuration description and make the switch explicitly WebApp-only.

@github-actions

github-actions Bot commented Sep 7, 2026 •

Copy link
Copy Markdown
Contributor

📖 Documentation Preview

The documentation has been built successfully!

To view locally:

  1. Download the artifacts
  2. Extract the zip file
  3. Open index.html in your browser

@codecov

codecov Bot commented Sep 7, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

המשך ל-#3351, אחרי שהתברר שהסרת בניית האינדקס מחיפוש `content` גררה רגרסיה
בהשלמה האוטומטית — ושהתצוגה שלה שבורה ממילא.

מתג `SEARCH_MEMORY_INDEX_EAGER_BUILD` (ברירת מחדל `false`)
----------------------------------------------------------

`suggest_completions` קורא את האינדקס דרך `_get_ready_index`, שבמכוון אינו
בונה אותו — ולכן ההשלמה נהנתה מאינדקס שחיפוש `content` בנה כתופעת לוואי.
הרצת בקרה מול הקוד שלפני #3351, אותו קלט: לפני הצעות `['needle','needlehelper']`,
אחרי `[]`. המתג מאפשר להחזיר את החימום מ-ENV, בלי דיפלוי קוד, כדי לשקול את
העלות מול התועלת על נתונים אמיתיים. הוא כפוף ל-`SEARCH_MEMORY_INDEX_ENABLED`.

תיקון זליגה באינדקס ב-`_text_search`
-------------------------------------

`word_index.get(word, set())` מחזיר את הקבוצה השמורה עצמה, וה-`update` אחריו
הזליג את התאמות ה-prefix לתוכה לצמיתות. מאותו רגע הן נספרו כהתאמות מדויקות
וקיבלו 2.0 במקום 1.0, כלומר הדירוג נשחק בכל חיפוש נוסף על אותו אינדקס.

תצוגת ההשלמה האוטומטית
-----------------------

שלושה שורשים, כולם נמדדו ב-Chromium על ה-CSS של הריפו:

- הוובאפ אינו טוען את ה-CSS של Bootstrap, ולכן `list-group-item` על כל הצעה
  לא עיצב דבר: ההצעות היו `<a>` עם `display:inline` וללא ריפוד, וחמש מהן
  נדחסו לשורה אחת בגובה 20 פיקסל. הוחלף במחלקה של הפרויקט, `search-suggestion`.
- `overflow-x:hidden` על `.filters-row` גרם לדפדפן לחשב `overflow-y: auto`.
  השורה הפכה למיכל גלילה אנכי שחתך את הדרופדאון, ולכן הוא נראה רק בגלילה
  בתוך אזור צר שלא נועד לכך. הוחלף ב-`overflow-x: clip`, עם `hidden` כנפילה-לאחור.
- הדרופדאון היה מעוגן לעמודה כולה ולא לתיבת הקלט. הועבר לתוך
  `.search-box-wrapper`, ועכשיו רוחבו וקצהו זהים לתיבה.

בנוסף: `background:#fff` הקשיח הוחלף בטוקני `--solid-surface-*`. הם נמדדו על
כל שבע ערכות הנושא — ניגודיות 17.7 בכולן, כולל `custom`, שבו `--bg-secondary`
אינו מוגדר כלל והתיבה הייתה יוצאת שקופה.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RgnHZBJaH4VhdFFgwYZKLr

Copy link
Copy Markdown
Owner Author

עדכון: מתג לחימום האינדקס, תיקון זליגה, ותיקון תצוגת ההשלמה

הקומיט 77e3391 מוסיף שלושה דברים על גבי הקומיט הראשון.

1. רגרסיה שנמצאה בסקירה, ואומתה בהרצת בקרה

הסרת בניית האינדקס מחיפוש content הסירה גם את הדבר היחיד שמאכלס את self.indexes בפועל. suggest_completions קורא את האינדקס דרך _get_ready_index, שבמכוון אינו בונה אותו (ההשלמה רצה תוך כדי הקלדה), ולכן היא נהנתה מאינדקס שנבנה כתופעת לוואי.

הרצת בקרה מול הקוד שלפני הקומיט הראשון, אותו קלט בדיוק:

לפני אחרי
eng.indexes אחרי חיפוש content [1] []
suggest_completions(1, "need") ['needle', 'needlehelper'] []

מה שנשאר עובד: שמות קבצים, תגיות ושפות — הם מגיעים מ-autocomplete_manager ומ-$text. מה שאבד: מילים מתוך תוכן הקבצים ושמות פונקציות.

הפתרון: SEARCH_MEMORY_INDEX_EAGER_BUILD, ברירת מחדל false. הוא מחזיר את החימום מ-ENV בלי דיפלוי קוד, כדי שאפשר יהיה לשקול עלות מול תועלת על נתונים אמיתיים במקום להכריע עכשיו. כפוף ל-SEARCH_MEMORY_INDEX_ENABLED, ויש טסט שנועל את זה.

2. זליגה לאינדקס ב-_text_search

word_index.get(word, set()) מחזיר את הקבוצה השמורה עצמה, וה-update אחריו הזליג את התאמות ה-prefix לתוכה לצמיתות. מאותו רגע הן נספרו כהתאמות מדויקות וקיבלו 2.0 במקום 1.0, כלומר הדירוג נשחק בכל חיפוש נוסף על אותו אינדקס. באג קיים מראש; הטסט החדש נופל בלעדיו.

3. תצוגת ההשלמה האוטומטית — שלושה שורשים, כולם נמדדו

הדרופדאון לא היה שמיש. נבנה דף בדיקה עם המרקאפ של files.html וכל הסגנונות ש-base.html טוען, ונמדד ב-Chromium בשני רוחבים ובערכת נושא בהירה וכהה.

א. אין CSS של Bootstrap באפליקציה. ה-JS הדביק class="list-group-item list-group-item-action" לכל הצעה, ואין אף כלל בשם list-group בכל קובצי ה-CSS של הוובאפ. ההצעות היו <a> עם display:inline וללא ריפוד — חמש מהן נדחסו לשורה אחת, וגובה כל התיבה היה 20 פיקסל. הוחלף במחלקה של הפרויקט, search-suggestion, עם סגנון אמיתי.

ב. overflow-x:hidden על .filters-row הפך את השורה למיכל גלילה אנכי. overflow-x:hidden יחד עם overflow-y:visible אינו אפשרי — הדפדפן כופה overflow-y:auto. נמדד: overflow-y יצא auto, והדרופדאון נחתך בתוך השורה. הוחלף ב-overflow-x: clip, שבולם אותה גלילה אופקית בלי להפוך את הציר האנכי, עם hidden כנפילה-לאחור לדפדפנים ישנים.

ג. עיגון לעמודה במקום לתיבה. הדרופדאון היה position:absolute ביחס לעמודה כולה. הועבר לתוך .search-box-wrapper; נמדד שרוחבו וקצהו זהים עכשיו לתיבת הקלט.

בנוסף, background:#fff הקשיח הוחלף בטוקני --solid-surface-* לפי docs/webapp/theming_and_css.rst. הבחירה נגזרה ממדידת ניגודיות על כל שבע ערכות הנושא, לא מהערכה:

ערכה --bg-secondary --solid-surface-bg
classic 3.9 — מתחת ל-AA 17.74
ocean / rose-pine-dawn / dark / dim / nebula 6.07–12.44 17.74
custom לא מוגדר — תיבה שקופה 17.74

מדידות אחרי, בארבעה שילובים

בהיר וכהה, 1280 ו-390 פיקסל — זהה בכולם: overflow-y: visible, הדרופדאון נראה מתחת לשורה (נבדק ב-elementFromPoint, לא בגאומטריה), display: block לכל פריט, מפריד 1px, ריפוד 8.8px, גובה תיבה 214 פיקסל במקום 20, יישור מדויק לתיבת הקלט, רקע אטום, ואין גלילה אופקית בעמוד.

שתי מוטציות מוכיחות שהמדידה מסוגלת ליפול: החזרת overflow-x:hidden מחזירה את overflow-y ל-auto ואת visible_below_row ל-false; הסרת מחלקת הפריט מחזירה את ההצעות לשורה אחת. בלי זה המדידה לא הייתה ראיה.

בדיקות

187 עוברים בכל קובצי הטסט הנוגעים ל-search_engine, ל-config ול-Config Inspector. בנוסף 832 עוברים במקטע הטסטים של ה-webapp והתבניות; שלוש הנפילות שם הן ModuleNotFoundError: No module named 'github' בסביבת הפיתוח, והן מופיעות זהות גם על הקוד שלפני השינוי.

ממצאים לדיווח, שלא תוקנו כאן

  • --solid-surface-* מוגדר פעם אחת ב-:root ואין לו override לערכות כהות. כלומר גם תפריט השפות באותו עמוד הוא לוחית לבנה בערכה כהה, וכך גם ההשלמה עכשיו. הבחירה בטוקנים האלה נעשתה כי הם היחידים שאטומים ועוברים AA בכל שבע הערכות; התיקון הנכון הוא להוסיף להם override כהה ב-base.html, וזה משנה שני רכיבים בבת אחת ולכן לא נכנס ל-PR הזה.
  • overflow: clip נמדד ב-Chromium בלבד — אין כאן Firefox או WebKit. הנפילה-לאחור ל-hidden שומרת על ההתנהגות הקיימת בדפדפן שלא מכיר אותו.

Generated by Claude Code

@amirbiron
amirbiron merged commit 86ade33 into main Sep 7, 2026
29 checks passed
amirbiron added a commit that referenced this pull request Sep 7, 2026
* perf(search): האינדקס בזיכרון נבנה רק בחיפושים שקוראים ממנו

‏`AdvancedSearchEngine.search` בנה את `SearchIndex` לפני שבדק איזה סוג חיפוש
התבקש. רק `TEXT` ו-`FUNCTION` קוראים מהאינדקס; `CONTENT`, `REGEX` ו-`FUZZY`
סורקים את ה-DB בעצמם ואינם נוגעים בו. מכיוון ש-`CONTENT` הוא ברירת המחדל של
החיפוש ב-WebApp, המסלול הרגיל שילם על סריקה מלאה של כל קבצי המשתמש (כולל
`code`, במנות של `SEARCH_PAGE_SIZE`, עם קיבוץ גרסאות מלא בכל עמוד) שאיש לא
קרא את תוצאתה — ומיד אחריה סריקה מלאה שנייה לחיפוש עצמו.

מה השתנה:

- ‏`get_index` נקרא רק בענפי `TEXT` ו-`FUNCTION`, דרך `_get_index_for_search`
  ששומרת על מדידת `search_index_get` הקיימת.
- הפרמטר `index` הוסר מ-`_fuzzy_search`; הוא התקבל ומעולם לא נקרא, וזה מה
  שהסתיר את העובדה שהמסלול אינו נשען על האינדקס.
- נוסף `SEARCH_MEMORY_INDEX_ENABLED` (ברירת מחדל `true`) לכיבוי מלא של
  האינדקס בזיכרון. כבוי ⇒ `TEXT` ו-`FUNCTION` מחזירים רשימה ריקה, ו-
  ‏`_safe_search` ב-WebApp נופל לחיפוש `$text` במונגו. מה שנעלם: התאמה
  חלקית (prefix/substring) ו-`function_index`.

התיעוד ב-`docs/workflows/search-flow.rst` עודכן בהתאם, כולל היחס בין האינדקס
בזיכרון לאינדקס `search_text_idx` של מונגו.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RgnHZBJaH4VhdFFgwYZKLr

* fix(search): מתג לחימום האינדקס, ותיקון תצוגת ההשלמה האוטומטית

המשך ל-#3351, אחרי שהתברר שהסרת בניית האינדקס מחיפוש `content` גררה רגרסיה
בהשלמה האוטומטית — ושהתצוגה שלה שבורה ממילא.

מתג `SEARCH_MEMORY_INDEX_EAGER_BUILD` (ברירת מחדל `false`)
----------------------------------------------------------

`suggest_completions` קורא את האינדקס דרך `_get_ready_index`, שבמכוון אינו
בונה אותו — ולכן ההשלמה נהנתה מאינדקס שחיפוש `content` בנה כתופעת לוואי.
הרצת בקרה מול הקוד שלפני #3351, אותו קלט: לפני הצעות `['needle','needlehelper']`,
אחרי `[]`. המתג מאפשר להחזיר את החימום מ-ENV, בלי דיפלוי קוד, כדי לשקול את
העלות מול התועלת על נתונים אמיתיים. הוא כפוף ל-`SEARCH_MEMORY_INDEX_ENABLED`.

תיקון זליגה באינדקס ב-`_text_search`
-------------------------------------

`word_index.get(word, set())` מחזיר את הקבוצה השמורה עצמה, וה-`update` אחריו
הזליג את התאמות ה-prefix לתוכה לצמיתות. מאותו רגע הן נספרו כהתאמות מדויקות
וקיבלו 2.0 במקום 1.0, כלומר הדירוג נשחק בכל חיפוש נוסף על אותו אינדקס.

תצוגת ההשלמה האוטומטית
-----------------------

שלושה שורשים, כולם נמדדו ב-Chromium על ה-CSS של הריפו:

- הוובאפ אינו טוען את ה-CSS של Bootstrap, ולכן `list-group-item` על כל הצעה
  לא עיצב דבר: ההצעות היו `<a>` עם `display:inline` וללא ריפוד, וחמש מהן
  נדחסו לשורה אחת בגובה 20 פיקסל. הוחלף במחלקה של הפרויקט, `search-suggestion`.
- `overflow-x:hidden` על `.filters-row` גרם לדפדפן לחשב `overflow-y: auto`.
  השורה הפכה למיכל גלילה אנכי שחתך את הדרופדאון, ולכן הוא נראה רק בגלילה
  בתוך אזור צר שלא נועד לכך. הוחלף ב-`overflow-x: clip`, עם `hidden` כנפילה-לאחור.
- הדרופדאון היה מעוגן לעמודה כולה ולא לתיבת הקלט. הועבר לתוך
  `.search-box-wrapper`, ועכשיו רוחבו וקצהו זהים לתיבה.

בנוסף: `background:#fff` הקשיח הוחלף בטוקני `--solid-surface-*`. הם נמדדו על
כל שבע ערכות הנושא — ניגודיות 17.7 בכולן, כולל `custom`, שבו `--bg-secondary`
אינו מוגדר כלל והתיבה הייתה יוצאת שקופה.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RgnHZBJaH4VhdFFgwYZKLr

* fix(webapp): רשימת ההצעות נכנסה מתחת לכרטיס הבא

אחרי שההצעות קיבלו גובה אמיתי, התגלה שהרשימה נחתכת מאחורי הכרטיס הבא בעמוד.
נמדד ב-Chromium: מתוך חמש הצעות, שתיים היו מכוסות על ידי תיבת החיפוש שבכרטיס
שאחריו.

השורש: ל-`.glass-card` יש `backdrop-filter: blur(20px)`, וזה יוצר
stacking context. לכן `z-index:1000` של רשימת ההצעות כלוא בגבולות הכרטיס
המארח, והכרטיס עצמו הוא `position:static; z-index:auto` — כלומר הכרטיס הבא
ב-DOM נצבע מעליו, ושום z-index פנימי אינו יכול לגבור על כך.

התיקון מרים את הכרטיס המארח עצמו (`position:relative; z-index:3`), כי זו
הרמה שבה ההכרעה באמת מתקבלת. הערך נמוך בכוונה: מודאלים ותפריטים גלובליים
יושבים ב-1000 ומעלה וממשיכים לגבור.

אימות: 12 הצעות, שתי ערכות נושא, שני רוחבים — אפס פריטים מכוסים בכל השילובים.
שתי מוטציות מוכיחות שהמדידה מסוגלת ליפול: הסרת ה-z-index מהכרטיס מחזירה
כיסוי של עד ארבעה פריטים, והחזרת `overflow-x:hidden` לשורה מחזירה כיסוי של
עד שבעה.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RgnHZBJaH4VhdFFgwYZKLr

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants