设为首页 收藏本站
查看: 1101|回复: 0

[经验分享] 使用 Apache Lucene 和 Solr 进行位置感知搜索——通过合并非结构化文本和空间数据改进搜索应用程序

[复制链接]

尚未签到

发表于 2015-7-17 12:06:36 | 显示全部楼层 |阅读模式
简介: 不管是通过支持 GPS 的智能手机查找最近的咖啡馆,还是通过社交站点查找附近的朋友,或是查看特定城市中运输某种商品的所有货车,越来越多的人和企业都使用位置感知的搜索服务。创建位置感知搜索服务通常属于昂贵的专用解决方案的一部分,并且一般由地理空间专家完成。不过,很流行的开源搜索库 Apache Lucene 和强大的 Lucene 搜索服务器 Apache Solr 最近添加了空间位置功能。Lucene 和 Solr 专家 Grant Ingersoll 将逐步向您介绍空间搜索的基础知识,并演示如何利用这些功能来增强您的下一个位置感知应用程序。
  地理位置在空间搜索中至关重要!地理位置不仅在地产中至尊为王,将其用在搜索中还能帮助位于特定位置的用户快速找到有用的信息。例如,如果您是企业名录提供商(比如一个 “黄页” 站点),当用户需要找一位水管维修员时,该站点必须返回在用户住所附近的维修员。如果您运营的是一个旅游站点,那么您必须让旅游者能够搜索到他们所在的位置附近的名胜,从而帮助他们丰富旅游行程。如果您要构建一个社交网络站点,那么最好使用位置信息来帮助用户与朋友联系。位置感知设备(比如汽车导航系统和支持 GPS 的摄像机)和大量免费地图数据的普及为构建能够为终端用户搜索高级结果的 Geographical Information Systems (GIS) 提供了各种机会。
  空间信息还可以被利用到搜索领域之外,但在本文中我将主要关注如何通过 Apache Lucene 和 Apache Solr 利用空间信息来改进搜索应用程序。为什么要使用搜索引擎?并不是因为它是许多很好(甚至免费)的 GIS 工具中的必要组成部分。不过,将应用程序构建在搜索引擎的基础上能够提供几个强大的功能,这是其他传统途径无法实现的。搜索系统在合并结构化和非结构化方面非常强劲,这允许用户输入自由形式的查询,比如在搜索免费文本的描述和标题的同时根据地理位置数据限制或修改结果。例如,旅游站点可以实现这样一个特性,它让用户能够在一秒之内找到马萨诸塞州波士顿市的所有 24 小时提供服务并且配有舒适床具的四星级宾馆。有些搜索系统(比如 Apache Solr)还提供对结果集进行分类(参考资料 部分提供关于 Solr 和分类的信息)、突出显示和拼写检查的功能,从而让应用程序能够帮助用户高效地查找所需的结果。
  我首先简单介绍 Lucene 的一些关键概念,深入的细节留给读者自己探索。接下来,我将介绍一些基础的地理空间搜索概念。GIS 是一个广泛的领域,本文难以对其进行详尽的描述,因此我仅关注一些查找服务、人和其他日常事项所需的基础概念。本文的末尾是关于使用 Lucene 和 Solr 索引和搜索空间信息的方法的讨论。我将通过一个真实但很简单的例子来阐述这些概念,并且使用来自 OpenStreetMap (OSM) 项目的数据(参见 参考资料)。
  回顾关键的 Lucene 概念
  Apache Lucene 是一个基于 Java™ 的高性能搜索库。Apache Solr 是一个使用 Lucene 通过 HTTP 来提供搜索、分类等功能的搜索服务器。它们都使用价格适中的 Apache Software License。参见 参考资料 了解更多关于每个产品提供的特性和 API 的信息。
  从本质上看,Solr 和 Lucene 都将内容表示为文档。文档由一个或多个字段 和一个表明文档的重要性的可选增强(boost)值 组成。字段由需要索引和储存的实际内容、告诉 Lucene 如何处理该内容的元数据和表明该字段的重要性的增强值组成。由您决定以何种方式将内容表示为文档和字段,这取决于您希望怎样搜索或访问文档中的信息。在每个内容单元中,您可以使用一对一的关系,也可以使用一对多的关系。例如,我可以选择用一个包含几个字段(比如 title、keywords 和 body)的文档来表示一个 Web 页面。如果是一本书,我则选择将它的每一页表示为一个独立的文档。稍后您将看到,这一区分在为搜索编码空间数据时非常重要。可以为字段中的内容建立索引,或者原样储存供应用程序使用。如果为内容建立了索引,应用程序就可以使用它。还可以分析建立了索引的内容来生成词汇(通常称为令牌)。词汇是在搜索过程中查找和使用的基础。词汇通常是一个词,但这不是必要的。我建议您通过 参考资料部分了解所有这些概念。
  在查询方面,Lucene 和 Solr 为表达用户查询(从基础的关键字查询到短语和通配符查询)提供丰富的功能。Lucene 和 Solr 还通过应用一个或多个对空间搜索非常重要的过滤器来提供限制空间的能力。范围查询范围过滤器 是限制空间的关键机制。在范围查询(或过滤器)中,用户声明需要将所有搜索到的文档限制在使用自然排序的两个值之间。例如,通常使用范围查询来查找发生在过去一年或上一个月的所有文档。在处理过程中,Lucene 必须枚举文档中的词汇以识别在范围之内的所有文档。如我在稍后展示的一样,正确地设置范围查询是提升空间搜索应用程序的查询性能的关键因素之一。
  Lucene 和 Solr 还提供函数查询 的概念,它允许您使用字段的值(比如经度和纬度)作为记录机制的一部分,而不是仅仅使用组成主要的记录机制的内部数据集合。该功能在后文我演示使用 Solr 的一些基于距离的函数时用到。

