This might be a dumb and simple question, but since I am a completely newbie I could not find a workaround.
Lets say I have these users in the DB :
Me with maxScore : 4
John with maxScore: 3
Doe with maxScore: 9
Ana with maxScore: 12
Bob with maxScore: 1
I would like to get "3" as a result since after ordering the users, "Me" is the third one with 4 maxScore. BUT if the app has lets say 2000 users, I want to get the ranking of "Me", for example 1375.
How can I achieve this?
thanks in advance
I have been able to sort my DB with
const usersAround = await prisma.user.findMany({
orderBy: {
maxScore: "desc",
},
cursor: {
id: myUser.id,
},
take: 5,
});
But I want to get the index (or the rank) of my user in the ranking of the scores.
You can use the Raw Query to pass a SQL query to find your global position.
This Stack Overflow answer provides the sql query, which you can pass in the $queryRaw function to get the position.
Here you go:
const users = await prisma.user.findMany({
orderBy: {
maxScore: "desc",
},
take: 5,
});
const ranking = users.findIndex(user => user.id === myUser.id) + 1
Related
So, I'm making a school project and I've made a system to give points, and store names and details of classmates, now the part, where I'm stuck, in that I want to be able to make a leaderboard that lists names and points from highest to lowest how would I do that
The below query will list the users from score highest to lowest
Class.find({Score: { $gt: 0}}).sort({ score: -1 }).exec( function(err, class){
message.channel.send(`Leaderboard\n ${class.FirstName}`)
})
Given Users your models in which are stored names, details and scores of your classmates, here're some solutions to sort based on a specified field (in this case, score), using the Mongoose's model built-in sort function:
Users.find({ Score: > 0 }).sort({ Score: 'asc' }).exec((err, docs) => { ... });
// Another way...
Users.find({ Score: > 0 }).sort({ Score: 'ascending' }).exec((err, docs) => { ... });
// Yet, another way...
Users.find({ Score: > 0 }).sort({ Score: 1 }).exec((err, docs) => { ... });
Check out the documentation for more.
I just use mongoose recently and a bit confused how to sort and paginate it.
let say I make some project like twitter and I had 3 schema. first is user second is post and third is post_detail. user schema contains data that user had, post is more like fb status or twitter tweet that we can reply it, post_detail is like the replies of the post
user
var userSchema = mongoose.Schema({
username: {
type: String
},
full_name: {
type: String
},
age: {
type: Number
}
});
post
var postDetailSchema = mongoose.Schema({
message: {
type: String
},
created_by: {
type: String
}
total_reply: {
type: Number
}
});
post_detail
var postDetailSchema = mongoose.Schema({
post_id: {
type: String
}
message: {
type: String
},
created_by: {
type: String
}
});
the relation is user._id = post.created_by, user._id = post_detail.created_by, post_detail.post_id = post._id
say user A make 1 post and 1000 other users comment on that posts, how can we sort the comment by the username of user? user can change the data(full_name, age in this case) so I cant put the data on the post_detail because the data can change dynamically or I just put it on the post_detail and if user change data I just change the post_detail too? but if I do that I need to change many rows because if the same users comment 100 posts then that data need to be changed too.
the problem is how to sort it, I think if I can sort it I can paginate it too. or in this case I should just use rdbms instead of nosql?
thanks anyway, really appreciate the help and guidance :))
Welcome to MongoDB.
If you want to do it in the way you describe, just don't go for Mongo.
You are designing the schema based on relations and not in documents.
Your design requires to do joins and this does not work well in mongo because there is not an easy/fast way of doing this.
First, I would not create a separate entity for the post details but embedded in the Post document the post details as a list.
Regarding your question:
or I just put it on the post_detail and if user change data I just
change the post_detail too?
Yes, that is what you should do. If you want to be able to sort the documents by the userName you should denormalize it and include in the post_details.
If I had to design the schema, it would be something like this:
{
"message": "blabl",
"authorId" : "userId12",
"total_reply" : 100,
"replies" : [
{
"message" : "okk",
"authorId" : "66234",
"authorName" : "Alberto Rodriguez"
},
{
"message" : "test",
"authorId" : "1231",
"authorName" : "Fina Lopez"
}
]
}
With this schema and using the aggregation framework, you can sort the comments by username.
If you don't like this approach, I rather would go for an RDBMS as you mentioned.
I have a collection of videos :
var VideoSchema = new mongoose.Schema({
url: {
type: String, required : true
},
orderBy: {
type: Number
},
views: [{
type: String
}]
});
'orderBy' lets me define the order in which I serve videos
'views' is a list of usernames, those who have watched the video.
I want to keep users from watching again the same video until they have watched all videos in the collection. So I keep the names of users who have watched the video inside the 'views'.
Now I query for videos using 'sort'.
return this.find(videoQuery)
.skip(offset)
.limit(count || 10) // #todo 10 : param
.sort({views: {$meta: username}, 'orderBy': -1})
.exec();
And I get the error message :
Can't canonicalize query: BadValue bad sort specification
Can you help ?
May be wrong but from mongo docs. When you specify $meta in sort you need to include it in projectedFieldName.
https://docs.mongodb.com/manual/reference/operator/projection/meta/#proj._S_meta
So it should looks something like this
return this.find(videoQuery, { views: { $meta: 'textScore' } })
.skip(offset)
.limit(count || 10) // #todo 10 : param
.sort({views: {$meta: textScore}, 'orderBy': -1})
.exec();
UPDATED
Seems that $meta only accepts 'textScore' as #Pierre mentioned.
So another suggestion is to use underscore or native js to sort data when response comes from DB. This will be less efficient and may need some adjustments, but this will work definitely.
Hope this helps.
I am using Mongoose to fetch data from MongoDB. Here is my model.
var EmployeeSchema = new Schema({
name: String,
viewCount: { type: Number, default: 0 },
description: {
type: String,
default: 'No description'
},
departments: []
});
I need to find top 5 employees where count(viewCount) is highest order by name.
I am thinking of finding all the employee by using find() & then read viewCount property & produce the result. is there any better way to get the desired result.
All you need here is .sort() and .limit():
Employee.find().sort({ "viewCount": -1, "name": 1 }).limit(5)
.exec(function(err,results) {
});
And that is the top 5 employees in views ordered by name after the viewCount.
If you want them ordered by "name" in your final five, then just sort that result:
Employee.find().sort({ "viewCount": -1, "name": 1 }).limit(5)
.exec(function(err,results) {
// sort it by name
results.sort(function(a,b) {
return a.name.localeCompare(b.name);
});
// do something with results
});
You can sort by the view count and limit the search results to 5.
In code it might look like this:
Employee
.find()
.sort([['viewCount',-1], ['name',-1]])
.limit(5)
.exec(function(err, results){
//do something with the results here
});
I am making a analytics system, the API call would provide a Unique User ID, but it's not in sequence and too sparse.
I need to give each Unique User ID an auto increment id to mark a analytics datapoint in a bitarray/bitset. So the first user encounters would corresponding to the first bit of the bitarray, second user would be the second bit in the bitarray, etc.
So is there a solid and fast way to generate incremental Unique User IDs in MongoDB?
As selected answer says you can use findAndModify to generate sequential IDs.
But I strongly disagree with opinion that you should not do that. It all depends on your business needs. Having 12-byte ID may be very resource consuming and cause significant scalability issues in future.
I have detailed answer here.
You can, but you should not
https://web.archive.org/web/20151009224806/http://docs.mongodb.org/manual/tutorial/create-an-auto-incrementing-field/
Each object in mongo already has an id, and they are sortable in insertion order. What is wrong with getting collection of user objects, iterating over it and use this as incremented ID? Er go for kind of map-reduce job entirely
I know this is an old question, but I shall post my answer for posterity...
It depends on the system that you are building and the particular business rules in place.
I am building a moderate to large scale CRM in MongoDb, C# (Backend API), and Angular (Frontend web app) and found ObjectId utterly terrible for use in Angular Routing for selecting particular entities. Same with API Controller routing.
The suggestion above worked perfectly for my project.
db.contacts.insert({
"id":db.contacts.find().Count()+1,
"name":"John Doe",
"emails":[
"john#doe.com",
"john.doe#business.com"
],
"phone":"555111322",
"status":"Active"
});
The reason it is perfect for my case, but not all cases is that as the above comment states, if you delete 3 records from the collection, you will get collisions.
My business rules state that due to our in house SLA's, we are not allowed to delete correspondence data or clients records for longer than the potential lifespan of the application I'm writing, and therefor, I simply mark records with an enum "Status" which is either "Active" or "Deleted". You can delete something from the UI, and it will say "Contact has been deleted" but all the application has done is change the status of the contact to "Deleted" and when the app calls the respository for a list of contacts, I filter out deleted records before pushing the data to the client app.
Therefore, db.collection.find().count() + 1 is a perfect solution for me...
It won't work for everyone, but if you will not be deleting data, it works fine.
Edit
latest versions of pymongo:
db.contacts.count() + 1
First Record should be add
"_id" = 1 in your db
$database = "demo";
$collections ="democollaction";
echo getnextid($database,$collections);
function getnextid($database,$collections){
$m = new MongoClient();
$db = $m->selectDB($database);
$cursor = $collection->find()->sort(array("_id" => -1))->limit(1);
$array = iterator_to_array($cursor);
foreach($array as $value){
return $value["_id"] + 1;
}
}
I had a similar issue, namely I was interested in generating unique numbers, which can be used as identifiers, but doesn't have to. I came up with the following solution. First to initialize the collection:
fun create(mongo: MongoTemplate) {
mongo.db.getCollection("sequence")
.insertOne(Document(mapOf("_id" to "globalCounter", "sequenceValue" to 0L)))
}
An then a service that return unique (and ascending) numbers:
#Service
class IdCounter(val mongoTemplate: MongoTemplate) {
companion object {
const val collection = "sequence"
}
private val idField = "_id"
private val idValue = "globalCounter"
private val sequence = "sequenceValue"
fun nextValue(): Long {
val filter = Document(mapOf(idField to idValue))
val update = Document("\$inc", Document(mapOf(sequence to 1)))
val updated: Document = mongoTemplate.db.getCollection(collection).findOneAndUpdate(filter, update)!!
return updated[sequence] as Long
}
}
I believe that id doesn't have the weaknesses related to concurrent environment that some of the other solutions may suffer from.
// await collection.insertOne({ autoIncrementId: 1 });
const { value: { autoIncrementId } } = await collection.findOneAndUpdate(
{ autoIncrementId: { $exists: true } },
{
$inc: { autoIncrementId: 1 },
},
);
return collection.insertOne({ id: autoIncrementId, ...data });
I used something like nested queries in MySQL to simulate auto increment, which worked for me. To get the latest id and increment one to it you can use:
lastContact = db.contacts.find().sort({$natural:-1}).limit(1)[0];
db.contacts.insert({
"id":lastContact ?lastContact ["id"] + 1 : 1,
"name":"John Doe",
"emails": ["john#doe.com", "john.doe#business.com"],
"phone":"555111322",
"status":"Active"
})
It solves the removal issue of Alex's answer. So no duplicate id will appear if any record is removed.
More explanation: I just get the id of the latest inserted document, add one to it, and then set it as the id of the new record. And ternary is for cases that we don't have any records yet or all of the records are removed.
this could be another approach
const mongoose = require("mongoose");
const contractSchema = mongoose.Schema(
{
account: {
type: mongoose.Schema.Types.ObjectId,
required: true,
},
idContract: {
type: Number,
default: 0,
},
},
{ timestamps: true }
);
contractSchema.pre("save", function (next) {
var docs = this;
mongoose
.model("contract", contractSchema)
.countDocuments({ account: docs.account }, function (error, counter) {
if (error) return next(error);
docs.idContract = counter + 1;
next();
});
});
module.exports = mongoose.model("contract", contractSchema);
// First check the table length
const data = await table.find()
if(data.length === 0){
const id = 1
// then post your query along with your id
}
else{
// find last item and then its id
const length = data.length
const lastItem = data[length-1]
const lastItemId = lastItem.id // or { id } = lastItem
const id = lastItemId + 1
// now apply new id to your new item
// even if you delete any item from middle also this work
}