elasticsearch地理位置总结

2023-11-05

参考:

https://blog.csdn.net/tang_jian_dong/article/details/104446526

https://blog.csdn.net/u013041642/article/details/94416631

在elasticsearch中默认支持了地理坐标排序,非常方便。

项目中我们位置相关的功能主要有:位置由近到远排序、计算距离值、距离与价格一起权重打分

springboot项目

<!-- es -->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>

实体类

package com.dto;

import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldIndex;
import org.springframework.data.elasticsearch.annotations.FieldType;
import org.springframework.data.elasticsearch.annotations.GeoPointField;

//商品
@Document(indexName = "sku", type = "sku", shards = 5, replicas = 1, indexStoreType = "fs", refreshInterval = "-1")
public class EsSku {

	@Id
	@Field(type = FieldType.Integer)
	private Integer skuId;


	/**
	 * 商品名称
	 */
	@Field(type = FieldType.String)
	private String skuName;

	/**
	 * 坐标,sql:CONCAT(ss.latitude,',',ss.longitude) position,坐标值得校验不然保存报错
	 */
	@GeoPointField
	private String position;

	/**
	 * 距离
	 */
	@Field(type = FieldType.String, index = FieldIndex.no)
	private String distance;

	/**
	 * 价格
	 */
	@Field(type = FieldType.Integer, index = FieldIndex.not_analyzed)
	private Integer price;

	get/set

}

dao

package com.repository;

import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;


public interface EsSkuRepository extends ElasticsearchRepository<EsSku, Integer> {

}

service

package com.impl;

import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.Resource;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.common.geo.GeoDistance;
import org.elasticsearch.common.lucene.search.function.CombineFunction;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.GeoDistanceQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.sort.GeoDistanceSortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.stereotype.Service;

import com.google.common.collect.Maps;
import com.google.common.collect.Sets;


@Service
public class SearchServiceImpl implements SearchService {

	private static final Logger LOGGER = LoggerFactory.getLogger(SearchServiceImpl.class);

	@Resource
	private EsSkuRepository esSkuRepository;

	private PageBean<EsSku> searchSku() {
		String keywords = "";
		Double lat = 39.939915;
		Double lng = 116.469635;
		boolean hasPosition = lat != null && lng != null;
		Double distance = 1000;
		Integer pageNum = 0;
		Integer pageSize = 10;
		// 分页
		Pageable pageable = new PageRequest(pageNum, pageSize);
		// 需要查询的字段
		QueryBuilder keywordsBuilder = null;
		// 如果是拼音则匹配全部同音汉字,如果是汉字则不匹配同音字
		if (StringUtils.isEmpty(keywords)) {
			// 0~10元之间评分为1, 1万元时评分为0.5,价格越高评分越低
			ScoreFunctionBuilder priceFunctionBuilder = ScoreFunctionBuilders.gaussDecayFunction("price", 500, 500000).setOffset(500);
			if (hasPosition) {
				// 1km之内评分为1, 10km时评分为0.5,距离越远评分越低
				Map<String, Double> origin = Maps.newHashMap();
				origin.put("lat", lat);
				origin.put("lon", lng);
				ScoreFunctionBuilder gaussDecayFunction = ScoreFunctionBuilders.gaussDecayFunction("position", origin, "10km").setOffset("1km");
				keywordsBuilder = QueryBuilders.functionScoreQuery(gaussDecayFunction).add(priceFunctionBuilder).boostMode(CombineFunction.MULT);
			} else {
				keywordsBuilder = QueryBuilders.functionScoreQuery(priceFunctionBuilder).boostMode(CombineFunction.MULT);
			}
		} else {
			keywordsBuilder = QueryBuilders.multiMatchQuery(keywords, "skuName");
		}
		// 坐标排序
		GeoDistanceQueryBuilder geoBuilder = null;
		GeoDistanceSortBuilder sortGeoBuilder = null;
		if (hasPosition) {
			geoBuilder = QueryBuilders.geoDistanceQuery("position")// 查询字段
					.point(lat, lng)// 设置经纬度
					.distance(distance, DistanceUnit.KILOMETERS)// 设置距离查询的距离范围
					.geoDistance(GeoDistance.ARC);
			sortGeoBuilder = SortBuilders.geoDistanceSort("position").point(lat, lng).unit(DistanceUnit.KILOMETERS);
			sortGeoBuilder.order(SortOrder.ASC);
		}

		// 创建查询对象
		BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery().must(keywordsBuilder);
		NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
		nativeSearchQueryBuilder.withQuery(queryBuilder).withPageable(pageable);
		
		// 如果开启定位,则只查询定位内数据
		if (hasPosition) {
			nativeSearchQueryBuilder.withFilter(geoBuilder);
		}
		SearchQuery searchQuery = nativeSearchQueryBuilder.build();
		// 结果
		Page<EsSku> page = esSkuRepository.search(searchQuery);
		List<EsSku> list = page.getContent();
		// 计算距离
		if (CollectionUtils.isNotEmpty(list) && hasPosition) {
			for (EsSku esSku : list) {
				Double latitude = Double.valueOf(esSku.getLatitude());
				Double longitude = Double.valueOf(esSku.getLongitude());
				double calculateDistance = GeoDistance.ARC.calculate(lat, lng, latitude, longitude, DistanceUnit.KILOMETERS);
				String formatDistance = formatDistance(calculateDistance);
				esSku.setDistance(formatDistance);
			}
		}
		return new PageBean<EsSku>(pageNum + 1, pageSize, (int) page.getTotalElements(), list);
	}

