Estou procurando obter um registro aleatório de um enorme (100 milhões de registros) mongodb
.
Qual é a maneira mais rápida e eficiente de fazer isso? Os dados já estão lá e não há campo em que eu possa gerar um número aleatório e obter uma linha aleatória.
Alguma sugestão?
mongodb
mongodb-query
Will M
fonte
fonte
Respostas:
A partir da versão 3.2 do MongoDB, é possível obter N documentos aleatórios de uma coleção usando o
$sample
operador de agregação de pipeline:Se você deseja selecionar o (s) documento (s) aleatório (s) de um subconjunto filtrado da coleção, inclua um
$match
estágio no pipeline:Conforme observado nos comentários, quando
size
for maior que 1, pode haver duplicatas na amostra de documento retornada.fonte
Faça uma contagem de todos os registros, gere um número aleatório entre 0 e a contagem e faça:
fonte
Atualização para o MongoDB 3.2
3.2 introduziu $ sample no pipeline de agregação.
Há também um bom post sobre como colocá-lo em prática.
Para versões mais antigas (resposta anterior)
Na verdade, era uma solicitação de recurso: http://jira.mongodb.org/browse/SERVER-533, mas foi arquivada em "Não será corrigido".
O livro de receitas tem uma receita muito boa para selecionar um documento aleatório de uma coleção: http://cookbook.mongodb.org/patterns/random-attribute/
Parafraseando a receita, atribua números aleatórios aos seus documentos:
Em seguida, selecione um documento aleatório:
Consultando com ambos
$gte
e$lte
é necessário encontrar o documento com um número aleatório mais próximorand
.E é claro que você deseja indexar no campo aleatório:
Se você já estiver consultando um índice, basta soltá-lo, anexá
random: 1
-lo e adicioná-lo novamente.fonte
$gte
está o primeiro. Solução alternativa stackoverflow.com/a/9499484/79201 funcionaria melhor nesse caso.Você também pode usar o recurso de indexação geoespacial do MongoDB para selecionar os documentos 'mais próximos' de um número aleatório.
Primeiro, ative a indexação geoespacial em uma coleção:
Para criar vários documentos com pontos aleatórios no eixo X:
Então você pode obter um documento aleatório da coleção como este:
Ou você pode recuperar vários documentos mais próximos de um ponto aleatório:
Isso requer apenas uma consulta e nenhuma verificação nula, além do código ser limpo, simples e flexível. Você pode até usar o eixo Y do ponto geográfico para adicionar uma segunda dimensão aleatória à sua consulta.
fonte
A receita a seguir é um pouco mais lenta que a solução do livro de receitas mongo (adicione uma chave aleatória em todos os documentos), mas retorna documentos aleatórios distribuídos de maneira mais uniforme. É um pouco menos uniformemente distribuído que a
skip( random )
solução, mas muito mais rápido e com mais segurança contra falhas caso os documentos sejam removidos.Também requer que você adicione um campo "aleatório" aleatório aos seus documentos, portanto, não se esqueça de adicioná-lo ao criá-los: pode ser necessário inicializar sua coleção, como mostra Geoffrey
Resultados de referência
Esse método é muito mais rápido que o
skip()
método (de ceejayoz) e gera documentos aleatórios mais uniformes que o método "livro de receitas" relatado por Michael:Para uma coleção com 1.000.000 de elementos:
Esse método leva menos de um milissegundo na minha máquina
o
skip()
método leva 180 ms em médiaO método do livro de receitas fará com que um grande número de documentos nunca seja escolhido porque o número aleatório deles não os favorece.
Este método seleciona todos os elementos uniformemente ao longo do tempo.
No meu benchmark, era apenas 30% mais lento que o método do livro de receitas.
a aleatoriedade não é 100% perfeita, mas é muito boa (e pode ser melhorada se necessário)
Esta receita não é perfeita - a solução perfeita seria um recurso interno, como outros observaram.
No entanto, deve ser um bom compromisso para muitos propósitos.
fonte
Aqui está uma maneira de usar os
ObjectId
valores padrão para_id
e um pouco de matemática e lógica.Essa é a lógica geral na representação de shell e facilmente adaptável.
Assim, em pontos:
Encontre os valores mínimo e máximo da chave primária na coleção
Gere um número aleatório que fique entre os carimbos de data e hora desses documentos.
Adicione o número aleatório ao valor mínimo e encontre o primeiro documento maior ou igual a esse valor.
Isso usa "preenchimento" do valor do carimbo de data e hora em "hex" para formar um
ObjectId
valor válido, pois é isso que estamos procurando. Usar números inteiros como_id
valor é essencialmente mais simples, mas é a mesma idéia básica nos pontos.fonte
Em Python usando pymongo:
fonte
count()
por oestimated_document_count()
quecount()
está obsoleto no Mongdo v4.2.Agora você pode usar o agregado. Exemplo:
Veja o doc .
fonte
é difícil se não houver dados para digitar. quais são os campos _id? eles são ids de objetos mongodb? Nesse caso, você pode obter os valores mais alto e mais baixo:
se você assumir que os IDs são distribuídos uniformemente (mas não são, mas pelo menos é um começo):
fonte
Usando Python (pymongo), a função agregada também funciona.
Essa abordagem é muito mais rápida do que executar uma consulta para um número aleatório (por exemplo, collection.find ([random_int])) .É o caso especialmente de coleções grandes.
fonte
Você pode escolher um carimbo de data e hora aleatório e procurar o primeiro objeto que foi criado posteriormente. Ele digitalizará apenas um único documento, embora não necessariamente forneça uma distribuição uniforme.
fonte
Minha solução em php:
fonte
Para obter um número determinado de documentos aleatórios sem duplicatas:
loop obter índice aleatório e pular duplicado
fonte
Eu sugeriria usar o mapa / reduzir, onde você usa a função de mapa para emitir apenas quando um valor aleatório está acima de uma determinada probabilidade.
A função reduzem-se acima funciona porque apenas uma tecla ('1') é emitida a partir da função de mapa.
O valor da "probabilidade" é definido no "escopo", ao chamar mapRreduce (...)
Usar o mapReduce como esse também deve ser usado em um banco de dados fragmentado.
Se você quiser selecionar exatamente n de m documentos no banco de dados, faça o seguinte:
Onde "countTotal" (m) é o número de documentos no banco de dados e "countSubset" (n) é o número de documentos a serem recuperados.
Essa abordagem pode causar alguns problemas em bancos de dados fragmentados.
fonte
Você pode escolher _id aleatório e retornar o objeto correspondente:
Aqui você não precisa gastar espaço armazenando números aleatórios na coleção.
fonte
Eu sugiro adicionar um campo int aleatório para cada objeto. Então você pode simplesmente fazer uma
para escolher um documento aleatório. Apenas certifique-se de garantir o Índice ({random_field: 1})
fonte
Quando me deparei com uma solução semelhante, voltei atrás e descobri que a solicitação de negócios era realmente para criar alguma forma de rotação do inventário que estava sendo apresentado. Nesse caso, existem opções muito melhores, que têm respostas de mecanismos de pesquisa como o Solr, não de repositórios de dados como o MongoDB.
Em resumo, com o requisito de "alternar inteligentemente" o conteúdo, o que devemos fazer em vez de um número aleatório em todos os documentos é incluir um modificador pessoal de pontuação q. Para implementar isso você mesmo, assumindo uma pequena população de usuários, você pode armazenar um documento por usuário que possua o ID do produto, a contagem de impressões, a contagem de cliques, a data da última visualização e quaisquer outros fatores que a empresa considere significativos para calcular a pontuação daq modificador. Ao recuperar o conjunto para exibição, normalmente você solicita mais documentos do armazenamento de dados do que o solicitado pelo usuário final, aplica o modificador q score, pega o número de registros solicitados pelo usuário final e randomiza a página de resultados, uma pequena quantidade definido, basta classificar os documentos na camada do aplicativo (na memória).
Se o universo de usuários for muito grande, você poderá categorizar os usuários em grupos de comportamento e indexar por grupo de comportamento, em vez de usuário.
Se o universo de produtos for pequeno o suficiente, você poderá criar um índice por usuário.
Eu descobri que essa técnica é muito mais eficiente, mas mais importante ainda, mais eficaz na criação de uma experiência relevante e interessante do uso da solução de software.
fonte
nenhuma das soluções funcionou bem para mim. especialmente quando existem muitas lacunas e o conjunto é pequeno. isso funcionou muito bem para mim (em php):
fonte
find
+skip
é muito ruim, você está devolvendo todos os documentos apenas para escolher um: S.Se você estiver usando mangusto, poderá usar mongoose-random mongoose-random
fonte
Minha classificação / ordem do PHP / MongoDB por solução RANDOM. Espero que isso ajude alguém.
Nota: Eu tenho IDs numéricos na minha coleção do MongoDB que se referem a um registro do banco de dados MySQL.
Primeiro, crio uma matriz com 10 números gerados aleatoriamente
Na minha agregação, uso o operador de pipeline $ addField combinado com $ arrayElemAt e $ mod (módulo). O operador do módulo me fornecerá um número de 0 a 9, que eu uso para escolher um número da matriz com números gerados aleatoriamente.
Depois disso, você pode usar a classificação Pipeline.
fonte
Se você tiver uma chave de identificação simples, poderá armazenar todos os IDs em uma matriz e escolher um ID aleatório. (Resposta Ruby):
fonte
Usando Map / Reduce, você certamente pode obter um registro aleatório, mas não necessariamente de maneira muito eficiente, dependendo do tamanho da coleção filtrada resultante com a qual você trabalha.
Testei esse método com 50.000 documentos (o filtro o reduz para cerca de 30.000) e é executado em aproximadamente 400ms em um Intel i3 com 16 GB de RAM e um disco rígido SATA3 ...
A função Mapa simplesmente cria uma matriz dos IDs de todos os documentos que correspondem à consulta. No meu caso, testei isso com aproximadamente 30.000 dos 50.000 documentos possíveis.
A função Reduzir simplesmente seleciona um número inteiro aleatório entre 0 e o número de itens (-1) na matriz e retorna esse _id da matriz.
400ms parece muito tempo, e realmente é, se você tivesse cinquenta milhões de registros em vez de cinquenta mil, isso poderá aumentar a sobrecarga a ponto de se tornar inutilizável em situações de multiusuários.
Há um problema em aberto para o MongoDB incluir esse recurso no núcleo ... https://jira.mongodb.org/browse/SERVER-533
Se essa seleção "aleatória" fosse incorporada a uma pesquisa de índice em vez de coletar IDs em uma matriz e selecionar uma, isso ajudaria incrivelmente. (vá votar!)
fonte
Isso funciona bem, é rápido, funciona com vários documentos e não requer
rand
campo preenchido, o que acabará se preenchendo:ps. Como encontrar registros aleatórios na pergunta mongodb está marcado como duplicado desta pergunta. A diferença é que esta questão pede explicitamente sobre registro único quanto o outro explicitamente sobre a obtenção de documentos aleatórios s .
fonte
Se você estiver usando mongoid, o invólucro de documento para objeto, você pode fazer o seguinte no Ruby. (Supondo que seu modelo seja Usuário)
No meu .irbrc, eu tenho
então no console do rails, eu posso fazer, por exemplo,
para obter documentos aleatoriamente de qualquer coleção.
fonte
você também pode usar o shuffle-array após executar sua consulta
var shuffle = require ('shuffle-array');
Accounts.find (qry, função (err, matriz de resultados)) {newIndexArr = shuffle (matriz de resultados);
fonte
O que funciona de maneira eficiente e confiável é o seguinte:
Adicione um campo chamado "aleatório" a cada documento e atribua um valor aleatório a ele, adicione um índice para o campo aleatório e proceda da seguinte maneira:
Vamos supor que temos uma coleção de links da web chamados "links" e queremos um link aleatório a partir dele:
Para garantir que o mesmo link não apareça uma segunda vez, atualize seu campo aleatório com um novo número aleatório:
fonte