回页首
  在搜索中将空间数据与文本合并
  一旦在索引中添加了数据之后,搜索应用程序在与数据交互时至少有 5 种基本要求:

  • 距离计算:根据给定点计算它到其他点的距离。
  • 限定框过滤器:查找某些特定区域内所有匹配项(文档)。
  • 排序:根据到固定点的距离对搜索结果进行排序。
  • 相关度改进:使用距离作为记录中的增强因素,同时允许其他因素发挥作用。
  • 查询解析:在给出位置的地址或其他一些用户规定时,创建可用于根据索引数据进行搜索的编码表示。
  这 5 个因素都可以在基于位置的应用程序中扮演重要的角色,但是我在这里主要关注距离计算、限定框过滤和查询解析。排序和相关度改进仅使用距离计算,我将在本文的后面介绍它们的实际应用。
  距离计算
  当计算用于 GIS 应用程序的距离时,一定要知道有许多不同的实现方法,并且每种方法都有其优缺点。距离计算可以划分成 3 个组,这取决于应用程序选择以什么方式对地球进行建模。在一些情况下,完全可以采用平面地球模型,通过牺牲一些精确性来获取速度。在平面地球模型中,大部分距离计算都是勾股定理的变体。在其他情况下使用球面模型,所使用的主要距离计算为大圆弧长(参见 参考资料)。大圆弧长计算球面两点之间的最短距离。当两点之间的距离相隔很远和要求更高的准确度时,需要使用球面模型。最后,可以使用椭圆的地球模型和 Vincenty 公式(参见 参考资料)来获取高度精确的距离(精确到 0.5 毫米),但是在许多应用程序中用不上这种复杂的模型。

