Hello! I’m quite new Realm and having a great time with it, but I recently got stuck converting a piece of code from the C# drivers into Realm Functions code.
My goal is to get X amount of documents from a leaderboards collection, sorting them by an “experience” value inside the document (think top 100 players in a certain skill for example). This is how I did with the C# drivers:
private static async Task<List<LeaderboardPlayerEntry>> ListEntriesByExperience(Skill skill, int limit) {
var database = MongoDatabase.GlobalMongoClient.GetDatabase("leaderboards");
var collection = database.GetCollection<LeaderboardEntryDocument>("entries");
return await collection
.Aggregate()
.Project(p => new LeaderboardPlayerEntry() { Name = p._id, Skill = skill, Value = p.Experience })
.SortByDescending(s => s.Value)
.Limit(limit)
.ToListAsync();
}
Any pointers on how the same could be accomplished in Functions? Thanks!
Here’s the base code in case someone in the future needs it:
// arg = skill, arg1 = min range, arg2 = max range
exports = function(arg, arg1, arg2) {
var collection = context.services.get("mongodb-atlas").db("dbName").collection("collectionName");
let query = {}
let sort = {}
query[arg] = { "$gte": 1000}
sort[arg] = -1
return collection.find(query)
.sort(sort)
.toArray()
.then(items => {
var entries = items.slice(arg1, arg2).map(function(x) {
var result = { "Name": x._id, "Rank" : items.indexOf(x) + 1, "Experience": x[arg] };
return result;
});
return entries;
});
};
The object initialization for the query is a bit funky since I want the queried fields to be passed in as arguments instead of having them hardcoded in the function.
If anyone has any improvement suggestions I’ll gladly take those as well. New to both Javascript and Mongo Realm so I’m sure the current code is less than optimal
exports = function(field, arg1, arg2) {
if (arg1 >= arg2) {
console.error("MinRange is same or higher than MaxRange!");
return;
}
var collection = context.services.get("mongodb-atlas").db("db").collection("entries");
let query = {}
let query2 = {}
let sort = {}
query[field] = { "$gte": 1000}
query2[field] = 1
sort[field] = -1
return collection.find(query, query2)
.skip(arg1)
.limit(arg2-arg1)
.sort(sort)
.toArray()
.then(items => {
items.forEach(function(item, index, items) {
var i = { }
i["Name"] = item["_id"];
i["Rank"] = items.indexOf(item) + 1;
i["Experience"] = item[field];
items[index] = i
});
return items;
});
};
I’m a little skeptical about the forEach loop in the end and creating a new object every iteration, but I’m not sure how else to insert the players “Rank” into to the object and replace the original field with “Experience” (or whatever field is set on the client to deserialize into), since the client doesn’t know what the field being queried is.
There is probably a way to replace that foreach loop by an aggregation pipeline instead.
The field renaming is trivial with a $project or $addFields. The rank might be a little trickier I guess there is way.