Mongodb: query using value from a nested array - arrays

I have the following schema, blog collection & friendscoll as below
blogpostcollection
{
"_id" : ObjectId("4fff0bf18bf0d19c4f1a5826"),
"author" : "joe",
"text" : "Here is the text...",
"userid" : 0
}
{
"_id" : ObjectId("4fff0bf18bf0d19c4f1a5827"),
"author" : "blake",
"text" : "Here is the text...",
"userid" : 1
}
{
"_id" : ObjectId("4fff0bf18bf0d19c4f1a5828"),
"author" : "joe",
"text" : "Here is the text...",
"userid" : 2
}
myfriendscoll
{
"myid": 999,
"data": [
{
"uid": 1,
"name": "Raul"
},
{
"uid": 3,
"name": "John K"
} ]
}
I want to find all documents in blogpostcollection, where the userid exists as uid, in the myfriendscoll collection.
So in effect, something like..
var user = db.myfriendscoll.findOne({"myid" : 999}, {"data.uid": 1});
db.blogpostcollection.find( {"userid" : {$in : user.data.uid}});
This doesn't work, but is there a way to get it to work?...Thanks!

If you are using development version 2.1 or when you move to 2.2 once it's released you can use the aggregation framework to get the format you want back from the first query:
var ret=db.myfriendscoll.aggregate([
{$match:{"myid" : 999}},
{$project:{_id:0,uid:"$data.uid"}}
]);
var uids=ret.result[0].uid;
db.blogpostcollection.find({userid:{$in:uids}})

You'll need to extract the actual uid values into an array to use with $in. Try this:
var user = db.myfriendscoll.findOne({"myid" : 999}, {"data.uid": 1});
var uids = user.data.map(function(v){ return v.uid });
db.blogpostcollection.find( {"userid" : {$in : uids}});

Related

How to group records based on array elements using MongoDB

I have a data collection which contains a set of records in the following format.
{
"_id" : 22,
"title" : "3D User Interfaces with Java 3D",
"isbn" : "1884777902",
"pageCount" : 520,
"publishedDate" : ISODate("2000-08-01T07:00:00Z"),
"thumbnailUrl" : "https://s3.amazonaws.com/AKIAJC5RLADLUMVRPFDQ.book-thumb-images/barrilleaux.jpg",
"longDescription" : "Description",
"status" : "PUBLISH",
"authors" : [
"Jon Barrilleaux"
],
"categories" : [
"Java",
"Computer Graphics"
]
},
{
"_id" : 23,
"title" : "Specification by Example",
"isbn" : "1617290084",
"pageCount" : 0,
"publishedDate" : ISODate("2011-06-03T07:00:00Z"),
"thumbnailUrl" : "https://s3.amazonaws.com/AKIAJC5RLADLUMVRPFDQ.book-thumb-images/adzic.jpg",
"status" : "PUBLISH",
"authors" : [
"Gojko Adzic"
],
"categories" : [
"Software Engineering"
]
}
Please note that the 'categories' is an array.
I want to count the published books for each category. I tried the following solution, but it treated the entire array as one group.
db.books.aggregate([
{
$group:{_id:"$categories", total:{$sum:1}}
}
])
Instead of so, I want to count the number of records for each individual category value inside 'categories' array.
You should first use $unwind which outputs one document for each element in the array.
db.books.aggregate([
{
$unwind : "$categories"
},
{
$group : { _id : "$categories", total: { $sum: 1 } }
}
])

mongoDB - How to update/insert a field in an object which is within an array?

I have a method which inserts a new document into the collection "items".
//Example data of variables
var home = "home1";
var itemId = "123";
var username = Meteor.user().username;
//Upsert command
items.upsert({
'_id': home
}, {
$push: {
'createdItems': {itemId, username, home}
}
});
This creates a document like this:
"_id" : "home1",
"createdItems" : [
{
"itemId" : "123",
"username" : "Jon Doe",
"home" : "home1"
},
{
"itemId" : "456",
"username" : "Jon Doe2",
"home" : "home1"
},
{
"itemId" : "789",
"username" : "Jon Doe3",
"home" : "home1"
}
]
}
Now I need to be able to update the existing fields and insert new fields into these objects later. The itemId within these objects is the "reference". So I need a mongoDB function which inserts or updates fields by using the "itemId" as a query operator. Something like this:
//update a field
items.upsert({
'itemId': "123"
}, {
$set: {
'createdItems': {username: "Clark Kent"}
}
});
//insert new field
items.upsert({
'itemId': "123"
}, {
$set: {
'createdItems': {Value: 1000}
}
});
Or do I have to use $push? These commands are not delivering the results that I need. At the end the document should look like this:
"_id" : "home1",
"createdItems" : [
{
"itemId" : "123",
"username" : "Clark Kent",
"home" : "home1",
"Value" : 1000
},
{
"itemId" : "456",
"username" : "Jon Doe2",
"home" : "home1"
},
{
"itemId" : "789",
"username" : "Jon Doe3",
"home" : "home1"
}
]
}
Or do I have to use another data schema for the collection? If yes, which one can I choose when I want to have "itemId, "username" and "home" still in an array but "packed together"?
Many thanks.
If you want to update object in array query below will work fine
items.update({
'createdItems.itemId': '123'
}, {
$set: {
'createdItems.$.username': 'Clark Kent'
}
})
You can also easily add new fields to the object using the same query.
items.update({
'createdItems.itemId': '123'
}, {
$set: {
'createdItems.$.username': 'Clark Kent'
'createdItems.$.newField': 'newFieldValue'
}
})

