在本文中,将探讨如何使用MongoDBLINQ查询全球不同目的地的详细信息。将通过MongoDB LINQ运行不同的场景搜索。
如果对如何构建和测试完整的.NET COREWebApi与MongoDB的结合使用感兴趣,请参考之前的文章:。也可以从GitHub下载项目和数据集:。
在本文中,将使用两个数据集:
使用这些数据集,可以更容易地运行一些示例查询,检索到一致的数据量。
以下是需要安装的所有内容:
安装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();