Getting an avarage value from MongoDB - mongodb

I have this mongoose model:
var juomaSchema = new mongoose.Schema({
...
valikoima: String,
img: String,
views: {default: 0, type: Number},
rating: [],
...
});
juomaSchema.methods.getAvarageRating = function() {
if (this.rating.length !== 0) {
var totalScore = 0;
this.rating.forEach(function(drinkRating) {
totalScore += drinkRating
});
return totalScore/this.rating.length;
} else {
return 0;
}
};
My problem is, that I need to sort a query by the avarage rating of the rating field.
I already have the method in the model, but I can't seem to use a function inside a sort query.
Here's my sort:
router.get("/oluet", function(req, res) {
var perPage = 20,
page = req.query.page,
sortBy = req.query.sort,
asc = req.query.asc;
var sort = {[sortBy] : asc};
console.log(sort);
if(req.xhr) {
//Sanitazes input
const regex = new RegExp(escapeRegExp(req.query.search), "gi");
Juoma.find({nimi: regex, tyyppi: "oluet"})
.sort(sort)
.limit(perPage)
.skip(perPage * page)
.exec(function(err, drinks) {
How would I do this? Making two fields: totalRating and timesRates, and doing some aggregation magic?

Felix solved this in the comments with
{
$project: {
avg: {$avg: "$rating"}
}
}

Related

How can I pass a variable in sort funtcion of mongobd?

I want to pass this name variable in sort function. I am getting the value of name in console.log but its not working in sort function.
var coldata = req.body.order[0].column;
var name = req.body.columns[coldata].data; // Want to pass this variable in sort()
var first = req.body.order[0].dir;
var last = req.body.order[0].dir;
var x,y;
if (first == 'asc'){
x = 1
}else{
x = -1;
}
if (last == 'asc'){
y = 1
}else{
y = -1;
}
var searchStr = req.body.search.value;
if(req.body.search.value)
{
var regex = new RegExp(req.body.search.value, "i")
searchStr = { $or: [{'firstname':regex },{'lastname': regex}] };
}
else
{
searchStr={};
}
console.log(req.body.search.value)
var recordsTotal = 0;
var recordsFiltered=0;
console.log(searchStr);
db.count({}, function(err, c) {
recordsTotal=c;
db.count(searchStr, function(err, c) {
recordsFiltered=c;
db.find(searchStr, 'firstname lastname',{'skip': Number( req.body.start), 'limit': Number(req.body.length) }, function (err, results) {
if (err) {
console.log('error while getting results'+err);
return;
}
var data = JSON.stringify({
"draw": req.body.draw,
"recordsFiltered": recordsFiltered,
"recordsTotal": recordsTotal,
"data": results
});
res.send(data);
}).sort({name:x});// Not getting value of name here
});
});
});
You can use an aggregation pipeline
const sort = {};
sort[name] = x
const pipeline = [
{ $match: searchStr },
{ $skip: Number( req.body.start) },
{ $limit: Number( req.body.length) },
{ $sort: sort }
];
db.aggregate(pipeline) ...

How to pass element index in array to mongoDB query?

Building cart on website and when product is added i want to first check if it is already in cart, if yes increment quantity by 1, if not add it. Cart is an array of objects and i want to pass index of object that contains added product to increment function but can't figure out how to do so.
async function add(product, userId) {
const user = await User.findById(userId);
const product = isProductInCart(product, user.cart); // returns true and index in cart if found
if (product.found === true) {
await User.findOneAndUpdate(
{ _id: userId },
{ $inc: { cart[product.index].quantity : 1 }} // not working
);
} else {
await User.findOneAndUpdate({ _id: userId }, { $push: { cart: product } });
}
}
function isProductInCart(product, cart) {
let productFound = { found: false, index: -1 };
for (let i = 0; i < cart.length; i++)
if (cart[i].name === product.name) {
productFound.found = true;
productFound.index = i;
break;
}
return productFound;
}
It looks like your code can be simplified if you consider using the $ positional operator:
let userWithCart = User.findOneAndUpdate(
{ _id: user, 'cart.name': product.name },
{ $inc: { 'cart.$.quantity' : 1 }}
)
if(!userWithCart ){
await User.findOneAndUpdate({ _id: userId }, { $push: { cart: product } });
}
First findOneAndUpdate will return no value when there's no corresponding cart.name (and you need to $push it). Otherwise MongoDB will automatically match the cart you want to update based on cart.name condition and increment the quantity subfield.
EDIT:
If you still need to proceed the way you've started you just need to evaluate the path in JavaScript:
{ $inc: { [`cart.${product.index}.quantity`] : 1 }}

mongoose $inc is not working would like to insert value increment by one

I am trying to increment view field by one always when it hit this api but does not work
error : Posts validation failed: view: Cast to number failed for value
view: {
type: Number,
default: 0
},
async request(req, res) => {
const post = await PostsModel.findOne({ _id: post_id });
post.view = { $inc: { view: 1 } };
await post.save();
}
In your way, you need to do post.view = post.view + 1 instead of post.view = { $inc: { view: 1 } }; because it will set the view field to be the object { $inc: { view: 1 } }.
Or if you want to use $inc, you need to make an update operation. Something like:
await PostsModel.findOneAndUpdate({ _id: post_id }, { $inc: { view: 1 } });

Null response in query for Relay Modern interogation on GraphQL

Maybe someone who has managed to pass this step is willing to provide some indications.
I have a schema, a resolver, i request the query and i have a null response.
Please can you help on topic?
module.exports = {
Query: {
allLinks: async (root, {filter}, {mongo: {Links, Users}}) => {
let query = filter ? {$or: buildFilters(filter)} : {};
return await Links.find(query).toArray();
}
and the query request looks like this:
query LinkListPageQuery {
allLinks {
...LinkList_allLinks
}
}
fragment LinkList_allLinks on LinkConnection {
edges {
cursor
...Link_link
}
}
fragment Link_link on LinkEdge {
node {
id
description
url
}
}
My schema looks like this:
const typeDefs = `
type Link implements Node {
id: ID!
url: String!
description: String!
postedBy: User
votes: [Vote!]!
}
interface Node {
id: ID!
}
type Query {
allLinks(filter: LinkFilter, first: Int): [LinkConnection]
node(
id: ID!
): Node
}
type LinkEdge {
node: Link!
cursor: String
}
type LinkConnection {
pageInfo: PageInfo
edges: LinkEdge
count: Int
}
input LinkFilter {
OR: [LinkFilter!]
description_contains: String
url_contains: String
}
}
`;
PS: This language schema is done according to graphql-tools package.
Resolver:
Query: {
users: async (root, { first, after }, { mongo: { Users }, user }) => {
const queryData = await Users.find(query).toArray();
first = first || queryData.length;
after = after ? parseInt(fromCursor(after), 10) : 0;
const edges = queryData.map((node, i) => ({
cursor: toCursor(i+1),
node: node._id,
})).slice(after, first + after);
const slicedUser = edges.map(({ node }) => node);
return {
edges,
pageInfo: {
startCursor: edges.length > 0 ? edges[0].cursor : null,
hasNextPage: first + after < queryData.length,
endCursor: edges.length > 0 ? edges[edges.length - 1].cursor : null
},
count: queryData.length,
};
},
UserConnection: {
edges: ({ edges }) => edges,
pageInfo: ({ pageInfo }) => pageInfo,
count: ({ count }) => count,
},
UserEdge: {
node: async ({ node },data, {dataloaders: {userLoader}}) => {
const user = await userLoader.load(node);
return user;
},
cursor: ({ cursor }) => cursor,
},

How to use $elemMatch in kinvey-flex-sdk?

Is there any way, how to use $elemMatch Projection Operators in kinvey-flex-sdk.
You can try this way.
var dataStore = modules.dataStore({ skipBl: true, useMasterSecret: true });
var collection = dataStore.collection('<collectionName>');
var query = new Kinvey.Query();
query.equalTo('items', { $elemMatch: { item: 'a', qty: { $gte: 23 } } });
collection.find(query, function (error, result) {
});