سؤال

لدي عدد كبير من المستندات والملفات النصية وأريد البحث عن المحتوى ذي الصلة.لقد رأيت أداة بحث، ولا أستطيع تذكر مكانها، وقد طبقت طريقة رائعة كما أصفها في متطلباتي أدناه.

متطلباتي هي كما يلي:

  • أحتاج إلى وظيفة بحث محسنة:أقوم بتزويد وظيفة البحث هذه بقائمة (واحدة أو أكثر) من الكلمات الكاملة جزئيًا (أو الكاملة) مفصولة بمسافات.
  • تقوم الوظيفة بعد ذلك بالبحث عن جميع المستندات التي تحتوي على كلمات تبدأ أو تساوي الكلمة الأولى، ثم تقوم بالبحث في هذه المستندات التي تم العثور عليها بنفس الطريقة باستخدام الكلمة الثانية، وهكذا، وفي النهاية تقوم بإرجاع قائمة تحتوي على الكلمات الفعلية التي تم العثور عليها مرتبطة مع المستندات (الاسم والموقع) التي تحتوي عليها، لاستكمال قائمة الكلمات.
  • يجب أن تحتوي الوثائق الجميع الكلمات في القائمة.
  • أريد استخدام هذه الوظيفة لإجراء بحث أثناء الكتابة حتى أتمكن من عرض النتائج وتحديثها في بنية تشبه الشجرة في الوقت الفعلي.

النهج المحتمل للحل الذي توصلت إليه هو كما يلي:أقوم بإنشاء قاعدة بيانات (على الأرجح باستخدام mysql) تحتوي على ثلاثة جداول:"المستندات" و"الكلمات" و"Word_Docs".

  • ستحتوي "المستندات" على (idDoc، الاسم، الموقع) لجميع المستندات.
  • ستحتوي "الكلمات" على (idWord، Word) وستكون عبارة عن قائمة بالكلمات الفريدة من جميع المستندات (تظهر كلمة معينة مرة واحدة فقط).
  • سيكون لـ "Word_Docs" (idWord، idDoc) وسيكون عبارة عن قائمة بمجموعات معرفات فريدة لكل كلمة ومستند تظهر فيه.

يتم بعد ذلك استدعاء الوظيفة مع محتوى مربع التحرير عند كل ضغطة مفتاح (باستثناء المسافة):

  • السلسلة مميزة
  • (هنا تدور عجلاتي قليلاً):أنا متأكد من أنه يمكن إنشاء عبارة SQL واحدة لإرجاع مجموعة البيانات المطلوبة:(actual_words، doc_name، doc_location)؛(أنا لست رقمًا ساخنًا مع SQL)، أو بدلاً من ذلك، سلسلة من الاستدعاءات لكل رمز مميز وتحليل idDocs غير المتكررة؟
  • ثم يتم إرجاع مجموعة البيانات هذه (/list/array).

يتم بعد ذلك عرض محتوى القائمة الذي تم إرجاعه:

على سبيل المثال:دعا مع:يعرض "Seq Sta Cod":