How to query the firebase database to fetch specific data?

{
"todo" : {
"Kujbghnkj04t56-" : {
"id" : 1,
"tags" : [ {
"name" : "Italy"
}, {
"name" : "Australia"
} ],
"username" : "ABC"
},
"oPikoieew9oR-" : {
"id" : 2,
"tags" : [ {
"name" : "Switzerland"
} ],
"username" : "XYZ"
}
}
}
I am trying to search in Firebase according to tags name. My code is:
var tagRef = new Firebase(FURL+'/todo');
var tagCollection = $firebaseArray(tagRef);
tagCollection.$ref().orderByChild("tags").equalTo("Australia").once("value", function(dataSnapshot){
// code
});`
How can I get the snapshot of all data containing tag name as "Australia"

How to get just element that matches query in a mongo find operation? [duplicate]

This question already has answers here:
Retrieve only the queried element in an object array in MongoDB collection
(18 answers)
Closed 8 years ago.
i've got some documents like this:
{
"name" : "xxx",
"address" : " ",
"mail" : "",
"url" : "",
"pos" : {
"lat" : yyy,
"lng" : zzz
},
"rooms" : [
{
"_id" : ObjectId("540ce3f8e4b016292085b387"),
"supplier" : "s1",
"price" : 41000,
"details" : [
{
"price" : 25200,
"key" : "2-1"
},
{
"price" : 15800,
"key" : "2-0"
}
]
},
{
"_id" : ObjectId("540ce3f8e4b016292085b3fd"),
"supplier" : "s2",
"price" : 44900,
"details" : [
{
"price" : 27000,
"key" : "2-1"
},
{
"price" : 17900,
"key" : "2-0"
}
]
},
{
"_id" : ObjectId("540ce3f8e4b016292085b53d"),
"supplier" : "s3",
"price" : 53500,
"details" : [
{
"price" : 32100,
"key" : "2-1"
},
{
"price" : 21400,
"keykey" : "2-0"
}
]
}
]
}
What i need to do is execute a find with some filters AND price range query to fetch just the matching array elements and not all:
This is what i try:
var sort = {};
var query = {name:new RegExp("xx",'i')};
query['$and'] = [{'rooms.price':{$gt:50000}},{'rooms.price':{$lt:100000}}];
var page = 1;
var ppp = 20;
db.collection("myCollection").
find(query).
sort(sort).
skip(page > 0 ? ((page-1)*ppp) : 0).limit(ppp).toArray(function(err, docs) {
res.send(docs);
});
and i retrieve the same document.
What i need is :
{
"name" : "xxx",
"address" : " ",
"mail" : "",
"url" : "",
"pos" : {
"lat" : yyy,
"lng" : zzz
},
"rooms" : [
{
"_id" : ObjectId("540ce3f8e4b016292085b53d"),
"supplier" : "s3",
"price" : 53500,
"details" : [
{
"price" : 32100,
"key" : "2-1"
},
{
"price" : 21400,
"keykey" : "2-0"
}
]
}
]
}
I googled about but i find just aggregate or map/reduce element.
I'd like to avoid it.
Is there a good solution??
Thanks!
If you don't want to use aggregate or map/reduce then you could change your data structure.
Extract rooms to it's own collection and make sure that every element has id reference to the appropriate document from myCollection. This would require at least two query though.
Alternatively, you could filter the content on the application side and not within mongodb.

Mongoose Query: Find an element inside an array

Mongoose/Mongo noob here:
My Data
Here is my simplified data, each user has his own document
{ "__v" : 1,
"_id" : ObjectId( "53440e94c02b3cae81eb0065" ),
"email" : "test#test.com",
"firstName" : "testFirstName",
"inventories" : [
{ "_id" : "active",
"tags" : [
"inventory",
"active",
"vehicles" ],
"title" : "activeInventory",
"vehicles" : [
{ "_id" : ObjectId( "53440e94c02b3cae81eb0069" ),
"tags" : [
"vehicle" ],
"details" : [
{ "_id" : ObjectId( "53440e94c02b3cae81eb0066" ),
"year" : 2007,
"transmission" : "Manual",
"price" : 1000,
"model" : "Firecar",
"mileageReading" : 50000,
"make" : "Bentley",
"interiorColor" : "blue",
"history" : "CarProof",
"exteriorColor" : "blue",
"driveTrain" : "SWD",
"description" : "test vehicle",
"cylinders" : 4,
"mileageType" : "kms" } ] } ] },
{ "title" : "soldInventory",
"_id" : "sold",
"vehicles" : [],
"tags" : [
"inventory",
"sold",
"vehicles" ] },
{ "title" : "deletedInventory",
"_id" : "deleted",
"vehicles" : [],
"tags" : [
"inventory",
"sold",
"vehicles" ] } ] }
As you can see, each user has an inventories property that is an array that contains 3 inventories (activeInventory, soldInventory and deletedInventory)
My Query
Given an user's email a a vehicle ID, i would like my query to go through find the user's activeInventory and return just the vehicle that matches the ID. Here is what I have so far:
user = api.mongodb.userModel;
ObjectId = require('mongoose').Types.ObjectId;
return user
.findOne({email : params.username})
.select('inventories')
.find({'title': 'activeInventory'})
//also tried
//.where('title')
//.equals('activeInventory')
.exec(function(err, result){
console.log(err);
console.log(result);
});
With this, result comes out as an empty array. I've also tried .find('inventories.title': 'activeInventory') which strangely returns the entire inventories array. If possible, I'd like to keep the chaining query format as I find it much more readable.
My Ideal Query
return user
.findOne({email : params.username})
.select('inventories')
.where('title')
.equals('activeInventory')
.select('vehicles')
.id(vehicleID)
.exec(cb)
Obviously it does not work but it can give you an idea what I'm trying to do.
Using the $ positional operator, you can get the results. However, if you have multiple elements in the vehicles array all of them will be returned in the result, as you can only use one positional operator in the projection and you are working with 2 arrays (one inside another).
I would suggest you take a look at the aggregation framework, as you'll get a lot more flexibility. Here's an example query for your question that runs in the shell. I'm not familiar with mongoose, but I guess this will still help you and you'd be able to translate it:
db.collection.aggregate([
// Get only the documents where "email" equals "test#test.com" -- REPLACE with params.username
{"$match" : {email : "test#test.com"}},
// Unwind the "inventories" array
{"$unwind" : "$inventories"},
// Get only elements where "inventories.title" equals "activeInventory"
{"$match" : {"inventories.title":"activeInventory"}},
// Unwind the "vehicles" array
{"$unwind" : "$inventories.vehicles"},
// Filter by vehicle ID -- REPLACE with vehicleID
{"$match" : {"inventories.vehicles._id":ObjectId("53440e94c02b3cae81eb0069")}},
// Tidy up the output
{"$project" : {_id:0, vehicle:"$inventories.vehicles"}}
])
This is the output you'll get:
{
"result" : [
{
"vehicle" : {
"_id" : ObjectId("53440e94c02b3cae81eb0069"),
"tags" : [
"vehicle"
],
"details" : [
{
"_id" : ObjectId("53440e94c02b3cae81eb0066"),
"year" : 2007,
"transmission" : "Manual",
"price" : 1000,
"model" : "Firecar",
"mileageReading" : 50000,
"make" : "Bentley",
"interiorColor" : "blue",
"history" : "CarProof",
"exteriorColor" : "blue",
"driveTrain" : "SWD",
"description" : "test vehicle",
"cylinders" : 4,
"mileageType" : "kms"
}
]
}
}
],
"ok" : 1
}
getting the chaining query format ... i dont know how to parse it but, what you are searching for is projection, you should take a look to http://docs.mongodb.org/manual/reference/operator/projection/
it would probably look like this :
user.findOne({email: params.username}, {'inventories.title': {$elemMatch: "activeInventory", 'invertories.vehicle.id': $elemMatch: params.vehicleId}, function(err, result) {
console.log(err);
console.log(result);
})

Resources