/ / MongoDB, MapReduce e classificação - mongodb, mongodb-php, mongodb-query

MongoDB, MapReduce e classificação - mongodb, mongodb-php, mongodb-query

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 № 1

Antes 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
}