将我的啤酒从Couchbase迁移到MongoDB

几天前,我在Twitter上了一个玩笑

因此,我决定将其从简单的图片转移到真实的项目中。 让我们看一下这个所谓的项目的两个阶段:

  • 将数据从Couchbase移至MongoDB
  • 更新应用程序代码以使用MongoDB

查看此截屏视频,以查看实际效果:

移动数据

我创建了一个复制服务器,该服务器使用Couchbase XDCR协议将文档取出并插入到MongoDB中。 该服务器使用此处提供的Couchbase CAPI Server项目。

该服务器将接收在Couchbase中进行的所有更改:

  • 插入或更新文档后,将发送完整文档
  • 删除文档后,仅发送元数据
  • replication server将数据保存到MongoDB中(插入和/或更新-不删除),然后将该列表作为XDCR协议的一部分返回给Couchbase。

挑战之一是Couchbase没有“类型”或“集合”的概念。 您将所有内容都放在了存储桶中 ,应用程序代码知道如何处理数据。 不一定要有问题,只是实现的选择,但要编写工具时,有时会比预期的要难。 因此,这里是我在复制服务器中应用的逻辑,用于在有意义的情况下(并在可能的情况下)组织多个集合中的数据:

  • 如果JSON文档不包含类型字段 ,则所有文档将保存在一个集合中
  • 如果JSON文档包含类型字段,则将为每种类型创建一个集合,并将在这些集合中插入/更新文档
  • MongoDB不允许属性key具有。 和$符号,因此有必要使用其他字符更改名称。 这是在复制数据期间自动完成的。

所有这些以及更多内容都可以在该工具中进行配置。

如您在截屏中所见,这很简单。 (请注意,我只测试了非常简单的用例和部署)

您可以在此处下载该工具和源代码:

更新应用程序代码

下一步是在应用程序中使用这些数据。 为此,我仅使用Couchbase存储库中可用的Beer Sample Java应用程序。

我只是重新创建了项目,并做了一些修改,以使应用程序正常运行:

  • 更改连接字符串
  • 删除生成视图的代码
  • 用MongoDB操作替换设置/获取
  • 通过简单查询替换对视图的调用

MongoDBeer应用程序的代码可在此处获得:

我没有更改任何业务逻辑,也没有添加功能,甚至没有替换导航和页面呈现的方式。 我只关注数据库访问,例如:

// Couchbase Query
View view = client.getView("beer", "by_name");
    Query query = new Query();
    query.setIncludeDocs(true).setLimit(20);
    ViewResponse result = client.query(view, query);

    ArrayList<HashMap<String, String>> beers =
      new ArrayList<HashMap<String, String>>();
    for(ViewRow row : result) {
      HashMap<String, String> parsedDoc = gson.fromJson(
        (String)row.getDocument(), HashMap.class);

      HashMap<String, String> beer = new HashMap<String, String>();
      beer.put("id", row.getId());
      beer.put("name", parsedDoc.get("name"));
      beer.put("brewery", parsedDoc.get("brewery_id"));
      beers.add(beer);
    }
    request.setAttribute("beers", beers);


// MongoDB Query
DBCursor cursor = db.getCollection("beer").find()
                                                   .sort( BasicDBObjectBuilder.start("name",1).get() )
                                                   .limit(20);
     ArrayList<HashMap<String, String>> beers =
             new ArrayList<HashMap<String, String>>();
     while (cursor.hasNext()) {
         DBObject row = cursor.next();
         HashMap<String, String> beer = new HashMap<String, String>();
         beer.put("id", (String)row.get("_id"));
         beer.put("name", (String)row.get("name"));
         beer.put("brewery", (String)row.get("brewery_id"));
         beers.add(beer);
     }



// Couchbase update
client.set(beerId, 0, gson.toJson(beer));

// MongoDB update
db.getCollection("beer").save(new BasicDBObject(beer));

我没有参加优化MongoDB代码的工作,只是为了替换尽可能少的代码行。

注意:在此过程中,我还没有创建任何索引。 显然,如果您的应用程序具有越来越多的数据,并且您需要对其进行大量工作,则必须分析您的应用程序/查询以查看必须创建哪些索引。

新增功能