sequence - start - code - Counting Sequences [file://docs/sample/con_seq.txt]
         - stop - code - Counting Sequences [file://docs/sample/con_seq.txt]
sequential - statement - code - SQL intro [file://somewhere/sql_intro.doc]

(وما إلى ذلك وهلم جرا)

هل هذه هي الطريقة المثلى للقيام بذلك؟يجب أن تكون الوظيفة سريعة أم يجب استدعاؤها فقط عند الضغط على مسافة؟هل يجب أن تقدم إكمال الكلمات؟(حصلت على الكلمات الموجودة في قاعدة البيانات) على الأقل هذا من شأنه أن يمنع الاستدعاءات غير المفيدة للوظيفة للكلمات غير الموجودة.إذا كان إكمال الكلمة:كيف سيتم تنفيذ ذلك؟

(ربما يمكن لـ SO أيضًا استخدام هذا النوع من حلول البحث لتصفح العلامات؟(في أعلى يمين الصفحة الرئيسية))

هل كانت مفيدة؟

المحلول

ما تتحدث عنه معروف باسم مؤشر مقلوب أو قائمة النشر، وتعمل بشكل مشابه لما تقترحه وما يقترحه ميكي.هناك الكثير من المؤلفات حول المؤشرات المقلوبة؛تعتبر مقالة ويكيبيديا مكانًا جيدًا للبدء.

من الأفضل، بدلاً من محاولة بنائه بنفسك، استخدام تطبيق الفهرس المقلوب الموجود.تحتوي كل من MySQL والإصدارات الحديثة من PostgreSQL على فهرسة نصية كاملة افتراضيًا.قد ترغب أيضًا في التحقق من ذلك لوسين من أجل حل مستقل.هناك الكثير من الأشياء التي يجب مراعاتها عند كتابة أ جيد الفهرس المقلوب، بما في ذلك الترميز، والاستخلاص، والاستعلامات متعددة الكلمات، وما إلى ذلك، والحل المبني مسبقًا سيفعل كل هذا نيابةً عنك.

نصائح أخرى

من المؤكد أن أسرع طريقة هي عدم استخدام قاعدة بيانات على الإطلاق، لأنه إذا قمت بالبحث يدويًا باستخدام البيانات المحسنة، فيمكنك بسهولة التغلب على أداء البحث المحدد.أسرع طريقة، على افتراض أن المستندات لا تتغير كثيرًا، هي إنشاء ملفات فهرس واستخدامها للعثور على الكلمات الأساسية.يتم إنشاء ملف الفهرس على النحو التالي:

  1. ابحث عن جميع الكلمات الفريدة في الملف النصي.يتم تقسيم الملف النصي بمسافات إلى كلمات وإضافة كل كلمة إلى القائمة ما لم تكن موجودة بالفعل في تلك القائمة.

  2. خذ كل الكلمات التي وجدتها وقم بفرزها أبجديًا؛أسرع طريقة للقيام بذلك هي استخدام الفرز السريع الجذري ثلاثي الاتجاه.من الصعب التغلب على هذه الخوارزمية في الأداء عند فرز السلاسل.

  3. اكتب القائمة التي تم فرزها على القرص، كلمة واحدة في السطر.

  4. عندما تريد الآن البحث في ملف المستند، تجاهله تمامًا، وبدلاً من ذلك قم بتحميل ملف الفهرس إلى الذاكرة واستخدم البحث الثنائي لمعرفة ما إذا كانت هناك كلمة في ملف الفهرس أم لا.من الصعب التغلب على البحث الثنائي عند البحث في قوائم كبيرة ومرتبة.

وبدلاً من ذلك، يمكنك دمج الخطوة (1) والخطوة (2) في خطوة واحدة.إذا كنت تستخدم InsertionSort (الذي يستخدم البحث الثنائي للعثور على موضع الإدراج الصحيح لإدراج عنصر جديد في قائمة تم فرزها بالفعل)، فلن يكون لديك فقط خوارزمية سريعة لمعرفة ما إذا كانت الكلمة موجودة بالفعل في القائمة أم لا، في حالة لم يكن الأمر كذلك، فستحصل على الموضع الصحيح لإدراجه على الفور، وإذا قمت دائمًا بإدراج مواقع جديدة بهذه الطريقة، فستكون لديك قائمة مرتبة تلقائيًا عندما تصل إلى الخطوة (3).

المشكلة هي أنك تحتاج إلى تحديث الفهرس كلما تغير المستند ...ومع ذلك، ألا يكون هذا صحيحًا بالنسبة لحل قاعدة البيانات أيضًا؟من ناحية أخرى، يوفر لك حل قاعدة البيانات بعض المزايا:يمكنك استخدامه، حتى لو كانت المستندات تحتوي على الكثير من الكلمات، بحيث لن تتناسب ملفات الفهرس مع الذاكرة بعد الآن (من غير المرجح، حتى قائمة جميع الكلمات الإنجليزية سوف تتناسب مع ذاكرة أي جهاز كمبيوتر مستخدم عادي)؛ومع ذلك، إذا كنت بحاجة إلى تحميل ملفات الفهرس لعدد كبير من المستندات، فقد تصبح الذاكرة مشكلة.حسنًا، يمكنك التغلب على ذلك باستخدام الحيل الذكية (على سبيل المثال:البحث مباشرة داخل الملفات التي قمت بتعيينها إلى الذاكرة باستخدام mmap وما إلى ذلك)، ولكن هذه هي نفس الحيل التي تستخدمها قواعد البيانات بالفعل لإجراء عمليات بحث سريعة، فلماذا إعادة اختراع العجلة؟علاوة على ذلك، يمكنك أيضًا منع مشاكل القفل بين كلمات البحث وتحديث الفهارس عند تغيير المستند (أي إذا كانت قاعدة البيانات يمكنها إجراء القفل نيابةً عنك أو يمكنها إجراء التحديث أو التحديثات كعملية ذرية).بالنسبة لحل الويب الذي يستدعي AJAX لتحديثات القائمة، ربما يكون استخدام قاعدة البيانات هو الحل الأفضل (الحل الأول مناسب إلى حد ما إذا كان هذا تطبيقًا قيد التشغيل محليًا ومكتوبًا بلغة منخفضة المستوى مثل C).

إذا كنت ترغب في القيام بكل ذلك في مكالمة تحديد واحدة (والتي قد لا تكون مثالية، ولكن عندما تقوم بتحديث محتوى الويب ديناميكيًا باستخدام AJAX، فعادةً ما يثبت أنه الحل الذي يسبب أقل قدر من الصداع)، فأنت بحاجة إلى ربط الجداول الثلاثة معًا.قد يكون SQL SQL صدئًا بعض الشيء، لكنني سأجربه:

SELECT COUNT(Document.idDoc) AS NumOfHits, Documents.Name AS Name, Documents.Location AS Location 
FROM Documents INNER JOIN Word_Docs ON Word_Docs.idDoc=Documents.idDoc 
INNER JOIN Words ON Words.idWord=Words_Docs.idWord
WHERE Words.Word IN ('Word1', 'Word2', 'Word3', ..., 'WordX')
GROUP BY Document.idDoc HAVING NumOfHits=X

حسنًا، ربما هذا ليس الاختيار الأسرع...أعتقد أنه يمكن القيام به بشكل أسرع.على أي حال، سيبحث عن جميع المستندات المطابقة التي تحتوي على كلمة واحدة على الأقل، ثم يقوم بتجميع جميع المستندات المتساوية معًا حسب المعرف، ويحسب عدد المستندات التي تم تجميعها معًا، وأخيرًا يعرض فقط النتائج حيث NumOfHits (عدد الكلمات الموجودة في عبارة IN) يساوي عدد الكلمات الموجودة في عبارة IN (إذا كنت تبحث عن 10 كلمات، فإن X يساوي 10).

لست متأكدًا من بناء الجملة (هذا هو بناء جملة خادم SQL)، ولكن:

-- N is the number of elements in the list

SELECT idDoc, COUNT(1)
FROM Word_Docs wd INNER JOIN Words w on w.idWord = wd.idWord
WHERE w.Word IN ('word1', ..., 'wordN')
GROUP BY wd.idDoc
HAVING COUNT(1) = N

أي بدون استخدام مثل.مع الأشياء المشابهة تكون أكثر تعقيدًا.

بحث جوجل لسطح المكتب أو قد تلبي أداة مماثلة متطلباتك.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top