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

في هذا المنشور ، سأتحدث باختصار عن الأدوات التي يمكن أن تساعد في حل هذه المشاكل. ثم سأقدم مشروعًا تجريبيًا لخدمة البحث ، حيث يتم تنفيذ ميزة أكثر إثارة للاهتمام وتعقيدًا - مزامنة الكيانات وقواعد البيانات وفهرس البحث. باستخدام مثال هذا المشروع التجريبي ، يمكنك التعرف على Hibernate Search ، وهي طريقة ملائمة للتواصل مع فهارس النص الكامل Solr ، Lucene ، ElasticSearch.
من بين أدوات نشر محركات البحث ، سأختار ثلاثة منها.
Lucene هي مكتبة جافا توفر واجهة قاعدة بيانات منخفضة المستوى للبحث عن النص الكامل. بمساعدته ، يمكنك إنشاء فهارس وملئها بالسجلات (المستندات). اقرأ المزيد عن Lucene
هنا .
Solr هو المنتج البرمجي النهائي القائم على Lucene ، وقاعدة بيانات النص الكامل ، وخادم ويب منفصل ومستقل. يحتوي على واجهة http للفهرسة واستعلامات النص الكامل ، ويسمح لك بفهرسة المستندات والبحث عنها. يحتوي Solr على واجهة برمجة تطبيقات بسيطة وواجهة مستخدم مدمجة ، مما يلغي الحاجة إلى المعالجة اليدوية للفهارس. في حبري كان هناك
تحليل مقارن جيد لسولر ولوسين.
مطاطية البحث هو
نظير أكثر حداثة من Solr. كما أنها تستند إلى Apache Lucene. مقارنةً بـ Solr ، يمكن لـ ElasticSearch أن يتحمل الأحمال الأعلى عند فهرسة المستندات ، وبالتالي يمكن استخدامه لفهرسة ملفات السجل. على الشبكة ، يمكنك العثور على
جدول مفصل يقارن Solr و ElasticSearch.
هذه بالطبع ليست قائمة كاملة ؛ أعلاه ، اخترت فقط تلك الأنظمة التي تستحق أكبر قدر من الاهتمام. هناك الكثير من الأنظمة لتنظيم البحث. PostgreSQL لديه إمكانيات البحث عن النص الكامل ؛ لا تنسى أبو الهول.
المشكلة الرئيسية
نحن ننتقل إلى الشيء الرئيسي. لتخزين بيانات موثوق / متناسق ، عادة ما يتم استخدام RDB (قاعدة بيانات علائقية). يوفر المعاملات وفقا لمبادئ ACID. لكي يعمل محرك البحث ، يتم استخدام الفهرس لإضافة الكيانات وحقول الجداول التي سيتم إجراء البحث من خلالها. أي ، عندما يدخل كائن جديد إلى النظام ، يجب حفظه في كل من قاعدة البيانات العلائقية وفي فهرس النص الكامل.
إذا لم يتم تنظيم طبيعة المعاملات لهذه التغييرات داخل التطبيق الخاص بك ، فقد تحدث أنواع مختلفة من عدم التزامن. على سبيل المثال ، حددت من قاعدة بيانات ، لكن هذا الكائن غير موجود في الفهرس. أو العكس: يوجد سجل كائن في الفهرس ، وتم حذفه من RDB.
هناك عدة طرق لحل هذه المشكلة. يمكنك تنظيم تغييرات المعاملات يدويًا باستخدام آليات JTA
وإدارة معاملات الربيع . أو يمكنك أن تذهب بطريقة أكثر إثارة للاهتمام - استخدم Hibernate Search ، والتي ستفعل كل ذلك بمفردها. بشكل افتراضي ، يتم استخدام Lucene ، الذي يخزن بيانات الفهرس داخل نظام الملفات ، بشكل عام ، يتم تكوين الاتصال بالفهرس. عند بدء تشغيل النظام ، تبدأ طريقة المزامنة startAndWait () ، وأثناء تشغيل النظام ، سيتم تخزين السجلات في RDB والفهرس.
لتوضيح هذا الحل ، قمت بإعداد مشروع تجريبي باستخدام Hibernate Search. سننشئ خدمة تحتوي على طرق لقراءة وتحديث وتحديث المستخدمين. يمكن أن تشكل أساسًا لقاعدة بيانات داخلية مع إمكانية البحث عن نص كامل بالاسم الأول أو الاسم الأخير أو بيانات تعريف أخرى. للتفاعل مع قواعد البيانات العلائقية ، نستخدم
إطار عمل Spring Data Jpa .
لنبدأ بفئة الكيان لتمثيل المستخدم:
import org.hibernate.search.annotations.Field import org.hibernate.search.annotations.Indexed import javax.persistence.Entity import javax.persistence.Id import javax.persistence.Table @Entity @Table(name = "users") @Indexed internal data class User( @Id val id: Long, @Field val name: String, @Field val surname: String, @Field val phoneNumber: String)
كل شيء قياسي ، نشير إلى الكيان بجميع التعليقات التوضيحية اللازمة لبيانات الربيع. باستخدام
الكيان ، نشير إلى الكيان ، باستخدام
الجدول ، نشير إلى التسمية في قاعدة البيانات. يشير التعليق
المفهرس إلى أن الكيان قابل للفهرسة وسيقع في فهرس نص كامل.
مطلوب مستودع JPA لعمليات CRUD على المستخدمين في قاعدة البيانات:
internal interface UserRepository: JpaRepository<User, Long>
خدمة للعمل مع المستخدمين ، UserService.java:
import org.springframework.stereotype.Service import javax.transaction.Transactional @Service @Transactional internal class UserService(private val userRepository: UserRepository, private val userSearch: UserSearch) { fun findAll(): List<User> { return userRepository.findAll() } fun search(text: String): List<User> { return userSearch.searchUsers(text) } fun saveUser(user: User): User { return userRepository.save(user) } }
يحصل FindAll على جميع المستخدمين مباشرة من قاعدة البيانات. يستخدم البحث مكون userSearch لاسترداد المستخدمين من الفهرس. مكون للعمل مع فهرس بحث المستخدم:
@Repository @Transactional internal class UserSearch(@PersistenceContext val entityManager: EntityManager) { fun searchUsers(text: String): List<User> { // fullTextEntityManager, entityManager val fullTextEntityManager = org.hibernate.search.jpa.Search.getFullTextEntityManager(entityManager) // Hibernate Search query DSL val queryBuilder = fullTextEntityManager.searchFactory .buildQueryBuilder().forEntity(User::class.java).get() // , val query = queryBuilder .keyword() .onFields("name") .matching(text) .createQuery() // Lucene Query Hibernate Query object val jpaQuery: FullTextQuery = fullTextEntityManager.createFullTextQuery(query, User::class.java) // return jpaQuery.resultList.map { result -> result as User }.toList() } }
وحدة تحكم REST ، UserController.java:
import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RestController import java.util.* @RestController internal class UserController(private val userService: UserService) { @GetMapping("/users") fun getAll(): List<User> { return userService.findAll() } @GetMapping("/users/search") fun search(text: String): List<User> { return userService.search(text) } @PostMapping("/users") fun insertUser(@RequestBody user: User): User { return userService.saveUser(user) } }
نستخدم طريقتين للاستخراج من قاعدة البيانات والبحث بالسلسلة.
قبل تشغيل التطبيق ، من الضروري تهيئة الفهرس ، نقوم بذلك باستخدام ApplicationListener.
package ru.rti import org.hibernate.search.jpa.Search import org.springframework.boot.context.event.ApplicationReadyEvent import org.springframework.context.ApplicationListener import org.springframework.stereotype.Component import javax.persistence.EntityManager import javax.persistence.PersistenceContext import javax.transaction.Transactional @Component @Transactional class BuildSearchService( @PersistenceContext val entityManager: EntityManager) : ApplicationListener<ApplicationReadyEvent> { override fun onApplicationEvent(event: ApplicationReadyEvent?) { try { val fullTextEntityManager = Search.getFullTextEntityManager(entityManager) fullTextEntityManager.createIndexer().startAndWait() } catch (e: InterruptedException) { println("An error occurred trying to build the search index: " + e.toString()) } } }
للاختبار استخدمنا PostgreSQL:
spring.datasource.url=jdbc:postgresql:users spring.datasource.username=postgres spring.datasource.password=postgres spring.datasource.driver-class-name=org.postgresql.Driver spring.datasource.name=users
أخيرًا ،
build.gradle
:
buildscript { ext.kotlin_version = '1.2.61' ext.spring_boot_version = '1.5.15.RELEASE' repositories { jcenter() } dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-allopen:$kotlin_version" classpath "org.springframework.boot:spring-boot-gradle-plugin:$spring_boot_version" classpath "org.jetbrains.kotlin:kotlin-noarg:$kotlin_version" } } apply plugin: 'kotlin' apply plugin: "kotlin-spring" apply plugin: "kotlin-jpa" apply plugin: 'org.springframework.boot' noArg { invokeInitializers = true } jar { baseName = 'gs-rest-service' version = '0.1.0' } repositories { jcenter() } dependencies { compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" compile 'org.springframework.boot:spring-boot-starter-web' compile 'org.springframework.boot:spring-boot-starter-data-jpa' compile group: 'postgresql', name: 'postgresql', version: '9.1-901.jdbc4' compile group: 'org.hibernate', name: 'hibernate-core', version: '5.3.6.Final' compile group: 'org.hibernate', name: 'hibernate-search-orm', version: '5.10.3.Final' compile group: 'com.h2database', name: 'h2', version: '1.3.148' testCompile('org.springframework.boot:spring-boot-starter-test') }
هذا العرض التوضيحي هو مثال بسيط على استخدام تقنية Hibernate Search ، والتي يمكنك من خلالها فهم كيفية تكوين صداقات Apache Lucene و Spring Data Jpa. إذا لزم الأمر ، يمكن توصيل المشاريع القائمة على هذا العرض التوضيحي بـ Apache Solr أو ElasticSearch. الاتجاه المحتمل للمشروع هو البحث عن فهارس كبيرة (> 10 جيجا بايت) وقياس الأداء فيها. يمكنك إنشاء تكوينات للبحث المرن أو تكوينات فهرس أكثر تعقيدًا من خلال استكشاف إمكانيات البحث في وضع الإسبات على مستوى أعمق.
روابط مفيدة: