Java for Web学习笔记(一二三):搜索(5)MySQL全文索引(下)

小例子

我们在表格Ticket和TicketComment中加入了fulltext key。小例子在Ticket的Subject或Body,以及在TicketComment的Body检索内容,按分页方式显示出来,同时显示关联分数,并按关联分数降序排列。

 -- Ticket中队Subject和Body这两列进行全文检索
 FULLTEXT KEY `Ticket_Search` (`Subject`,`Body`),
 -- TicketComment中对Body列进行全文检索。
 FULLTEXT KEY `TicketComment_Search` (`Body`),

增加一个搜索结果,涵盖TicketEntity和关联分数

根据需求,我们将检索两个表格的内容,获取分数,以检索hello为例子,SQL语句如下
-- 显示Ticket表的内容
SELECT DISTINCT t.*,
   -- 显示关联分数(将两个表格的关联分数加起来),列名为 ft_scoreColumn
   (MATCH(t.Subject, t.Body) AGAINST("hello") + MATCH(c.Body) AGAINST("hello")) AS _ft_scoreColumn 
      -- 将表格Ticket和TicketComment 根据ticket.id join在一起
      FROM Ticket t LEFT OUTER JOIN TicketComment c ON c.TicketId = t.TicketId 
         -- where存在关联性
         WHERE MATCH(t.Subject, t.Body) AGAINST("hello") OR  MATCH(c.Body) AGAINST("hello")
            -- 设置排序
            ORDER BY _ft_scoreColumn DESC, TicketId DESC;

在关联表格的返回中,通常会包含多个内容,并不是只与某个Entity相对应,在本例中就含有分值,我们将学习如何映射这样的结果。

创建存放结果的类SearchResult
public class SearchResult<T> {
	private final T entity;
	private final double relevance;
	public SearchResult(T entity, double relevance) {
		this.entity = entity;
		this.relevance = relevance;
	}
	public T getEntity() {
		return entity;
	}
	public double getRelevance() {
		return relevance;
	}	
}
@SqlResultSetMapping提供返回结果的映射关系

我们将这个映射关系命名为"searchResultMapping.ticket",放在TicketEntity中,当然也可以放在其他的Class,只要标记@SqlResultSetMapping即可。

 @Entity
 @Table(name = "Ticket")
 @SqlResultSetMapping(
     name = "searchResultMapping.ticket",
     entities = { @EntityResult(entityClass = TicketEntity.class) },
     columns = { @ColumnResult(name = "_ft_scoreColumn", type = Double.class)}
 )
 public class TicketEntity implements Serializable

这种方式等同与xml的配置。

<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http://xmlns.jcp.org/xml/ns/persistence/orm" 
                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                 xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence/orm
                                     http://xmlns.jcp.org/xml/ns/persistence/orm_2_1.xsd"
                 version="2.1">
    <sql-result-set-mapping name="searchResultMapping.ticket">
        <entity-result entity-class="com.wrox.site.entities.TicketEntity" />
        <column-result name="_ft_scoreColumn" class="java.lang.Double" />
    </sql-result-set-mapping>
</entity-mappings>

这个配置一般位于/META-INF/orm.xml。也可以在persistence.xml中通过<mapping-file>来执行位置,或者通过下面的代码来执行位置。

@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(){
    ...
    factory.setJpaPropertyMap(properties);
    factory.setMappingResources("com/example/config/mappings.xml");
    return factory;
}
在createNativeQuery中将结果进行映射

我们使用了原生的SQL语句,映射方式如下,例子的具体实现在后面介绍

Query query = entityManager.createNativeQuery(sql, "searchResultMapping.ticket");
List<Object[]> results = query.getResultList();
for(Object[] result : results){
    //result[0]对应searchResultMapping.ticket的第一项entites里面的TicketEntity.class
    //result[1]对应searchResultMapping.ticket的第二项,因为entities只有一项,因此为columns中的name = "_ft_scoreColumn", type = Double.class
}

在仓库中增加查询接口

public interface SearchableRepository<T>{
    Page<SearchResult<T>> search(String query, boolean useBooleanMode, Pageable pageable);
}
public interface TicketRepository extends CrudRepository<TicketEntity, Long>,SearchableRepository<TicketEntity>{
}

