MongoDB/Mongoid: Can one query for ObjectID in embedded documents? - ruby-on-rails

For the record, I'm a bit of a newbie when it comes to Rails and MongoDB.
I'm using Rails+Mongoid+MongoDB to build an app and I've noticed that Mongoid adds ObjectID to embedded documents for some reason.
Is there any way to query all documents in a collection by ObjectID both the main documents and nested ones?
If I run this command
db.programs.findOne( { _id: ObjectId( "4d1a035cfa87b171e9000002" ) } )
I get these results which is normal since I'm querying for the ObjectID at root level.
{
"_id" : ObjectId("4d1a035cfa87b171e9000002"),
"created_at" : "Tue Dec 28 2010 00:00:00 GMT+0000 (GMT)",
"name" : "program",
"routines" : [
{
"name" : "Day 1",
"_id" : ObjectId("4d1a7689fa87b17f50000020")
},
{
"name" : "Day 2",
"_id" : ObjectId("4d1a7695fa87b17f50000022")
},
{
"name" : "Day 3",
"_id" : ObjectId("4d1a76acfa87b17f50000024")
},
{
"name" : "Day 4",
"_id" : ObjectId("4d1a76ecfa87b17f50000026")
},
{
"name" : "Day 5",
"_id" : ObjectId("4d1a7708fa87b17f50000028")
},
{
"name" : "Day 6",
"_id" : ObjectId("4d1a7713fa87b17f5000002a")
},
{
"name" : "Day 7",
"_id" : ObjectId("4d1a7721fa87b17f5000002c")
}
],
"user_id" : ObjectId("4d190cdbfa87b15c2900000a")
}
Now if I try to query with an ObjectID with one of the embedded document (routines) I get null like so.
db.programs.findOne( { _id: ObjectId( "4d1a7689fa87b17f50000020" ) } )
null
I know one can query embedded objects like so
db.postings.find( { "author.name" : "joe" } );
but that seems a bit redundant if you get passed an ObjectID of some sort and want to find in what document that ObjectID resides.
So I guess my question is this...
Is it possible, with some method I'm not familiar with, to query by ObjectID and search the ObjectID's in the embedded documents?
Thanks.

You can't query ObjectIDs globally like that. You would have to do
db.programs.find({"routines._id": ObjectId("4d1a7689fa87b17f50000020")})

no, you can search only by field like { "routines._id" : ObjectId("4d1a7689fa87b17f50000020")}

If you want to get the matched sub-document only you can use $elemMatch with '$' operator like below:
db.programs.find({"_id" : ObjectId("4d1a035cfa87b171e9000002"),
routines:{$elemMatch:{"_id" : ObjectId("4d1a7689fa87b17f50000020")}}},{"routines.$":1})
It will return you only that matched sub-document instead of the complete sub-document.

Related

firebase database filtering is not working. Need some assistance

I have a simple test database that I cant get to filter. I indexed the category in the rules:
"questions":{
".indexOn": ["category"]
},
My filter for the quiz app:
/questions.json?orderBy="category"&equalTo="Basics"&print=pretty
and my database:
"-MKoucSP33zm4jC43AnY" : {
"title" : {
"answers" : [ {
"score" : 30,
"text" : "Pineapple"
}, {
"score" : 5,
"text" : "Ham"
}, {
"score" : 20,
"text" : "Yogurt"
}, {
"score" : 10,
"text" : "Crab"
} ],
"category" : "Basics",
"questionId" : "101",
"questionImage" : "",
"questionLink" : "",
"questionText" : "What topping do you like the best on pizza?"
}
}
The category property is nested under the title node, so the property you need to order/filter on is title/category:
/questions.json?orderBy="title/category"&equalTo="Basics"&print=pretty
You'll also need to update your index definition for that path, so:
"questions": { ".indexOn": "title/category" }
Working example: https://stackoverflow.firebaseio.com/64596200/questions.json?orderBy="title/category"&equalTo="Basics"

