Сохранение результата запроса к mongodb в переменную
Очень нужна ваша помощь. Вроде бы простой вопрос, но не могу понять как сохранить результат запроса к базе в переменную, используя mongoose. Предположим есть база данных test. Нужно запросить все записи из этой базы и сохранить в виде массива в переменной data.
Пробовал так:
var data = Test.find({}, (err,results) => {
if(err) throw(err);
console.log(results);
return results;
});
console.log(data);
console.log(results) внутри функции печатает идельный массив. console.log(data) вне функции печатает огромный массив в котором есть масса инфрормации о структуре коллекции, описание запроса, но нет данных из БД.
Пробовал так:
function getResult() {
Test.find({}, (err,result) => {
if(err) throw(err);
return result;
})
};
var data = getResult();
console.log(data);
Результат - undefiened.
Помогите пожалуйста. Если объясните суть проблемы то буду вдвойне благодарен.
Ответы (3 шт):
Потому что операция асинхронная.
Думаю, можно так:
let data;
function getResult() {
Test.find({}, (err,result) => {
if(err) throw(err);
data = result;
})
};
async function getResult() {
const data = await Test.find({});
console.log(data);
}
UPDATE
если Вы хотите вынести получения данных из mongoose в отдельную функцию (в чем я не особо вижу смысл), то попробуйте так:
// функция для получения данных с базы
const getResult = async () => await Test.find({});
// вызвать функцию и получить данные, но получать эти данные можно только внутри async функциях
const data = await getResult();
NEXT UPDATE Во-первых спасибо Вам за ответы. Надеюсь в итоге пойму всю глубину глубин. То есть чтобы получить результат от
await getResult()мне нужно завернуть это в другуюasync function, я правильно понимаю? Но опять таки, результат будет доступен только внутри этой функции. Наверное я неправильно описал вопрос. Итак есть функция, которая делает запрос к БД, получает какие то данные внутри себя и потом нужно чтобы эти данные сохранились в переменную за пределами функции. Чтобы потом впоследствии можно было использовать эту переменную где то дальше в коде.
Суть проблемы - вы ожидаете, что код выполняется сверху вниз, последовательно, то есть синхронно. А JS выполняется асинхронно. Чтобы получить последовательное выполнение асинхронного, нужно использовать async/await совместно с Promise и колбеками.
Это - теория. А теперь - практика. Ниже рабочий код, все разъяснения - в комментариях.
"use strinct";
const
mongodb = require("mongodb"),
assert = require("assert"),
MongoClient = mongodb.MongoClient,
config = {
mongo: {
uri: "mongodb://mongo:27017",
db: "test_db",
}
};
class App {
constructor(config) {
this.config = config;
// конструктор будет возвращать Promise, который можно "then()" и "catch()"
return new Promise(async (appResolve, appReject) => {
try {
// ждем подключение к Монге, оно также асинхронное
await this.connectMongo();
// инициализация (не обязательно)
await this.initMongo();
appResolve(this);
} catch (err) {
appReject(err);
}
});
}
// эта часть взята с моего проекта
// тут создаем коллекцию с уникальным полем email
// разумеется, в Монге специально коллекции создавать не нужно, они создаются при первой вставке в них,
// но тут добавляется уникальный индекс email
async initMongo() {
let collection = this.mongo.collection("mails");
await collection.createIndex( { "email": 1 }, { unique: true } );
// для примера добавляем один элемент
// replaceOne создает ИЛИ перезаписывает существующую запись,
// это я сделал для возможности повторного запуска скрипта без ошибок
// если использовать insertOne, то при втором запуске выскочит ошибка
// "duplicate key error collection"
let email = "[email protected]";
await collection.replaceOne( { email: email }, { email: email, blocked: true }, { upsert: true } );
}
// асинхронное подключение, тут колбек подключения к Монге завернут в Promise,
// при подключении выполняется `resolve();` который яваскрипт ждет в строчке `await this.connectMongo();`
async connectMongo() {
return new Promise((resolve, reject) => {
MongoClient.connect(this.config.mongo.uri, {
useNewUrlParser: true,
keepAlive: 1,
connectTimeoutMS: 5000
}, (err, db) => {
assert.equal(null, err);
this.mongo = db.db(this.config.mongo.db);
resolve();
});
});
}
// а вот и "проблемная" функция, она асинхронная
async getResult(email) {
let collection = this.mongo.collection("mails");
return await collection.findOne({email: email.trim().toLowerCase()});
}
}
let api = new App(config);
api.then(async (api) => {
// тут вызывается асинхронная функция, и яваскрипт "ждет" на этой строчке
let data = await api.getResult("[email protected]");
// после завершения асинхронной функции данные в переменной уже будут
console.log(data);
}).catch(err => {
// тут обработка ошибок
console.log("API fatal error:", err);
process.exit(1);
});
UPD
Как я понял, используется express. Тогда код можно написать так:
// я поменял POST => GET для демонстрации
api.get('/test', async (req,res) => {
// тут замените на свой клиент, я думаю это Resource
let mongo = this.mongo;
let collection = mongo.collection("mails");
let data = await collection.findOne();
// данные будут отправлены в ответе сервера
res.send(JSON.stringify(data));
});
Ключевое тут - async (req,res) и использование внутри await.