尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

OpenCloud 搜索引擎的地理空间检索:bleve geoshape 字段类型、GeoJSON 索引与 S2 空间分词

OpenCloud 搜索引擎的地理空间检索:bleve geoshape 字段类型、GeoJSON 索引与 S2 空间分词 OpenCloud 搜索引擎的地理空间检索bleve geoshape 字段类型、GeoJSON 索引与 S2 空间分词【免费下载链接】opencloud️ OpenCloud is the open source platform for file management, sharing and collaboration. Simple and sovereign.项目地址: https://gitcode.com/GitHub_Trending/op/opencloud本文围绕 bleve 搜索引擎的空间搜索能力文档geo/README.md展开讲解geoshape字段类型支持的全部 GeoJSON 形状、Circle/Envelope 扩展形状、三种空间查询关系intersects/contains/within的用法与限制并结合 OpenCloud 仓库中 search 服务的映射代码与测试展示地理坐标字段如何进入 bleve 索引、距离查询如何被执行。读完后你将掌握如何为文档声明地理空间字段、如何构造 GeoShape 查询、以及底层 S2 分层空间 token 的索引与查询机制。为什么 OpenCloud 的搜索栈里有一份 bleve geo 文档OpenCloud 的 search 服务 提供两种后端OpenSearch 与纯 Go 的 bleve见 bleve 后端封装。bleve 通过 Go module vendoring 进入本仓库其地理空间搜索子系统的说明文档即 vendor/github.com/blevesearch/bleve/v2/geo/README.md。该文档的核心结论是最新一代空间能力由s2geometry 生成的空间分层 tokenspatial hierarchical tokens驱动bleve 使用的是其 fork 的 Go 端口github.com/blevesearch/geo在源码中以 geojson/s2 包引入旧的geopoint字段类型及其 Point Distance、Bounded Rectangle、Bounded Polygon 查询保持兼容新增geoshape字段类型解锁 GeoJSON 全部形状的空间能力。从源码结构看geov2子包query_intersects.go、query_contains.go、query_within.go正是文档所述三种关系过滤器的查询实现与文档中的 Relation 表格一一对应。旧能力geopoint 字段与三种查询在geoshape出现之前bleve 已支持geopoint字段类型可用于索引单个坐标点并支持以下查询形式Point Distance点到点距离查询Bounded Rectangle有界矩形查询;Bounded Polygon有界多边形查询。这套旧实现是 Go 语言对 Lucene 5.3.2 sandbox geo 支持的移植。其核心数据结构在 geo/geo.go 中// Point represents a geo point. type Point struct { Lon float64 json:lon Lat float64 json:lat } // MortonHash computes the morton hash value for the provided geo point // This point is ordered as lon, lat. func MortonHash(lon, lat float64) uint64 { return numeric.Interleave(scaleLon(lon), scaleLat(lat)) }要点单点使用 64 位 Morton 编码经度 32 位、纬度 32 位见GeoBits常量经度按 360° 范围、纬度按 180° 范围缩放后位交织经纬度合法范围是lon ∈ [-180, 180]、lat ∈ [-90, 90]越界会返回 errorcheckLatitude/checkLongitude距离查询会先调用 RectFromPointDistance以平均地球半径 6371008.7714 米把半径换算成角度算出查询矩形含极点特判再落到矩形过滤上。旧查询几何体在 geo_s2plugin_impl.go 中同样以boundedRectangle、boundedPolygon、pointDistance三个类型实现它们的QueryTokens分别用 S2 的RectFromDegrees、PolygonFromOrientedLoops、CapFromCenterAndRadius构造查询区域取出区域覆盖词后调用geojson.StripCoveringTerms去除覆盖词只保留实际可命中的 cell token。新字段类型geoshapebleve 引入geoshape字段类型来表示新的空间类型。使用该类型后用户可以解锁对 GeoJSONRFC 7946形状的空间能力PointLineStringPolygonMultiPointMultiLineStringMultiPolygonGeometryCollection在此之外bleve 还额外支持两种形状Circle圆Envelope有界盒/包围矩形GeoJSON 数据的声明方式指定 GeoJSON 数据时使用嵌套字段约定名为type的字段值为 GeoJSON 对象类型大小写不敏感名为coordinates的字段指定对象的坐标。fieldName: { type: GeoJSON Type, coordinates: coordinates }坐标规则经纬度坐标必须先经度、后纬度lon, lat经度合法范围 [-180, 180]闭区间纬度合法范围 [-90, 90]闭区间形状在内部以**大地线geodesic**形式表示GeoJSON 规范强烈建议拆分几何使其任何一部分都不跨越反子午线antimeridian。各形状的示例Point{ type: point, coordinates: [75.05687713623047, 22.53539059204079] }LineString{ type: linestring, coordinates: [ [77.01416015625, 23.0797317624497], [78.134765625, 20.385825381874263] ] }Polygon首尾坐标必须相同以闭合多边形且外环坐标必须按**逆时针CCW**方向排列。{ type: polygon, coordinates: [ [ [85.605, 57.207], [86.396, 55.998], [87.033, 56.716], [85.605, 57.207] ] ] }MultiPoint{ type: multipoint, coordinates: [ [-115.8343505859375, 38.45789034424927], [-115.81237792968749, 38.19502155795575], [-120.80017089843749, 36.54053616262899], [-120.67932128906249, 36.33725319397006] ] }MultiLineString{ type: multilinestring, coordinates: [ [ [-118.31726074, 35.250105158], [-117.509765624, 35.3756141] ], [ [-118.696289, 34.624167789], [-118.317260742, 35.03899204] ], [ [-117.9492187, 35.146862906], [-117.6745605, 34.41144164] ] ] }MultiPolygon{ type: multipolygon, coordinates: [ [ [ [-73.958, 40.8003], [-73.9498, 40.7968], [-73.9737, 40.7648], [-73.9814, 40.7681], [-73.958, 40.8003] ] ], [ [ [-73.958, 40.8003], [-73.9498, 40.7968], [-73.9737, 40.7648], [-73.958, 40.8003] ] ] ] }GeometryCollection注意此形状使用geometries数组承载多个成员形状而不是coordinates。{ type: geometrycollection, geometries: [ { type: multipoint, coordinates: [ [-73.958, 40.8003], [-73.9498, 40.7968], [-73.9737, 40.7648], [-73.9814, 40.7681] ] }, { type: multilinestring, coordinates: [ [ [-73.96943, 40.78519], [-73.96082, 40.78095] ], [ [-73.96415, 40.79229], [-73.95544, 40.78854] ], [ [-73.97162, 40.78205], [-73.96374, 40.77715] ], [ [-73.9788, 40.77247], [-73.97036, 40.76811] ] ] }, { type: polygon, coordinates: [ [ [0, 0], [3, 6], [6, 1], [0, 0] ], [ [2, 2], [3, 3], [4, 2], [2, 2] ] ] } ] }Circle覆盖地球表面上的圆形区域由中心点坐标加半径指定。{ type: circle, coordinates: [75.05687713623047, 22.53539059204079], radius: 1000m }半径支持的格式5in、5inch、7yd、7yards、9ft、9feet、11km、11kilometers、3nm、3nauticalmiles、13mm、13millimeters、15cm、15centimeters、17mi、17miles、19m、19meters。若无法识别单位则整个字符串被解析为米meters。这一点在 geo_dist.go 中得到印证ParseDistance遍历inch/yard/feet/kilom/nauticalm/millim/centim/miles/meters九个单位的后缀表换算系数分别为 0.0254 / 0.9144 / 0.3048 / 1000 / 1852 / 0.001 / 0.01 / 1609.344 / 1 米按后缀匹配后返回米制距离匹配不到任何单位时按纯数字解析并假定单位为米。Envelope由包围矩形的左上角与右下角坐标组成格式为[[minLon, maxLat], [maxLon, minLat]]。{ type: envelope, coordinates: [ [72.83, 18.979], [78.508, 17.4555] ] }形状解析的源码验证文档描述的“type 大小写不敏感、coordinates 提取”等行为对应 geo/parse.go 中的实现ParseGeoShapeField遍历输入 map取出type经strings.ToLower归一化即大小写不敏感与coordinatesExtractGeoShapeCoordinates按形状类型做结构化校验——Point 需要 2 个坐标值Envelope 必须恰好 2 个坐标点LineString 至少 2 个点Polygon 首环至少 3 个点MultiLineString 每条线至少 2 个点MultiPolygon 每个多边形外环至少 3 个点。校验失败的形状会被静默忽略返回foundfalseExtractCircle额外读取radius字段字符串中心点复用ExtractGeoPoint解析ExtractGeometryCollection遍历geometries数组对每个成员递归调用extractGeoShape。形状类型常量集中定义在 geo_s2plugin_impl.gopoint、multipoint、linestring、multilinestring、polygon、multipolygon、geometrycollection、circle、envelope与文档枚举一致。GeoShape 查询Geoshape 查询提供三种空间过滤能力可以跨异构类型mixed types的已索引文档执行查询。查询结构{ query: { geometry: { shape: { type: shapeType, coordinates: [ [[]] ] }, relation: filterName } } }shapeType可以是前述任意类型——Point、LineString、Polygon、MultiPoint、GeometryCollection、MultiLineString、MultiPolygon、Circle、EnvelopefilterName三种关系之一——intersects、contains、within。Relation 语义过滤名说明intersects返回所有形状字段与查询几何相交的文档contains返回所有形状字段包含查询几何的文档within返回所有形状字段位于查询几何之内的文档在 vendored 源码中这三种关系分别由 geov2/query_intersects.go、geov2/query_contains.go、geov2/query_within.go 实现配合 geov2/evaluator.go 的求值器与 geov2/cells.go 的 cell 覆盖计算geov2/score.go 中亦注明了 geoshape_v2 索引与查询共享同一 cell level。底层原理S2 空间分层 tokengeoshape的索引与查询都建立在 S2 Geometry 的分层空间索引之上。bleve 在包初始化时注册S2SpatialAnalyzerPluginregisterS2RegionTermIndexer该插件持有三个s2.RegionTermIndexer索引器用途maxLevelminLevellevelModmaxCells其他s2IndexerGeoJSON 形状的索引时token162120s2SearcherGeoJSON 形状的查询时token16218s2GeoPointsRegionTermIndexer旧版 geopoint 的索引/查询16428PointsOnly: true各参数含义来自源码注释maxLevel近似区域所用 S2Cell 的最大级别越深 cell 越小minLevel最小级别cell 大小的下界levelMod大于 1 时通过跳级提高 S2Cell 层次的有效分支因子maxCells近似每个区域时使用的 cell 数上限。从源码结构看索引时允许每个形状最多 20 个 cell而查询时上限为 8 个——查询侧用更粗的覆盖换取更低代价再由后续精确判定Intersects/Contains等过滤假阳性。token 生成流程在 GetIndexTokens / GetQueryTokensGeometryCollection 会被拆成成员形状分别取词最终经geojson.DeduplicateTerms去重。索引时这些 token 直接写入倒排索引——scorch 引擎 会把field.EncodedShape()的字节串作为 term 建TokenFreq文档值docvalues侧document/field_geoshape.go 注释说明geoshape 字段总是启用 docvalues保存形状字节以便查询期精确判定且跳过 snappy 压缩与 chunking。查询路径则是“先粗筛后精判”查询几何生成 cell 覆盖词 → 倒排索引按 term 求交集缩小候选集 → 再读取 docvalues 中保存的原始形状做精确的空间关系判定即前文 intersects/contains/within 三种关系。这也是为什么文档强调“形状内部以大地线表示”——精确判定发生在球面几何层而非矩形近似层。OpenCloud 中的实际应用_geopoint兄弟字段OpenCloud 的 search 服务将资源Resource中的Locationlibre-graph 的GeoCoordinates含 longitude/latitude/altitude映射进 bleve。其策略见 mapping/geo.go原始location对象保持原样保留用于数据取回与数值查询额外生成一个名为location_geopointGeopointSuffix _geopoint的兄弟字段承载{lat, lon}形式即 bleve 的geopoint类型BleveBuildMapping 对该兄弟字段生成type: geopoint的 mapping且要求 geopoint 覆盖必须落在 struct 类型字段上否则报错。对应的 mapping 产物可见 mapping.golden.jsonlocation_geopoint被声明为enabled、dynamic、store、index的geopoint字段。geo_verify_test.go 用内存索引bleveSearch.NewMemOnly验证了整条管线字段往返location.longitude/latitude/altitude三个子字段都进入hit.Fields这是 Move/Delete/Restore 往返依赖的不变量数值范围查询对location.latitude做[49.0, 50.0]数值范围查询命中[0, 10]不命中location.altitude做下界查询同样生效距离查询对location_geopoint字段执行query.NewGeoDistanceQuery(11.10, 49.48, 10km)索引点在 10km 半径内命中把中心移到约 400km 外的柏林则不命中——这正对应文档中旧实现保留的 “Point Distance” 能力其距离字符串10km恰好走的就是前文ParseDistance的km → 1000换算路径。也就是说OpenCloud 当前在 bleve 后端上使用文档所述旧一代geopoint 距离查询组合geoshape及其 intersects/contains/within 查询是 bleve 提供的完整能力面OpenCloud 的映射层目前没有对文档直接声明geoshape类型字段——从源码结构看未来若需要对 GeoJSON 形状做包含/相交检索映射层只需为对应字段生成geoshape类型的 mapping 即可复用本文所述的查询结构。已知限制与注意事项Notes文档最后列出的使用限制在接入前必须了解所有 API 对 lon/lat 值使用float64描述点时函数参数或返回值顺序恒为lon, lat高层 API 用 TopLeft/BottomRight 描述包围盒。跨越日期变更线时这可能无法干净地映射到 min/max lon/lat——低层 API 使用 min/max lon/lat需要高层代码相应地拆分盒子Points 与 MultiPoints 只能包含 Points 与 MultiPointsLineStrings 与 MultiLineStrings 只能包含 Points 与 MultiPoints两个 Polygons 或 MultiPolygons 相交时若重叠仅为一条边或一个顶点可能返回任意结果Circle 包含 Polygon 查询若多边形所有顶点都在圆内但顶点方向为顺时针会返回假阳性结果Envelope 的边沿纬线/经线走而不是球面上的最短路径Envelope 与 LineStrings、MultiLineStrings、Polygons、MultiPolygons 的相交查询会把 Envelope 隐式转换为 Polygon边的曲率变化会导致少数边界情形结果不准确。小结与延伸阅读本文以 bleve geo 文档 为主体完整覆盖了三块内容geopoint旧能力与geoshape新字段类型的全部 9 种形状声明格式、GeoShape 查询的三种空间关系、以及 S2 分层 token 的索引/查询参数差异。建议按以下路径继续深入仓库形状常量与 S2 插件注册vendor/github.com/blevesearch/bleve/v2/geo/geo_s2plugin_impl.go形状解析与结构校验vendor/github.com/blevesearch/bleve/v2/geo/parse.go距离单位解析vendor/github.com/blevesearch/bleve/v2/geo/geo_dist.goMorton 编码与矩形/极点处理vendor/github.com/blevesearch/bleve/v2/geo/geo.go空间关系查询实现vendor/github.com/blevesearch/bleve/v2/geov2/OpenCloud 侧映射与测试services/search/pkg/mapping/geo.go、services/search/pkg/bleve/geo_verify_test.go【免费下载链接】opencloud️ OpenCloud is the open source platform for file management, sharing and collaboration. Simple and sovereign.项目地址: https://gitcode.com/GitHub_Trending/op/opencloud创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表