Getting nested children from Firebase Database

I am attempting to populate a table view with values from my Firebase database. I was able to use queryOrdered on the root of my database to get the children I required, as seen here:
func oneTimeInit() {
databaseRef?.queryOrdered(byChild: "Seasons").observe(.value, with:
{ snapshot in
for item in snapshot.children {
self.seasons.append(Season( snapshot: item as! DataSnapshot))
}
self.seasonsTableView.reloadData()
})
}
But now I am trying to get the children of the next node in my database. I tried to do this by calling queryOrdered on the child of the root but I'm not getting any data. Here is my code:
func oneTimeInit() {
databaseRef?.child("Seasons").queryOrdered(byChild: season.name).observe(.value, with:
{ snapshot in
for item in snapshot.children {
print("ITEM VALUE: \(item)")
self.races.append(Race(snapshot: item as! DataSnapshot))
}
self.racesTableView.reloadData()
})
}
The Database contains, at its highest level, Cross Country Seasons, which each contain Cross Country Races, which each contain results. Im able to populate the Seasons table but not the Races table, and so on.
Heres the Json structure of my database:
{
"Seasons" : {
"XC 2018" : {
"NCAA DI West Regional" : {
"Men" : {
"Results" : {
"Aaron BRUMBAUGH" : {
"finishTime" : "31:59.0",
"lapTimes" : [ "5:05.1", "10:43.3", "11:07.1", "5:03.5" ],
"name" : "Aaron BRUMBAUGH",
"position" : "118",
"splitTimes" : [ "5:05.1", "15:48.4", "26:55.5", "31:59.0" ],
"team" : "Santa Clara",
"year" : "SR"
},
"Addison DEHAVEN" : {
"finishTime" : "29:47.8",
"lapTimes" : [ "4:58.2", "10:14.0", "10:04.1", "4:31.5" ],
"name" : "Addison DEHAVEN",
"position" : "7",
"splitTimes" : [ "4:58.2", "15:12.2", "25:16.3", "29:47.8" ],
"team" : "Boise State",
"year" : "SR"
},
"Ahmed MUHUMED" : {
"finishTime" : "30:08.1",
"lapTimes" : [ "4:58.6", "10:14.6", "10:08.5", "4:46.4" ],
"name" : "Ahmed MUHUMED",
"position" : "29",
"splitTimes" : [ "4:58.6", "15:13.2", "25:21.7", "30:08.1" ],
"team" : "Boise State",
"year" : "SO"
},
As of now there is only one "Season" (XC 2018) with one "Race" (NCAA D1 West Regional). I'm trying to get a reference to all of the "Races" under the "Season" XC 2018.
The variable season.name would contain the value "XC 2018". With my second one time init I am trying to look at the children of "XC 2018".

Query object in child array

I am trying to use a query to retrieve data of student who are only 4 years old from my database but i cant figure out how to use Firebase to query the age (The array in each child).
{
"student" : {
"-Kv2RVDDsI-v6V7g_LBn" : [ {
"name" : "sam",
"age" : "6"
}, {
"name" : "tom",
"age" : "4"
}
],
"-hguyu-v6V7g_LBn" : [ {
"name" : "Tim",
"age" : "12"
}, {
"name" : "tom",
"age" : "4"
}
]
}
}
This is my code but it does no return anything.
ref.child("student").queryOrdered(byChild: "age").queryEqual(toValue: 4).observe(.childAdded, with: { (snapshot) in
let value = snapshot.value
print(value)
}) { (error) in
print(error.localizedDescription)
}
However, if i remove the queryOrdered(byChild: "age") it works.
Thanks.
You are referencing "Student" when actually your JSON database format child path name is "Students".
Also make sure that you are querying by key. Refer to the Firebase documentation to see how to query by key. I don't understand your structure though why you have 2 children per key.
Why not have a new key for each child?

mongodb: best way to get specific documents and then the rest

lets say I have 1000 documents where each one has:
user_id
text
Now, I would like to pull all those documents but first pull the documents from a few specific users (given an array of user ids) and then all the rest.
I was thinking to use map reduce to create a new weight inline attribute if the user_id exists in the specific users array (using scope to pass the array) and then to sort that new attribute. But from what I could understand, you can not sort after map reduce.
Any one has a good suggestion how to pull this off? Any suggestion will be welcome.
Thanks!
Well there isn't a lot of detail here, but I can give a sample case for consideration. Consider the following set of documents:
{ "user" : "fred", "color" : "black" }
{ "user" : "bill", "color" : "blue" }
{ "user" : "ted", "color" : "red" }
{ "user" : "ted", "color" : "black" }
{ "user" : "fred", "color" : "blue" }
{ "user" : "bill", "color" : "red" }
{ "user" : "bill", "color" : "orange" }
{ "user" : "fred", "color" : "orange" }
{ "user" : "ted", "color" : "orange" }
{ "user" : "ally", "color" : "orange" }
{ "user" : "alice", "color" : "orange" }
{ "user" : "alice", "color" : "red" }
{ "user" : "bill", "color" : "purple" }
So suppose you want to bubble the items for the users "bill" and "ted" to the top of your results, then everything else sorted by the user and the color. What you can do is run the documents through a $project stage in aggregate, as follows:
db.bubble.aggregate([
// Project selects the fields to show, and we add a weight value
{$project: {
_id: 0,
"user": 1,
"color": 1,
"weight": {$cond:[
{$or: [
{$eq: ["$user","bill"]},
{$eq: ["$user","ted"]}
]},
1,
0
]}
}},
// Then sort the results with the `weight` first, then `user` and `color`
{$sort: { weight: -1, user: 1, color: 1 }}
])
So what that does is conditionally assign a value to weight based on whether the user was matched to one of the required values. Documents that do not match are simply given a 0 value.
When we move this modified document on to the $sort phase, the new weight key can be used to order the results so the "weighted" documents are on top, and anything else will then follow.
There a quite a few things you can do to $project a weight in this way. See the operator reference for more information:
http://docs.mongodb.org/manual/reference/operator/aggregation/

How to join query in mongodb?

I have user document collection like this:
User {
id:"001"
name:"John",
age:30,
friends:["userId1","userId2","userId3"....]
}
A user has many friends, I have the following query in SQL:
select * from user where in (select friends from user where id=?) order by age
I would like to have something similar in MongoDB.
To have everything with just one query using the $lookup feature of the aggregation framework, try this :
db.User.aggregate(
[
// First step is to extract the "friends" field to work with the values
{
$unwind: "$friends"
},
// Lookup all the linked friends from the User collection
{
$lookup:
{
from: "User",
localField: "friends",
foreignField: "_id",
as: "friendsData"
}
},
// Sort the results by age
{
$sort: { 'friendsData.age': 1 }
},
// Get the results into a single array
{
$unwind: "$friendsData"
},
// Group the friends by user id
{
$group:
{
_id: "$_id",
friends: { $push: "$friends" },
friendsData: { $push: "$friendsData" }
}
}
]
)
Let's say the content of your User collection is the following:
{
"_id" : ObjectId("573b09e6322304d5e7c6256e"),
"name" : "John",
"age" : 30,
"friends" : [
"userId1",
"userId2",
"userId3"
]
}
{ "_id" : "userId1", "name" : "Derek", "age" : 34 }
{ "_id" : "userId2", "name" : "Homer", "age" : 44 }
{ "_id" : "userId3", "name" : "Bobby", "age" : 12 }
The result of the query will be:
{
"_id" : ObjectId("573b09e6322304d5e7c6256e"),
"friends" : [
"userId3",
"userId1",
"userId2"
],
"friendsData" : [
{
"_id" : "userId3",
"name" : "Bobby",
"age" : 12
},
{
"_id" : "userId1",
"name" : "Derek",
"age" : 34
},
{
"_id" : "userId2",
"name" : "Homer",
"age" : 44
}
]
}
Edit: this answer only applies to versions of MongoDb prior to v3.2.
You can't do what you want in just one query. You would have to first retrieve the list of friend user ids, then pass those ids to the second query to retrieve the documents and sort them by age.
var user = db.user.findOne({"id" : "001"}, {"friends": 1})
db.user.find( {"id" : {$in : user.friends }}).sort("age" : 1);
https://docs.mongodb.org/manual/reference/operator/aggregation/lookup/
This is the doc for join query in mongodb , this is new feature from version 3.2.
So this will be helpful.
You can use in Moongoose JS .populate() and { populate : { path : 'field' } }.
Example:
Models:
mongoose.model('users', new Schema({
name:String,
status: true,
friends: [{type: Schema.Types.ObjectId, ref:'users'}],
posts: [{type: Schema.Types.ObjectId, ref:'posts'}],
}));
mongoose.model('posts', new Schema({
description: String,
comments: [{type: Schema.Types.ObjectId, ref:'comments'}],
}));
mongoose.model('comments', new Schema({
comment:String,
status: true
}));
If you want to see your friends' posts, you can use this.
Users.find(). //Collection 1
populate({path:'friends', //Collection 2
populate:{path:'posts' //Collection 3
}})
.exec();
If you want to see your friends' posts and also bring all the comments, you can use this and too, you can indentify the collection if this not find and the query is wrong.
Users.find(). //Collection 1
populate({path:'friends', //Collection 2
populate:{path:'posts', //Collection 3
populate:{path:'commets, model:Collection'//Collection 4 and more
}}})
.exec();
And to finish, if you want get only some fields of some Collection, you can use the propiertie select Example:
Users.find().
populate({path:'friends', select:'name status friends'
populate:{path:'comments'
}})
.exec();
MongoDB doesn't have joins, but in your case you can do:
db.coll.find({friends: userId}).sort({age: -1})
one kind of join a query in mongoDB, is ask at one collection for id that match , put ids in a list (idlist) , and do find using on other (or same) collection with $in : idlist
u = db.friends.find({"friends": ? }).toArray()
idlist= []
u.forEach(function(myDoc) { idlist.push(myDoc.id ); } )
db.friends.find({"id": {$in : idlist} } )
Only populate array friends.
User.findOne({ _id: "userId"})
.populate('friends')
.exec((err, user) => {
//do something
});
Result is same like this:
{
"_id" : "userId",
"name" : "John",
"age" : 30,
"friends" : [
{ "_id" : "userId1", "name" : "Derek", "age" : 34 }
{ "_id" : "userId2", "name" : "Homer", "age" : 44 }
{ "_id" : "userId3", "name" : "Bobby", "age" : 12 }
]
}
Same this: Mongoose - using Populate on an array of ObjectId
You can use playOrm to do what you want in one Query(with S-SQL Scalable SQL).
var p = db.sample1.find().limit(2) ,
h = [];
for (var i = 0; i < p.length(); i++)
{
h.push(p[i]['name']);
}
db.sample2.find( { 'doc_name': { $in : h } } );
it works for me.
You can do it in one go using mongo-join-query. Here is how it would look like:
const joinQuery = require("mongo-join-query");
joinQuery(
mongoose.models.User,
{
find: {},
populate: ["friends"],
sort: { age: 1 },
},
(err, res) => (err ? console.log("Error:", err) : console.log("Success:", res.results))
);
The result will have your users ordered by age and all of the friends objects embedded.
How does it work?
Behind the scenes mongo-join-query will use your Mongoose schema to determine which models to join and will create an aggregation pipeline that will perform the join and the query.

Resources