`
SonofGod
  • 浏览: 20752 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

solr

 
阅读更多

 

转自:http://blog.sina.com.cn/s/blog_466678e80100l2an.html

 

 

Solrj已经是很强大的solr客户端了。它本身就包装了httpCliet,以完全对象的方式对solr进行交互。很小很好很强大。
不过在实际使用中,设置SolrQuery 的过程中,为了设置多个搜索条件和排序规则等等参数,我们往往会陷入并接字符串的地步,实在是很丑陋,不符合面向对象的思想。扩展性几乎为0,。基于这点,开发了一个小东西,我们只需要设置搜索对象,将对象扔给后台就可以了。
比如,我们搭建的solr服务支持某10个字段的搜索,我们要搜索其中的一些,那么我们只需要传入要搜索的对象POJO,将要搜索的字段内容,set到POJO对象对应额字段即可。
比如如下一个类:
Java代码
package org.uppower.tnt.biz.core.manager.blog.dataobject;


public class SolrPropertyDO {
private String auction_id;
private String opt_tag;
private String exp_tag;
private String title;
private String desc;
private String brand;
private String category;
private String price;
private String add_prov;
private String add_city;
private String quality;
private String flag;
private String sales;
private String sellerrate;
private String selleruid;
private String ipv15;

public String getAuction_id() {
return auction_id;
}

public void setAuction_id(String auctionId) {
auction_id = auctionId;
}

……

public String getExp_tag() {
return exp_tag;
}

public void setExp_tag(String expTag) {
exp_tag = expTag;
}
}
package org.uppower.tnt.biz.core.manager.blog.dataobject;

public class SolrPropertyDO {
private String auction_id;
private String opt_tag;
private String exp_tag;
private String title;
private String desc;
private String brand;
private String category;
private String price;
private String add_prov;
private String add_city;
private String quality;
private String flag;
private String sales;
private String sellerrate;
private String selleruid;
private String ipv15;
public String getAuction_id() {
return auction_id;
}
public void setAuction_id(String auctionId) {
auction_id = auctionId;
}
……
public String getExp_tag() {
return exp_tag;
}
public void setExp_tag(String expTag) {
exp_tag = expTag;
}
}
那么我们在定义搜索对象时候,就按照如下设置:
Java代码
1. SolrPropertyDO propertyDO = new SolrPropertyDO();
2. propertyDO.setAdd_city("(杭州AND成都)OR北京");
3. propertyDO.setTitle("丝绸OR剪刀");
4. ……
SolrPropertyDO propertyDO = new SolrPropertyDO();
propertyDO.setAdd_city("(杭州AND成都)OR北京");
propertyDO.setTitle("丝绸OR剪刀");
……
设置排序条件,也是类似的做法:
Java代码
1. SolrPropertyDO compositorDO = new SolrPropertyDO();
2. compositorDO.setPrice ("desc");
3. compositorDO.setQuality ("asc");
4. ……
SolrPropertyDO compositorDO = new SolrPropertyDO();
compositorDO.setPrice ("desc");
compositorDO.setQuality ("asc");
……
将定义好的两个对象扔给后面的接口就可以了。
接口函数querySolrResult传入四个参数,其中包含搜索字段对象,排序条件对象。为了提供类似limit的操作,用于分页查询,提供了startIndex和pageSize。
函数querySolrResultCount是单纯为了获得搜索条数,配合分页使用。
以下是定义的接口:
Java代码
package org.uppower.tnt.biz.core.manager.blog;

import java.util.List;

import org.uppower.tnt.biz.core.manager.isearch.dataobject.SolrPropertyDO;


public interface SolrjOperator {


public List<Object> querySolrResult(Object propertyDO,
Object compositorDO, Long startIndex, Long pageSize)
throws Exception;


public Long querySolrResultCount(SolrPropertyDO propertyDO,
Object compositorDO) throws Exception;

}
package org.uppower.tnt.biz.core.manager.blog;
import java.util.List;
import org.uppower.tnt.biz.core.manager.isearch.dataobject.SolrPropertyDO;

public interface SolrjOperator {

public List<Object> querySolrResult(Object propertyDO,
Object compositorDO, Long startIndex, Long pageSize)
throws Exception;

public Long querySolrResultCount(SolrPropertyDO propertyDO,
Object compositorDO) throws Exception;
}

实现逻辑为,首先将传入的两个实体对象,解析为<K,V>结构的Map当中,将解析完成的Map放入solrj实际的搜索对象当中。返回的对象为solrj的API提供的SolrDocument,其中结果数量为直接返回SolrDocumentList对象的getNumFound()
具体实现类:
Java代码
package org.uppower.tnt.biz.core.manager.blog;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import org.apache.solr.common.SolrDocumentList;

import org.uppower.tnt.biz.core.manager.isearch.common.SolrjCommonUtil;
import org.uppower.tnt.biz.core.manager.isearch.dataobject.SolrPropertyDO;
import org.uppower.tnt.biz.core.manager.isearch.solrj.SolrjQuery;


public class DefaultSolrOperator implements SolrjOperator {

private Logger logger = LoggerFactory.getLogger(this.getClass());
private SolrjQuery solrjQuery;

public void setSolrjQuery(SolrjQuery solrjQuery) {
this.solrjQuery = solrjQuery;
}

@Override
public List<Object> querySolrResult(Object propertyDO,
Object compositorDO, Long startIndex, Long pageSize)
throws Exception {
Map<String, String> propertyMap = new TreeMap<String, String>();
//排序有顺序,使用TreeMap
Map<String, String> compositorMap = new TreeMap<String, String>();
try {
propertyMap = SolrjCommonUtil.getSearchProperty(propertyDO);
compositorMap = SolrjCommonUtil.getSearchProperty(compositorDO);
} catch (Exception e) {
logger.error("SolrjCommonUtil.getSearchProperty() is error !"+ e);
}
SolrDocumentList solrDocumentList = solrjQuery.query(propertyMap, compositorMap,
startIndex, pageSize);
List<Object> resultList = new ArrayList<Object>();
for (int i = 0; i < solrDocumentList.size(); i++) {
resultList.add(solrDocumentList.get(i));
}
return resultList;
}

@Override
public Long querySolrResultCount(SolrPropertyDO propertyDO,
Object compositorDO) throws Exception {
Map<String, String> propertyMap = new TreeMap<String, String>();
Map<String, String> compositorMap = new TreeMap<String, String>();
try {
propertyMap = SolrjCommonUtil.getSearchProperty(propertyDO);
compositorMap = SolrjCommonUtil.getSearchProperty(compositorDO);
} catch (Exception e) {
logger.error("SolrjCommonUtil.getSearchProperty() is error !" + e);
}
SolrDocumentList solrDocument = solrjQuery.query(propertyMap, compositorMap,
null, null);
return solrDocument.getNumFound();
}

}
package org.uppower.tnt.biz.core.manager.blog;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import org.apache.solr.common.SolrDocumentList;
import org.uppower.tnt.biz.core.manager.isearch.common.SolrjCommonUtil;
import org.uppower.tnt.biz.core.manager.isearch.dataobject.SolrPropertyDO;
import org.uppower.tnt.biz.core.manager.isearch.solrj.SolrjQuery;

public class DefaultSolrOperator implements SolrjOperator {

private Logger logger = LoggerFactory.getLogger(this.getClass());
private SolrjQuery solrjQuery;
public void setSolrjQuery(SolrjQuery solrjQuery) {
this.solrjQuery = solrjQuery;
}
@Override
public List<Object> querySolrResult(Object propertyDO,
Object compositorDO, Long startIndex, Long pageSize)
throws Exception {
Map<String, String> propertyMap = new TreeMap<String, String>();
//排序有顺序,使用TreeMap
Map<String, String> compositorMap = new TreeMap<String, String>();
try {
propertyMap = SolrjCommonUtil.getSearchProperty(propertyDO);
compositorMap = SolrjCommonUtil.getSearchProperty(compositorDO);
} catch (Exception e) {
logger.error("SolrjCommonUtil.getSearchProperty() is error !"+ e);
}
SolrDocumentList solrDocumentList = solrjQuery.query(propertyMap, compositorMap,
startIndex, pageSize);
List<Object> resultList = new ArrayList<Object>();
for (int i = 0; i < solrDocumentList.size(); i++) {
resultList.add(solrDocumentList.get(i));
}
return resultList;
}
@Override
public Long querySolrResultCount(SolrPropertyDO propertyDO,
Object compositorDO) throws Exception {
Map<String, String> propertyMap = new TreeMap<String, String>();
Map<String, String> compositorMap = new TreeMap<String, String>();
try {
propertyMap = SolrjCommonUtil.getSearchProperty(propertyDO);
compositorMap = SolrjCommonUtil.getSearchProperty(compositorDO);
} catch (Exception e) {
logger.error("SolrjCommonUtil.getSearchProperty() is error !" + e);
}
SolrDocumentList solrDocument = solrjQuery.query(propertyMap, compositorMap,
null, null);
return solrDocument.getNumFound();
}
}

其中,对象的解析式利用反射原理,将实体对象中不为空的值,以映射的方式,转化为一个Map,其中排序对象在转化的过程中,使用TreeMap,保证其顺序性。
解析公共类实现如下:
Java代码
package org.uppower.tnt.biz.core.manager.blog.common;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;


public class SolrjCommonUtil {

public static Map<String, String> getSearchProperty(Object model)
throws NoSuchMethodException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException {
Map<String, String> resultMap = new TreeMap<String, String>();
// 获取实体类的所有属性,返回Field数组
Field[] field = model.getClass().getDeclaredFields();
for (int i = 0; i < field.length; i++) { // 遍历所有属性
String name = field[i].getName(); // 获取属性的名字
// 获取属性的类型
String type = field[i].getGenericType().toString();
if (type.equals("class java.lang.String")) { // 如果type是类类型,则前面包含"class ",后面跟类名
Method m = model.getClass().getMethod(
"get" + UpperCaseField(name));
String value = (String) m.invoke(model); // 调用getter方法获取属性值
if (value != null) {
resultMap.put(name, value);
}
}
}
return resultMap;
}

// 转化字段首字母为大写
private static String UpperCaseField(String fieldName) {
fieldName = fieldName.replaceFirst(fieldName.substring(0, 1), fieldName
.substring(0, 1).toUpperCase());
return fieldName;
}

}
package org.uppower.tnt.biz.core.manager.blog.common;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class SolrjCommonUtil {
public static Map<String, String> getSearchProperty(Object model)
throws NoSuchMethodException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException {
Map<String, String> resultMap = new TreeMap<String, String>();
// 获取实体类的所有属性,返回Field数组
Field[] field = model.getClass().getDeclaredFields();
for (int i = 0; i < field.length; i++) { // 遍历所有属性
String name = field[i].getName(); // 获取属性的名字
// 获取属性的类型
String type = field[i].getGenericType().toString();
if (type.equals("class java.lang.String")) { // 如果type是类类型,则前面包含"class ",后面跟类名
Method m = model.getClass().getMethod(
"get" + UpperCaseField(name));
String value = (String) m.invoke(model); // 调用getter方法获取属性值
if (value != null) {
resultMap.put(name, value);
}
}
}
return resultMap;
}
// 转化字段首字母为大写
private static String UpperCaseField(String fieldName) {
fieldName = fieldName.replaceFirst(fieldName.substring(0, 1), fieldName
.substring(0, 1).toUpperCase());
return fieldName;
}
}

搜索直接调用solr客户端solrj,基本逻辑为循环两个解析之后的TreeMap,设置到SolrQuery当中,最后直接调用solrj的API,获得搜索结果。最终将搜索结构以List<Object>的形式返回。
具体实现:
Java代码
package org.uppower.tnt.biz.core.manager.blog.solrj;

import java.net.MalformedURLException;
import java.util.Map;

import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.impl.CommonsHttpSolrServer;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrDocumentList;


public class SolrjQuery {
private String url;
private Integer soTimeOut;
private Integer connectionTimeOut;
private Integer maxConnectionsPerHost;
private Integer maxTotalConnections;
private Integer maxRetries;
private CommonsHttpSolrServer solrServer = null;
private final static String ASC = "asc";

public void init() throws MalformedURLException {
solrServer = new CommonsHttpSolrServer(url);
solrServer.setSoTimeout(soTimeOut);
solrServer.setConnectionTimeout(connectionTimeOut);
solrServer.setDefaultMaxConnectionsPerHost(maxConnectionsPerHost);
solrServer.setMaxTotalConnections(maxTotalConnections);
solrServer.setFollowRedirects(false);
solrServer.setAllowCompression(true);
solrServer.setMaxRetries(maxRetries);
}

public SolrDocumentList query(Map<String, String> propertyMap,
Map<String, String> compositorMap, Long startIndex, Long pageSize)
throws Exception {
SolrQuery query = new SolrQuery();
// 设置搜索字段
if (null == propertyMap) {
throw new Exception("搜索字段不可为空!");
} else {
for (Object o : propertyMap.keySet()) {
StringBuffer sb = new StringBuffer();
sb.append(o.toString()).append(":");
sb.append(propertyMap.get(o));
String queryString = addBlank2expression_r(sb.toString());
query.setQuery(queryString);
}
}
// 设置排序条件
if (null != compositorMap) {
for (Object co : compositorMap.keySet()) {
if (ASC == compositorMap.get(co)
|| ASC.equals(compositorMap.get(co))) {
query.addSortField(co.toString(), SolrQuery.ORDER.asc);
} else {
query.addSortField(co.toString(), SolrQuery.ORDER.desc);
}
}
}

if (null != startIndex) {
query.setStart(Integer.parseInt(String.valueOf(startIndex)));
}
if (null != pageSize && 0L != pageSize.longValue()) {
query.setRows(Integer.parseInt(String.valueOf(pageSize)));
}
try {
QueryResponse qrsp = solrServer.query(query);
SolrDocumentList docs = qrsp.getResults();
return docs;
} catch (Exception e) {
throw new Exception(e);
}
}

private String addBlank2expression_r(String oldExpression) {
String lastExpression;
lastExpression = oldExpression.replace("AND", " AND ").replace("NOT",
" NOT ").replace("OR", " OR ");
return lastExpression;
}

public Integer getMaxRetries() {
return maxRetries;
}

……

public void setMaxTotalConnections(Integer maxTotalConnections) {
this.maxTotalConnections = maxTotalConnections;
}
}
package org.uppower.tnt.biz.core.manager.blog.solrj;
import java.net.MalformedURLException;
import java.util.Map;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.impl.CommonsHttpSolrServer;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrDocumentList;

public class SolrjQuery {
private String url;
private Integer soTimeOut;
private Integer connectionTimeOut;
private Integer maxConnectionsPerHost;
private Integer maxTotalConnections;
private Integer maxRetries;
private CommonsHttpSolrServer solrServer = null;
private final static String ASC = "asc";
public void init() throws MalformedURLException {
solrServer = new CommonsHttpSolrServer(url);
solrServer.setSoTimeout(soTimeOut);
solrServer.setConnectionTimeout(connectionTimeOut);
solrServer.setDefaultMaxConnectionsPerHost(maxConnectionsPerHost);
solrServer.setMaxTotalConnections(maxTotalConnections);
solrServer.setFollowRedirects(false);
solrServer.setAllowCompression(true);
solrServer.setMaxRetries(maxRetries);
}
public SolrDocumentList query(Map<String, String> propertyMap,
Map<String, String> compositorMap, Long startIndex, Long pageSize)
throws Exception {
SolrQuery query = new SolrQuery();
// 设置搜索字段
if (null == propertyMap) {
throw new Exception("搜索字段不可为空!");
} else {
for (Object o : propertyMap.keySet()) {
StringBuffer sb = new StringBuffer();
sb.append(o.toString()).append(":");
sb.append(propertyMap.get(o));
String queryString = addBlank2expression_r(sb.toString());
query.setQuery(queryString);
}
}
// 设置排序条件
if (null != compositorMap) {
for (Object co : compositorMap.keySet()) {
if (ASC == compositorMap.get(co)
|| ASC.equals(compositorMap.get(co))) {
query.addSortField(co.toString(), SolrQuery.ORDER.asc);
} else {
query.addSortField(co.toString(), SolrQuery.ORDER.desc);
}
}
}

if (null != startIndex) {
query.setStart(Integer.parseInt(String.valueOf(startIndex)));
}
if (null != pageSize && 0L != pageSize.longValue()) {
query.setRows(Integer.parseInt(String.valueOf(pageSize)));
}
try {
QueryResponse qrsp = solrServer.query(query);
SolrDocumentList docs = qrsp.getResults();
return docs;
} catch (Exception e) {
throw new Exception(e);
}
}
private String addBlank2expression_r(String oldExpression) {
String lastExpression;
lastExpression = oldExpression.replace("AND", " AND ").replace("NOT",
" NOT ").replace("OR", " OR ");
return lastExpression;
}
public Integer getMaxRetries() {
return maxRetries;
}
……

public void setMaxTotalConnections(Integer maxTotalConnections) {
this.maxTotalConnections = maxTotalConnections;
}
}

整个实现是在Spring的基础上完成的,其中SolrjQuery的init()方法在Spring容器启动是初始化。Init()方法内的属性,也是直接注入的。上层与下层之间也完全用注入的方式解决。具体配置就不贴不出来了,大家都会。
整个代码很简陋,但是几乎支持了你想要搜索的条件设置,而且不会暴露任何与solr相关的内容给上层调用,使整个搜索几乎以sql语言的思想在设置条件。

 

转自:http://blog.sina.com.cn/s/blog_466678e80100l2an.html

分享到:
评论

相关推荐

    Apache Solr(solr-8.11.1.tgz)

    Apache Solr 是一个开源的全文搜索引擎,由Apache软件基金会维护,是Lucene项目的一部分。它提供了高效、可扩展的搜索和导航功能,广泛应用于企业级的搜索应用中。Solr-8.11.1是该软件的一个特定版本,包含了最新的...

    solr(solr-9.0.0-src.tgz)源码

    Solr是Apache软件基金会的一个开源项目,它是基于Java的全文搜索服务器,被广泛应用于企业级搜索引擎的构建。源码分析是深入理解一个软件系统工作原理的重要途径,对于Solr这样的复杂系统尤其如此。这里我们将围绕...

    ikanalyzer-solr8.4.0_solr8_solr_ikanalyzer_中文分词_

    Solr8.4.0 是 Apache Solr 的一个版本,这是一个高度可配置、高性能的全文搜索和分析引擎,广泛用于构建企业级搜索应用。 在 Solr 中,ikanalyzer 是一个重要的组件,它通过自定义Analyzer来实现中文的分词处理。...

    Linux上Solr的启动方式

    使用Solr内置的Jetty服务器启动Solr (1)借助X Shell上传solr的安装包到/usr/local/目录下,使用 tar -zxvf命令进行解压.  (2)使用内置的Jetty来启动Solr服务器只需要在example目录下,执行start.jar程序即可,...

    solr4.7服务搭建

    ### Solr 4.7 服务搭建详细指南 #### 一、环境准备 为了搭建 Solr 4.7 服务,我们需要确保以下环境已经准备好: 1. **Java Development Kit (JDK) 1.7**:Solr 需要 Java 运行环境支持,这里我们选择 JDK 1.7 ...

    solr增量更新架包apache-solr-dataimportscheduler.jar

    Apache Solr 是一个开源的全文搜索引擎,广泛应用于各种企业级数据搜索和分析场景。增量更新是Solr的一个关键特性,它允许系统仅处理自上次完整索引以来发生更改的数据,从而提高了性能并降低了资源消耗。"apache-...

    apache-solr-dataimportscheduler-1.0.zip_official54l_solr 5.x定时生成

    Apache Solr是一个流行的开源搜索引擎,它提供全文搜索、命中高亮、拼写建议等功能,广泛应用于网站内容管理和企业级信息检索。在Solr 5.x版本中,为了实现数据的定时更新,用户需要借助特定的扩展来实现定时生成...

    solr-7.4.0.zip

    Solr,全称为Apache Solr,是一款开源的企业级全文搜索引擎,由Apache软件基金会开发并维护。它是基于Java的,因此在使用Solr之前,确保你的系统已经安装了Java 8或更高版本是至关重要的。标题"solr-7.4.0.zip"表明...

    solr-6.2.0源码

    Solr是Apache软件基金会开发的一款开源全文搜索引擎,它基于Java平台,是Lucene的一个扩展,提供了更为方便和强大的搜索功能。在Solr 6.2.0版本中,这个强大的分布式搜索引擎引入了许多新特性和改进,使其在处理大...

    solr ik分词器

    Solr是中国最流行的开源搜索引擎平台之一,而IK Analyzer是一款针对中文的高性能分词器,尤其在处理现代汉语的复杂情况时表现出色。本教程将详细解释如何在Solr中安装和使用IK分词器。 首先,让我们理解一下什么是...

    解决solr启动404问题

    Solr是Apache Lucene项目的一个子项目,是一个高性能、基于Java的企业级全文搜索引擎服务器。当你在尝试启动Solr时遇到404错误,这通常意味着Solr服务没有正确地启动或者配置文件设置不正确。404错误表示“未找到”...

    solr-dataimport-scheduler.jar 可使用于solr7.x版本

    Solr 数据导入调度器(solr-dataimport-scheduler.jar)是一个专门为Apache Solr 7.x版本设计的组件,用于实现数据的定期索引更新。在理解这个知识点之前,我们需要先了解Solr的基本概念以及数据导入处理...

    solr-4.10.3.rar

    Solr 是一个开源的企业级搜索平台,由Apache软件基金会维护,是Lucene项目的一部分。它提供了全文检索、命中高亮、拼写检查、缓存、近实时搜索等特性,广泛应用于网站内容搜索、电子商务产品搜索等领域。本次分享的...

    solr定时自动同步数据库需要用到的apache-solr-dataimportscheduler.jar包

    Apache Solr是一款强大的开源搜索引擎,它能够高效地处理和索引大量数据,提供快速的全文检索、 faceting、高亮显示等高级功能。在实际应用中,为了保持搜索结果的实时性,我们往往需要将数据库中的数据实时或定时...

    支持solr5.5 solr6.0中IK分词需要的资料

    Solr是中国最流行的全文搜索引擎框架Apache Lucene的一个扩展,它提供了高级的搜索功能,并且能够进行复杂的全文检索、分布式搜索和处理大量数据。在Solr中,分词器(Tokenizer)是文本分析的重要组成部分,它负责将...

    Solr(Cloudera)使用手册

    ### Solr(Cloudera)使用手册 #### 一、创建Collection与管理实例 在使用Solr(Cloudera)时,创建Collection是基本的操作之一。Collection是Solr中的数据存储单元,相当于关系数据库中的表。 ##### 创建路径与实例 ...

    最新版windows solr-8.8.2.zip

    Solr是Apache软件基金会的一个开源项目,是一款强大的全文搜索引擎服务器,它提供了分布式、可扩展、高可用性的搜索和分析服务。此压缩包“最新版windows solr-8.8.2.zip”包含了Windows环境下Solr的最新版本8.8.2的...

    最新版linux solr-8.8.2.tgz

    Linux Solr 8.8.2 是一个针对大型文本数据集进行高效全文搜索和处理的开源平台。Solr 是由 Apache 软件基金会维护的一个项目,它基于 Java 并可部署在各种操作系统上,包括 Linux。这个最新的版本 8.8.2 带来了若干...

    solr实现电商自定义打分

    Solr,全称为Apache Solr,是一款开源的全文搜索引擎,广泛应用于电商、新闻、文档检索等领域。它提供了高效、可扩展的搜索与分析能力。在电商领域,搜索结果的排序和打分对于用户体验至关重要,因为它直接影响到...

    最新版linux solr-8.11.0.gz

    Linux Solr 8.11.0是一款针对大型文本数据集进行高效全文搜索和分析的开源平台。Solr是Apache Lucene项目的一部分,专为处理和检索大量文档而设计,广泛应用于网站搜索、企业级信息检索、电子商务产品搜索等领域。在...

Global site tag (gtag.js) - Google Analytics