65.9K
CodeProject 正在变化。 阅读更多。
Home

如何搜索旅游好去处(MongoDb LINQ & .NET Core)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.74/5 (11投票s)

2017年4月24日

CPOL

5分钟阅读

viewsIcon

18148

downloadIcon

165

演示文章,介绍如何使用 LINQ 查询 MongoDB,导入数据并定义索引

我们将使用 .NET Core 和 MongoDb 构建一个简单的 WebApi,以查询全球各地景点的详细信息。我们将使用 MongoDb LINQ 进行搜索,并运行不同的场景。

有关如何构建和测试完整的 .NET CORE WebApi 和 MongoDB 的简要介绍,请参阅我之前的文章:将 MongoDB .NET 驱动程序与 .NET Core WebAPI 结合使用

您也可以从 GitHub 下载项目和 数据集github.com/fpetru/WebApiQueryMongoDb

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

  • Wikivoyage 提供了全球最受游客欢迎的博物馆、景点、餐厅和酒店的更多详细信息。原始数据集可从 以下网址 访问。
  • 第二个数据集来自 GeoNames,这是一个涵盖所有国家的地理数据库。出于演示目的,我仅选择了人口超过 5000 居民的城市。

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

涵盖的主题

  • MongoDb – 安装和安全设置
  • MongoDB – 使用 mongoimport 工具
  • 创建一个完整的 ASP.NET WebApi 项目,使用 MongoDB C# Driver v.2 进行异步连接
  • 运行 LINQ 查询

安装说明

以下是所有需要安装的东西

MongoDB 配置

安装 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"

#Once the admin user is created, remove the comments, and let the authorization be enabled
#security:
#  authorization: enabled

命令提示符 中运行以下命令。这将启动 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 服务器。

MongoImport – 使用大型数据集初始化数据库

我们将从 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 选项,我们可以导入所需类型的数据(例如 intdoublestring 等)。

结果应该如下所示

MongoDB – LINQ 支持

此处包含的 .NET Core 解决方案遵循我之前文章的结构 – 将 MongoDB .NET 驱动程序与 .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;
    }
}

查询在服务器端渲染,我们只接收有限的数据集。这是可能的,因为我们有 MongoDB C# Driver 原生提供的 IQueryable 类型 接口

...
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 函数调用。假设我们想搜索巴黎有趣的活动(https://:61612/api/travelquery/Paris?doAction=do),我们将得到有趣的结果,其中之一是以下内容。

运行更快的查询

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

使用 mongo shell 执行 JavaScript 文件,将在 City 上添加索引,然后是 Action

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

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

分组项目

如果我们只想查看标题怎么办?特定城市有哪些类型的活动,而不深入了解细节?

一个按 CityAction 字段分组的示例查询将是。

await _context.TravelItems
            .GroupBy(grp => new { grp.City, grp.Action })
            .Select(g => new { g.Key.City, g.Key.Action }).ToListAsync();

继续

我将创建此文章的第二部分,添加分页支持以及较新 MongoDB 版本带来的聚合增强功能,同时也会考虑第二个数据集。也许您已经知道了这些,也许您学到了一些新东西。您希望看到更多内容吗?

更新

如果您有兴趣,请也查看我关于 MongoDB 分页的文章(MongoDB 分页 – 如何真正避免性能低下?)。

© . All rights reserved.