如何使用apache lucene实现嵌套深层嵌套对象的全文搜索?在 Spring 引导项目中
How to achieve full text search for nested deep nested objects using apache lucene? in a Spring boot project
我是 apache lucene 或任何其他 fts 引擎的新手,对此深感抱歉
我已经成功地实现了 apache lucene 和 hibernate search orm 但是当谈到嵌套关系时,只要我理解并且到目前为止已经尝试过,它只能向下一层工作。所以我可以实现搜索超过两层的嵌套对象,如下所示
FTS should be performed on this model, specifically fields User and UserSubCategory
@Entity
@org.hibernate.search.annotations.Indexed
class SubCategoryUserManyToMany(
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "user_id", nullable = false)
@IndexedEmbedded
var user: User? = null,
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "category_id", nullable = false)
@IndexedEmbedded
var subCategory: UserSubCategory? = null,
@Field(index = Index.NO)
var jobType: JobType?= JobType.HOURLY,
@Field(index = Index.NO)
var price: Long = 0
): BaseModel()
I am doing fts on two fields of User model, firstname and lastname. It is working just fine
@Entity
@Table(name = "users")
class User() : BaseModel() {
@Column(unique = true, name = "email")
@NotBlank
@Email
var email: String? = null
@Column(name = "firstname")
@Size(max = 100)
@Field
var firstname: String? = null
@Column(name = "lastname")
@Size(max = 100)
@Field
var lastname: String? = null
@Column(name = "midname")
@Size(max = 100)
var midname: String? = null
@Column(name = "password")
@Size(max = 100)
var password: String? = null
@NotBlank
@Column(name = "phone", unique = true)
@Size(max = 100)
var phone: String? = null
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "user_roles",
joinColumns = [JoinColumn(name = "user_id", referencedColumnName = "id")],
inverseJoinColumns = [JoinColumn(name = "role_id", referencedColumnName = "id")])
var roles: MutableList<Role> = mutableListOf()
//TODO CATEGORY RELATIONSHIPS
@OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
@ContainedIn
var subCategoryUserManyToMany: MutableList<SubCategoryUserManyToMany> = mutableListOf()
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "user_images_many_to_many",
joinColumns = [JoinColumn(name = "user_id", referencedColumnName = "id")],
inverseJoinColumns = [JoinColumn(name = "attachment_id", referencedColumnName = "id")])
var images: MutableList<Attachment> = mutableListOf()
constructor(id: Long) : this() {
this.id = id
}
}
However when it comes to UserSubCategory which is
@Entity
class UserSubCategory (
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "category_id", nullable = false)
var category: UserCategory? = null,
@OneToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "title_id", nullable = false)
var title: TextValueShort? = null,
@OneToMany(mappedBy = "subCategory", fetch = FetchType.LAZY)
@ContainedIn
var subCategoryUserManyToMany: MutableList<SubCategoryUserManyToMany> = mutableListOf(),
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "master_subcategory_job_descriptions",
joinColumns = [JoinColumn(name = "sub_cat_id", referencedColumnName = "id")],
inverseJoinColumns = [JoinColumn(name = "text_id", referencedColumnName = "id")])
var whatShouldBeDoneList: MutableList<TextValueMedium> = mutableListOf()
) : BaseModel(){
constructor(id: Long): this(){
this.id = id
}
}
As you can see I have title field which is another relationship on which FTS should be done
Model
@Entity
@Table(name = "txt_value_short")
class TextValueShort {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
var id: Long? = null
@Size(max=150)
var ru : String? = null
@Size(max=150)
var en : String? = null
@Size(max=150)
var uz : String? = null
}
This is my search function
override fun masterSearch(searchTerm: String, fields: Array<String>, pageNo: Int, resultsPerPage: Int): Page<SubCategoryUserManyToMany> {
val fullTextEntityManager = Search.getFullTextEntityManager(entityManager)
val qb = fullTextEntityManager.searchFactory.buildQueryBuilder().forEntity(SubCategoryUserManyToMany::class.java).get()
val luceneQuery: org.apache.lucene.search.Query = qb
.bool()
.should(qb.keyword().onFields("user.firstname", "user.lastname", "subCategory.title.en").matching(searchTerm).createQuery())
.should(qb.keyword().wildcard().onFields("user.firstname", "user.lastname", "subCategory.title.en").matching("*$searchTerm*").createQuery())
.createQuery()
val jpaQuery = fullTextEntityManager.createFullTextQuery(luceneQuery, SubCategoryUserManyToMany::class.java)
jpaQuery.maxResults = resultsPerPage
jpaQuery.firstResult = (pageNo) * resultsPerPage
var result = listOf<SubCategoryUserManyToMany>()
try {
result = jpaQuery.resultList as List<SubCategoryUserManyToMany>
}catch (ex: Exception){}
val page = PageRequest(pageNo, resultsPerPage )
return PageImpl(result, page, result.size.toLong())
}
你可以看到我有一个 "subCategory.title.en" 抛出异常
我在 hibernate-search 标签下搜索时找到了解决我的问题的方法,有一个类似的情况,应该为三层嵌套关系建立索引,但我的只有 2 层,它起作用了,感谢这个人谁回答关于休眠搜索的每一个问题
Link:
UseSubCategory.title
未编入索引。
要对其进行索引,请在 属性 上添加 @IndexedEmbedded
,就像对 SubCategoryUserManyToMany.title
所做的一样,并在 TextValueShort
的各种属性上添加 @Field
注释(en
, ru
, ...).
不过有一个警告:由于 TextValueShort
没有对 UseSubCategory
的反向引用,您不能使用 @ContainedIn
。这意味着对 TextValueShort
的更改不会触发 UseSubCategory
和 SubCategoryUserManyToMany
.
的重新索引
这个问题有三种解决方法:
- 可能是最好的解决方案,如果您可以更改数据库模式:将
TextValueShort
变成 @Embeddable
而不是 @Entity
。在这种情况下,Hibernate Search 将很好地处理重建索引。
- 可能不可以:在
TextValueShort
中添加一个UserSubCategory userSubCategory
属性并用@OneToOne(mappedBy = "title)
和@ContainedIn
注释。
- 每次更改标题时手动处理重新索引。参见 https://docs.jboss.org/hibernate/search/5.11/reference/en-US/html_single/#_adding_instances_to_the_index
我是 apache lucene 或任何其他 fts 引擎的新手,对此深感抱歉 我已经成功地实现了 apache lucene 和 hibernate search orm 但是当谈到嵌套关系时,只要我理解并且到目前为止已经尝试过,它只能向下一层工作。所以我可以实现搜索超过两层的嵌套对象,如下所示
FTS should be performed on this model, specifically fields User and UserSubCategory
@Entity
@org.hibernate.search.annotations.Indexed
class SubCategoryUserManyToMany(
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "user_id", nullable = false)
@IndexedEmbedded
var user: User? = null,
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "category_id", nullable = false)
@IndexedEmbedded
var subCategory: UserSubCategory? = null,
@Field(index = Index.NO)
var jobType: JobType?= JobType.HOURLY,
@Field(index = Index.NO)
var price: Long = 0
): BaseModel()
I am doing fts on two fields of User model, firstname and lastname. It is working just fine
@Entity
@Table(name = "users")
class User() : BaseModel() {
@Column(unique = true, name = "email")
@NotBlank
@Email
var email: String? = null
@Column(name = "firstname")
@Size(max = 100)
@Field
var firstname: String? = null
@Column(name = "lastname")
@Size(max = 100)
@Field
var lastname: String? = null
@Column(name = "midname")
@Size(max = 100)
var midname: String? = null
@Column(name = "password")
@Size(max = 100)
var password: String? = null
@NotBlank
@Column(name = "phone", unique = true)
@Size(max = 100)
var phone: String? = null
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "user_roles",
joinColumns = [JoinColumn(name = "user_id", referencedColumnName = "id")],
inverseJoinColumns = [JoinColumn(name = "role_id", referencedColumnName = "id")])
var roles: MutableList<Role> = mutableListOf()
//TODO CATEGORY RELATIONSHIPS
@OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
@ContainedIn
var subCategoryUserManyToMany: MutableList<SubCategoryUserManyToMany> = mutableListOf()
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "user_images_many_to_many",
joinColumns = [JoinColumn(name = "user_id", referencedColumnName = "id")],
inverseJoinColumns = [JoinColumn(name = "attachment_id", referencedColumnName = "id")])
var images: MutableList<Attachment> = mutableListOf()
constructor(id: Long) : this() {
this.id = id
}
}
However when it comes to UserSubCategory which is
@Entity
class UserSubCategory (
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "category_id", nullable = false)
var category: UserCategory? = null,
@OneToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "title_id", nullable = false)
var title: TextValueShort? = null,
@OneToMany(mappedBy = "subCategory", fetch = FetchType.LAZY)
@ContainedIn
var subCategoryUserManyToMany: MutableList<SubCategoryUserManyToMany> = mutableListOf(),
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "master_subcategory_job_descriptions",
joinColumns = [JoinColumn(name = "sub_cat_id", referencedColumnName = "id")],
inverseJoinColumns = [JoinColumn(name = "text_id", referencedColumnName = "id")])
var whatShouldBeDoneList: MutableList<TextValueMedium> = mutableListOf()
) : BaseModel(){
constructor(id: Long): this(){
this.id = id
}
}
As you can see I have title field which is another relationship on which FTS should be done Model
@Entity
@Table(name = "txt_value_short")
class TextValueShort {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
var id: Long? = null
@Size(max=150)
var ru : String? = null
@Size(max=150)
var en : String? = null
@Size(max=150)
var uz : String? = null
}
This is my search function
override fun masterSearch(searchTerm: String, fields: Array<String>, pageNo: Int, resultsPerPage: Int): Page<SubCategoryUserManyToMany> {
val fullTextEntityManager = Search.getFullTextEntityManager(entityManager)
val qb = fullTextEntityManager.searchFactory.buildQueryBuilder().forEntity(SubCategoryUserManyToMany::class.java).get()
val luceneQuery: org.apache.lucene.search.Query = qb
.bool()
.should(qb.keyword().onFields("user.firstname", "user.lastname", "subCategory.title.en").matching(searchTerm).createQuery())
.should(qb.keyword().wildcard().onFields("user.firstname", "user.lastname", "subCategory.title.en").matching("*$searchTerm*").createQuery())
.createQuery()
val jpaQuery = fullTextEntityManager.createFullTextQuery(luceneQuery, SubCategoryUserManyToMany::class.java)
jpaQuery.maxResults = resultsPerPage
jpaQuery.firstResult = (pageNo) * resultsPerPage
var result = listOf<SubCategoryUserManyToMany>()
try {
result = jpaQuery.resultList as List<SubCategoryUserManyToMany>
}catch (ex: Exception){}
val page = PageRequest(pageNo, resultsPerPage )
return PageImpl(result, page, result.size.toLong())
}
你可以看到我有一个 "subCategory.title.en" 抛出异常
我在 hibernate-search 标签下搜索时找到了解决我的问题的方法,有一个类似的情况,应该为三层嵌套关系建立索引,但我的只有 2 层,它起作用了,感谢这个人谁回答关于休眠搜索的每一个问题
Link:
UseSubCategory.title
未编入索引。
要对其进行索引,请在 属性 上添加 @IndexedEmbedded
,就像对 SubCategoryUserManyToMany.title
所做的一样,并在 TextValueShort
的各种属性上添加 @Field
注释(en
, ru
, ...).
不过有一个警告:由于 TextValueShort
没有对 UseSubCategory
的反向引用,您不能使用 @ContainedIn
。这意味着对 TextValueShort
的更改不会触发 UseSubCategory
和 SubCategoryUserManyToMany
.
这个问题有三种解决方法:
- 可能是最好的解决方案,如果您可以更改数据库模式:将
TextValueShort
变成@Embeddable
而不是@Entity
。在这种情况下,Hibernate Search 将很好地处理重建索引。 - 可能不可以:在
TextValueShort
中添加一个UserSubCategory userSubCategory
属性并用@OneToOne(mappedBy = "title)
和@ContainedIn
注释。 - 每次更改标题时手动处理重新索引。参见 https://docs.jboss.org/hibernate/search/5.11/reference/en-US/html_single/#_adding_instances_to_the_index