У меня есть сервер, на котором работает Monogdb, на котором также есть коллекция с именем mycoll. Новые документы добавляются в коллекцию постоянно. Я намерен создать локальный файл с копией всех документов коллекции. Мой текущий подход следующий:
String host = "myHost";
int port = 3717;
String user = "user";
String password = "pass";
String databaseName = "dbName";
String collectionName = "mycoll";
MongoCredential credential = MongoCredential.createCredential(user, databaseName, password.toCharArray());
MongoClient mongoClient = new MongoClient(new ServerAddress(host, port), Arrays.asList(credential));
MongoDatabase database = mongoClient.getDatabase(databaseName);
MongoCollection<Document> collection = database.getCollection(collectionName);
FindIterable<Document> cursor = collection.find();
String path = "./outputData.txt";
while(cursor.iterator().hasNext()){
try(FileWriter fw = new FileWriter(path, true);
BufferedWriter bw = new BufferedWriter(fw);
PrintWriter out = new PrintWriter(bw))
{
out.println(cursor.iterator().next());
} catch (IOException e) {
System.out.println("Error writing to file.");
}
}
Я знаю, что могу создать буфер для хранения данных и их последующей записи, но большая проблема все еще не решена: доступ к документам осуществляется один за другим. Поэтому у меня вопрос: как мне получить доступ к нескольким документам одновременно, чтобы записать их в ./outputData.txt?
Я бы предпочел открыть файловое соединение один раз, а затем он может снова использовать соединение! Также я предпочитаю создать новый поток для записи в файл и заставить их писать Async, чтобы это не повлияло на чтение исходного файла




Проще говоря, просто верните MongoCursor, вызвав iterator() «один раз», потому что это все, что вам нужно сделать.
Затем очень просто создайте дескриптор файла «вне» цикла. Единственное, что действительно находится «внутри» цикла, - это фактическая итерация и запись курсора.
try {
MongoCursor<Document> cursor = collection.find().iterator();
String path = "./outputData.txt";
PrintWriter out = new PrintWriter(
new BufferedWriter(new FileWriter(path, true))
);
while(cursor.hasNext()){
out.println(cursor.next().toJson());
}
out.flush(); // flush to ensure writes
out.close(); // close the handle when done
} catch (IOException e) {
System.out.println("Error writing to file.");
}
Как только вы закончите с курсором, просто закройте возвращенный дескриптор файла.
Все, что вам действительно нужно знать, это то, что вы не «создаете» файловый дескриптор и не открываете его повторно «внутри» цикла. Это то, что вы делаете один раз, а затем просто «записываете» любыми способами для каждой итерации.
В качестве полного списка вы можете затем запустить код, как указано, для локальной MongoDB на порту по умолчанию без аутентификации. Добавьте параметры, чтобы изменить это по мере необходимости, но это необходимый «базовый» код.
import com.mongodb.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoDatabase;
import org.bson.Document;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
public class Application {
public static void main(String[] args) {
PrintWriter out = null;
MongoClient mongoClient = null;
try {
mongoClient = new MongoClient();
MongoDatabase db = mongoClient.getDatabase("test");
MongoCollection<Document> collection = db.getCollection("sample");
out = new PrintWriter(
new BufferedWriter(new FileWriter("output.txt"))
);
// Clear collection and insert data
collection.deleteMany(new Document());
collection.insertMany(Arrays.asList(
new Document("a", 1),
new Document("a", 2),
new Document("a", 3)
));
MongoCursor<Document> cursor = collection.find().iterator();
while ( cursor.hasNext() ) {
out.println(cursor.next().toJson());
//System.out.println(cursor.next().toJson());
}
out.flush();
} catch(Exception e) {
System.out.println(e.getMessage());
} finally {
if (out != null) {
out.close();
}
if (mongoClient != null) {
mongoClient.close();
}
}
}
}
Создает новый файл при каждом запуске и выводит документы, вставленные в коллекцию.
Дает вам файл с чем-то вроде этого:
{ "_id" : { "$oid" : "5b0bd9576a6bfa1f30e6c320" }, "a" : 1 }
{ "_id" : { "$oid" : "5b0bd9576a6bfa1f30e6c321" }, "a" : 2 }
{ "_id" : { "$oid" : "5b0bd9576a6bfa1f30e6c322" }, "a" : 3 }
Он не возвращает никаких ошибок и ничего не записывает в outputData.txt: /
@qwerty Вам нужно убедиться, что у вас действительно что-то есть в коллекции. Я даже упрощу ваш код и назову flush() специально для вас. Если вы раньше не писали подобный код, вам следовало бы четко указать на это в своем вопросе.
@qwerty Добавлен полный список, чтобы вы не ошиблись. Все, что вам нужно сделать, это просто запустить поставляемый код для установки сервера по умолчанию.
Вы также можете использовать Spring Integration + решение MongoDB
23.4 MongoDB Inbound Channel Adapter
The MongoDb Inbound Channel Adapter is a polling consumer that reads data from MongoDb and sends it as a Message payload.
<int-mongodb:inbound-channel-adapter id = "mongoInboundAdapter" channel = "replyChannel" query = "{'name' : 'Bob'}" entity-class = "java.lang.Object" auto-startup = "false"> <int:poller fixed-rate = "100"/> </int-mongodb:inbound-channel-adapter>
MongoDB Inbound Channel Adapter может непрерывно запрашивать вашу базу данных MongoDB, а затем обрабатывать информацию, чтобы делать с ней все, что вы хотите.
Вы можете найти несколько примеров в документации.
Я надеюсь, что это поможет вам.
Вы можете оптимизировать, используя массовое чтение с .batchsize (). При извлечении данных с помощью курсора вы можете указать количество строк, извлекаемых в каждой операции, с помощью предложения размер партии.
пример:
FindIterable<Document> collection = database.getCollection(collectionName)
.find()
.batchSize(arraySize);
//if you want to read 10 documents at once,
//set arraySize to 10.. so on
for(Document doc : collection) {
// save to file
}
Обратите внимание, что оператор batchSize на самом деле не возвращает массив в программу - он просто контролирует количество документов, извлекаемых в каждом сетевом цикле приема-передачи. С точки зрения ваших программ, все это происходит «под капотом». Надеюсь, это поможет
Вы можете узнать больше о размере партии на https://docs.mongodb.com/manual/reference/method/cursor.batchSize/
На мой взгляд, вам нужно много чего, вроде триггеров. MongoDB не поддерживает триггеры, однако некоторые люди «накатили свои собственные», используя некоторые уловки. Ключевым моментом здесь является oplog.
Когда вы запускаете MongoDB в наборе реплик, все действия MongoDB регистрируются в журнале операций (известном как журнал операций). Журнал операций - это просто текущий список изменений, внесенных в данные. Наборы реплик функционируют путем прослушивания изменений в этом журнале операций и последующего применения изменений локально.
Вы также можете прочитать о настраиваемых курсорах.
также вы можете воспользоваться помощью текущего проекта для решения той же проблемы:
https://github.com/deftlabs/mongo-java-tailable-cursor
Создайте файл «вне» цикла. Просто напишите в него на каждой итерации, а затем закройте файл.