Eu posso estar um pouco preocupado com isso, pois ainda estou aprendendo os meandros do MongoDB, mas aqui vai.
No momento, estou trabalhando em uma ferramenta para pesquisar / filtraratravés de um conjunto de dados, classifique-o por um ponto de dados arbitrário (por exemplo, popularidade) e, em seguida, agrupe-o por um ID. A única maneira que vejo de fazer isso é através da funcionalidade MapReduce do Mongo.
Não posso usar .group () porque estou trabalhando com mais de 10.000 chaves e também preciso classificar o conjunto de dados.
Meu código MapReduce está funcionando bem, exceto por uma coisa: classificação. A classificação simplesmente não quer funcionar.
db.comando de execução({ "mapreduce": "produtos", "mapa": function () { emitir({ id_produto: this.product_id, popularidade: this.popularity } 1); } "reduzir": função (chave, valores) { var sum = 0; values.forEach (function (v) { soma + = v; }); soma de retorno; } "consulta": {category_id: 20}, "fora": {inline: 1}, "sort": {popularidade: -1} });
Eu já tenho um índice decrescente no ponto de dados de popularidade, então definitivamente não está funcionando devido à falta disso:
{"v": 1, "key": {"popularidade": -1}, "ns": "app.products", "nome": "popularidade_-1"}
Eu simplesmente não consigo descobrir por que ele não quer classificar.
Em vez de delinear o conjunto de resultados, não posso produzi-lo para outra coleção e, em seguida, executar um .find (). Sort ({popularidade: -1}) sobre isso por causa da maneira como esse recurso funcionará.
Respostas:
14 para resposta № 1Antes de tudo, o mapeamento / redução do Mongo não foi projetado para ser usado como uma ferramenta de consulta (como no CouchDB); ele foi projetado para você executar tarefas em segundo plano. Eu o uso no trabalho para analisar dados de tráfego.
O que você está fazendo de errado, no entanto, é que você está aplicando o sort () à sua entrada, mas é inútil porque quando o map()
etapa é feita, os documentos intermediários são classificados por cada keys
. Como sua chave é um documento, ela está sendo classificada por product_id
, popularity
.
Foi assim que eu gerei meu conjunto de dados
function generate_dummy_data() {
for (i=2; i < 1000000; i++) {
db.foobar.save({
_id: i,
category_id: parseInt(Math.random() * 30),
popularity: parseInt(Math.random() * 50)
})
}
}
E este meu mapa / reduzir tarefa:
var data = db.runCommand({
"mapreduce": "foobar",
"map": function() {
emit({
sorting: this.popularity * -1,
product_id: this._id,
popularity: this.popularity,
}, 1);
},
"reduce": function(key, values) {
var sum = 0;
values.forEach(function(v) {
sum += v;
});
return sum;
},
"query": {category_id: 20},
"out": {inline: 1},
});
E este é o resultado final (muito tempo para colá-lo aqui):
http://cesarodas.com/results.txt
Isso funciona porque agora estamos classificando por sorting, product_id, popularity
. Você pode brincar com a classificação da maneira que quiser, apenas lembre-se de que a classificação final é por key
independentemente de como sua entrada é classificada.
De qualquer forma, como eu disse antes, você deve evitar fazerconsultas com Map / Reduce foram projetadas para processamento em segundo plano. Se eu fosse você, projetaria meus dados de tal maneira que pudesse acessá-los com consultas simples; nesse caso, sempre há uma troca / inserção complexa de atualizações para consultas simples (é assim que vejo o MongoDB).
8 para resposta № 2
Como observado na discussão sobre a pergunta original:
Mapear / Reduzir com saída embutida atualmente não pode usar um código
sort
chave (ver SERVER-3973)As possíveis soluções alternativas incluem confiar na ordem da chave emitida (consulte a resposta de @crodas); enviar para uma coleção e consultar essa coleção com ordem de classificação; ou classificar os resultados em seu aplicativo usando algo como usort ().A preferência do OP é para resultados em linha em vez de criar / excluir coleções temporárias.
o Framework de agregação no MongoDB 2.2 (atualmente um candidato a lançamento de produção) forneceria uma solução adequada.
Aqui está um exemplo de uma consulta semelhante ao Mapear / Reduzir original, mas em vez disso, usando a Estrutura de agregação:
db.products.aggregate(
{ $match: { category_id: 20 }},
{ $group : {
_id : "$product_id",
"popularity" : { $sum : "$popularity" },
}},
{ $sort: { "popularity": -1 }}
)
.. e amostra de saída:
{
"result" : [
{
"_id" : 50,
"popularity" : 139
},
{
"_id" : 150,
"popularity" : 99
},
{
"_id" : 123,
"popularity" : 55
}
],
"ok" : 1
}