将数据存入MongoDB之后,您可以做更多的事情,而无非MongoDB:

全文搜索

您可以在集合中的各个字段上创建文本索引,以为用户提供高级搜索功能。

db.brewery.ensureIndex(
  {
    "name" : "text",
    "description" : "text"
  },
  {
    "weights" :
    {
      "name" : 10,
      "description" : 5
    },
    "name" : "TextIndex"
  }

);

然后,您可以使用$text操作查询数据库,例如所有有比利时且没有Ale的啤酒厂

db.brewery.find( { "$text" : { "$search" : "belgium -ale" }   }  , { "name" : 1  } );
{ "_id" : "daas", "name" : "Daas" }
{ "_id" : "chimay_abbaye_notre_dame_de_scourmont", "name" : "Chimay (Abbaye Notre Dame de Scourmont)" }
{ "_id" : "brasserie_de_cazeau", "name" : "Brasserie de Cazeau" }
{ "_id" : "inbev", "name" : "InBev" }
{ "_id" : "new_belgium_brewing", "name" : "New Belgium Brewing" }
{ "_id" : "palm_breweries", "name" : "Palm Breweries" }

一些分析

不确定这些查询是否真的有意义,但这只是表明现在您可以利用文档而无需任何第三方工具。

啤酒的种类(从最常见到较少):

db.beer.aggregate([
  {"$group" : { "_id" : "$category","count" : {"$sum" : 1 } } },
  {"$sort" : { "count" : -1 } },
  {"$project" : {  "category" : "$_id", "count" : 1, "_id" : 0 } }
]);

{ "count" : 1996, "category" : "North American Ale" }
{ "count" : 1468, "category" : null }
{ "count" : 564, "category" : "North American Lager" }
{ "count" : 441, "category" : "German Lager" }
...
...

例如,按啤酒厂的特定ABV的啤酒数量:啤酒产量最高的前3家啤酒厂,其Abv大于或等于某个值,例如5:

db.beer.aggregate([
... { "$match" : { "abv" : { "$gte" : 5 }  } },
... { "$group" : { "_id" : "$brewery_id", "count" : { "$sum" : 1} }},
... { "$sort" : { "count" : -1 } },
... { "$limit" : 3 }
... ])

{ "_id" : "midnight_sun_brewing_co", "count" : 53 }
{ "_id" : "troegs_brewing", "count" : 33 }
{ "_id" : "rogue_ales", "count" : 31 }

地理空间查询

处理数据的第一件事是更改数据结构,以将各种数据保存为GeoJSON格式,为此,我们可以简单地在MongoDB Shell中使用脚本:

>mongo

use beers

db.brewery.find().forEach(
  function( doc ) {
    var loc = { type : "Point" };
    if (doc.geo && doc.geo.lat && doc.geo.lon) {
      loc.coordinates = [ doc.geo.lon , doc.geo.lat  ];
      db.brewery.update( { _id : doc._id } , {$set : { loc : loc } }  );
    }
  }
);

db.brewery.ensureIndex( { "loc" : "2dsphere" } );

此调用将使用所有啤酒厂并添加一个新属性,名称loc为GeoJSON点。 我还可以选择使用'$ unset'删除旧的地理信息,但是我没有这样做; 让我们想象一些API /应用程序正在使用它。 这是灵活模式的一个很好的例子。

现在,我可以搜索距离旧金山金门大桥不到30公里的所有啤酒厂:[-122.478255,37.819929]

db.brewery.find(
  { "loc" :
    { "$near" :
      { "$geometry" :
        {
          "type" : "Point",
          "coordinates" : [-122.478255,37.819929]
        },
        "$maxDistance" : 20000

      }
    }
  }
  , { name : 1 }
)

您还可以在上面使用的聚合查询中使用地理空间索引和运算符

结论

正如引言中所述,本周结束的项目在Twitter上只是一个玩笑而已,最后是一个小博客文章和Gitub存储库。

我的目的不是要比较这两种解决方案-我几个月前就做出了选择-只是简单地展示了如何轻松地从一个迁移到另一个,不仅是数据,还包括应用程序代码。

翻译自: https://www.javacodegeeks.com/2015/02/moving-my-beers-from-couchbase-to-mongodb.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值