I have document like below. Here peoples contains array of ObjectId which points to user collection. And contribution field contains as many number of subdocument as peoples field. Length is variable like if some group has 2 ObjectId in people then contribution will have 2 sub document. I need to create mongoDb schema for this, please tell me schema for this.
{
name: "person name",
_id: ObjectId(""),
creater: ObjectId("1"), //referencing to user collection
peoples: [ObjectId("1"), ObjectId("2"),...upto n], //all referencing to user table
contribution: {
ObjectId("1"):{
paid: 1200,
due: 1000,
prevDue: 200,
Advance: 0
},
ObjectId("2"):{
paid: 1200,
due: 1000,
prevDue: 200,
Advance: 0
},
//upto end of lists in peoples array
},
estimated: 30000,
collected: 15379,
left: 14721
}
You just need to reference the nested schema in your main schema. For eg:
let user = new Schema({
name: String
})
let schema = new Schema({
followers: [user]
})
In the followers field of schema, you just referenced the user schema. Whenever this kind of reference is done, the types of nested schema get injected into the reference point.
Here is an implementation. Notice that I defined contribution property as an array of peopleContributionSchema. This makes the collection data easier to be accessed as you can loop for items in that array. The way you implemented is not flexible as ObjectId is a property of contribution, so you would need to know before hand the number of people in the contribution.
var peopleContributionSchema = new Schema({
_id: Schema.Types.ObjectId,
paid: Number,
due: Number,
prevDue: Number,
advance: Number
});
var parentSchema = new Schema({
_id: Schema.Types.ObjectId, // not necessary to define. Mongoose will add by default
name: String,
creater: Schema.Types.ObjectId,
peoples: [Schema.Types.ObjectId],
contribution: [peopleContributionSchema],
estimated: Number,
collected: Number,
left: Number
});
I got it what you want to do but you can't iteration a schema object like an 'n' no of series. An object schema in MongoDB is like key-value pair in JSON Object, you can't iterate it like a loop. Instead, the approach to do that is, define two different schemas and assign one schema in an array to use it in another schema as a subschema.
Follow the below-mentioned code:
{
name: "person name",
_id: ObjectId(""),
creater: ObjectId[1],
peoples: [ObjectId[0], ObjectId[1],...upto n-1],
ObjectId: [
{
paid: 1200,
due: 1000,
prevDue: 200,
Advance: 0
},
{
paid: 1200,
due: 1000,
prevDue: 200,
Advance: 0
}
],
estimated: 30000,
collected: 15379,
left: 14721
}
Related
I have 2 collections one is user and other is college. I want a rating schema with aggregate query user 1 gives 2 rating to college 1
I made a rating schema like
rate = mongoose.Schema({
rating: number,
userId:
collegeid:})
college comes on the basis of score user gets. And then user is allowed to rate the colleges, he has been shown.
So how to write the query to fetch the rating. How can I achieve this?
Let's assume the rate collection has data like this:
var r = [
{ rating: 1, userId: "U1", collegeid: "C1" },
{ rating: 1, userId: "U1", collegeid: "C2" },
{ rating: 3, userId: "U1", collegeid: "C3" },
{ rating: 2, userId: "U2", collegeid: "C1" },
{ rating: 6, userId: "U2", collegeid: "C2" },
{ rating: 7, userId: "U3", collegeid: "C1" },
{ rating: 5, userId: "U3", collegeid: "C4" },
{ rating: 3, userId: "U3", collegeid: "C3" }
];
Here are some useful queries to get started. In general, aggregate() is The New find() (since at least v3.2) and although slightly more complicated for the simplest expressions, it is vastly more powerful and it should be your starting point.
// Lookup all ratings available for collegeid C1:
c=db.rate.aggregate([
{$match: {"collegeid":"C1"}}
]);
// Lookup all ratings by user U3:
c=db.rate.aggregate([
{$match: {"userId":"U3"}}
]);
// Lookup all ratings by user U3 for college C1:
c=db.rate.aggregate([
{$match: {"userId":"U3","collegeid":"C1"}}
]);
// Lookup all ratings by user U3 OR college C1:
c=db.rate.aggregate([
{$match: {$or: [
{"userId":"U3"},
{"collegeid":"C1"}
]}}
]);
// Get counts, avg, max & min ratings for all colleges:
c=db.rate.aggregate([
{$group: {_id:"$collegeid", n:{$sum:1},
avg:{$avg:"$rating"},
max:{$max:"$rating"},
min:{$min:"$rating"}}}
]);
// Who rated colleges the highest?
c=db.rate.aggregate([
{$sort: {"rating":-1}}, // sort descending
// Use $first operator to capture only the first item for each unique collegeid
// in the sorted material flowing into this stage:
{$group: {_id:"$collegeid", who:{$first:"$userId"}, rating:{$first:"$rating"} }}
]);
You can use the built-in mongoose populate function if you define your Schema correctly.
Please check the link below
https://mongoosejs.com/docs/populate.html
The "mongoose" way would look something like
ratingSchema = mongoose.Schema({
rating: Number,
userId: {
ref: 'users', // Name of the collection that you would like to link to
type: mongoose.SchemaTypes.ObjectId // I Just added the object ID type you could use
// a string or whatever your id is
},
collegeId: {
ref: 'colleges', // Name of the collection that you would like to link to
type: mongoose.SchemaTypes.ObjectId
},
})
const Rating = mongoose.model('rating', ratingSchema);
And then you can use
Rating
.findOne({})
.populate('userId')
.populate('collegeId');
It is good however to read up and learn about the aggregate pipeline, but mongoose can do a lot of heavy lifting for you.
I'm trying to store in MongoDB a collection of cards for each user. A collection is basically an array of owned cards with an associated quantity.
Collection {
user: ...,
cards: [ {cardId: 133, qty: 3}, {cardId: 22, qty: 1} ]
}
I'm building an API with node.js and mongoose where I receive a request in the form of [ {cardId: 133, qty: 2}, {cardId: 111, qty: 4} ].
Now I need to either create the card entry in the cards array if it doesn't exist or update the quantity if it is already there.
I need to do this efficiently as collections may contain thousands of cards so I came up with this Schema:
var OwnedCard = new Schema({
cardId: { type: String, index: true, required: true},
qty: { type: Number, required: true}
});
var Collection = new Schema({
userId: { type: String, index: true },
cards: [OwnedCard]
});
I'm not sure however how to take advantage of the index on cardId to quickly locate and update (or create if missing) cards in the subarray
Essentially, for each { cardId: ..., qty: xxx } in request => find/update, or create the right entry in the cards array.
So far I have (to locate the collection of the user):
Collection.findOne({userId: userId}, function (err, collection) {
var cards = collection.cards; // the cards
});
But I don't want to filter through them as a Javascript object since it doesn't take advantage of the index and might be slow, and instead look for a way to get mongo to retrieve the individual card entry quickly.
Any ideas on how to achieve this?
I have collection named TradeRequest with the following schema
const TradeRequestSchema = mongoose.Schema({
status: {
type: String,
enum: ["opened", "rejected", "accepted"],
default: "opened"
},
_requester: {
required: true,
type: mongoose.Schema.Types.ObjectId
},
_requestedBook: {
required: true,
type: mongoose.Schema.Types.ObjectId
}
});
My database should have only one open request for a book by any person.
So I added a unique index like so -
TradeRequestSchema.index({_requester: 1, _requestedBook: 1, status: 1}, {unique: true});
Problem is this prevents some data to be entered in the database which should be allowed.
For example it prevents my database to have following documents which is fine for my use case -
{_id: 1, _requester: 12345, _requestedBook: 9871212, status: "opened"}
{_id: 2, _requester: 12345, _requestedBook: 9871212, status: "opened"}
But it also prevents the following which is wrong for my use case.
For example if my database already has following documents -
{_id: 3, _requester: 12345, _requestedBook: 9871212, status: "closed"}
{_id: 4, _requester: 12345, _requestedBook: 9871212, status: "opened"}
I cannot change request with id 4 to closed now which is wrong.
Basically what I want is to have a single opened request for a book but multiple closed requests.
How do I achieve this?
Mongodb has many types of indices like unique indices, sparse indices and partial indices. Your case doesn't need just the unique index, it needs unique partial index.
Try creating something like -
db.<<collectionName>>.createIndex(
{_requester: 1, _requestedBook: 1, status: 1},
{unique: true, partialFilterExpression: { "status" : "opened" }}
)
Hope it helps...
I have a schema which contains Friends . Friends is an array where each element is an object that contains an id, gender, and emoji.
var userSchema = new Schema({
id: {
type: String,
unique: true,
required: true
},
gender: String,
Friends: [{
id: String
gender: String
emoji: String
}]
});
In the code below, I'm accessing one document which contains a Friends array and specifying that search with distinct such it only shows the document's array of Friends. I need to access only one element of that array that contains a specified id. Instead of filtering that array after the query like what's done in the code, is there a way to just get the element purely from the query? In other words, is there an extra functionality to distinct or some kind of mongoose operator that allows that?
User.findOne().distinct('Friends', { id: req.body.myId }).then(function(myDoc) {
var friendAtt = myDoc.filter(function(obj) {
return obj.id == req.body.id
})[0]
})
Thanks to bertrand I was able to find that the answer lies in 'Projection'. In mongodb it's '$', in mongoose its select. Here is how I made it work:
User.findOne({id: req.body.myId}).select({ Friends: {$elemMatch: {id: req.body.id}}}),
It only returns the element that matched the id specified in friends.
You don't really need distinct here, you can use find() on Friends.id and filter the first subdocument that match Friends.id with the positional parameter $ :
db.user.find(
{ 'id': 'id1', 'Friends.id': 'id2'},
{ 'Friends.$': 1 }
)
In mongoose :
User.find({ 'id': req.body.myId, 'Friends.id': req.body.id }, { 'Friends.$': 1 }).then(function(myDoc) {
console.log("_id :" + myDoc[0].Friends[0].id);
console.log("gender:" + myDoc[0].Friends[0].gender);
})
Hopefully I can explain this well.
I have 3 Model types in play here: Users, Products, and Stores. What I'm after is a sorted list of Stores, per user, based on how many Products they've added from that Store. So basically "show me this User's top Stores".
pseudo-schemas:
var User = {
name: String
};
var Store = {
name: String
};
var Product = {
title: String,
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
}
store: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Store'
}
};
So how can I find which Stores the User has added the most Products to? This may be obvious, it's late. :-P
Thanks!
You can try to use Aggregation framework to solve it.
And especially $group pipeline:
// aggregate whole `Product` collection
Product.aggregate([
// count products by `user` and `store` and save result to `products_count`
{$group: {
_id: {user_id:"$user", store_id:"$store"},
products_count: {$sum: 1}
}},
// sort by most products count
{$sort: {products_count: -1}}
])
There are also $limit and $skip pipelines, that help to paginate all this stuff.