一、官方文档
https://esdoc.bbossgroups.com/#/README
二、官方demo
https://github.com/bbossgroups/elasticsearch-example.git
三、maven构建
<dependency>
<groupId>com.bbossgroups.plugins</groupId>
<artifactId>bboss-elasticsearch-rest-jdbc</artifactId>
<version>6.1.3</version>
<exclusions>
<exclusion>
<artifactId>slf4j-log4j12</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.bbossgroups.plugins</groupId>
<artifactId>bboss-elasticsearch-spring-boot-starter</artifactId>
<version>6.1.3</version>
<exclusions>
<exclusion>
<artifactId>slf4j-log4j12</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
四、我的代码
1,application.yml
spring:
elasticsearch:
bboss:
# elasticUser: elastic
# elasticPassword: changeme
elasticsearch:
rest:
hostNames: 127.0.0.1:9200
dateFormat: yyyy.MM.dd
timeZone: Asia/Shanghai
showTemplate: true
discoverHost: false
dslfile:
refreshInterval: -1
http:
timeoutConnection: 5000
timeoutSocket: 5000
connectionRequestTimeout: 5000
retryTime: 1
maxLineLength: -1
maxHeaderCount: 200
maxTotal: 400
defaultMaxPerRoute: 200
soReuseAddress: false
soKeepAlive: false
timeToLive: 3600000
keepAlive: 3600000
keystore:
keyPassword:
hostnameVerifier:
2,esmapper/search.xml
<properties>
<!-- 构建书籍内容索引-->
<property name="createBookcontent">
<![CDATA[{
"settings": {
"number_of_shards": 1,
"index.refresh_interval": "5s"
},
"mappings": {
"properties": {
"name":{
"type":"text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"hid": {
"type": "keyword"
},
"lastSection": {
"type": "keyword"
},
"nextSection": {
"type": "keyword"
},
"sectionName": {
"type": "keyword"
},
"sectionNote": {
"type": "text"
}
}
}
}]]>
</property>
<!-- 关键字检索检索-->
<property name="searchPagineDatas">
<![CDATA[{
"query": {
#if($keyword)
"multi_match": {
"query": #[keyword],
"type": "most_fields",
"fields": [
#foreach($column in $columns)
#if($velocityCount > 0),#end"$column"
#end
]
}
#else
"match_all": {}
#end
},
"track_total_hits": true,
"from":#[from],
"size":#[size]
}]]>
</property>
<!-- ## 根据hid查找书籍内容-->
<property name="searchBookContent">
<![CDATA[{
"query": {
"term": {
"hid": #[hid]
}
},
"track_total_hits": true,
"size": 1
}]]>
</property>
<!-- 使用父子文档存储书籍及其目录 -->
<property name="createBookIndex">
<![CDATA[{
"settings": {
"number_of_shards": 1,
"index.refresh_interval": "5s"
},
"mappings": {
"properties": {
"name":{
"type":"text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"id0":{
"type":"keyword"
},
"hi0": {
"type": "keyword"
},
"directory0": {
"type": "text"
},
#foreach($i in [1,2,3])
#if($velocityCount > 0),#end
"id$i":{
"type":"keyword"
},
"hi$i": {
"type": "keyword"
},
"directory$i": {
"type": "text"
}
#end
,
"level":{
"type":"keyword"
},
"book_join": {
"type": "join",
"relations": {
"book": ["bookmark"]
}
}
}
}
}]]>
</property>
<!-- 根据子文档条件筛选父文档 -->
<property name="searchBookByBookMark">
<![CDATA[{
"query": {
"has_child": {
"type": "bookmark",
"query": {
#if($keyword)
"multi_match": {
"query": #[keyword],
"type": "most_fields",
"fields": ["name","directory0","directory1","directory2","directory3"]
}
#else
"match_all": {}
#end
}
}
},
"track_total_hits": true,
"from":#[from],
"size":#[size]
}]]>
</property>
<!-- 根据父文档条件筛选子文档 -->
<property name="searchBookMarkByBook">
<![CDATA[{
"query": {
"parent_id": {
"type": "bookmark",
"id": #[keyword]
}
},
"track_total_hits": true,
"size": 5000
}]]>
</property>
<!-- group by -->
<property name="searchBook">
<![CDATA[{
"query": {
#if($keyword)
"multi_match": {
"query": #[keyword],
"type": "most_fields",
"fields": ["name","directory0","directory1","directory2","directory3"]
}
#else
"match_all": {}
#end
},
"track_total_hits": true,
"sort": [
{
"_score": {
"order": "desc"
}
}
],
"collapse":{
"field":"name.keyword"
},
"from":#[from],
"size":#[size]
}]]>
</property>
<!-- count(*) gruop by -->
<property name="countBook">
<![CDATA[{
"query": {
#if($keyword)
"multi_match": {
"query": #[keyword],
"type": "most_fields",
"fields": ["name","directory0","directory1","directory2","directory3"]
}
#else
"match_all": {}
#end
},
"track_total_hits": true,
"size" : 0,
"aggs": {
"count": {
"cardinality": {
"field": "name.keyword"
}
}
}
}]]>
</property>
<!-- scroll -->
<property name="scrollQuery">
<![CDATA[
{
"size":1000,
"query": {
"match_all": {}
}
}
]]>
</property>
</properties>
3,ElasticSearchService.java
@Slf4j
@Service
public class ElasticSearchService {
private String mappath = "esmapper/search.xml";
private static final int fromLimit = 10000; //设置查询上限条数
public static final String INDEX_BOOKMARK = BookMark.class.getAnnotation(ESIndex.class).name();
public static final String INDEX_BOOKCONTENT = BookContent.class.getAnnotation(ESIndex.class).name();
/**
* 检索
* @param searchDto
* @param clz
* @param <T>
* @return
*/
public <T> MyPage<T> search(KnowSearchDto searchDto, Class<T> clz){
ClientInterface client = ElasticSearchHelper.getConfigRestClientUtil(mappath);
int page = searchDto.getPage();
int size = searchDto.getLimit();
Map<String,Object> params = new HashMap<String,Object>();
if(StringUtils.isNotBlank(searchDto.getKeyword())){
params.put("keyword", searchDto.getKeyword());
}
params.put("from", Math.min((page-1) * size, fromLimit));
params.put("size", size);
Field[] fields = clz.getDeclaredFields();
List<String> columns = new ArrayList<>(fields.length-1);
Arrays.stream(fields).forEach(field -> {
if(!"id".equals(field.getName())) columns.add(field.getName());
});
params.put("columns", columns);
ESDatas<T> esDatas = client.searchList(clz.getAnnotation(ESIndex.class).name() +"/_search", "searchPagineDatas", params, clz);
List<T> records = null == esDatas.getDatas() ? new ArrayList<>() : esDatas.getDatas();
return new MyPage<T>(records, esDatas.getTotalSize(), size, page);
}
/**
* 检索索引所有数据,每批次返回默认为5000条数据
* @param index
* @param clz
* @param <T>
* @return
*/
public <T> ESDatas<T> searchAll(String index, Class<T> clz){
ClientInterface clientUtil = ElasticSearchHelper.getRestClientUtil();
ESDatas<T> esDatas = clientUtil.searchAll(index, clz);
return esDatas;
}
/**
* 根据父文档id查询其所有子文档(根据书籍查找目录)
* @param esId
* @return
*/
public ESDatas<BookMark> searchBookMarkByBook(String esId){
ClientInterface client = ElasticSearchHelper.getConfigRestClientUtil(mappath);
ESDatas<BookMark> esDatas = client.searchList(INDEX_BOOKMARK+"/_search", "searchBookMarkByBook", ImmutableMap.of("keyword", esId), BookMark.class);
return esDatas;
}
/**
* (分组统计分页)
* @param searchDto
* @return
*/
@Deprecated
public MyPage<BookVo> searchBook(KnowSearchDto searchDto){
ClientInterface client = ElasticSearchHelper.getConfigRestClientUtil(mappath);
int page = searchDto.getPage();
int size = searchDto.getLimit();
Map<String,Object> params = new HashMap<String,Object>();
if(StringUtils.isNotBlank(searchDto.getKeyword())){
params.put("keyword", searchDto.getKeyword());
}
params.put("from", (page-1) * size);
params.put("size", size);
MapRestResponse restResponse = client.search(INDEX_BOOKMARK+"/_search", "countBook", params);
int total = (int) restResponse.getAggregations().get("count").get("value");
MyPage<BookVo> myPage = new MyPage<>(total, size, page);
if(total > 0){
ESDatas<BookMark> esDatas = client.searchList(INDEX_BOOKMARK +"/_search", "searchBook", params, BookMark.class);
List<BookVo> bookVoList = new ArrayList<>();
esDatas.getDatas().stream().forEach(bookMark -> {
bookVoList.add(new BookVo(bookMark.getName(), ""));
});
myPage.setRecords(bookVoList);
}
return myPage;
}
public MyPage<BookMark> searchBookByBookMark(KnowSearchDto searchDto){
ClientInterface clientInterface = ElasticSearchHelper.getConfigRestClientUtil(mappath);
int page = searchDto.getPage();
int size = searchDto.getLimit();
Map<String,Object> params = new HashMap<String,Object>();
if(StringUtils.isNotBlank(searchDto.getKeyword())){
params.put("keyword", searchDto.getKeyword());
}
params.put("from", (page-1) * size);
params.put("size", size);
ESDatas<BookMark> esDatas = clientInterface.searchList(INDEX_BOOKMARK +"/_search", "searchBookByBookMark", params, BookMark.class);
List<BookMark> records = null == esDatas.getDatas() ? new ArrayList<>() : esDatas.getDatas();
return new MyPage(records, esDatas.getTotalSize(), size, page);
}
/**
* 根据hid获取书籍篇章内容
* @param hid
* @return
*/
public BookContent getBookContentByHid(String hid){
ClientInterface client = ElasticSearchHelper.getConfigRestClientUtil(mappath);
return client.searchObject(INDEX_BOOKCONTENT +"/_search", "searchBookContent", ImmutableMap.of("hid", hid), BookContent.class);
}
}
4,entity.BookMark.java
@Slf4j
@Data
@ESIndex(name = "bookmark", type = "_doc")
public class BookMark implements Serializable {
@ESId
private String id;
private String name;
private String id0;
private String id1;
private String id2;
private String id3;
private String hi0;
private String hi1;
private String hi2;
private String hi3;
private String directory0;
private String directory1;
private String directory2;
private String directory3;
@JsonProperty("book_join")
private JoinSon bookJoin;
@ESRouting
private String routingId;
private Integer level;
public BookMark() {
}
public BookMark(String name) {
this.name = name;
}
}
5,test.ESBook.java(建索引,灌数据)
@RunWith(SpringRunner.class)
@SpringBootTest(classes = CmaiDoctorApplication.class)
@Slf4j
@ActiveProfiles("test")
public class ESBook {
private String index = "bookmark";
// @Test
public void createBookMarkIndex(){
ClientInterface clientInterface = ElasticSearchHelper.getConfigRestClientUtil("esmapper/search.xml");
boolean exist = clientInterface.existIndice(index);
if(exist){
clientInterface.dropIndice(index);
}
String response = clientInterface.createIndiceMapping(index, "createBookIndex");
System.out.println(response);
String mapping = clientInterface.getIndexMapping(index);
System.out.println(mapping);
}
@Test
public void insertBook() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
File dir = new File("C:\\Users\\guang\\Desktop\\中医书籍\\test3");
File[] files = dir.listFiles();
for (File file : files){
String ext = file.getName().substring(file.getName().lastIndexOf("."));
if(!".txt".equals(ext)){
continue;
}
JSONObject jsonObject = JSON.parseObject(txt2String(file));
String name = jsonObject.getString("name");
JSONArray jsonArray = jsonObject.getJSONArray("bookmark");
BookMark bookMark = new BookMark(name);
List<BookMark> bookMarkList = new ArrayList<>();
flatDeal(jsonArray, bookMarkList, bookMark, 0);
log.info(JSON.toJSONString(bookMarkList));
//移出书名作者,将其作为父文档
BookMark bookName = bookMarkList.remove(0);
JoinSon joinSon = new JoinSon();
joinSon.setName("book");
bookName.setBookJoin(joinSon);
ClientInterface clientUtil = ElasticSearchHelper.getRestClientUtil();
String response = clientUtil.addDocument(index, bookName ,"refresh");
JSONObject addResponse = JSONObject.parseObject(response);
// log.info("addResponse={}", JSON.toJSONString(addResponse));
String id = addResponse.getString("_id");
for (BookMark next : bookMarkList) {
JoinSon js = new JoinSon();
js.setName("bookmark");
js.setParent(id);
next.setBookJoin(js);
next.setRoutingId(id);
}
String result = clientUtil.addDocuments(index, bookMarkList,"refresh");
log.info("result ={}", result);
}
}
/**
* 将目录扁平化
* @param jsonArray
* @param bookMarkList
* @param bookMark
* @param level
* @throws NoSuchMethodException
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
private void flatDeal(JSONArray jsonArray, List<BookMark> bookMarkList, BookMark bookMark, Integer level) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
for (Object o : jsonArray) {
JSONObject next = (JSONObject) o;
setValue(bookMark, level, next);
if (null != bookMark.getHi0() && bookMarkList.size() == 0) { //如果没有作者,直接遍历到了目录,则手动生成一个假的作者
bookMarkList.add(new BookMark(bookMark.getName()));
}
bookMarkList.add(MyUtils.CloneObj(bookMark));
JSONArray childs = next.getJSONArray("bookmarkChildren");
if (childs != null) {
flatDeal(childs, bookMarkList, bookMark, level + 1);
}
}
}
/**
* 通过反射给指定对象赋值
* @param bookMark
* @param level
* @param value
* @throws NoSuchMethodException
* @throws InvocationTargetException
* @throws IllegalAccessException
*/
private void setValue(BookMark bookMark, int level, JSONObject value) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
bookMark.setLevel(level);
Class clz = bookMark.getClass();
Method id = clz.getMethod("setId"+level, String.class);
Method hi = clz.getMethod("setHi"+level, String.class);
Method directory = clz.getMethod("setDirectory"+level, String.class);
id.invoke(bookMark, value.getString("id"));
hi.invoke(bookMark, value.getString("hi"));
directory.invoke(bookMark, value.getString("directory"));
}
/**
* 读取文件转为字符串
* @param file
* @return
*/
private String txt2String(File file){
StringBuilder result = new StringBuilder();
try{
BufferedReader br = new BufferedReader(new FileReader(file));
String s = null;
while((s = br.readLine())!=null){
result.append(System.lineSeparator()+s);
}
br.close();
}catch(Exception e){
e.printStackTrace();
}
return result.toString();
}
}
5,scroll用法
@RunWith(SpringRunner.class)
@SpringBootTest(classes = CmaiDoctorApplication.class)
@Slf4j
@ActiveProfiles("test")
public class AddImgToYaocai {
private String mappath = "esmapper/search.xml";
@Resource
private FastDfsUtils fastDfsUtils;
@Test
public void testUpload(){
File dir = new File("C:\\Users\\guang\\Desktop\\医药图片第一次汇总");
File[] files = dir.listFiles();
Map<String, String> imgMap = new HashMap<>(files.length);
Arrays.stream(files).forEach(file -> imgMap.put(file.getName().substring(0, file.getName().lastIndexOf(".")), file.getAbsolutePath()));
log.info("imgMap.size = {}", imgMap.size());
ClientInterface client = ElasticSearchHelper.getConfigRestClientUtil(mappath);
List<String > scrollIds = new ArrayList<>();
ESDatas<YaoCai> response = client.searchList("yaocai/_search?scroll=1m", "scrollQuery", YaoCai.class);
List<YaoCai> datas = response.getDatas();
String scrollId = response.getScrollId();
if(scrollId != null) scrollIds.add(scrollId);
if(datas != null && datas.size() > 0) {
for (Iterator<YaoCai> iterator = datas.iterator(); iterator.hasNext(); ) {
YaoCai yaoCai = iterator.next();
String imgFile = imgMap.get(yaoCai.getName());
if(!StringUtils.isEmpty(imgFile)){
yaoCai.setTupian(uploadImg(imgFile));
}else{
iterator.remove();
}
}
log.info("update = {}", client.updateDocuments(datas));
do {
response = client.searchScroll("1m", scrollId, YaoCai.class);
scrollId = response.getScrollId();
if(scrollId != null) scrollIds.add(scrollId);
datas.clear();
datas = response.getDatas();
if(datas == null || datas.size() == 0){
break;
}else{
for (Iterator<YaoCai> iterator = datas.iterator(); iterator.hasNext(); ) {
YaoCai yaoCai = iterator.next();
String imgFile = imgMap.get(yaoCai.getName());
if(!StringUtils.isEmpty(imgFile)){
yaoCai.setTupian(uploadImg(imgFile));
}else{
iterator.remove();
}
}
log.info("update = {}", client.updateDocuments(datas));
}
} while (true);
}
//查询并打印存在于es服务器上的scroll上下文信息
String scrolls = client.executeHttp("_nodes/stats/indices/search", ClientUtil.HTTP_GET);
// System.out.println(scrolls);
//清除scroll上下文信息,虽然说超过1分钟后,scrollid会自动失效,
//但是手动删除不用的scrollid,是一个好习惯
if(scrollIds.size() > 0) {
scrolls = client.deleteScrolls(scrollIds);
System.out.println(scrolls);
}
//清理完毕后查看scroll上下文信息
scrolls = client.executeHttp("_nodes/stats/indices/search", ClientUtil.HTTP_GET);
System.out.println(scrolls);
}
private String uploadImg(String fileAbsolutePath) {
try{
File file = new File(fileAbsolutePath);
FileInputStream input = new FileInputStream(file);
MultipartFile multipartFile = new MockMultipartFile("file", file.getName(), "text/plain", IOUtils.toByteArray(input));
return fastDfsUtils.uploadImg(multipartFile);
}catch (Exception e){
log.error("uploadImg error . file ={}", fileAbsolutePath, e);
return "";
}
}
// @Test
public void updateTest(){
ClientInterface client = ElasticSearchHelper.getConfigRestClientUtil(mappath);
ESIndex esIndex = YaoCai.class.getAnnotation(ESIndex.class);
YaoCai yaoCai = client.getDocument(esIndex.name(), esIndex.type(), "wKdpFnIBHBOuI-wQ1k3n", YaoCai.class);
log.info(JSON.toJSONString(yaoCai));
yaoCai.setTupian("this is tupian");
log.info("update = {}", client.updateDocument("wKdpFnIBHBOuI-wQ1k3n", yaoCai));
}
}