接口实现

public class TicketRepositoryImpl implements SearchableRepository<TicketEntity>{
	@PersistenceContext EntityManager entityManager;
	
	@Override
	public Page<SearchResult<TicketEntity>> search(String query, boolean useBooleanMode, Pageable pageable) {
		String mode = useBooleanMode ?	"IN BOOLEAN MODE" : "IN NATURAL LANGUAGE MODE";
		String matchTicket = "MATCH(t.Subject, t.Body) AGAINST(?1 " + mode + ")";
		String matchComment = "MATCH(c.Body) AGAINST(?1 " + mode + ")";
		
		//1】分页需要获得总数以及该页的数据,显示获取总数。请参考前面对sql的说明。
		String sql = "SELECT COUNT(DISTINCT t.TicketId) FROM Ticket t " +
						"LEFT OUTER JOIN TicketComment c ON c.TicketId = " +
						"t.TicketId WHERE " + matchTicket + " OR " + matchComment;
		//对于原生SQL的方式,返回结果是BigInteger不能直接转换为Long。采用了Number来进行。
		long total = ((Number) this.entityManager.createNativeQuery(sql).setParameter(1, query).getSingleResult())
				.longValue();
		
		//2】获取该页的信息,
		sql = "SELECT DISTINCT t.*, (" + matchTicket + " + " + matchComment +") AS _ft_scoreColumn " + 
				"FROM Ticket t LEFT OUTER JOIN TicketComment c ON c.TicketId = t.TicketId " +
				"WHERE " + matchTicket + " OR " + matchComment + " " +
				"ORDER BY _ft_scoreColumn DESC, TicketId DESC";
		@SuppressWarnings("unchecked")
		List<Object[]> results = this.entityManager.createNativeQuery(sql, "searchResultMapping.ticket")
			.setParameter(1, query)
				.setFirstResult(pageable.getOffset())
				.setMaxResults(pageable.getPageSize())
					.getResultList();

		//3】将结果转为我们定义SearchResult。
		List<SearchResult<TicketEntity>> list = new ArrayList<>();
		results.forEach(o -> list.add(
		                        new SearchResult<TicketEntity>((TicketEntity)o[0], (Double)o[1])));
		
		return new PageImpl<>(list,pageable,total);
	}
}
使用createNativeQuery而不是criteria JPA接口意味着实现和底层和数据库相关,如果更换为其他数据库,需要重新编写代码,而有些数据库支持fulltext key有些不支持。


相关链接:我的Professional Java for Web Applications相关文章

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在Java中实现Elasticsearch的插入操作,您需要导入相应的包,并创建一个RestHighLevelClient对象来与Elasticsearch进行通信。下面是一个示例代码来演示如何实现插入操作: ```java import org.apache.http.HttpHost; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.client.RequestOptions; import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestHighLevelClient; import org.elasticsearch.common.xcontent.XContentType; import java.io.IOException; public class ESTestInsert { public static void main(String[] args) throws IOException { // 创建ES客户端 RestHighLevelClient esClient = new RestHighLevelClient( RestClient.builder(new HttpHost("localhost", 9200, "http")) ); // 创建索引请求对象 IndexRequest request = new IndexRequest("user"); request.id("1"); // 设置文档ID // 设置文档内容,这里使用JSON格式 String jsonString = "{\"name\":\"John Doe\",\"age\":30,\"city\":\"New York\"}"; request.source(jsonString, XContentType.JSON); // 发送请求并获取响应 esClient.index(request, RequestOptions.DEFAULT); // 关闭ES客户端 esClient.close(); } } ``` 请注意,上述示例中的代码假定您已经在本地主机上运行了Elasticsearch,并且在默认端口9200上可用。您还需要将相应的包依赖项添加到您的项目中。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [JAVA elasticsearch批量插入](https://blog.csdn.net/qq_38403590/article/details/129798715)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [Elasticsearch——使用Java API实现ES中的索引、映射、文档操作](https://blog.csdn.net/weixin_43823808/article/details/119930308)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值