差之毫厘,失之千里

  在许多本地搜索应用程序中,精度的需求由应用程序本身决定。在某些情况下,偏离一公里问题并不大,而在另一些情况下,偏离几毫米就会导致严重的问题。例如,欧几里得距离计算对于跨度很长的距离(比如跨州)通常不够精确,即使是半正矢(大圆)方法也不足以为某些场合提供所需的精度,因为将地球建模成椭圆体比建模成球体更精确。对于这些情况,使用 Vincenty 公式将得到更加满意的结果。在其他应用程序中,唯一需要注意的事情是对结果的排序,因此可以使用 Squared Euclidean Distance(实际不是距离),从而避免平方根计算。
  当然,其他距离计算也是有用的,比如曼哈顿距离,它反映在由街区组成的城市中行走的距离(例如在一辆出租车中穿越纽约城的曼哈顿)。但是为了实现本文的目的,我将使用平面地球模型和大圆弧长距离来演示距离,其他方法留给读者探索。此外,本文不将海拔作为影响因素,但是一些应用程序可能需要考虑海拔。要获取更多关于地理距离的信息,请参见 参考资料。
  限定框过滤器
  在许多基于位置的应用程序中,可以搜索到数百万条地址信息。遍历所有这些数据来查找既包含关键字又在用户指定的距离之内的文档集将需要花费大量时间。一种合理的做法是先缩小文档集的范围然后再计算相关的子集。如果仅储存了纬度和经度信息,那么缩小文档集的首选方法是传入包含指定位置的周边区域的范围。这可以通过 图 1 来表示,其中不完全透明的方框表示包含南卡罗来纳州的查尔斯顿(Charleston)市及其周边地区的限定框:

图 1. 位于 Charleston 中央上方的限定框
DSC0000.jpg
  如果应用程序还使用层信息或 Geohash 信息,那么可以使用这些值来更好地缩小需要搜索的文档的范围。我将在讨论使用 Lucene 和 Solr 建立索引和搜索的细节时演示这点。
  查询解析
  查询解析的目的是确定查询的哪个部分包含所搜索的关键字,哪个部分包含位置信息。这个过程的后半部分称为地理编码(geocoding)(参见 参考资料)。尽管我在这里在查询解析的上下文中讨论地理编码,它在索引期间也非常有用。请考虑下面的用户查询例子:

  • 1600 Pennsylvania Ave. Washington, DC
  • 1 Washington Av. Philadelphia Pennsylvania
  • Mall of America, 60 East Broadway Bloomington, MN 55425
  • Restaurants near Mall of America
  • Restaurants in the Mall of America
  查看前两个查询可以发现一些有趣的东西:

  • 词汇的顺序通常很重要,但是在纯文本搜索中,顺序可能不重要。
  • 地名表和其他空间资源,比如 GeoNames(参见 参考资料)可能在将地址转换成位置时非常有用。这些资源通常包含旅游景点的列表 —— 例如,白宫等标志性建筑。
  • 规范化缩写,比如 Ave.DC,或使用同义词来包含用户输入地址信息的各种变体非常重要。
  剩余的查询将展示几个微妙的地方。例如,在第三个查询中,用户指定了完整的地址;如果您要搜索每个字段以获得名称、地址、城市和 ZIP,那么就必须正确地解析这些属性。在最后两个查询中,用户选择 near 还是 in 是非常重要的。与 Mall 的距离在一定范围内的所有饭店都符合第四个查询的用户,而最后一个查询的用户仅对在 Mall 内部的饭店感兴趣。查询解析可能相差甚远,因为描述与位置的关系很复杂,更何况还存在拼写错误、语言歧义和不良数据等。
  虽然地理编码很复杂,但是可以使用服务来将地址转换成位置。两种常用的服务为 Google Maps 公共 API 和 GeoNames(参见 参考资料)。不幸的是,使用这些 Web 服务必须遵循使用条款(通常带有某些限制)和网络流量。对于现实的生产系统,您最好自己实现这些功能。尽管实现这些功能超出了本文的范围,但一定要记住 GeoNames 数据和其他许多空间资源是可以完全免费下载的(参见 参考资料)。有了好的资源之后,最好从基础开始积累(地址、城市和州),然后再添加旅游景点和健壮的异常处理。随着时间的推移,您的查询记录将能够创建健壮的查询解析器,足以应付用户的各种输入。不管是什么搜索应用程序,良好的猜测和请求用户证实猜测结果都是好实践,如 图 2 的 Google Maps 截屏所示:

图 2. 在 Google Maps 上的良好猜测和请求用户证实猜测结果
DSC0001.jpg
  对于本文,我将展示使用 GeoNames 服务并具有一些其他特性的基础查询解析器,但生成版本的解析器将留给用户实现。至此,您应该具备了足够的背景知识,可以进入主题了。本文后面的内容将关注如何使用 Lucene 和 Solr 为空间信息建立索引并搜索它们。