	// 美化距离文本
	private String formatDistance(double distance) {
		if (distance == 0) {
			return "0m";
		} else if (distance > 1) {
			distance = (double) Math.round(distance * 100) / 100;
			String s = new StringBuilder().append(distance).append("km").toString();
			return s;
		} else {
			distance = distance * 1000;
			String s = new StringBuilder().append((int) distance).append("m").toString();
			return s;
		}
	}

}

pagebean

package com.dto;

import java.util.List;

/**
 * 分页功能中的一页的信息
 */
public class PageBean<T> {

	// 指定的或是页面参数
	private int pageNum; // 当前页
	private int pageSize; // 每页显示多少条

	// 查询数据库
	private int recordCount; // 总记录数
	private List<T> recordList; // 本页的数据列表

	// 计算
	private int pageCount; // 总页数
	private int beginPageIndex; // 页码列表的开始索引(包含)
	private int endPageIndex; // 页码列表的结束索引(包含)

	/**
	 * 只接受前4个必要的属性,会自动的计算出其他3个属生的值
	 * 
	 * @param pageNum
	 * @param pageSize
	 * @param recordCount
	 * @param recordList
	 */
	public PageBean(int pageNum, int pageSize, int recordCount, List<T> recordList) {
		this.pageNum = pageNum;
		this.pageSize = pageSize;
		this.recordCount = recordCount;
		this.recordList = recordList;

		// 计算总页码
		pageCount = (recordCount + pageSize - 1) / pageSize;

		// 计算 beginPageIndex 和 endPageIndex
		// >> 总页数不多于10页,则全部显示
		if (pageCount <= 10) {
			beginPageIndex = 1;
			endPageIndex = pageCount;
		}
		// >> 总页数多于10页,则显示当前页附近的共10个页码
		else {
			// 当前页附近的共10个页码(前4个 + 当前页 + 后5个)
			beginPageIndex = pageNum - 4;
			endPageIndex = pageNum + 5;
			// 当前面的页码不足4个时,则显示前10个页码
			if (beginPageIndex < 1) {
				beginPageIndex = 1;
				endPageIndex = 10;
			}
			// 当后面的页码不足5个时,则显示后10个页码
			if (endPageIndex > pageCount) {
				endPageIndex = pageCount;
				beginPageIndex = pageCount - 10 + 1;
			}
		}
	}

	public List<T> getRecordList() {
		return recordList;
	}

	public void setRecordList(List<T> recordList) {
		this.recordList = recordList;
	}

	public int getPageNum() {
		return pageNum;
	}

	public void setPageNum(int pageNum) {
		this.pageNum = pageNum;
	}

	public int getPageCount() {
		return pageCount;
	}

	public void setPageCount(int pageCount) {
		this.pageCount = pageCount;
	}

	public int getPageSize() {
		return pageSize;
	}

	public void setPageSize(int pageSize) {
		this.pageSize = pageSize;
	}

	public int getRecordCount() {
		return recordCount;
	}

	public void setRecordCount(int recordCount) {
		this.recordCount = recordCount;
	}

	public int getBeginPageIndex() {
		return beginPageIndex;
	}

	public void setBeginPageIndex(int beginPageIndex) {
		this.beginPageIndex = beginPageIndex;
	}

	public int getEndPageIndex() {
		return endPageIndex;
	}

	public void setEndPageIndex(int endPageIndex) {
		this.endPageIndex = endPageIndex;
	}

}

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

