Como trabalhar com cursores no MongoDB
- Postado por Adriano Bonacin
- Categorias mongodb
- Data 02/02/2022
- Comentários 0 comentário
Para quem é do mundo de banco de dados, cursores sempre estão aparecendo no nosso dia a dia. Eles são utilizados para iterar sobre um conjunto de registros de forma programática, fazendo alguma operação para cada registro retornado. Por exemplo, para cada pedido que tenho em aberto eu posso chamar uma API para ver a situação atual, e se necessário, atualizar o status na tabela de pedidos. Mas você sabe como trabalhar com cursores no MongoDB?
Primeiros passos
Quando falamos em cursores no MongoDB, trata-se um conjunto finito de documentos, e quando processado o último, ele será fechado. Também será automaticamente fechado caso a sessão se encerre ou ocorra um timeout (30 minutos de inatividade (default)).
Temos basicamente duas formas de iterar os cursores, uma com o .next() e outra com o .forEach(), e também temos algumas opções extras que vou citar aqui.
Primeiramente, para os casos que vou citar, estamos olhando para variáveis ou métodos que nos retornam um “Cursor”, como o .find(). Vamos começar pela nossa collection de carros.
Vou recriá-la para garantir que você tenha os mesmos 15 documentos.
rsYadax0 [direct: primary] yadax> db.cars.drop()
true
rsYadax0 [direct: primary] yadax> db.cars.insertMany([
... {"marca":"HONDA", "modelo":"CIVIC", "ano_fabricacao": 2015 , "ano_modelo": 2016, "cor": "Prata", "preco": 60000},
...
... {"marca":"FORD", "modelo":"PAMPA", "ano_fabricacao": 1989, "ano_modelo": 1989, "cor": "Prata", "preco": null}
... ])
{
acknowledged: true,
insertedIds: {
'0': ObjectId("61facad32a8d3513c49dfa6c"),
...
'14': ObjectId("61facad32a8d3513c49dfa7a")
}
}
rsYadax0 [direct: primary] yadax> db.cars.find().constructor.name
Cursor
Iterando um cursor com .next()
Bom, agora sabendo que temos um Cursor, vamos começar pelo .next(). Vamos precisar de um pouco de conhecimento de programação, que vou assumir que você já tenha. O método .hasNext() retorna TRUE se ainda temos documentos para iterar. E se temos, o .next() retorna o próximo documento. Se você precisa de algum ordenamento, você pode fazer na query e guardar na variável myCars. Para simplificar a leitura, vou atribuir cada documento a uma variável no singular, myCar.
//guardo o resultado da query ordenado por marca em myCars
var myCars = db.cars.find().sort({marca:1})
//enquanto tiver documentos, continue
while (myCars.hasNext()){
// guardo cada documento na variavel myCar e acesso com "myCar.campo"
myCar = myCars.next()
print("Marca: " + myCar.marca + " - modelo: " + myCar.modelo + " - preco: " + myCar.preco)
}
Marca: FIAT - modelo: UNO - preco: 10000
...
Marca: VW - modelo: GOLF - preco: 90000
Marca: VW - modelo: GOL - preco: 10000
Iterando um cursor com .forEach()
O mesmo resultado com o .forEach, porém sem precisar se preocupar se há documentos para iterar. Ele vai automaticamente iterar todos. Um ponto imporatante é que usamos aqui o que chamamos de funções anônimas do JavaScript. Não vou entrar em detalhes, mas você pode ter uma referência aqui.
//guardo o resultado da query ordenado por marca em myCars
var myCars = db.cars.find().sort({marca:1})
// myCar eh o nome da variavel que eu escolhi, voce pode escolher qualquer um
myCars.forEach(function(myCar){
print("Marca: " + myCar.marca + " - modelo: " + myCar.modelo + " - preco: " + myCar.preco)
})
Um detalhe é que se você tentar executar o comando com o .forEach sem declarar novamente o myCars, o retorno será um erro. Isso ocorre porquê você já percorreu todos os documentos e o Cursor já foi fechado. Você pode declarar novamente e iterar.
rsYadax0 [direct: primary] yadax> myCars.forEach(function (myCar) { print("Marca: " + myCar.marca + " - modelo: " + myCar.modelo + " - preco: " + myCar.preco); })
MongoCursorExhaustedError: Cursor is exhausted
Cursores indexados
A última opção que gostaria de comentar é sobre os incluir artificialmente um index no cursor. Como isso funciona? Vamos transformar nosso cursor em um array, usando o método .toArray(). Isso agora vai ficar em uma variável que podemos percorrer quantas vezes quisermos e o cursor será automaticamente fechado.
var myCars = db.cars.find().sort({marca:1})
var myCarsArray = myCars.toArray();
myCarsArray
[
{
_id: ObjectId("61fad9dc2a8d3513c49dfa8d"),
marca: 'FIAT',
modelo: 'UNO',
ano_fabricacao: 2000,
ano_modelo: 2001,
cor: 'Preto',
preco: 10000
},
...
]
// acessando o terceiro elemento do array, que começa do elemento 0
myCarsArray[2]
{
_id: ObjectId("61fad9dc2a8d3513c49dfa8f"),
marca: 'FIAT',
modelo: 'STRADA',
ano_fabricacao: 2018,
ano_modelo: 2018,
cor: 'Branco',
preco: 40000
}
Métricas de cursores
Do lado do banco de dados, sempre precisamos de informações a respeito do consumo, performance e outras métricas. Você pode obter algumas métricas relativas a cursores usando o método db.serverStatus(). Dentro de metrics.cursor você verá:
rsYadax0 [direct: primary] yadax> db.serverStatus().metrics.cursor
{
moreThanOneBatch: Long("12"),
timedOut: Long("5"),
totalOpened: Long("30"),
lifespan: {
greaterThanOrEqual10Minutes: Long("11"),
lessThan10Minutes: Long("0"),
lessThan15Seconds: Long("1"),
lessThan1Minute: Long("0"),
lessThan1Second: Long("11"),
lessThan30Seconds: Long("0"),
lessThan5Seconds: Long("1")
},
open: { noTimeout: Long("0"), pinned: Long("1"), total: Long("1") }
}
Bom, espero que tenha contribuído um pouco para você entender como podemos trabalhar com cursores no mongosh. Com eles podemos automatizar um pouco do nosso dia a dia, as vezes deixar as informações mais claras selecionando apenas alguns campos de interesse, particularmente uso para deixar os resultados tabulares. E você, comente aí para que usa cursores no MongoDB.
Tag:mongodb