回页首
  在 Lucene 中为空间数据建立索引
  Lucene 2.9 添加了两个在空间搜索方面起到重大作用的新特性。首先,Lucene 实现了更好的数字范围查询和过滤功能,它们通常用在限定框方法中。其次,Lucene 有一个新的贡献软件(contrib)模块,它包含以前称为 Local Lucene 的独立项目(参见 参考资料)。(该代码位于 Lucene 的 contrib/spatial;我已经在 样例代码 中包含了 JAR 文件)。空间贡献软件为创建笛卡儿层和 Geohash 代码提供工具,并且为创建 Lucene 查询和过滤器对象提供工具。
  在查看为数据建立索引的代码之前,您需要评估如何与数据交互以及您的应用程序需要处理多少数据,这非常重要。例如,对于大多数拥有少量或中等程度文档数量(少于 1000 万)的人而言,为纬度和经度创建索引和使用简单的数字范围查询可以得到优异的性能。但是对于数据量更大的应用程序,就需要做更多的工作(比如添加笛卡尔层)来减少词汇的数量和需要过滤和记录的文档。此外,考虑使用什么格式储存信息也很重要。许多空间距离算法要求采用以弧度表示的数据,而其他算法则要求使用以度表示的数据。因此在建立索引时将纬度/经度值转换成弧度是值得的,从而避免在每次搜索都执行转换。当然,如果您需要保留两种格式的数据,则意味着需要更多的空间(磁盘,甚至内存)。最后,您是不是对位置特性进行分类、排序和记录,而不是仅将它们用于过滤?如果是这样,那么将需要交替使用不同的表示。
  因为本文仅演示概念而没有考虑生产使用,所以我将用一些 Java 代码在同一个地方显示如何为 Geohash、笛卡尔层创建索引。我已经在 Solr 模式中定义了许多值(模式的位置为 geospatial-examples/solr/conf/schema.xml)来捕捉 OSM 数据。清单 1 显示了用于表示位置的主要字段:

清单 1. 样例 Solr 模式
                                   


Lucene 和 Solr

  尽管我使用 Solr 模式来展示需要建立索引的字段,这里的所有概念在 Lucene 中都是可用的。例如,Lucene 2.9.1 中的 tdouble 实际上就是精度为 8 的NumericField。
  我将纬度/经度值存储为 tdouble 字段。一个 tdouble 就是在内部使用 Trie 结构表示的一个 double。Lucene 可以使用它来大大减少在范围计算期间需要计算的词汇的数量,尽管实际上它向索引添加了更多词汇。我将 Geohash 储存为一个简单的 string(未分析)因为我仅需要它的精确匹配。严格而言,我进行的这些计算用不到海拔,但我将它储存为tfloat,它是存储在 Trie 结构中的 float。最后,tier_* 动态字段允许应用程序动态地添加笛卡尔层字段,而不需要提前声明它们。至于索引过程捕捉的其他元数据字段,我将留给读者探索。
  负责为数据创建索引的代码位于 sample.zip 的 source 树中。Driver 类是一个用于启动索引过程的命令行实用程序,但实际的索引过程发生在名为 OSMHandler 的实现的 SAX ContentHandler 部分。在 OSMHandler 代码内部,最关键的代码行是 startElement() 方法。我将它分成 3 个部分。第一个例子(见清单 2)以 double 的形式为纬度和经度建立索引,并将它们转换成可以索引的弧度:

清单 2. 纬度/经度的样例索引
//... current is a SolrInputDocument double latitude = Double.parseDouble(attributes.getValue("lat")); double longitude = Double.parseDouble(attributes.getValue("lon")); current.addField("lat", latitude); current.addField("lon", longitude); current.addField("lat_rad", latitude * TO_RADS); current.addField("lon_rad", longitude * TO_RADS);

  为纬度/经度建立索引非常简单。接下来,我为纬度/经度对索引 Geohash 值,如清单 3 所示:

