Mastering Solr Spatial Search: From Configuration to Real-World Queries
This guide explains how to implement location‑based job search in a WeChat mini‑program using Solr’s spatial capabilities, covering schema setup, index building, query syntax, performance considerations, and practical code examples, while also comparing Solr with Elasticsearch for similar use cases.
Background and Problem
A WeChat mini‑program needs to display job positions within a configurable radius (e.g., 3 km, 5 km, 10 km) using Tencent Map. The dataset contains millions of job records, making relational‑database queries too slow for interactive use, so a search engine with geospatial capabilities is required.
Choice of Search Engine
Elasticsearch considered
Native support for geo_point and geo_shape types, enabling distance, bounding‑box and polygon queries.
Rich boolean, nested and aggregation queries for complex filtering.
Bulk and near‑real‑time indexing for frequent data updates.
Advanced full‑text analysis and tokenizers.
Scalable performance on large data sets.
Simple RESTful API and visual tools (Kibana).
Active community and extensive documentation.
Solr selected
The existing platform already uses Solr; switching to Elasticsearch would require extensive refactoring of indexing, query and configuration code. Therefore the solution is built on Solr’s spatial search.
Solr Spatial Search Overview
Solr, built on Lucene, provides spatial search for points, polygons and lines. It is widely used in location‑based services (LBS), GIS, logistics, real‑estate and other location‑aware applications.
Index Structures
R‑Tree
Stores multi‑dimensional objects using their minimum bounding rectangles, allowing fast pruning of irrelevant branches during queries.
Quad‑Tree
Recursively divides 2‑D space into four quadrants; each node represents a rectangle, enabling efficient point‑in‑area searches.
Spatial Field Types
Typical field types are PointType for simple coordinates and solr.LatLonPointSpatialField (or LatLonPointSpatialField) for latitude/longitude pairs.
Query Syntax
Solr supports BBox, Distance and IsWithin queries. Example distance query (10 km around New York City): {!geofilt sfield=location pt=40.7128,-74.0060 d=10}.
Implementation Steps
1. Schema Configuration
<field name="location" type="location" indexed="true" stored="true"/>
<fieldType name="location" class="solr.LatLonPointSpatialField" docValues="true"/>
<field name="GPSX" type="pdouble" indexed="true" stored="true"/>
<field name="GPSY" type="pdouble" indexed="true" stored="true"/>
<field name="id" type="string" indexed="true" stored="true" required="true"/>2. Java DTO
package com.neusoft.lpleaf6.job.api.dto.v;
import org.apache.solr.client.solrj.beans.Field;
import org.springframework.data.annotation.Id;
import lombok.Data;
@Data
public class VJobSorlDataDto implements java.io.Serializable {
private static final long serialVersionUID = 1L;
@Id
@Field("id")
private String acb210;
@Field("GPSX")
private double gpsx;
@Field("GPSY")
private double gpsy;
@Field("location")
private String location;
}3. Building the Index
@Autowired
private SolrService solrService;
@Override
@Transactional
public void buildJobIndex() {
// 1. Clear existing index
SolrDataQuery query = new SimpleQuery("*:*");
solrService.deleteByQuery("JOB", query);
// 2. Load job data from DB
List<Job> jobList = jobService.queryJobList();
if (jobList != null && !jobList.isEmpty()) {
List<VJobSorlDataDto> list = DTOUtil.copyList(jobList, VJobSorlDataDto.class)
.stream()
.peek(dto -> dto.setLocation(dto.getGpsy() + "," + dto.getGpsx())) // location = "lat,lon"
.collect(Collectors.toList());
// 3. Upload to Solr
solrService.uploadBeans("JOB", list);
}
}4. Querying the Index (SolrJ)
@Override
@Transactional
public QueryResult<VCb21IndexDto> queryJobIndexPage(VCb21IndexDto dto, Integer pageNo, Integer pageSize) {
StringBuilder querysql = new StringBuilder();
if (StringUtils.isNotBlank(dto.getAce751())) {
querysql.append(" +ACE751:").append(dto.getAce751());
}
SimpleQuery query = new SimpleQuery(querysql.toString());
if (dto.getGpsx() != null && dto.getGpsy() != null && dto.getRange() != null) {
String geofilt = "{!geofilt sfield=location pt=" + dto.getGpsy() + "," + dto.getGpsx() + " d=" + dto.getRange() + "}";
query.addFilterQuery(new SimpleFilterQuery(new Criteria(geofilt)));
}
// pagination & sorting (omitted for brevity)
Page<VJobSorlDataDto> page = solrService.query(IndexConstDto.INDEX_JOB_COLLECTION, query, VJobSorlDataDto.class);
return SolrUtil.copyPage(page.getContent(), page.getTotalElements(), pageNo, pageSize, VCb21IndexDto.class);
}5. Simple HTTP Distance Query
String url = "http://localhost:8983/solr/mycore";
SolrClient solr = new HttpSolrClient.Builder(url).build();
SolrQuery query = new SolrQuery();
query.set("q", "{!geofilt sfield=location pt=40.7128,-74.0060 d=10}");
QueryResponse resp = solr.query(query);
for (SolrDocument doc : resp.getResults()) {
System.out.println(doc);
}Typical Application Scenarios
Location‑based services (food delivery, ride‑hailing, etc.)
Geographic Information Systems (maps, point‑of‑interest search)
Logistics and transportation tracking
Real‑estate recommendation based on proximity
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Architect
Professional architect sharing high‑quality architecture insights. Topics include high‑availability, high‑performance, high‑stability architectures, big data, machine learning, Java, system and distributed architecture, AI, and practical large‑scale architecture case studies. Open to ideas‑driven architects who enjoy sharing and learning.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
