I have 2 different collections and I am trying to query the first collection and take the output of that as an input to the second collection.
var mycursor = db.item.find({"itemId":NumberLong(123)},{"_id":0})
var outp = "";
while(mycursor.hasNext()){
var rec = mycursor.next()
outp = outp + rec.eventId;
}
This query works fine and returns me a list of eventIds.
I have another collection named users, which has eventId field in it. A eventId can repeat in multiple users. So for each eventId I get in the above query I want to get the list of users too.
My query for the second collection would be something like this :
db.users.find({"eventId":ObjectdId("each eventId from above query")},{"_id":0})
My final result would be a unique list of users.
Whell this should basically work ( to a point that is ):
db.events.find({
"eventId": { "$in": db.item.find({
"itemId":NumberLong(123)
}).map(function(doc) { return doc.eventId }) }
})
Or even a bit better:
db.events.find({
"eventId": { "$in": db.item.distinct("eventId",{
"itemId":NumberLong(123) }) }
})
The reason is that "inner query" is evaluated before the outer query is sent. So you get an array of arguments for use with $in.
That is basically the same as doing the following, which translates better outside of the shell:
var events = db.item.distinct("eventId",{ "itemId":NumberLong(123) });
db.events.find({ "eventId": { "$in": events } })
If the results are "too large" however, then your best approach is to loop the initial results as you have done already and build up an array of arguments. Once at a certain size then do the same $in query several times in "pages" to get the results.
But looking for "ditinct" eventId via .distinct() or .aggregate() will help.
Related
I need to update some documents in one collection, and send an array of the _ids of the updated documents to another collection.
Since update() returns the number of updated items not their ids, I've come up with the following to get the array:
var docsUpdated = [];
var cursor = myCollection.find(<myQuery>);
cursor.forEach(function(doc) {
myCollection.update({_id : doc._id}, <myUpdate>, function(error, response){
docsUpdated.push(doc._id);
});
});
Or I could do:
var docsUpdated = myCollection.distinct("_id", <myQuery>);
myCollection.update(<myQuery>, <myUpdate>, {multi : true});
I'm guessing the second version would be faster because it only calls the database twice. But both seem annoyingly inefficient - is there another way of doing this without multiple database calls? Or am I overcomplicating things?
I think you need the cursor operator ".aggregate()"
db.orders.aggregate([
{ $group: { _id: "$_id"} }
])
something along those lines that returns the results of all the id's in the collection
Everything is in the question : I don't find an efficient way to return the first and the two last documents of my query...
I pass in parameters and array containing the requireed _id, that I select with { parentPost: { $in: myArray } }, but I don't figure out how to get both the two lasts and the first document in the same query...
The goal would be to return a collection of documents containing all the wanted posts of each thread (identified by parentPost) in a single query I can return in one subscription.
Could someone lighten my path ?
Thank you
David
I assume each post in an separate doc, and there are other fields in that upon which we can filter, like name.
Let's start with a 2 query example just to "get it done." Assuming something in docs is sortable like post number or a datetime field, then this clearly works.
c = db.foo.find({"name": "matt"}).sort({"post":1}).limit(N);
c.forEach(function(r) { printjson(r); });
c = db.foo.find({"name": "matt"}).sort({"post":-1}).limit(M);
c.forEach(function(r) { printjson(r); });
Of course, if you don't have a sortable thing, just go based on natural on-disk ordering:
c = db.foo.find({"name": "matt"}).sort({"$natural":1}).limit(N);
c.forEach(function(r) { printjson(r); });
c = db.foo.find({"name": "matt"}).sort({"$natural":-1}).limit(M);
c.forEach(function(r) { printjson(r); });
Here is a variation. It is likely faster because it avoids sorting twice only to restrict the output to just a few things:
n = db.foo.find({"name": "matt"}).count();
c = db.foo.find({
"name": "matt",
"$or": [
{"post": {$lte: N}}
,{"post": {$gte: n-M}}
]
});
I'm using Mongoose.js to perform an $in query, like so:
userModel.find({
'twitter_username': {
$in: friends
}
})
friends is just an array of strings. However, I'm having some case issues, and wondering if I can use Mongo's $regex functionality to make this $in query case-insensitive?
From the docs:
To include a regular expression in an $in query expression, you can
only use JavaScript regular expression objects (i.e. /pattern/ ). For
example:
{ name: { $in: [ /^acme/i, /^ack/ ] } }
One way is to create regular Expression for each Match and form the friends array.
var friends = [/^name1$/i,/^name2$/i];
or,
var friends = [/^(name1|name2)$/i]
userModel.find({"twitter_username":{$in:friends }})
Its a little tricky to do something like that
at first you should convert friends to new regex array list with:
var insesitiveFriends = [];
friends.forEach(function(item)
{
var re = new RegExp(item, "i");
insesitiveFriends.push(re);
})
then run the query
db.test.find(
{
'twitter_username':
{
$in: insesitiveFriends
}
})
I have the sample documents in test collection
/* 0 */
{
"_id" : ObjectId("5485e2111bb8a63952bc933d"),
"twitter_username" : "David"
}
/* 1 */
{
"_id" : ObjectId("5485e2111bb8a63952bc933e"),
"twitter_username" : "david"
}
and with var friends = ['DAvid','bob']; I got both documents
Sadly, mongodb tends to be pretty case-sensitive at the core. You have a couple of options:
1) Create a separate field that is a lowercase'd version of the twitter_username, and index that one instead. Your object would have twitter_username and twitter_username_lc. The non-lowerecase one you can use for display etc, but the lowercase one you index and use in your where clause etc.
This is the route I chose to go for my application.
2) Create a really ugly regex from your string of usernames in a loop prior to your find, then pass it in:
db.users.find({handle:{$regex: /^benhowdle89|^will shaver|^superman/i } })
Note that using the 'starts with' ^ carrot performs better if the field is indexed.
I want to avoid to write conditional statements to check parameters before query my database
so that i don't have to write multiple times the similar query but i have to write one single query.
So my first query is
db.results.find( { travel_class : "first" } })
and returns several documents
my second query is
var travel_class_filter = "all";
db.results.find( {'$and' : [ { travel_class_filter : "all"} , { travel_class : "first" } ]})
and returns 0 document instead of the same number of documents of the second query
Do you know why?
You can't test parameters as part of your query, but you can do this by building up your query object programmatically:
var query = {};
if (travel_class_filter != 'all') {
query.travel_class = travel_class_filter;
}
db.results.find(query);
I am using the $in clause within a mongodb query. However, I want my results to be ordered exactly as my input list used in the $in clause.
The results I get back are in the order the objects were inserted.
All the documentation I have read so far tells me that the sort cursor function only works with document fields. Does this mean that I cannot do the above?
MongoDB does not insert in any particular order. You can sort your results though.
See:
http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24in
db.updates.ensureIndex( { ts : 1 } ); // ts == timestamp
var myFriends = myUserObject.friends; // let's assume this gives us an array of DBRef's of my friends
var latestUpdatesForMe = db.updates.find( { user : { $in : myFriends } } ).sort( { ts : -1 } ).limit(10);