Cách sao chép bộ sưu tập từ cơ sở dữ liệu này sang cơ sở dữ liệu khác trong MongoDB


221

Có một cách đơn giản để làm điều này?


40
Câu trả lời được chấp nhận được cho là phương pháp tốt nhất vào năm 2012, nhưng bây giờ db.cloneCollection () thường là một giải pháp tốt hơn. Có một vài câu trả lời gần đây ở đây đề cập đến điều này, vì vậy nếu bạn đến đây từ Google (như tôi đã làm) hãy xem tất cả các câu trả lời!
Kelvin

4
Hãy chắc chắn đọc các câu trả lời khác mặc dù để đảm bảo rằng nó phù hợp với nhu cầu của bạn, không chỉ của @kelvin trong tình huống của anh ấy / cô ấy
PW Kad

Câu trả lời:


206

Hiện tại không có lệnh nào trong MongoDB sẽ làm điều này. Xin lưu ý vé JIRA với yêu cầu tính năng liên quan .

Bạn có thể làm một cái gì đó như:

db.<collection_name>.find().forEach(function(d){ db.getSiblingDB('<new_database>')['<collection_name>'].insert(d); });

Xin lưu ý rằng với điều này, hai cơ sở dữ liệu sẽ cần chia sẻ cùng một mongod để điều này hoạt động.

Bên cạnh đó, bạn có thể thực hiện một bộ sưu tập của một bộ sưu tập từ một cơ sở dữ liệu và sau đó chuyển tiếp bộ sưu tập sang cơ sở dữ liệu khác.


13
Lưu ý rằng nếu bạn sao chép trong trình bao JS, các tài liệu BSON được giải mã thành JSON trong quá trình để một số tài liệu có thể phải chịu các thay đổi về loại. mongodump / mongorestore nói chung là cách tiếp cận tốt hơn.
Stennie

1
Đã đồng ý. Đó không chỉ là một gợi ý thú vị cho việc chơi đùa với vỏ. Thêm vào đó, nó sẽ không mang lại các chỉ số. Nếu tôi đang làm điều này, tôi sẽ làm mongodump / mongorestore mỗi lần.
Jason McCay

2
Cảm ơn. Xin lưu ý rằng bạn có một lỗi đánh máy trong mã, không đóng chức năng getSiblingDB. Đây là mã đã sửa: db. <Collection_name> .find (). ForEach (function (d) {db.getSiblingDB ('<new_database>') ['<Collection_name>']. Insert (d);});
Flaviu

1
điều này hoạt động tốt để đặt lại một mongodb thử nghiệm từ một bản sao vàng giữa các lần chạy thử. thay vì mã hóa cứng các tên bộ sưu tập, bạn có thể thực hiện một vòng lặp for trên tất cả các tên bộ sưu tập mà bạn muốn sao chép bằng db.getCollection (tên) .find (). forEach và cung cấp một hàm có db.getSiblingDB ("otherdb"). getCollection (tên) .insert (d).
simbo1905

2
Là hiệu quả cho các bộ sưu tập kích thước lớn?
Khalil Awada

284

Cách tốt nhất là thực hiện một mongodump sau đó mongorestore.

Bạn có thể chọn bộ sưu tập qua:

mongodump -d some_database -c some_collection

