特点:
- 分布式,无需人工搭建集群(solr就需要人为配置,使用Zookeeper作为注册中心)
- Restful风格,一切API都遵循Rest原则,容易上手
- 近实时搜索,数据更新在Elasticsearch中几乎是完全同步的
倒排索引
倒排索引是引擎搜素的基础;
在存文档的时候,将数据进行分词,并记录每个分词下的文档编号,查找的通过分词找到对应的倒排列表的数据
安装es
点击:官网下载es
-
将下载的压缩包解压到自定义目录即可
执行该文件,浏览器访问http://localhost:9200/
Java程序访问端口9300
安装Head插件
elasticsearch-head相当于可视化工具
此链接下载Head插件,解压即可
确保已安装
node及grunt
-
grunt下载:npm install -g grunt-cli
-
通过npm run start 开启
访问localhost:9100
安装IK分词器
Elastic Search默认的分词器是单个分词,效果很差
下载 一定要选择与Elastic Search版本同步
-
将下载的Ik分词器改名为ik放在elasticsearch的目录下
-
重启es,如下图成功
-
在IKAnalyzer.cfg.xml中配置扩展词和停用词(根据需要)
SpringBoot整合Spring Data Elasticsearch
SpringDataElasticSearch是SpringData项目下的一个子模块
SpringData的使命是给各种数据访问提供统一的编程接口,不管是关系型数据库(如MySQL),还是非关系数据库(如Redis),或者类似Elasticsearch这样的索引数据库。从而简化开发人员的代码,提高开发效率。
- 所需依赖
<!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-elasticsearch -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-elasticsearch</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
- application.yml配置文件
spring:
data:
elasticsearch:
cluster-name: my-application
cluster-nodes: 127.0.0.1:9300 # 程序连接es的端口号是9300
在SpringDataElasticSearch中,只需要操作对象,就可以操作elasticsearch中的数据
索引操作
-
创建索引及映射
-
@Document 作用在类,标记实体类为文档对象,一般有两个属性
- indexName:对应索引库名称
- type:对应在索引库中的类型
- shards:分片数量,默认5
- replicas:副本数量,默认1
-
@Id 作用在成员变量,标记一个字段作为id主键
-
@Field 作用在成员变量,标记为文档的字段,并指定字段映射属性:
-
-
type:字段类型,是枚举:FieldType,可以是text、long、short、date、integer、object等
- text:存储数据时候,会自动分词,并生成索引
- keyword:存储数据时候,不会分词建立索引
- Numerical:数值类型,分两类
- 基本数据类型:long、interger、short、byte、double、float、half_float
- 浮点数的高精度类型:scaled_float
- 需要指定一个精度因子,比如10或100。elasticsearch会把真实值乘以这个因子后存储,取出时再还原。
- Date:日期类型
- elasticsearch可以对日期格式化为字符串存储,但是建议我们存储为毫秒值,存储为long,节省空间。
-
index:是否索引,布尔类型,默认是true
-
store:是否存储,布尔类型,默认是false
-
analyzer:分词器名称,这里的ik_max_word即使用ik分词器
POJO
@Data
@AllArgsConstructor
@NoArgsConstructor
@Table(name = "student")
@Document(indexName = "capgeminitest",type = "student")
public class Student {
@Id
@Column(name = "id")
@GeneratedValue(generator = "JDBC")
private Integer id;
@Field(type = FieldType.Text,analyzer = "ik_max_word")
@Column(name = "sname")
private String sname;
}
测试
@RestController
@RequestMapping("/stu")
public class StudentController {
@Autowired
private ElasticsearchTemplate elasticsearchTemplate;
@GetMapping
public void testEs(){
// 创建索引
elasticsearchTemplate.createIndex(Student.class);
// 映射
elasticsearchTemplate.putMapping(Student.class);
Logger.getLogger("Esearch").info("创建索引测试成功");
}
@DeleteMapping
public void testDelEs(){
// 删除索引
elasticsearchTemplate.deleteIndex(Student.class);
Logger.getLogger("Esearch").info("删除索引测试成功");
}
}
- 新增文档数据
Repository接口
不用写任何DAO处理,自动根据方法名或类的信息进行CRUD操作。只要你定义一个接口,然后继承Repository提供的一些子接口
public interface StudentRepository extends ElasticsearchRepository<Student,Long> {
}
- 添加一个对象,批量添加
@RestController
@RequestMapping("/stu")
public class StudentController {
@Autowired
private ElasticsearchTemplate elasticsearchTemplate;
@Autowired
private StudentRepository studentRepository;
@GetMapping("/addAllStu")
public void addAllStu(){
Student student = new Student();
student.setSname("李木子");
Student student2 = new Student();
student2.setSname("李七夜");
ArrayList<Student> students = new ArrayList<>();
students.add(student);
students.add(student2);
studentRepository.saveAll(students);
Logger.getLogger("Esearch").info("批量新增对象测试成功");
}
@GetMapping("/addStu")
public void addStu(){
Student student = new Student();
student.setSname("李淳罡");
studentRepository.save(student);
Logger.getLogger("Esearch").info("添加一个对象测试成功");
}
}
页面查询
没有修改,需先删除,再修改,根据id作为依据
查询
查询
- 基本查询
- 分页查询
- 排序查找
ascending升序
descending降序 - 自定义方法(SpringData的功能,根据方法名自动实现功能)
Keyword | Sample |
---|---|
And | findByTitleAndPricefindBy属性名1And属性名2 |
Or | findByTitleOrPrice |
Is= | findByTitle |
Not | findByTitleNot |
Between | findByPriceBetween |
LessThanEqual | findByPriceLessThan |
GreaterThanEqual | findByPriceGreaterThan |
Before | findByPriceBefore |
After | findByPriceAfter |
Like | findByNameLike |
StartingWith | findByNameStartingWith |
EndingWith | findByNameEndingWith |
Contains/Containing | findByNameContaining |
In | findByNameIn(Collectionnames) |
NotIn | findByNameNotIn(Collectionnames) |
Near | findByStoreNear |
True | findByAvailableTrue |
False | findByAvailableFalse |
OrderBy | findByAvailableTrueOrderByNameDesc |
自定义方法
- id在x-y之间
- id在x-y之间并倒序
- 模糊匹配
- Repository
public interface StudentRepository extends ElasticsearchRepository<Student,Long> {
/**
* 根据id区间搜索
*/
public List<Student> findByidBetween(Integer id, Integer id2);
/**
* 模糊匹配
* @param sname
* @return
*/
public List<Student> findBySname(String sname);
/**
* id在xxx-yyy区间内的,并且根据id倒序排序输出
*
*/
public List<Student> findByidBetweenOrderByIdDesc(Integer id, Integer id2);
}
- 测试
@RestController
@RequestMapping("/stu")
public class StudentController {
@Autowired
private ElasticsearchTemplate elasticsearchTemplate;
@Autowired
private StudentRepository studentRepository;
/**
* 基本查询
*/
@GetMapping("/queryAll")
public void query(){
Iterable<Student> all = studentRepository.findAll();
Iterator<Student> iterator = all.iterator();
while (iterator.hasNext()){
EsearchConsont.ElASTICSEARCH_LOG_CONSONT.info(""+iterator.next());
}
}
/**
* 分页查询
* @param page 页数
* @param size 数量
*/
@GetMapping("/limitPage")
public void findPage(int page, int size){
Page<Student> all = studentRepository.findAll(PageRequest.of(page, size));
for (Student student : all) {
EsearchConsont.ElASTICSEARCH_LOG_CONSONT.info("分页:"+student);
}
EsearchConsont.ElASTICSEARCH_LOG_CONSONT.info("总条数"+all.getTotalElements());
EsearchConsont.ElASTICSEARCH_LOG_CONSONT.info("总页数"+all.getTotalPages());
}
/**
* 排序查询
*/
@GetMapping("/findAllOrder")
public void findAllOrder(){
// 降序
Iterable<Student> id = studentRepository.findAll(Sort.by("id").descending());
for (Student student : id) {
EsearchConsont.ElASTICSEARCH_LOG_CONSONT.info("根据id进行排序查找:"+student);
}
}
/**
* 查找id在x-y之间的数据
* @param id
* @param id2
*/
@GetMapping("/findIdBetween")
public void findIdBetween(Integer id, Integer id2){
List<Student> byIdBetween = studentRepository.findByidBetween(id, id2);
for (Student student : byIdBetween) {
EsearchConsont.ElASTICSEARCH_LOG_CONSONT.info("id在x-y之间"+student);
}
}
/**
* 查找id在x-y之间的数据并倒序打印
* @param id
* @param id2
*/
@GetMapping("/findByIDBetweenOrderByIdDesc")
public void findByIDBetweenOrderByIdDesc(Integer id, Integer id2){
List<Student> byIDBetweenOrderByIdDesc = studentRepository.findByidBetweenOrderByIdDesc(id, id2);
for (Student student : byIDBetweenOrderByIdDesc) {
EsearchConsont.ElASTICSEARCH_LOG_CONSONT.info("id在x-y之间,并按id倒序查询"+student);
}
}
/**
* 模糊匹配
* @param sname
*/
@GetMapping("/findBySname")
public void findBySname(String sname){
List<Student> bySname = studentRepository.findBySname(sname);
for (Student student : bySname) {
EsearchConsont.ElASTICSEARCH_LOG_CONSONT.info("模糊匹配"+student);
}
}
}
自定义查询
NativeSearchQueryBuilder:Spring提供的一个查询条件构建器,帮助构建json格式的请求体
- matchQuery:词条匹配,先分词然后在调用termQuery进行匹配
- TermQuery:词条匹配,不分词
- wildcardQuery:通配符匹配
- fuzzyQuery:模糊匹配(允许错两个字母)
- rangeQuery:范围匹配
- booleanQuery:布尔查询
分页—排序----高亮
@RestController
@RequestMapping("/stu2")
public class StudnetController2 {
@Resource
private StudentRepository studentRepository;
/**
* 自定义查询 分词 matchQuery
*
* @param sname
*/
@GetMapping("/testSearch")
public void testSearch(String sname) {
// 创建查询构建器- 构建条件
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
// 构建条件
nativeSearchQueryBuilder.withQuery(QueryBuilders.matchQuery("sname", sname));
Page<Student> search = studentRepository.search(nativeSearchQueryBuilder.build());
for (Student student : search) {
EsearchConsont.ElASTICSEARCH_LOG_CONSONT.info("先分词,在调用termQuery进行匹配" + student);
}
}
/**
* 不分词匹配 termQuery
*
* @param sname
*/
@GetMapping("/testQuery")
public void testQuery(String sname) {
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
nativeSearchQueryBuilder.withQuery(QueryBuilders.termQuery("sname", sname));
Page<Student> search = studentRepository.search(nativeSearchQueryBuilder.build());
for (Student student : search) {
EsearchConsont.ElASTICSEARCH_LOG_CONSONT.info("词条匹配,不分词" + student);
}
}
/**
* 分页查询
*
* @param page 第n页
* @param size 每页y条数据
* @param sname
*/
@GetMapping("/testPage")
public void testPage(int page, int size, String sname) {
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
nativeSearchQueryBuilder.withQuery(QueryBuilders.termQuery("sname", sname));
// 进行分页
nativeSearchQueryBuilder.withPageable(PageRequest.of(page, size));
Page<Student> search = studentRepository.search(nativeSearchQueryBuilder.build());
for (Student student : search) {
EsearchConsont.ElASTICSEARCH_LOG_CONSONT.info("分页查询:" + student);
}
}
/**
* 排序查询
*
* @param sname
*/
@GetMapping("/testOrder")
public void testOrder(String sname) {
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
nativeSearchQueryBuilder.withQuery(QueryBuilders.termQuery("sname", sname));
// 进行排序
nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort("id").order(SortOrder.DESC));
Page<Student> search = studentRepository.search(nativeSearchQueryBuilder.build());
for (Student student : search) {
EsearchConsont.ElASTICSEARCH_LOG_CONSONT.info("按照id降序排序查找Sname:" + student);
}
}
/**
* 高亮显示
* @param sname
*/
@GetMapping("/testHighlight")
public void testHighlight(String sname) {
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
nativeSearchQueryBuilder.withQuery(QueryBuilders.fuzzyQuery("sname", sname));
nativeSearchQueryBuilder.
withHighlightFields(new HighlightBuilder.
Field("sname").
// 添加前缀和后缀
preTags("<em>").postTags("<em>"));
Page<Student> search = studentRepository.search(nativeSearchQueryBuilder.build());
for (Student student : search) {
EsearchConsont.ElASTICSEARCH_LOG_CONSONT.info("高亮显示:"+student);
}
}
}
聚合 桶bucket
(1)统计某个字段的数量
ValueCountBuilder vcb = AggregationBuilders.count(“count_uid”).field(“uid”);
(2)去重统计某个字段的数量(有少量误差)
CardinalityBuilder cb =AggregationBuilders.cardinality(“distinct_count_uid”).field(“uid”);
(3)按日期间隔分组
DateHistogramBuilder dhb = AggregationBuilders.dateHistogram(“dh”).field(“date”);
(4)按某个字段分组
TermsBuilder tb = AggregationBuilders.terms(“group_name”).field(“name”);
(5)求和
SumBuilder sumBuilder = AggregationBuilders.sum(“sum_price”).field(“price”);
(6)求平均
AvgBuilder ab=
AggregationBuilders.avg(“avg_price”).field(“price”);
(7)求最大值
MaxBuilder mb = AggregationBuilders.max(“max_price”).field(“price”);
(8)求最小值
MinBuilder min = AggregationBuilders.min(“min_price”).field(“price”);
(9)聚合过滤
FilterAggregationBuilder fab = AggregationBuilders.filter(“uid_filter”).filter(QueryBuilders.queryStringQuery(“uid:001”));
(10)获取聚合里面的结果
平均值
TopHitsBuilder thb = AggregationBuilders.topHits(“top_result”);
(11)嵌套的聚合
NestedBuilder nb = AggregationBuilders.nested(“negsted_path”).path(“quests”);
(12)反转嵌套
AggregationBuilders.reverseNested(“res_negsted”).path(“kps”);
在这里插入代码片 /**
* 聚合 分组
*/
@GetMapping("/testBucket")
public void testBucket() {
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
// 添加分组,分组名称brands,根据id分组
nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("brands").field("id"));
// 查询.放到聚合分组
AggregatedPage<Student> search = (AggregatedPage<Student>) studentRepository.search(nativeSearchQueryBuilder.build());
// 根据键取出数据
StringTerms stringTerms = (StringTerms) search.getAggregation("brands");
List<StringTerms.Bucket> buckets = stringTerms.getBuckets();
for (StringTerms.Bucket bucket : buckets) {
EsearchConsont.ElASTICSEARCH_LOG_CONSONT.info("分组后名称:"+bucket.getKeyAsString());
EsearchConsont.ElASTICSEARCH_LOG_CONSONT.info("分组后数量:"+bucket.getDocCount());
}
}