MongoDB, sort big collection by a field frequently updated - mongodb

I have this schema (using nodejs - mongoose):
const Post = new mongoose.Schema({
title: {
type: String,
required: true,
unique: true,
},
description: {
type: String,
required: true,
},
likes: {
type: Number,
required: true,
default: 0,
min: 0,
}
}, {
timestamps: true,
});
Let's say my collection has millions of these documents and I want to sort by 'likes'. Meanwhile 'likes' is something very frequently updated so I don't think I should use sorting index on it. If I serve that content in pagination using sort and limit, does this guarantee I have good performance on reading the data even if I don't use index? (I know mongo by default uses some algorithm to create in-memory buckets to sort data, when no index is provided)

Related

Optimizing query that makes 3 API calls to the MongoDB server

const user_schema = mongoose.Schema(
{
user_name: {
type: String,
required: true,
},
},
{
collection: `user`,
timestamps: true,
}
);
const test_schema = mongoose.Schema(
{
test_name: {
type: String,
required: true,
},
},
{
collection: `test`,
timestamps: true,
}
);
const score_schema = mongoose.Schema(
{
user_id: {
type: mongoose.Schema.Types.ObjectId,
ref: "user",
required: true,
},
test_id: {
type: mongoose.Schema.Types.ObjectId,
ref: "test",
required: true,
},
test_score: {
type: Number,
required: true,
},
},
{
collection: `score`,
timestamps: true,
}
);
query:
Given an array of user_id and an array of test_id, query the score model to find out the test scores.
To get the array of user_id, a set of conditions is given and the user model must be queried to find the set of users matching the conditions.
To get the array of test_id, a set of conditions is given and the test model must be queried to find the set of tests matching the conditions.
What needs to be done:
Make one query request to the MongoDB server to get the array of user_id.
Make a separate query request to the MongoDB server to get the array of test_id.
Make another query request to the MongoDB server to get the test scores:
db.getCollection("score").aggregate([
{$match: {$and: {user_id: {$in: array_of_user_id}, {test_id: {$in: array_of_test_id}}}}}
])
Is this the most optimal way to get the test scores? Is it possible to make just one request to the MongoDB server?

Mongoose one-to-many relationship not being populated

I have a one-to-many relationship where a place can have multiple reviews. Here are the 2 schemas
export const PlaceSchema = new mongoose.Schema({
name: { type: String, required: true, unique: true },
center: { type: [Number], required: true },
borders: { type: [], required: true },
reviews: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Review' }]
});
export const ReviewSchema = new mongoose.Schema({
user: { type: String, required: true },
city: { type: mongoose.Schema.Types.ObjectId, ref: 'Place', required: true },
creation_date: { type: Date, required: true },
...
});
I have reviews with correct place ID. But when I do a simple this.placeModel.find().populate('reviews').exec(), the reviews always come back as an empty array. But the IDs seem to be fine, as visible here (place on the left, review on the right)
It's my first side project where I play with with Mongo, so I don't really see what I'm missing.
Your query this.placeModel.find().populate('reviews').exec() will work in this manner:
Find all place documents from the places collection.
For each place document, iterate through the reviews field (of array type) and search for the document in the reviews collection with the matching id, and replace the array element with the review document.
Return the list of place documents where the reviews field has been populated with the review documents.
Hence, you need to ensure that your place documents contain the correct id of the review documents in the reviews field instead of ensuring that you have the correct place id in the review documents for the query you want to execute.

How to structure a referenced document that needs to be filtered on mongoDB?

I have an user document and a transactions document.
The Transaction has a owner (User),
The User has an set of Transactions that has no limit to grow.
I need to get the User transactions and filter it by some properties like, date range and paid or non paid.
As my transactions Schema is unbounded I used the mongoose virtuals to populate the User with the transactions, but as I see it's not possible to make MongoDb queries to a virtual because it's not really in the Data Base.
The use Schema:
const userSchema = new mongoose.Schema(
{
name: {
type: String,
required: true,
},
email: {
type: String,
required: true,
trim: true,
unique: true,
},
...
},
);
userSchema.virtual('transactions', {
ref: 'Transaction',
localField: '_id',
foreignField: 'owner',
});
The Transaction Schema:
const transactionSchema = new Schema({
amount: {
type: Number,
required: true,
},
...
owner: {
type: Schema.Types.ObjectId,
ref: 'User',
required: true,
},
});
I feel like I made some mistake on modeling the database this way. Which would be the best approach to this case?
I don't think it's a good idea to use virtuals here. You have the user reference in your transaction model and it's enough. You can query whatever you like with mongo aggregations
You can do the aggregation on transactions collection and use $lookup (as described here) to populate the users or query them.

Have unique mongodb index on different layers of tree

I have a tree-like document model like the image below. Is it possible to create a unique index for different layers? For example, in the below example, I have index field 1, then different index fields in objects of l2 array and l3 array. I am trying to create an index where index of all layers together should be unique. For example, if I have an index 1, I can't have the same index value throughout the child documents or any other documents. I tried searching a solution for it, but couldn't find any. Please help me with this issue. Thanks in advance.
I'm assuming you are using NodeJs and Mongoose since you did not specify that. You can get an ObjectId for every level by using different schemas in nested objects like the below example.
const level2Schema = new Schema({
unit: {
type: String,
required: true
},
price: {
type: Number,
required: true
}
});
const level1Schema = new Schema({
feildx: {
type: String,
required: true
},
anyNameArray: {
type: [level2Schema],
required: true
}
});
var MainSchema = new Schema(
{
field1: String,
field2: String,
anyNameArray: {
type: [level1Schema],
default: [],
required: true
},
},
{ timestamps: true }
);
This will create a unique ObjectId for every nested document.

How to model a pending trade in MongoDB?

I'm wondering what the "Mongo Way" is for modeling a pending trade of an item between two users.
I have a user collection and I have a book collection. In my app, the users will be able to propose trades to one another. Until the trade proposal is accepted, the trade needs to be stored as a pending trade in the database.
It seems to me that the best option is to have a 'trades' property on each book document modeled like this (using Mongoose):
const booksSchema = new Schema({
title: { type: String, required: true },
createdAt: { type: Date, 'default': Date.now },
updatedAt: { type: Date, 'default': Date.now },
author: { type: String, required: false},
imageUrl: { type: String, required: false},
ownerUser: { type: Schema.ObjectId, required: true },
trades: [{
fromUser: { type: Schema.ObjectId, required: true },
bookOffered: { type: Schema.ObjectId, required: true }
}]
});
The problem I see with this is that it will involve updating two documents when the trade is accepted. Assuming that the trade is accepted, the ownerUser on each document will need to be changed and the trades array will need to be cleared out.
It seems that to do this you'd want the changes to be in some sort of "Transaction" so that if one didn't update for some reason, then the other wouldn't either.
Is this a typical way to model this type of situation? What to do about the "Transaction" part of the situation?
There is no way to do a transaction including multiple documents in MongoDB.
You might consider a separate Trade collection with documents like:
{
book: ...,
ownerUser: ...,
buyerUser: ...,
status: 'pending'
dateSold: null
}
When the trade is approved you can change this document first, then update any related documents next. Should something else fail, this document would decide whether the transaction had actually happened.