使用ElasticSearch完成百万级数据查询附近的人功能
上一篇文章介绍了ElasticSearch使用Repository和ElasticSearchTemplate完成构建复杂查询条件,简单介绍了ElasticSearch使用地理位置的功能。
这一篇我们来看一下使用ElasticSearch完成大数据量查询附近的人功能,搜索N米范围的内的数据。
准备环境
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.tianyalei</groupId> <artifactId>elasticsearch</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>elasticsearch</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.4.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-elasticsearch</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.sun.jna</groupId> <artifactId>jna</artifactId> <version>3.0.9</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
新建model类Person
package com.tianyalei.elasticsearch.model; import org.springframework.data.annotation.Id; import org.springframework.data.elasticsearch.annotations.Document; import org.springframework.data.elasticsearch.annotations.GeoPointField; import java.io.Serializable; /** * model类 */ @Document(indexName="elastic_search_project",type="person",indexStoreType="fs",shards=5,replicas=1,refreshInterval="-1") public class Person implements Serializable { @Id private int id; private String name; private String phone; /** * 地理位置经纬度 * lat纬度,lon经度 "40.715,-74.011" * 如果用数组则相反[-73.983, 40.719] */ @GeoPointField private String address; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
我用address字段表示经纬度位置。注意,使用String[]和String分别来表示经纬度时是不同的,见注释。
import com.tianyalei.elasticsearch.model.Person; import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; public interface PersonRepository extends ElasticsearchRepository<Person, Integer> { }
看一下Service类,完成插入测试数据的功能,查询的功能我放在Controller里了,为了方便查看,正常是应该放在Service里
package com.tianyalei.elasticsearch.service; import com.tianyalei.elasticsearch.model.Person; import com.tianyalei.elasticsearch.repository.PersonRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.elasticsearch.core.ElasticsearchTemplate; import org.springframework.data.elasticsearch.core.query.IndexQuery; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; @Service public class PersonService { @Autowired PersonRepository personRepository; @Autowired ElasticsearchTemplate elasticsearchTemplate; private static final String PERSON_INDEX_NAME = "elastic_search_project"; private static final String PERSON_INDEX_TYPE = "person"; public Person add(Person person) { return personRepository.save(person); } public void bulkIndex(List<Person> personList) { int counter = 0; try { if (!elasticsearchTemplate.indexExists(PERSON_INDEX_NAME)) { elasticsearchTemplate.createIndex(PERSON_INDEX_TYPE); } List<IndexQuery> queries = new ArrayList<>(); for (Person person : personList) { IndexQuery indexQuery = new IndexQuery(); indexQuery.setId(person.getId() + ""); indexQuery.setObject(person); indexQuery.setIndexName(PERSON_INDEX_NAME); indexQuery.setType(PERSON_INDEX_TYPE); //上面的那几步也可以使用IndexQueryBuilder来构建 //IndexQuery index = new IndexQueryBuilder().withId(person.getId() + "").withObject(person).build(); queries.add(indexQuery); if (counter % 500 == 0) { elasticsearchTemplate.bulkIndex(queries); queries.clear(); System.out.println("bulkIndex counter : " + counter); } counter++; } if (queries.size() > 0) { elasticsearchTemplate.bulkIndex(queries); } System.out.println("bulkIndex completed."); } catch (Exception e) { System.out.println("IndexerService.bulkIndex e;" + e.getMessage()); throw e; } } }
注意看bulkIndex方法,这个是批量插入数据用的,bulk也是ES官方推荐使用的批量插入数据的方法。这里是每逢500的整数倍就bulk插入一次。
package com.tianyalei.elasticsearch.controller; import com.tianyalei.elasticsearch.model.Person; import com.tianyalei.elasticsearch.service.PersonService; import org.elasticsearch.common.unit.DistanceUnit; import org.elasticsearch.index.query.GeoDistanceQueryBuilder; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.sort.GeoDistanceSortBuilder; import org.elasticsearch.search.sort.SortBuilders; import org.elasticsearch.search.sort.SortOrder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.elasticsearch.core.ElasticsearchTemplate; import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder; import org.springframework.data.elasticsearch.core.query.SearchQuery; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.List; import java.util.Random; @RestController public class PersonController { @Autowired PersonService personService; @Autowired ElasticsearchTemplate elasticsearchTemplate; @GetMapping("/add") public Object add() { double lat = 39.929986; double lon = 116.395645; List<Person> personList = new ArrayList<>(900000); for (int i = 100000; i < 1000000; i++) { double max = 0.00001; double min = 0.000001; Random random = new Random(); double s = random.nextDouble() % (max - min + 1) + max; DecimalFormat df = new DecimalFormat("######0.000000"); // System.out.println(s); String lons = df.format(s + lon); String lats = df.format(s + lat); Double dlon = Double.valueOf(lons); Double dlat = Double.valueOf(lats); Person person = new Person(); person.setId(i); person.setName("名字" + i); person.setPhone("电话" + i); person.setAddress(dlat + "," + dlon); personList.add(person); } personService.bulkIndex(personList); // SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(QueryBuilders.queryStringQuery("spring boot OR 书籍")).build(); // List<Article> articles = elas、ticsearchTemplate.queryForList(se、archQuery, Article.class); // for (Article article : articles) { // System.out.println(article.toString()); // } return "添加数据"; } /** * geo_distance: 查找距离某个中心点距离在一定范围内的位置 geo_bounding_box: 查找某个长方形区域内的位置 geo_distance_range: 查找距离某个中心的距离在min和max之间的位置 geo_polygon: 查找位于多边形内的地点。 sort可以用来排序 */ @GetMapping("/query") public Object query() { double lat = 39.929986; double lon = 116.395645; Long nowTime = System.currentTimeMillis(); //查询某经纬度100米范围内 GeoDistanceQueryBuilder builder = QueryBuilders.geoDistanceQuery("address").point(lat, lon) .distance(100, DistanceUnit.METERS); GeoDistanceSortBuilder sortBuilder = SortBuilders.geoDistanceSort("address") .point(lat, lon) .unit(DistanceUnit.METERS) .order(SortOrder.ASC); Pageable pageable = new PageRequest(0, 50); NativeSearchQueryBuilder builder1 = new NativeSearchQueryBuilder().withFilter(builder).withSort(sortBuilder).withPageable(pageable); SearchQuery searchQuery = builder1.build(); //queryForList默认是分页,走的是queryForPage,默认10个 List<Person> personList = elasticsearchTemplate.queryForList(searchQuery, Person.class); System.out.println("耗时:" + (System.currentTimeMillis() - nowTime)); return personList; } }
看Controller类,在add方法中,我们插入90万条测试数据,随机产生不同的经纬度地址。
参考:ES根据地理位置查询 http://blog.csdn.net/bingduanlbd/article/details/52253542
使用ElasticSearch完成百万级数据查询附近的人功能的更多相关文章
- 百万级数据查询到datatable中,提示内存溢出
参考资料: http://group.cnblogs.com/topic/32230.html
- EF查询百万级数据的性能测试--多表连接复杂查询
相关文章:EF查询百万级数据的性能测试--单表查询 一.起因 上次做的是EF百万级数据的单表查询,总结了一下,在200w以下的数据量的情况(Sql Server 2012),EF是可以使用,但是由于 ...
- Mongo查询百万级数据性能问题及JAVA优化问题
Mongo查询百万级数据 使用分页 skip和limit 效率会相当慢 那么怎么解决呢 上代码 全部查询数据也会特别慢 Criteria criteria = new Criteria(); ...
- Sql Server分页分段查询百万级数据四种项目实例
实际项目中需要实现自定义分页,最关键第一步就是写分页SQL语句,要求语句效率要高. 那么本文的一个查询示例是查询第100000-100050条记录,即每页50条的结果集.查询的表名为infoTab,且 ...
- 构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(37)-文章发布系统④-百万级数据和千万级数据简单测试
原文:构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(37)-文章发布系统④-百万级数据和千万级数据简单测试 系列目录 我想测试EF在一百万条数据下的显示时间! ...
- poi实现百万级数据导出
注意使用 SXSSFWorkbook 此类在构造表格和处理行高的时候效率极高,刚开始时我使用的 XSSFWorkbook 就出现构造表格效率极低,一万行基本需要3秒左右,那当导出百万级数据就慢的要死啦 ...
- Sql Server中百万级数据的查询优化
原文:Sql Server中百万级数据的查询优化 万级别的数据真的算不上什么大数据,但是这个档的数据确实考核了普通的查询语句的性能,不同的书写方法有着千差万别的性能,都在这个级别中显现出来了,它不仅考 ...
- 实战手记:让百万级数据瞬间导入SQL Server
想必每个DBA都喜欢挑战数据导入时间,用时越短工作效率越高,也充分的能够证明自己的实力.实际工作中有时候需要把大量数据导入数据库,然后用于各种程序计算,本文将向大家推荐一个挑战4秒极限让百万级数据瞬间 ...
- 【转 】实战手记:让百万级数据瞬间导入SQL Server
想必每个DBA都喜欢挑战数据导入时间,用时越短工作效率越高,也充分的能够证明自己的实力.实际工作中有时候需要把大量数据导入数据库,然后用于各种程序计算,本文将向大家推荐一个挑战4秒极限让百万级数据瞬间 ...
随机推荐
- Android系统启动过程[典☆☆☆]
Android系统启动过程 首先Android框架架构图:(来自网上,我觉得这张图看起来很清晰) Linux内核启动之后就到Android Init进程,进而启动Android相关的服务和应用. 启动 ...
- Fiddler4工具配置及调试手机和PC端浏览器
Fiddler最大的用处: 模拟请求.修改请求.手机应用调试 Fiddler最新版本 下载地址: http://www.telerik.com/download/fiddler Fiddler 想要监 ...
- 浅谈web应用的负载均衡、集群、高可用(HA)解决方案
http://aokunsang.iteye.com/blog/2053719 声明:以下仅为个人的一些总结和随写,如有不对之处,还请看到的网友指出,以免误导. (详细的配置方案请google,这 ...
- mac下ssh到远程服务器时中文乱码
前言:mac本地的语言环境为英文,远程是支持中文的, 问题: 一开始是在iterm2下登录远程服务器更新数据库时发现中文注释不能正常显示,以为是iterms2下设置有问题,使用系统自带的termina ...
- RedHat5.4 使用 centOS5源更新
1.卸载HedHat5.4的yum命令 先查看RedHat上是否安装yum 删除所有的yum软件 rpm -qa | grep yum | xargs rpm - ...
- Spring容器基础ClassPathXmlApplicationContext(一起看源码)
ApplicationContext相比较于BeanFactory,扩展了很多功能.也就是说前者包含了后者的所有功能.使用前者加载XML的方式:ApplicationContext app=new C ...
- Leetcode——Third Maximum Number
Question Given a non-empty array of integers, return the third maximum number in this array. If it d ...
- Lucene TF-IDF 相关性算分公式
转自: http://lutaf.com/210.htm Lucene在进行关键词查询的时候,默认用TF-IDF算法来计算关键词和文档的相关性,用这个数据排序 TF:词频,IDF:逆向文档频率,TF- ...
- JDK 中的监控与故障处理工具-01
当给系统定位问题的时候, 我们经常需要了解并分析 JVM 的运行时状态 . 那应该从哪些方面入手呢? 答案就是从数据入手 . 这里的数据包括: GC日志,异常堆栈, 线程快照(threaddump) ...
- SQL 循环 FOR 语句
) DECLARE My_Cursor CURSOR --定义游标 FOR (SELECT userid FROM User) --查出需要的集合放到游标中 OPEN My_Cursor; --打开游 ...