NodeJS: Jak pobrać dane w postaci zwykłych obiektów w Mongoose?

Opublikowano: 06.05.2023 - tagi: NodeJS Baza danych MongoDB Mongoose JavaScript TypeScript Dane

Problem

Pobierając dane za pomocą biblioteki Mongoose dostajesz obiekt lub listę obiektów typu Mongoose document. Tego typu dokument jest znacznie większy od zwykłego obiektu JavaScript.

Ostatnio natknąłem się na jeden problem. Przykład:

const getFromDb = async (id) => {
	return await SomeModel.findOne({ id });
}

const someRow = await getFromDb(id);

expect(someRow.data).toEqual([1, 2, 3, 4]);

Funkcja getFromDb zwraca jakieś dane dla konkretnego rekordu. Sprawdzane jest, czy pole data zawiera odpowiednie dane. Jeśli ten przykład napisany jest w TypeScript to test nie przejdzie, mimo że dane się zgadzają!

W czym jest problem? W typie, jaki Mongoose nadało polu data. Nie jest to zwykłe Array a CoreMongooseArray!

Co zrobić, żeby zwracało zwykłe obiekty JavaScript?

Rozwiązanie

Żeby nie pobierać Mongoose document a zwykłe obiekty wystarczy do zapytania dodać opcję lean:

const getFromDb = async (id) => {
	return await SomeModel.findOne({ id }).lean();
}

Problem rozwiązany!

Dodatkowo wg dokumentacji pobieranie danych w taki sposób jest znacznie szybsze! Czyli same plusy prawda?

Nie do końca. Najpierw trzeba zrozumieć jak działa Mongoose document.

Dokumenty Mongoose

Mongoose pozwala na tworzenie różnych użytecznych funkcji w modelu do przetwarzania danych. Na przykład:

const personSchema = new mongoose.Schema({
  firstName: {
    type: String,
    get: capitalizeFirstLetter
  },
  lastName: {
    type: String,
    get: capitalizeFirstLetter
  }
});

personSchema.virtual('fullName').get(function() {
  return `${this.firstName} ${this.lastName}`;
});

function capitalizeFirstLetter(v) {
  return v.charAt(0).toUpperCase() + v.substring(1);
}

const Person = mongoose.model('Person', personSchema);

await Person.create({ firstName: 'benjamin', lastName: 'sisko' });

const somePerson = await Person.findOne();

console.log(somePerson.fullName); // 'benjamin sisko' zmienione w 'Benjamin Sisko'
console.log(somePerson.firstName); // 'benjamin' zmienione w 'Benjamin' wywołuje capitalizeFirstLetter
console.log(somePerson.lastName); // 'sisko' zmienione w 'Sisko' wywołuje capitalizeFirstLetter

Problem wystąpi gdy wywołasz to samo zapytanie z opcją lean:

...

const somePerson = await Person.findOne().lean();

console.log(somePerson.fullName); // undefined
console.log(somePerson.firstName); // benjamin
console.log(somePerson.lastName); // sisko

Jak widać kosztem użycia lean jest pozbycie się różnego rodzaju pomocniczych funkcjonalności do przetwarzania danych.

Jeśli nie jest Ci to koniecznie potrzebne korzystaj z lean.