问题
Lets say I have the following collection structure:
{type: 1, value: "f"},
{type: 2, value: "c"},
{type: 2, value: "b"},
{type: 1, value: "d"},
{type: 1, value: "e"},
{type: 2, value: "a"}
Now I want to get the 2 alphabetically first documents ("value") for each "type", the result should look like:
{type: 1, value: "d"},
{type: 1, value: "e"},
{type: 2, value: "a"},
{type: 2, value: "b"},
With MongoDB I have to retrieve all documents and drop the unnecessary ones.
Is there another NoSQL system which has a built-in function to do that?
Another neat requirement would be the possibility to simply update a more than 2-dimensionally nested document, which also isn't possible in MongoDB, since you can only use the positional operator "$" once (see https://jira.mongodb.org/browse/SERVER-831). I need at least 3-dimensional documents, I already have thought that through, everything else would mean a huge performance decrease when reading.
Or are there possibilities to do that in MongoDB I am missing?
I am working with golang, so there should be a well-maintained package for the DB system.
回答1:
To do this you need to sort your documents by value
in ascending order using the $sort operator and then $group them by _id
and use the $push operator to return array of "values". From there and since aggregation result is an array you can you can use the .map() method which returns array of "key/values" where key
is type
and values
array of first two elements in values ( return by .splice() or .slice() )
db.collection.aggregate([
{ "$sort": { "value": 1 }},
{ "$group": { "_id": "$type", "values": { "$push": "$value" }}}
]).map( function( doc ) {
return { "type": doc._id, "values": doc.values.splice(0, 2) }})
Output
[
{
"type" : 1,
"values" : [ "d", "e" ]
},
{
"type" : 2,
"values" : [ "a", "b" ]
}
]
Starting from MongoDB 3.2 you can use the $slice operator in your $project
stage.
db.collection.aggregate([
{ "$sort": { "value": 1 }},
{ "$group": { "_id": "$type", "values": { "$push": "$value" }}},
{ "$project": { "values": { "$slice": [ "$values", 0, 2 ] }}}
])
回答2:
Graph databases may be a nice alternative to MongoDB, as they are more suitable for relationships modeling. You do not have to repeat a type in every document, you can make a node out of it.
Depending on a way you model your data, the task you are asking about may be simpler or more difficult. Not sure if there is a chance of built-in out-of-the box solution, but in worst case you may simply chain your values as nodes connected by relations, maintaining some kind of sorted list of values for each type, which will make getting N-top values for each type easy (and you won't have to retrieve unnecessary data).
There is a library for Neo4j that supports Go, but not sure if it is mature...
When it comes to MongoDB, you may also try to experiment with its own aggregation capabilities. I'm not that much familiar with it, but it seems to be a little bit hard, as $group aggregation has limited number of possible accumulators (you would probably need to apply limit to $push, but it does not seem to be possible)...
来源:https://stackoverflow.com/questions/31940862/get-the-2-alphabetically-first-documents-by-type