I am creating a new field during aggregation in order to sort result by relevancy. Unfortunately the $sort method just slow down my request execution. Can you explain me how to make it faster ?
Here my code :
MyClass.aggregate([
{"$match": query}
,{
"$project" : Object.assign({
//
// Build relevancy score
//
"relevancyScore": {blablabla...}
}, otherFields)
}
,{"$sort":{"relevancyScore":-1}} // This line slow down the request
,{"$skip": currentPage * pageSize}
,{"$limit": pageSize}
])
.exec(function (err, res) {
if (err) return console.log("error", err);
resolve(res);
});
If I $sort by '_id' it is really fast because _id field is indexed. Unfortunately my relevancyScore field is created in flight...
Thank you ! :D
Related
This is my code which searches the whole collection and returns the documents that the value of their name fields are either Dexter or Prison Break or Breaking bad.
Why should $search be at the top of stages; otherwise, I will get an error. Plus, I read on MongoDB doc that "The $match stage that includes a $text must be the first stage in the pipeline."
Here
app.get('/', (req, res) => {
db.collection('subs')
.aggregate([
{ $match: { $text: { $search: 'honey' } } },
{ $match: { name: { $in: ['Dexter', 'Prison Break', 'Breaking Bad'] } } },
])
.toArray((err, result) => {
if (err) {
throw new err();
}
res.json({
length: result.length,
body: { result },
});
});
});
I suppose the second line which filters the documents based on their name should come first; in order to reduce time and get a quick result because in this case, MongoDB will not have to search the whole collection and just search a few documents and returns the result.
why is that? is there any way for optimization?
I think it's because text search is required a special "text" index. Moving the "$search" operator to the second place makes impossible to use this index.
I'm trying to build queries to have actually interesting ways to interact with a database. One of them is to search for documents in a certain range of years. My thinkingnwas to build an aggregation pipeline where I check if the year range have been selected in the search form, then return the matching documents. If no range is selected, then return all the documents and go to the next stage of the aggregation pipeline.
Here is what I have tried (there's only one stage in the aggregate because i haven't managed to make this first one work, yet) :
db.collection('archives').aggregate([
{ $cond: { if: yearStart.length === 4 && yearEnd === 4 },
then: { $match:
{ $and:
[
{ year: {$gte: yearStart} },
{ year: {$lte: yearEnd} }
]
}
},
else: { $match: {} }
}
]).toArray( (err, docs) => {
if (err) throw (err)
res.json(docs)
})
So, this doesn't work. I get the error MongoError: A pipeline stage specification object must contain exactly one field.. So I thought it was just a matter of enclosing the $cond statement in curlies. But nope. I get a full on unexpected token { crash at this. So I'm thinkning that maybe $cond aren't meant to be used like this (returning $match stages). I'm inclined to think so, as the documentation only shows an example returning a simple value... Is it right?
Thanks in advance!
Create the query object to be used with $match outside the pipeline first with native JavaScript conditionals as using the $cond operator is only applicable within specified pipeline stages. Here you are using
it as a pipeline step hence the error thrown.
Consider the following:
const query = { "year": { } };
if (yearStart.length === 4 && yearEnd === 4) {
query["year"]["$gte"] = yearStart;
query["year"]["$lte"] = yearEnd;
}
db.collection('archives').aggregate([ { "$match": query } ]).toArray((err, docs) => {
if (err) throw (err);
res.json(docs);
});
Or using the find() method as
db.collection('archives').find(query).toArray((err, docs) => {
if (err) throw (err);
res.json(docs);
});
I have a query as mentioned below.
var projstat = ['A' , 'B'];
Post.native(function(err, collection) {
if (err)
console.log(err);
collection.find({
'status': {
"$in": projstat
}
}, {multi: true}, function(err, result) {
console.log(result);
if (req.isSocket) {
return res.json(result);
}
});
});
Please correct me if i am wrong as it doesnot return any results. Please help.
You're not using the native find correctly; rather than using a callback as an argument (like Waterline does), you chain the call to toArray and use the callback as its argument:
collection.find({
'status': {
"$in": projstat
}
}).toArray(function(err, results) {...});
Docs for the native Mongo driver are here.
However, the more important point in this case is that you don't need native at all. You can use the regular Waterline find, which automatically does an in query when an attribute is set to an array:
Post.find({status: projstat}).exec(function(err, results) {...});
I wants to get records with all fields using $group in mongodb.
i.e: SELECT * FROM users GROUP BY state, equivalent query in mongodb.
Can any one help me.
AFAIK there is no way to return all object in group query. You can use $addToSet operator to add fields into the array to return. The example code is shown in the below. You can add all the fields to the array using addToSet operator. It will return array as a response and you should get data from those array(s).
db.users.aggregate({$group : {_id : "$state", id : {$addToSet : "$_id"}, field1 : {$addToSet : "$field1"}}});
You can do it with MapReduce instead:
db.runCommand({
mapreduce: 'tests',
map: function() {
return emit(this.state, { docs: [this] });
},
reduce: function(key, vals) {
var res = vals.pop();
vals.forEach(function(val) {
[].push.apply(res.docs, val.docs);
});
return res;
},
finalize: function(key, reducedValue) {
return reducedValue.docs;
},
out: { inline: 1 }
})
I'm using finalize function in my example because MapReduce not supports arrays in reducedValue.
But, regardless of the method, you should try to avoid such queries in productions. They are fine for rare requests, like analytics, db migrations or daily scripting, but not for frequent ones.
I have this document stored in mongodb document:
{
"_id":ObjectId("4eb7642ba899edcc31000001")
"hash":"abcd123"
"value":"some_text_here"
}
I am using NodeJS to access the database:
collection.findOne({'hash' : req.param('query') },
function(err, result){
console.log(res);
});
The result of this query is the whole document, however I need to get only the "value" text: "some_text_here"
How can this be done?
You can specify the fields that you are interested in (_id will always be returned, though):
collection.findOne({'hash': req.param('query') },
{fields: ['value'] },
callbackFunction );
You can do it such way:
collection.findOne({'hash' : req.param('query') },
function(err, result){
console.log(result.value);
});