使用MongoDB LINQ进行全球目的地搜索

在本文中,将探讨如何使用MongoDBLINQ查询全球不同目的地的详细信息。将通过MongoDB LINQ运行不同的场景搜索。

如果对如何构建和测试完整的.NET COREWebApi与MongoDB的结合使用感兴趣,请参考之前的文章:。也可以从GitHub下载项目和数据集:。

在本文中,将使用两个数据集:

  • Wikivoyage提供了全球最友好的博物馆、景点、餐厅和酒店的详细信息。原始数据集可以从以下URL访问。
  • 第二个数据集来自GeoNames,这是一个覆盖所有国家的地理数据库。为了演示目的,只选择了人口超过5000的城镇。

使用这些数据集,可以更容易地运行一些示例查询,检索到一致的数据量。

涵盖主题:

  • MongoDB - 安装和安全设置
  • MongoDB - 使用mongoimport工具
  • 创建完整的ASP.NETWebApi项目,异步连接使用MongoDBC# Driver v.2
  • 运行LINQ查询

安装指南

以下是需要安装的所有内容:

  • Visual Studio Community 2017,包括.NET Core选项
  • MongoDB
  • Robomongo

安装MongoDB后,需要配置访问权限以及数据存储位置。为此,请创建一个名为mongod.cfg的本地文件。这将包括设置MongoDB服务器的数据文件夹路径,以及MongoDB日志文件的路径,最初不启用任何身份验证(最后两行被注释)。请使用本地设置更新这些本地路径:

systemLog: destination: file path: "C:\\tools\\mongodb\\db\\log\\mongo.log" logAppend: true storage: dbPath: "C:\\tools\\mongodb\\db\\data"

一旦创建了管理员用户,删除最后两行的注释,并启用授权。

在命令提示符中运行以下命令。这将启动MongoDB服务器,指向已经创建的配置文件(如果服务器安装在自定义文件夹中,请先更新命令)。

"C:\Program Files\MongoDB\Server\3.4\bin\mongod.exe" --config C:\Dev\Data.Config\mongod.cfg

服务器启动后(可以在日志文件中看到详细信息),在命令提示符中运行mongo.exe。下一步是向数据库添加管理员用户。使用完整路径运行MongoDB(例如:“C:\Program Files\MongoDB\Server\3.4\bin\mongo.exe”),然后在控制台中复制粘贴以下代码:

use admin db.createUser( { user: "admin", pwd: "abc123!", roles: [ { role: "root", db: "admin" } ] } ); exit;

停止服务器,取消注释mongod.cfg文件中的最后两行,然后重新启动MongoDB服务器。

将从Wikivoyage开始。原始数据集最初可以在这里找到(链接)。为了更容易导入,已经稍微转换了它(改为制表符分隔的文件,并进行了最小数据清理)。该文件在GitHub上可用(链接)。

第二个数据集GeoNames也位于同一个GitHub文件夹中(链接)。运行同一文件夹中的import.bat脚本将导入数据,同时创建一个名为TravelDb的新数据库以及相关索引。脚本包含在这里,但最好直接运行脚本文件:

mongoimport --db TravelDb ^ --collection WikiVoyage ^ --type tsv ^ --fieldFile enwikivoyage-fields.txt^ --file enwikivoyage-20150901-listings.result.tsv^ --columnsHaveTypes^ --username admin ^ --password abc123! ^ --authenticationDatabase admin ^ --numInsertionWorkers 4 mongoimport --db TravelDb ^ --collection Cities ^ --type tsv ^ --fieldFile cities5000-fields.txt^ --file cities5000.txt ^ --columnsHaveTypes^ --username admin ^ --password abc123! ^ --authenticationDatabase admin ^ --numInsertionWorkers 4

字段文件指定了字段名称以及它们关联的类型。使用columnsHaveTypes选项,可以根据需要的类型进行导入(例如int、double、string等)。

结果应该如下所示:

这里包含的.NET Core解决方案遵循之前文章中相同的结构——使用MongoDB .NET Driver与.NET Core WebAPI。在那里,已经逐步介绍了如何从头开始创建一个WebApi解决方案,连接到MongoDB并实现REST API的所有基本操作。

相比之下,这里的Web控制器将只实现一个动作(GET)——主要关注运行不同的查询:

[NoCache] [HttpGet] public Task<IEnumerable<TravelItem>> Get() { return GetTravelItemsInternal(); } private async Task<IEnumerable<TravelItem>> GetTravelItemsInternal() { return await _travelItemRepository.GetTravelItems(); }

在后台,查询使用LINQ语法运行,并返回前500条记录。

public async Task<IEnumerable<TravelItem>> GetTravelItems() { try { return await _context.TravelItems.Take(500).ToListAsync(); } catch (Exception ex) { // log or manage the exception throw ex; } }

查询在服务器上呈现,只接收有限的数据集。这是因为有IQueryable类型接口,由MongoDB C# Driver原生提供。

using MongoDB.Driver.Linq; public IMongoQueryable<TravelItem> TravelItems { get { return _database.GetCollection<TravelItem>("WikiVoyage").AsQueryable<TravelItem>(); } }

假设想在城市中找到有趣的事情。要么显示城市中的所有项目,按行动类型排序,要么只选择特定的行动(例如购买、做、吃、喝等)。

public async Task<IEnumerable<TravelItem>> GetTravelItems(string cityName, string action) { try { if (action != null) return await _context.TravelItems.Where(p => p.City == cityName && p.Action == action).ToListAsync(); return await _context.TravelItems.Where(p => p.City == cityName) .OrderBy(p => p.Action) .ToListAsync(); } catch (Exception ex) { // log or manage the exception throw ex; } }

这个方法将被GET函数调用。假设想在巴黎搜索有趣的事情(http://localhost:61612/api/travelquery/Paris?doAction=do),得到了有趣的结果,其中一个是:

提高查询速度的一种方法是应用索引。在集合中搜索City和Action,建议添加一个包含这两个字段的简单索引。

执行JavaScript文件,将添加一个索引在City上,然后是Action。

db = db.getSiblingDB('TravelDb'); db.WikiVoyage.createIndex({ City: 1, Action: 1 });

检索速度将从平均0.150毫秒提高到大约0.001毫秒。

如果只想看到标题呢?特定城市有哪些行动类型可用,而不涉及细节?

一个按City和Action字段分组的示例查询将是:

await _context.TravelItems .GroupBy(grp => new { grp.City, grp.Action }) .Select(g => new { g.Key.City, g.Key.Action }).ToListAsync();
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485