[Tùy chọn, nén zip ( zip some_database.zip some_database/* -r) và scpnó ở nơi khác]

Sau đó khôi phục nó:

mongorestore -d some_other_db -c some_or_other_collection dump/some_collection.bson

Dữ liệu hiện có trong some_or_other_collectionsẽ được bảo tồn. Bằng cách đó bạn có thể "nối" một bộ sưu tập từ cơ sở dữ liệu này sang cơ sở dữ liệu khác.

Trước phiên bản 2.4.3, bạn cũng sẽ cần thêm lại các chỉ mục của mình sau khi bạn sao chép dữ liệu của mình. Bắt đầu với 2.4.3, quá trình này là tự động và bạn có thể vô hiệu hóa nó với --noIndexRestore.


Có vẻ như mongodump không hoạt động nếu bạn có mật khẩu mongo được bảo vệ (và bạn nên!)
Luciano Camilo

3
Nó hoạt động trên các DB được bảo vệ bởi PW, bạn chỉ cần vượt qua auth trong params
Ben

2
Tốc độ này nhanh hơn nhiều so với find / forEach / insert, trong trường hợp của tôi là 2 phút so với 2 giờ
Juraj Paulo

Nhập tên người dùng cho cơ sở dữ liệu bằng --username nhưng không --password để nhận lời nhắc nhập mật khẩu. Tốt nhất là không đặt mật khẩu vào dòng lệnh của bạn (cuối cùng lưu nó vào .bash_history hoặc tương tự)
Chanoch

Nhỏ: Tôi đã tìm thấy tệp trong thư mục con có tên là some_database nên nó hoạt động với tôi: mongorestore -d some_other_db -c some_or_other_collection dump / some_database / some_collection.bson
Aviko

88

Trên thực tế, có một lệnh để di chuyển một bộ sưu tập từ một cơ sở dữ liệu khác. Nó chỉ không được gọi là "di chuyển" hoặc "sao chép".

Để sao chép một bộ sưu tập, bạn có thể sao chép nó trên cùng một db, sau đó di chuyển bản sao.

Để nhân bản:

> use db1
> db.source_collection.find().forEach( function(x){db.collection_copy.insert(x)} );

Để di chuyển:

> use admin
switched to db admin
> db.runCommand({renameCollection: 'db1.source_collection', to: 'db2.target_collection'}) // who'd think rename could move?

Các câu trả lời khác là tốt hơn để sao chép bộ sưu tập, nhưng điều này đặc biệt hữu ích nếu bạn đang muốn di chuyển nó.


3
Thx hoạt động tuyệt vời! Chỉ cần một dấu nháy đơn kết thúc trong'db1.source_collection'
andrrs

4
Thay vì "sử dụng quản trị viên" theo sau là "db.runCommand (..." Bạn chỉ có thể thực hiện một lệnh, "db.adminCommand (..."
Hamid

25

Tôi sẽ lạm dụng chức năng kết nối trong mongo cli mongo doc . vì vậy điều đó có nghĩa là bạn có thể bắt đầu một hoặc nhiều kết nối. nếu bạn muốn sao chép bộ sưu tập của khách hàng từ test sang test2 trong cùng một máy chủ. đầu tiên bạn bắt đầu vỏ mongo

use test
var db2 = connect('localhost:27017/test2')

thực hiện tìm kiếm bình thường và sao chép 20 bản ghi đầu tiên vào test2.

db.customer.find().limit(20).forEach(function(p) { db2.customer.insert(p); });

hoặc lọc theo một số tiêu chí

db.customer.find({"active": 1}).forEach(function(p) { db2.customer.insert(p); });

chỉ cần thay đổi localhost thành IP hoặc tên máy chủ để kết nối với máy chủ từ xa. Tôi sử dụng điều này để sao chép dữ liệu thử nghiệm vào cơ sở dữ liệu thử nghiệm để thử nghiệm.


4
Như tôi đã nhận xét về đề xuất của Jason, lưu ý rằng nếu bạn sao chép trong vỏ JS, các tài liệu BSON được giải mã thành JSON trong quá trình để một số tài liệu có thể phải chịu các thay đổi về loại. Có những cân nhắc tương tự với Hạn chế của eval và đây sẽ là một quá trình chậm hơn để sao chép lượng dữ liệu đáng kể giữa các cơ sở dữ liệu (đặc biệt trên cùng một máy chủ). Vì vậy, mongodump / mongorestore FTW :).
Stennie

19

Nếu giữa hai trường hợp mongod từ xa, sử dụng

{ cloneCollection: "<collection>", from: "<hostname>", query: { <query> }, copyIndexes: <true|false> } 

Xem http://docs.mongodb.org/manual/reference/command/cloneCollection/


Trường copyIndexestùy chọn thực sự không được tôn trọng. Các chỉ mục luôn được sao chép. Xem SERVER-11418
Gianfranco P.

6
Gói nó trong db.runCommand () tức là db.runCommand ({cloneCollection: "<sưu tập>", từ: "<tên máy chủ>", truy vấn: {<truy vấn>}})
Daniel de Zwaan

Làm thế nào điều này có thể được sử dụng để cập nhật gia tăng từ một mongo từ xa sang khác?
Nishant

Tôi có dữ liệu người dùng được thêm vào một ví dụ mongo trong suốt cả ngày. Vào cuối ngày, tôi cần chuyển các hàng vừa được thêm vào một ví dụ mongo khác. Làm thế nào điều này có thể đạt được?
Nishant

@NishantKumar thử đặt trong truy vấn: {} mã này: $ where: function () {today = new Date (); // hôm nay.setHours (0,0,0,0); return (this._id.getTimestamp ()> = hôm nay). Xem stackoverflow.com/questions/42456375/ Lần .
es cologne

18

Tôi thường làm:

use sourcedatabase;
var docs=db.sourcetable.find();
use targetdatabase;
docs.forEach(function(doc) { db.targettable.insert(doc); });

11

đối với các bộ sưu tập kích thước khổng lồ, bạn có thể sử dụng Bulk.insert ()

var bulk = db.getSiblingDB(dbName)[targetCollectionName].initializeUnorderedBulkOp();
db.getCollection(sourceCollectionName).find().forEach(function (d) {
    bulk.insert(d);
});
bulk.execute();

Điều này sẽ tiết kiệm rất nhiều thời gian . Trong trường hợp của tôi, tôi đang sao chép bộ sưu tập với 1219 tài liệu: iter vs Bulk (67 giây so với 3 giây)


đây là cách tốt hơn, hiệu quả hơn, búa ít db hơn, hoạt động với mọi kích thước của tập dữ liệu.
Jeremie

Nếu bạn đang làm điều này với hơn 300k hồ sơ, bạn có thể cần thêm .limit (300000) sau khi tìm thấy và trước khi tiến hành. Khác hệ thống có thể bị khóa. Tôi thường giới hạn thay đổi số lượng lớn khoảng 100k cho an toàn. Gói toàn bộ trong một vòng lặp for dựa trên số lượng và giới hạn.
triunenature

6

Bạn có thể sử dụng khung tổng hợp để giải quyết vấn đề của mình

db.oldCollection.aggregate([{$out : "newCollection"}])

Cần lưu ý rằng các chỉ mục từ oldCollection sẽ không được sao chép trong newCollection.


5

Tôi biết câu hỏi này đã được trả lời tuy nhiên cá nhân tôi sẽ không trả lời @JasonMcCays do luồng con trỏ và điều này có thể gây ra một vòng lặp con trỏ vô hạn nếu bộ sưu tập vẫn đang được sử dụng. Thay vào đó, tôi sẽ sử dụng một snapshot ():

http://www.mongodb.org/display/DOCS/How+to+do+Snapshiated+Queries+in+the+Mongo+Database

Câu trả lời @bens cũng là một câu trả lời hay và hoạt động tốt cho các bản sao lưu nóng của các bộ sưu tập không chỉ vậy mà mongorestore không cần phải chia sẻ cùng một mongod.


5

Đây có thể chỉ là một trường hợp đặc biệt, nhưng đối với bộ sưu tập 100k tài liệu với hai trường chuỗi ngẫu nhiên (độ dài là 15-20 ký tự), sử dụng mapreduce câm nhanh gần gấp đôi so với find-insert / copyTo:

db.coll.mapReduce(function() { emit(this._id, this); }, function(k,vs) { return vs[0]; }, { out : "coll2" })

5

Sử dụng pymongo, bạn cần có cả hai cơ sở dữ liệu trên cùng một mongod, tôi đã làm như sau:


db = cơ sở dữ liệu gốc
db2 = cơ sở dữ liệu sẽ được sao chép vào

cursor = db["<collection to copy from>"].find()
for data in cursor:
    db2["<new collection>"].insert(data)

1
điều này sẽ mất rất nhiều thời gian nếu kích thước dữ liệu rất lớn. Hoặc bạn có thể sử dụng bulk_insert
Nishant

1
Vâng, đây chỉ là một cách nhanh chóng và bẩn thỉu mà tôi thấy để làm việc cho tôi, cơ sở dữ liệu của tôi không quá lớn, nhưng cũng không nhỏ và không mất quá nhiều thời gian, nhưng vâng bạn đúng.
vbhakta

2

Điều này sẽ không giải quyết được vấn đề của bạn nhưng shell mongodb có một copyTophương thức sao chép một bộ sưu tập vào một bộ khác trong cùng một cơ sở dữ liệu :

db.mycoll.copyTo('my_other_collection');

Nó cũng dịch từ BSON sang JSON, vì vậy mongodump/ mongorestorelà cách tốt nhất để đi, như những người khác đã nói.


Thông minh. Đáng buồn thay, tham chiếu vỏ Mongo dường như không đề cập đến phương pháp này.
pgl

Vâng, tôi biết, nhưng trình bao MongoDB thật tuyệt vời, nếu bạn nhập db.collname. [TAB] bạn sẽ thấy tất cả các phương thức có sẵn trên đối tượng bộ sưu tập. Mẹo này hoạt động cho tất cả các đối tượng khác.
Roberto

Vấn đề là thiếu sự giúp đỡ cho các lệnh đó! Nó rất hữu ích để có thể xem mã, mặc dù bằng cách bỏ qua các parens cho một cuộc gọi phương thức.
pgl

2
Đáng buồn thay, lệnh này hiện đã bị từ chối kể từ phiên bản 3.0.
Harry

2

Nếu RAM không phải là vấn đề thì sử dụng insertManynhanh hơn forEachvòng lặp.

var db1 = connect('<ip_1>:<port_1>/<db_name_1>')
var db2 = connect('<ip_2>:<port_2>/<db_name_2>')

var _list = db1.getCollection('collection_to_copy_from').find({})
db2.collection_to_copy_to.insertMany(_list.toArray())

1

Trong trường hợp một số người dùng heroku vấp ngã ở đây và như tôi muốn sao chép một số dữ liệu từ cơ sở dữ liệu dàn dựng sang cơ sở dữ liệu sản xuất hoặc ngược lại đây là cách bạn làm điều đó rất thuận tiện (NB Tôi hy vọng không có lỗi chính tả nào trong đó, không thể kiểm tra nó. Tôi sẽ thử xác nhận tính hợp lệ của mã càng sớm càng tốt):

to_app="The name of the app you want to migrate data to"
from_app="The name of the app you want to migrate data from"
collection="the collection you want to copy"
mongohq_url=`heroku config:get --app "$to_app" MONGOHQ_URL`
parts=(`echo $mongohq_url | sed "s_mongodb://heroku:__" | sed "s_[@/]_ _g"`)
to_token=${parts[0]}; to_url=${parts[1]}; to_db=${parts[2]}
mongohq_url=`heroku config:get --app "$from_app" MONGOHQ_URL`
parts=(`echo $mongohq_url | sed "s_mongodb://heroku:__" | sed "s_[@/]_ _g"`)
from_token=${parts[0]}; from_url=${parts[1]}; from_db=${parts[2]}
mongodump -h "$from_url" -u heroku -d "$from_db" -p"$from_token" -c "$collection" -o col_dump
mongorestore -h "$prod_url" -u heroku -d "$to_app" -p"$to_token" --dir col_dump/"$col_dump"/$collection".bson -c "$collection"

1

Bạn luôn có thể sử dụng Robomongo. Kể từ v0.8.3, có một công cụ có thể thực hiện việc này bằng cách nhấp chuột phải vào bộ sưu tập và chọn "Sao chép bộ sưu tập vào cơ sở dữ liệu"

Để biết chi tiết, xem http://blog.robomongo.org/whats-new-in-robomongo-0-8-3/

Tính năng này đã bị xóa trong 0.8.5 do tính chất lỗi của nó, do đó bạn sẽ phải sử dụng 0.8.3 hoặc 0.8.4 nếu bạn muốn dùng thử.


6
Tính năng này của Robomongo vẫn chưa ổn định. Đó là cơ hội 50/50 để làm cho nó hoạt động.
14 lúc 18 giờ 41

2
Điều này dường như đã bị xóa khỏi
0.8.5

0

Trong trường hợp của tôi, tôi đã phải sử dụng một tập hợp các thuộc tính từ bộ sưu tập cũ trong bộ sưu tập mới của tôi. Vì vậy, tôi đã kết thúc việc chọn các thuộc tính đó trong khi gọi chèn vào bộ sưu tập mới.

db.<sourceColl>.find().forEach(function(doc) { 
    db.<newColl>.insert({
        "new_field1":doc.field1,
        "new_field2":doc.field2,
        ....
    })
});`

0

sử dụng "Studio3T cho MongoDB" có công cụ Xuất và Nhập bằng cách nhấp vào cơ sở dữ liệu, bộ sưu tập hoặc liên kết tải xuống bộ sưu tập cụ thể: https://studio3t.com/doad/


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.