elasticsearch地理位置总结 的相关文章

  • 将 Elasticsearch 结果导出到 CSV 文件

    我正在尝试将使用以下查询找到的结果导出到桌面上的 CSV 中 这是我第一次使用 Elasticsearch 和 cURL 所以我对如何做到这一点感到困惑 from elasticsearch import Elasticsearch es
  • 在ElasticSearch中搜索没有时间的日期字段值

    我的数据中有一个日期字段为 type date format dateOptionalTime 现在我的日期字段和值是 INITIAL EXTRACT DATE 2015 04 02T06 47 57 78 05 30 在搜索时 我仅根据
  • 为什么我在elasticsearch中需要“store”:“yes”?

    我真的不明白为什么核心类型链接 http www elasticsearch org guide reference mapping core types 它在属性描述中说 例如 对于数字 store 设置为 yes 将实际字段存储在索引中
  • 了解elasticsearch如何在内部存储日期

    我想了解 ES 如何在其索引内部存储日期值 它会转换为 UTC 吗 我有一个日期类型的字段 t 这是映射 t type date 现在 当我向 ES 插入 添加文档时 它如何存储在索引中 t 1427700477165 从 Date now
  • Elasticsearch TransportClient NetworkPlugin NoClassDefFoundError

    我期待将 Elasticsearch 集成到 Spring Boot Web 应用程序中 这是创建传输客户端的配置 Configuration public class ElasticsearchConfig private Transpo
  • Python elasticsearch DSL 聚合/每个文档嵌套值的度量

    我试图找到 2 级嵌套中的最小值 每个文档单独的最小值 到目前为止 我能够进行聚合 计算搜索结果中所有嵌套值的最小值 但无需按文档进行分隔 我的示例架构 class MyExample DocType myexample id Intege
  • Elasticsearch 中的嵌套与对象

    有人可以解释 Elasticsearch 文档中 对象 和 嵌套 字段之间的区别吗 我知道默认情况下字段被定义为对象 我还知道我可以用这样的点访问对象字段 my field name my field title 等 对象的文档 http
  • 使用 KMS 的 AWS RDS 加密是否会影响性能?

    Amazon states https aws amazon com about aws whats new 2015 01 06 amazon rds encryption with kms mysql postgresql that 加
  • 如何使用elasticsearch进行分页?来自 vs 滚动 API

    我使用elasticsearch作为数据库来存储大量日志数据 我知道有两种方法可以进行分页 使用大小并来自 API 使用滚动API 现在我使用 from 进行分页 从前端和后端获取页面和大小参数 Java searchSourceBuild
  • 从中间部分匹配完成建议elasticsearch

    我有一个名为搜索建议具有以下 search suggest type completion analyzer simple payloads true preserve separators false preserve position
  • 全文搜索 DynamoDB

    以下情况 我正在为我的客户将元素存储在 DyanmoDb 中 HashKey 是元素 ID Range Key 是客户 ID 除了这些字段之外 我还存储一个字符串数组 gt 标签 例如 Pets House 和多行文本 我想在我的应用程序中
  • C# Elasticsearch NEST 无法转换 lambda 表达式

    我遇到了与此处描述的完全相同的问题 但未得到解答 ElasticSearch NEST 搜索 https stackoverflow com questions 24615676 elasticsearch nest search I us
  • 在流浪机器中使用elasticsearch设置graylog2服务器

    我正在尝试在本地开发计算机上安装graylog2 服务器 但遇到了elasticsearch 设置问题 我的elasticsearch作为服务安装在我的开发机器上运行的vagrant机器上 所以我的elasticsearch没有安装在127
  • 弹性搜索 - search_after 参数

    我读了这个doc https www elastic co guide en elasticsearch reference 5 0 search request search after html要理解 search after 并有两个
  • Elasticsearch 关于“空索引”的查询

    在我的应用程序中 我使用了几个elasticsearch索引 它们在初始状态下不包含索引文档 我认为这可以称为 空 该文档的映射是正确且有效的 该应用程序还有一个包含实体的关系数据库 这些实体可能具有在 elasticsearch 中关联的
  • 如何增加vm.max_map_count?

    我正在尝试在 Ubuntu EC2 计算机 t2 medium 中运行弹性搜索 但我收到消息 最大虚拟内存区域 vm max map count 65530 太低 至少增加到 262144 我怎样才能增加vm max map count v
  • Elasticsearch:根据类型对不同字段进行排序

    我的索引中有两种类型 Event and City 我正在尝试按日期将它们全部排序 但是 每种类型的日期字段名称都不同 为了Event该值是在updated at领域和City日期是在update at其嵌套对象之一中的字段city eve
  • 在 Elasticsearch Java API 中使用 slop 维护匹配短语查询的序列顺序

    我正在尝试执行一个查询 该查询应该与短语查询类似 只是连续标记内可能存在间隙 例子 Document a b c d Search a c gt gt This should return the document Search c a g
  • 使用文件的 AWS Elasticsearch 同义词

    我正在尝试使用文件将同义词添加到弹性搜索 如本链接所示 但它给了我以下错误https www elastic co guide en elasticsearch reference 5 2 analysis synonym tokenfil
  • 用于全文搜索和 2 亿多条记录的数据库

    我即将创建一个包含至少 2 亿个条目的庞大数据库 数据库需要可使用全文进行搜索 并且速度应该很快 我的数据库从许多不同的数据源获取数据 我需要定期导入新的或更新的数据 将我的所有数据存储在像 mysql 这样的关系数据库中 然后创建一个 n

随机推荐