هيبرنيت
هيبرنيت بحد ذاته لا يوفر دعم للبحث الكامل النصي. يعتمد على دعم محرك قاعدة البيانات أو الحلول الخارجية.
تم توفير تمديد يسمى بحث هيبرنيت يربط مع أباتشي لوسن أو بحث الكتان (هناك أيضا تكامل مع OpenSearch).
بوستغرس
بوستغرس كان لديه وظيفة البحث الكامل النصي منذ الإصدار 7.3. على الرغم من أنه لا يمكن التنافس مع محركات البحث مثل بحث الكتان أو لوسن ، إلا أنه لا يزال يوفر حلاً مرنًا وقويًا قد يكفي لتلبية التوقعات الخاصة بمستخدمي التطبيقات – ميزات مثل التبديل الجذري والترتيب والفهرسة.
سنشرح باختصار كيف يمكننا القيام ببحث كامل النص في بوستغرس. لمزيد من المعلومات ، يرجى زيارة توثيق بوستغرس. بالنسبة للمباراة النصية الأساسية ، الجزء الأكثر أهمية هو مشغل الرياضيات @@
.
ترجع true
إذا كان الوثيقة (كائن من النوع tsvector
) تطابق الاستعلام (كائن من النوع tsquery
).
الترتيب ليس أمراً حاسماً بالنسبة للمشغل. لذا ، لا يهم إذا وضعنا الوثيقة على الجانب الأيسر من المشغل والاستعلام على الجانب الأيمن أو بترتيب مختلف.
لشرح أفضل، نستخدم جدول قاعدة البيانات يسمى tweet
.
create table tweet (
id bigint not null,
short_content varchar(255),
title varchar(255),
primary key (id)
)
مع مثل هذه البيانات:
INSERT INTO tweet (id, title, short_content) VALUES (1, 'Cats', 'Cats rules the world');
INSERT INTO tweet (id, title, short_content) VALUES (2, 'Rats', 'Rats rules in the sewers');
INSERT INTO tweet (id, title, short_content) VALUES (3, 'Rats vs Cats', 'Rats and Cats hates each other');
INSERT INTO tweet (id, title, short_content) VALUES (4, 'Feature', 'This project is design to wrap already existed functions of Postgres');
INSERT INTO tweet (id, title, short_content) VALUES (5, 'Postgres database', 'Postgres is one of the widly used database on the market');
INSERT INTO tweet (id, title, short_content) VALUES (6, 'Database', 'On the market there is a lot of database that have similar features like Oracle');
الآن دعونا نرى كيف يبدو كائن tsvector
لعمود short_content
لكل من السجلات.
SELECT id, to_tsvector('english', short_content) FROM tweet;
الناتج:
يوضح الناتج كيف يحول to_tsvector
عمود النص إلى كائن tsvector
لتكوين البحث النصي ‘english
‘.
تكوين البحث النصي
كان المعامل الأول الممرر لدالة to_tsvector
في المثال أعلاه هو اسم تكوين البحث النصي. في تلك الحالة، كان “english
“. وفقًا لوثائق Postgres، يتم تعريف تكوين البحث النصي على النحو التالي:
… وظائف البحث النصي الكاملة تشمل القدرة على القيام بأشياء أكثر: تخطي فهارس بعض الكلمات (كلمات التوقف)، معالجة المرادفات، واستخدام التحليل المتقدم، على سبيل المثال، التحليل بناءً على أكثر من مجرد مسافات بيضاء. يتم التحكم في هذه الوظائف من خلال تكوينات البحث النصي.
إذاً، التكوين هو جزء حاسم من العملية وأساسي لنتائج البحث في النص الكامل. بتكوينات مختلفة، يمكن لمحرك Postgres إرجاع نتائج مختلفة. لا يجب أن يكون هذا هو الحال بين القواميس لللغات المختلفة. على سبيل المثال، يمكنك امتلاك تكوينين لنفس اللغة، لكن أحدهما يتجاهل الأسماء التي تحتوي على أرقام (مثل بعض رقم السلسلة). إذا ما أرسلنا في استعلامنا الرقم التسلسلي المحدد الذي نبحث عنه، وهو إلزامي، فلن نجد أي سجل للتكوين الذي يتجاهل الكلمات التي تحتوي على أرقام. حتى لو كان لدينا مثل هذه السجلات في قاعدة البيانات، يرجى الاطلاع على وثائق التكوين لمزيد من المعلومات.
استعلام النص
استعلام النص يدعم مثل هذه المشغلين مثل &
(AND)، |
(OR)، !
(NOT)، و <->
(FOLLOWED BY). لا تتطلب المشغلات الثلاثة الأولى توضيحاً أكثر. يتحقق المشغل <->
مما إذا كانت الكلمات موجودة وإذا كانت موضوعة في ترتيب محدد. لذا، على سبيل المثال، لاستعلام “rat <-> cat
“، نتوقع أن تكون كلمة “cat” موجودة، تليها كلمة “rat”.
أمثلة
- محتوى يحتوي على rat وcat:
SELECT t.id, t.short_content FROM tweet t WHERE to_tsvector('english', t.short_content) @@ to_tsquery('english', 'Rat & cat');
- محتوى يحتوي على database وmarket, وأن السوق هو الكلمة الثالثة بعد قاعدة البيانات:
SELECT t.id, t.short_content FROM tweet t WHERE to_tsvector('english', t.short_content) @@ to_tsquery('english', 'database <3> market');
- محتوى يحتوي على database ولكن ليس Postgres:
SELECT t.id, t.short_content FROM tweet t WHERE to_tsvector('english', t.short_content) @@ to_tsquery('english', 'database & !Postgres');
- المحتوى الذي يحتوي على Postgres أو Oracle:
SELECT t.id, t.short_content FROM tweet t WHERE to_tsvector('english', t.short_content) @@ to_tsquery('english', 'Postgres | Oracle');
وظائف التغليف
ذكرت إحدى وظائف التغليف التي تنشئ استعلامات نصية في هذا المقال مسبقًا ، وهي to_tsquery
. هناك المزيد من هذه الوظائف مثل:
plainto_tsquery
phraseto_tsquery
websearch_to_tsquery
plainto_tsquery
تحول plainto_tsquery
جميع الكلمات المرسلة إلى استعلام حيث تتم دمج جميع الكلمات باستخدام عامل التشغيل &
(AND). على سبيل المثال ، المكافئ لـ plainto_tsquery('english', 'Rat cat')
هو to_tsquery('english', 'Rat & cat')
.
للاستخدام التالي:
SELECT t.id, t.short_content FROM tweet t WHERE to_tsvector('english', t.short_content) @@ plainto_tsquery('english', 'Rat cat');
نحصل على النتيجة التالية:
phraseto_tsquery
تحول phraseto_tsquery
جميع الكلمات المرسلة إلى استعلام حيث تتم دمج جميع الكلمات باستخدام عامل التشغيل <->
(FOLLOW BY). على سبيل المثال ، المكافئ لـ phraseto_tsquery('english', 'cat rule')
هو to_tsquery('english', 'cat <-> rule')
.
للاستخدام التالي:
SELECT t.id, t.short_content FROM tweet t WHERE to_tsvector('english', t.short_content) @@ phraseto_tsquery('english', 'cat rule');
نحصل على النتيجة التالية:
websearch_to_tsquery
يستخدم websearch_to_tsquery بناء جملة متنوع لإنشاء استعلام نص صالح.
- نص غير مُقتبس: يحول جزءًا من البناء النحوي بنفس طريقة
plainto_tsquery
- نص مُقتبس: يحول جزءًا من البناء النحوي بنفس طريقة
phraseto_tsquery
- أو: يحول إلى “
|
” (أو) المشغل - “
-
“: يعادل “!
” (لا) المشغل
على سبيل المثال، المكافئ لـ websearch_to_tsquery('english', '"cat rule" or database -Postgres')
هو to_tsquery('english', 'cat <-> rule | database & !Postgres')
.
للاستخدام التالي:
SELECT t.id, t.short_content FROM tweet t WHERE to_tsvector('english', t.short_content) @@ websearch_to_tsquery('english', '"cat rule" or database -Postgres');
نحصل على النتيجة التالية:
دعم Postgres و Hibernate الأصلي
كما ذكر في المقال، لا يمتلك Hibernate دعمًا للبحث النصي الكامل. يجب أن يعتمد على دعم محرك قاعدة البيانات. مما يعني أنه يُسمح لنا بتنفيذ استعلامات SQL أصلية كما هو موضح في الأمثلة التالية:
plainto_tsquery
public List<Tweet> findBySinglePlainQueryInDescriptionForConfigurationWithNativeSQL(String textQuery, String configuration) {
return entityManager.createNativeQuery(String.format("select * from tweet t1_0 where to_tsvector('%1$s', t1_0.short_content) @@ plainto_tsquery('%1$s', :textQuery)", configuration), Tweet.class).setParameter("textQuery", textQuery).getResultList();
}
websearch_to_tsquery
public List<Tweet> findCorrectTweetsByWebSearchToTSQueryInDescriptionWithNativeSQL(String textQuery, String configuration) {
return entityManager.createNativeQuery(String.format("select * from tweet t1_0 where to_tsvector('%1$s', t1_0.short_content) @@ websearch_to_tsquery('%1$s', :textQuery)", configuration), Tweet.class).setParameter("textQuery", textQuery).getResultList();
}
Hibernate مع مكتبة posjsonhelper
مكتبة posjsonhelper هي مشروع مفتوح المصدر يضيف الدعم لاستعلامات Hibernate لـ وظائف PostgreSQL JSON والبحث النصي الكامل.
لمشروع Maven، نحتاج لإضافة المراحل التبعيات التالية:
<dependency>
<groupId>com.github.starnowski.posjsonhelper.text</groupId>
<artifactId>hibernate6-text</artifactId>
<version>0.3.0</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>6.4.0.Final</version>
</dependency>
لاستخدام المكونات الموجودة في مكتبة posjsonhelper
، نحتاج لتسجيلها في سياق Hibernate.
هذا يعني أنه يجب أن يكون هناك تنفيذ محدد لـ org.hibernate.boot.model.FunctionContributor
. المكتبة لديها تنفيذ لهذه الواجهة، وهو com.github.starnowski.posjsonhelper.hibernate6.PosjsonhelperFunctionContributor
.
A file with the name “org.hibernate.boot.model.FunctionContributor” under the “resources/META-INF/services” directory is required to use this implementation.
هناك طريقة أخرى لتسجيل مكونات posjsonhelper
، والتي يمكن القيام بها من خلال البرمجة. لمعرفة كيفية القيام بذلك، تحقق من هذا الرابط.
الآن، يمكننا استخدام عوامل البحث النصي الكامل في استعلامات Hibernate.
PlainToTSQueryFunction
هذا مكون يغلف وظيفة plainto_tsquery.
public List<Tweet> findBySinglePlainQueryInDescriptionForConfiguration(String textQuery, String configuration) {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Tweet> query = cb.createQuery(Tweet.class);
Root<Tweet> root = query.from(Tweet.class);
query.select(root);
query.where(new TextOperatorFunction((NodeBuilder) cb, new TSVectorFunction(root.get("shortContent"), configuration, (NodeBuilder) cb), new PlainToTSQueryFunction((NodeBuilder) cb, configuration, textQuery), hibernateContext));
return entityManager.createQuery(query).getResultList();
}
للتكوين بالقيمة 'english'
، سيولد الكود البيان التالي:
select
t1_0.id,
t1_0.short_content,
t1_0.title
from
tweet t1_0
where
to_tsvector('english', t1_0.short_content) @@ plainto_tsquery('english', ?);
PhraseToTSQueryFunction
يُغلِفُ هذا المكوِّنُ وظيفة phraseto_tsquery.
public List<Tweet> findBySinglePhraseInDescriptionForConfiguration(String textQuery, String configuration) {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Tweet> query = cb.createQuery(Tweet.class);
Root<Tweet> root = query.from(Tweet.class);
query.select(root);
query.where(new TextOperatorFunction((NodeBuilder) cb, new TSVectorFunction(root.get("shortContent"), configuration, (NodeBuilder) cb), new PhraseToTSQueryFunction((NodeBuilder) cb, configuration, textQuery), hibernateContext));
return entityManager.createQuery(query).getResultList();
}
للتكيّفِ بالقيمة 'english'
، سيولد الكود البيان التالي:
select
t1_0.id,
t1_0.short_content,
t1_0.title
from
tweet t1_0
where
to_tsvector('english', t1_0.short_content) @@ phraseto_tsquery('english', ?)
WebsearchToTSQueryFunction
يُغلِفُ هذا المكوِّنُ وظيفة websearch_to_tsquery.
public List<Tweet> findCorrectTweetsByWebSearchToTSQueryInDescription(String phrase, String configuration) {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Tweet> query = cb.createQuery(Tweet.class);
Root<Tweet> root = query.from(Tweet.class);
query.select(root);
query.where(new TextOperatorFunction((NodeBuilder) cb, new TSVectorFunction(root.get("shortContent"), configuration, (NodeBuilder) cb), new WebsearchToTSQueryFunction((NodeBuilder) cb, configuration, phrase), hibernateContext));
return entityManager.createQuery(query).getResultList();
}
للتكيّفِ بالقيمة 'english'
، سيولد الكود البيان التالي:
select
t1_0.id,
t1_0.short_content,
t1_0.title
from
tweet t1_0
where
to_tsvector('english', t1_0.short_content) @@ websearch_to_tsquery('english', ?)
استعلامات HQL
يمكن استخدام جميع المكوِّنات المذكورة في استعلامات HQL. للتحقق من كيفية القيام بذلك، يرجى النقر فوق هذا الرابط.
لماذا نستخدم مكتبة posjsonhelper عندما يمكننا استخدام الطريقة الأصلية مع Hibernate؟
على الرغم من أن جمع سلسلة أحادية اللغة يُفترض أن تكون كود HQL أو SQL قد يكون سهلًا، إلا أن تنفيذ العوازق سيكون ممارسة أفضل، خاصة عندما تضطر إلى التعامل مع معايير البحث المبنية على سمات ديناميكية من واجهة برمجة التطبيقات الخاصة بك.
الخاتمة
كما ذُكرَ في المقالة السابقة، يمكن أن يكون دعم بوستغريس للبحث الكامل النصي خيارًا جيدًا لأجهزة البحث الهامة مثل Elasticsearch أو Lucene، في بعض الحالات. يُمكن أن يوفر لنا من اتخاذ قرار لإضافة حلول ثالثة لمجموعة تكنولوجياتنا، والتي قد تضيف مزيدًا من التعقيد والتكاليف الإضافية.
Source:
https://dzone.com/articles/postgres-full-text-search-with-hibernate-6