Filtrando datas com mongolite

A biblioteca mais popular do R para busca de dados do banco
MongoDB é a mongolite. Mas existem diversos desafios ao se utilizar esta biblioteca para trabalhar com o MongoDB, e um caso especial que quero tratar hoje é a filtragem de datas.

Para exemplificar o problema, tenho no MongoDB uma coleção de pedidos chamada Orders, com diversos atributos, dentre os quais a data de emissão do pedido, chamado EmissionDate, do tipo Date. Veja um fragmento do documento de um pedido:

{
"EmissionDate" : ISODate("2018-09-02T03:00:00.000Z"),
"ClientNumber" : "0019",
"IsOrderOpen" : false,
"Route" : "01",
...
"Items" : [
...
]
}

Uma consulta muito simples do MongoDB para retornar todos os pedidos lançados no mês de setembro de 2018 seria:

db.Orders.find({
  'EmissionDate': {
    $gte: new ISODate("2018-09-01"),
    $lt: new ISODate("2018-10-01")}
})

Ao estudarmos a documentação da sintaxe de consulta do mongolite (disponível aqui) construímos naturalmente um script R similar a:

# executado apenas se o pacote ainda não estiver instalado
install.packages("mongolite")

# carga do pacote mongolite
library(mongolite)

#conexão do MongoDB
m <- mongo(collection="Orders", url="mongodb://localhost:27017, db="Base")

m$find(query =
'{"EmissionDate": {
"$gte": new ISODate("2018-09-01"),
"$lt": new ISODate("2018-10-01")
}
}'
)

Só que, ao invés dos dados desejados, recebemos o seguinte erro:

Error: Invalid JSON object: {"EmissionDate" : {$gte: new ISODate("2018-09-01"), $lt: new ISODate("2018-10-01")}}

O problema: Parse da consulta JSON

A consulta falha pois o mongolite utiliza a bilioteca jsonlite do R para interpretação do JSON e essa, por sua vez, exige aspas em torno de todo string.

Se testarmos a consulta com a biblioteca jsonlite, identificamos exatamente onde está o problema:

jsonlite::fromJSON('{"EmissionDate" : {"$gte": new ISODate("2018-09-01"), "$lt": new ISODate("2018-10-01")}}')
Error: lexical error: invalid string in json text.
            {"EmissionDate" : {"$gte": new ISODate("2018-09-01"), "$lt
                     (right here) ------^

O Parse falha no trecho new ISODate(“2018-09-01”). O problema é que não há como colocar este trecho entre aspas, assim não sendo possível rodar uma consulta como essa através do mongolite.

A solução: manipulação numérica de datas

A solução está longe de ser a ideal na minha opinião, mas pesquisando na internet, é a única forma que encontrei de contornar o problema.

A classe POSIXct do R representa a data como a quantidade de segundos passados desde o início do ano de 1970 (UNIX epoch).

Já o tipo Date do MongoDB representa a quantidade de milisegundos deste o UNIX epoch.

Então para montar a consulta sem utilização dos termos problemáticos, podemos multiplicar por 1000 a representação numérica do POSIXct, para adequarmos a mesma para a representação do tipo Date do MongoDB. Veja como fica a consulta:

m$find(query = paste0(
'{ "EmissionDate": {
"$gte": { "$date" : { "$numberLong" : "',
as.integer(as.POSIXct("2018-09-01")) * 1000, '" } },
"$lt": { "$date" : { "$numberLong" : "',
as.integer(as.POSIXct("2018-10-01")) * 1000, '" } }
}
}'
)
)

Esta consulta está de acordo com a sintaxe esperada pela biblioteca jsonlite, e nos retorna o resultado esperado.

Esta é forma como encontrei para resolver o problema. Se você conhece uma estratégia mais elegante, compartilha conosco nos comentários 🙂

Ficou com alguma dúvida ou tem alguma consideração sobre o problema? Então deixa um comentário aí, que será uma satisfação poder ajudar!

1 comentário em “Filtrando datas com mongolite”

Deixe um comentário

%d blogueiros gostam disto: