Несколько дней назад я опубликовал в 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 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:
Полнотекстовый поиск
Вы можете создать текстовый индекс для различных полей в коллекции, чтобы предоставить пользователям расширенные возможности поиска.
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
|
>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]
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 . |