清单 3. 样例 Geohash 索引
//... //See http://en.wikipedia.org/wiki/Geohash String geoHash = GeoHashUtils.encode(latitude, longitude); current.addField("geohash", geoHash);

  在清单 3 的 Geohash 代码中,我使用随 Lucene 空间 contrib 包附带的 GeoHashUtils.encode()(有一个等效的 decode() 方法)方法将纬度/经度对转换成一个 Geohash 字符串,然后再把该字符串添加到 Solr。最后,为了添加笛卡尔层,我在 OSMHandler 代码中完成了两件事情:

  • 我在构造器中创建 CartesianTierPlotter 类的 n 个实例,每个需要建立索引的层一个。
  • 在 startElement() 方法中,我遍历所有 n 个描绘器,并为每个包含当前 OSM 元素的纬度和经度的每个网格元素获取标识符。该代码如清单 4 所示:
    清单 4. 笛卡尔层的样例索引
    //... //Cartesian Tiers int tier = START_TIER; //4 //Create a bunch of tiers, each deeper level has more precision for (CartesianTierPlotter plotter : plotters)    {current.addField("tier_" + tier, plotter.getTierBoxId(latitude, longitude)); tier++; }

  一般情况下,查询一次仅需搜索一个层,因此拥有多个层通常不会造成任何问题。您应该根据搜索所需的粒度来选择层数。如果您花时间查看剩余的索引代码,将看到我添加了许多与 OSM 文件中的数据点相关的其他元数据值。我现在仅为两种 OSM 数据类型建立索引:界点(node)路线(way)。界点是特定的纬度和经度上的一个点,而路线是所有在某种程度上相关的界点的集合,比如街道(参见 参考资料 中的 OSM Data Primitives 链接更多地了解 OSM 文件)。

什么是 CartesianTierPlotter?

  CartesianTierPlotter 的工作是对地球进行投影(在我的例子中,我使用正弦曲线投影;参见 参考资料)和纬度/经度信息,将其转换成层系统所使用的网格,并且给每个网格一个唯一的号码。在搜索时,应用程序就可以通过指定网格 ID 来限制搜索范围。
  您已经了解创建包含空间信息的 Solr 文档的基础知识,接下来将进行实践。Driver 类接收数据和事实文件以及运行 Solr 的 URL,并将该工作转交给 OSM2Solr 类。OSM2Solr 类将使用 Solr 的 Java 客户端 SolrJ 来接收 OSMHandler SAX 解析器创建的文档,并将它们批量发送到 Solr 服务器。您可以在命令行运行 Driver 类,或者只需运行 ant index,让 Ant 完成运行驱动程序所需的工作。完成该步骤之后,在浏览器中访问 http://localhost:8983/solr/select/?q=*:* 并确认 Solr 找到 68,945 个文档。花些时间细读返回到结果,熟悉其中包含的内容。
  处理 OSM 数据的方法非常多,我在这里仅介绍了一些,不过,我们应该讨论如何在应用程序中使用这些数据了。

回页首
  结束语
  至此,我已经演示了 Lucene 和 Solr 根据基于点的位置模型搜索、排序和过滤文本文档的功能。接下来,将要实现一个真实的位置搜索应用程序来处理用户查询和呈现搜索结果。部分关于应用程序的伸缩性的问题可以从创建限定框过滤器时需要计算的词汇量找到答案。除了关于伸缩性的过滤器问题之外,还需要考虑其他与搜索相关的因素,比如是分发索引还是仅复制索引。请参见 Lucene 和 Solr 参考资料。
  如果您对构建更加高级的 GIS 应用程序感兴趣,您将需要为路线查找、形状交叉等添加更加复杂的功能。如果您需要构建一个可靠搜索应用程序,用于合并基于点的位置的结构和非结构化文本,那么关注 Lucene 和 Solr 就足够了。
  致谢
  本文作者对 Lucene/Solr 的提交者 Ryan McKinley 和 Yonik Seeley 为本文提供独到见解并审阅本文表示感谢!

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-87650-1-1.html 上篇帖子: solr 术语 下篇帖子: Solr添加文档
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表