Несколько дней назад я опубликовал в Twitter шутку :
Поэтому я решил перенести его из простой картины в реальный проект. Давайте посмотрим на две фазы этого так называемого проекта:
- Перемещение данных из Couchbase в MongoDB
- Обновление кода приложения для использования MongoDB
Посмотрите на этот скринкаст, чтобы увидеть его в действии:
Перемещение данных
Я создал сервер репликации, который использует протокол Couchbase XDCR, чтобы вывести документ и вставить его в MongoDB. Этот сервер использует проект Couchbase CAPI Server, доступный здесь .
Этот сервер получит все мутации, сделанные в Couchbase:
- При вставке или обновлении документа отправляется полный документ
- При удалении документа отправляются только медаты
- Сервер
replication serverсохранит данные в MongoDB (вставляет и / или обновляет — не удаляет), а затем возвращает список в Couchbase как часть протокола XDCR.
Одна из проблем заключается в том, что у Couchbase нет понятия «типы» или «коллекции». Вы помещаете все в корзину, и код приложения знает, как обращаться с данными. Не обязательно проблема, просто выбор реализации, но сделать ее иногда сложнее, чем ожидалось, когда вы хотите написать инструменты. Итак, вот логика, которую я применяю на своем сервере репликации, чтобы организовать данные в несколько коллекций, когда это имеет смысл (и когда это возможно) :
- Если документ JSON не содержит поле типа , все документы будут сохранены в одной коллекции
- Если документ JSON содержит поле типа, для каждого типа будет создана коллекция, и документы будут вставлены / обновлены в этих коллекциях.
- MongoDB не позволяет иметь атрибуты ключа. и знаки $, поэтому необходимо изменить имя на альтернативные символы. Это делается автоматически при копировании данных.
Все это и многое другое настраивается в инструменте.
Как вы можете видеть на скринкасте, это просто. (обратите внимание, что я тестировал только очень простые варианты использования и развертывания)
Вы можете скачать инструмент и исходный код здесь:
- https://github.com/tgrall/mongodb-cb-replicator
- Загрузите файл MongoCBReplicator.jar .
Обновление кода приложения
Следующим шагом является использование этих данных в приложении. Для этого я просто использую Java-приложение Beer Sample, доступное в репозитории Couchbase .
Я просто пересоздал проект и изменил несколько вещей, чтобы запустить приложение:
- Изменить строку подключения
- Удалить код, который генерирует представления
- Заменить set / get операциями MongoDB
- Замените вызов на представления простыми запросами
Код приложения MongoDBeer доступен здесь:
Я не изменил бизнес-логику, не добавил функции и даже не заменил способ навигации и отображения страницы. Я просто сосредоточился на доступе к базе данных, например:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
// Couchbase QueryView 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 QueryDBCursor 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 updateclient.set(beerId, 0, gson.toJson(beer));// MongoDB updatedb.getCollection("beer").save(new BasicDBObject(beer)); |
Я не занимался оптимизацией кода MongoDB, а просто заменил как можно меньше строк кода.
Примечание: я не создал ни одного индекса в процессе. Очевидно, что если в вашем приложении появляется все больше и больше данных и вы интенсивно работаете с ним, вы должны проанализировать свое приложение / запросы, чтобы увидеть, какие индексы должны быть созданы.
Добавление новых функций
Как только у вас есть данные в MongoDB, вы можете сделать гораздо больше без чего-либо большего, чем MongoDB:
Полнотекстовый поиск
Вы можете создать текстовый индекс для различных полей в коллекции, чтобы предоставить пользователям расширенные возможности поиска.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
db.brewery.ensureIndex( { "name" : "text", "description" : "text" }, { "weights" : { "name" : 10, "description" : 5 }, "name" : "TextIndex" }); |
Затем вы можете запросить базу данных, используя операцию $text , например, все пивоваренные заводы с Бельгией и без Але.
|
1
2
3
4
5
6
7
|
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" } |
Немного аналитики
Не уверен, что эти запросы действительно имеют смысл, но это просто для того, чтобы показать, что теперь вы можете использовать свои документы без использования какого-либо стороннего инструмента.
Количество пива по категориям, от самого распространенного к меньшему:
|
01
02
03
04
05
06
07
08
09
10
11
12
|
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 лучших пивоварни с наибольшим количеством пива, у которых показатель выше или равен значению, скажем 5:
|
01
02
03
04
05
06
07
08
09
10
|
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:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
>mongouse beersdb.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]
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
db.brewery.find( { "loc" : { "$near" : { "$geometry" : { "type" : "Point", "coordinates" : [-122.478255,37.819929] }, "$maxDistance" : 20000 } } } , { name : 1 }) |
Вы также можете использовать геопространственные индексы и операторы в запросах агрегации, использованных выше.
Вывод
Как было сказано во введении, проект на этой неделе начался как шутка в Твиттере и завершился небольшим сообщением в блоге и репозиториями Gitub.
Моя цель здесь не в том, чтобы сравнить два решения — я сделал свой выбор несколько месяцев назад, а просто показать, как вы можете переходить от одного к другому практически без усилий, не только данных, но и кода приложения.
| Ссылка: | Перемещение моего пива из Couchbase в MongoDB от нашего партнера по JCG Тугдуала Граля в блоге Tug’s Blog . |