RMongo dbGetQueryForKeys(), what is the structure of "keys," and how do I sub-key them? - mongodb

I am trying to query a mongo database from R using RMongo and return the values of a couple nested documents.
Looking through the documentation for RMongo, I understand the following query:
output <- dbGetQueryForKeys(mongo, 'test_data', '{"foo": "bar"}', '{"foo":1}')
Where the arguments are...
db = mongo
collection = 'test_data'
query = '{"foo": "bar"}'
keys = 'Specify a set of keys to return.'
What is the 1 in '{"foo":1}'? What is the structure of this key set? Checking against this blog post, I found a format like:
result < - dbGetQueryForKeys(mongo, "items", "{'publish_date' : { '$gte' : '2011-04-01', '$lt' : '2011-05-01'}}", "{'publish_date' : 1, 'rank' : 1}")
So, apparently, the keys need the value 1?
How would I get keys for nested documents? If I wanted something like...
output <- dbGetQueryForKeys(mongo, 'test_data', '{"foo": "bar"}', '{"foo1.foo2.foo3.foo4":1,"foo1.foo2.foo3.bar4":1}')
For nested keys, I'm currently returning something more like...
X_id
1 50fabd42a29d6013864fb9d7
foo1
1 { "foo2" : { "foo3" : { "foo4" : "090909" , "bar4" : "1"}}}
...where output[,2] is a looooong string, rather than as two separate variables for the values associated with the keys foo4 and bar4, ("090909", "1") as I would have expected.

What is the 1 in '{"foo":1}'? What is the structure of this key set?
These keys are the query projections to return for read operations in MongoDB. A value of "1" means to include a specific field and "0" excludes. The default behaviour is to include all fields in the projection.
How would I get keys for nested documents?
For nested keys, I'm currently returning something more like...
1 { "foo2" : { "foo3" : { "foo4" : "090909" , "bar4" : "1"}}}
...where output[,2] is a looooong string, rather than as two
separate variables for the values associated with the keys foo4
and bar4, ("090909", "1") as I would have expected.
The RMongo driver is returning data including the embedding hiearchy.
You can reshape & flatten the result output using the RMongo dbAggregate() command and the $project operator which is part of the Aggregation Framework in MongoDB 2.2+.

If your end goal is to extract the values from the nested object for some type of downstream processing in R this will get you there. It avoids having to build an aggregation pipeline and is a simple solution to your problem. Instead of trying to get deep into the nested structure and access bar4 directly, extract the top level of the object which will provide the long string that you've referenced.
output <- dbGetQueryForKeys(mongo, 'test_data', '{"foo": "bar"}', '{"foo1.foo2.foo3.foo4":1,"foo1":1}')
Since the output is a data.frame, you can use the 'jsonlite' library to get to your data:
library(jsonlite)
foo1 <- fromJSON(output$foo1)
bar4 <- foo1$foo2$foo3$bar4

Related

MongoDB check if value exists within array

I'm using MongoDB inside a twig framework. I'm trying to determine if the user has access to a certain module.
(a part of) my DB entry looks like:
_id: "579b50a4f5092761a20f4e71",
approvedModules: [
"examplemodule",
"examplemodule1",
"examplemodule2",
"examplemodule3"
],
My code looks like:
session.get('__AUTH_USER').find({ approvedModules : { '$in' : ["examplemodule"]}}, { '$exists' : true })
(the standard functions have to be in quotes).
I keeps returning false. I can only return the value if I use session.get('__AUTH_USER').approvedModules.0
I don't want to include the .0 because that might change.
What am I doing wrong?
Thanks in advance!
What am I doing wrong?
Many things. The worst one is using queries to database inside a template, but it is another problem.
You misunderstood purpose of the $in operator, which is used to match a field in the database to any element of array in the query.
To match any element of array in the collection to a single value you can do simple $eq:
session.get('__AUTH_USER').find({ approvedModules : "examplemodule"})
When you are using $in operator, you need to have 2 input arguments, the first one is the value for which you are checking the array, and the second one should be the array itself.
So, your bson element should look like this:
isModuleInArray : { '$in' : ["examplemodule","$approvedModules"] }

MongoDb script : Cannot compare two types correctly [duplicate]

I have a straightforward tool for building collections of documents and then automatically formatting them for EPUB or LaTeX rendering, written on top of ExpressJS. I'm using Coffeescript, if that matters (I doubt it).
Using Mongoose, I have the following:
DocumentSchema = new Schema
title: String
Offrefs = new Schema
ref: { type: ObjectId }
isa: String
BinderSchema = new Schema
title: String
contains: [Offrefs]
Offrefs doesn't specify what it refers to because because I want to be able to contain some binders in other binders, to create logical collections: "These are for the printer," "These are for epub," "These are web only," etc. (I've stripped out all the miscellaneous stuff out.)
Unfortunately, I have run into queries where, for retrieved objects
(story._id == offref.ref) -> True
And the two do indeed look the same. But:
(binder._id == offref.ref) -> False
(String(binder._id) == String(offref.ref)) -> True
And a visual comparison of the two references in the last two, they are the same ID number, but the ObjectId objects don't compare correctly.
I don't want to have to do string conversions constantly, which is a strong possiblity when I'm converting these complex objects into trees of data. Tree relationships are a bear in any DB; they shouldn't be difficult in MongoDB.
How do you do ObjectId comparisons in MongoDB?
A straight == (or ===) comparison will compare the two objects by reference, not value. So that will only evaluate to true if they both reference the very same instance.
Instead, you should be using the equals method of ObjectID to compare their values:
story._id.equals(offref.ref)
As #bendytree notes in the comments, if either value could be null (and you want nulls to compare as equal), then you can use the following instead:
String(story._id) === String(offref.ref)
This goes somewhat beyond the original asked question, but I have found that the .equals method of ObjectID's will return false in some cases where a string comparison will return true even when the values are not null. Example:
var compare1 = invitationOwningUser.toString() === linkedOwningUser.toString();
var compare2 = invitationOwningUser.equals(linkedOwningUser);
var compare3 = String(invitationOwningUser) === String(linkedOwningUser);
logger.debug("compare1: " + compare1 + "; " + "compare2: " + compare2 + "; " + "compare3: " + compare3);
Output:
compare1: true; compare2: false; compare3: true
This occurred when invitationOwningUser (an ObjectID) came from a Collection created using a Mongoose schema, and linkedOwningUser (also an ObjectID) came from a Collection not created using Mongoose (just regular MongoDB methods).
Here is the document containing invitationOwningUser (the owningUser field):
{
"_id" : ObjectId("5782faec1f3b568d58d09518"),
"owningUser" : ObjectId("5781a5685a06e69b158763ea"),
"capabilities" : [
"Read",
"Update"
],
"redeemed" : true,
"expiry" : ISODate("2016-07-12T01:45:18.017Z"),
"__v" : 0
}
Here is the document containing linkedOwningUser (the owningUser field):
{
"_id" : ObjectId("05fb8257c95d538d58be7a89"),
"linked" : [
{
"owningUser" : ObjectId("5781a5685a06e69b158763ea"),
"capabilities" : [
"Read",
"Update"
]
}
]
}
So, as a bottom line for me, I'll be using the string comparison technique to compare ObjectID's, not the .equals method.

Find query result to List

I have got a database filled with documents like the following :
{
"_id" : ObjectId("56zeffb2abcf7ff24b46"),
"id_thing" : -1,
"data" : {
"info1" : 36.0709427,
"date" : ISODate('2005-11-01T00:33:21.987+07:00'),
"info2" : 24563.87148077
}
}
My find method returns a List which I operate some operations over:
for (d <- result_of_find_method_here)
{
val l_d = d("data")
}
But I would like to l_d a List which is currently not, and the toList method does not work.
How do I retrieve all the fields and their value of the data container as a list?
EDIT:
I have tried multiple methods, and none work because neither applies to AnyRef which is what I get when I iterate through the l_d with a foreach loop.
Find method returns a list because there are more objects returned.
l_d is not a list, because d['data'] is not a list is a key value store: a dictionary, json or map in Scala. The question is how do you want to represent this data?
Maybe you want to take out the values from the map as a list.
You can convert map to list using: l_d.toList or map values to list: l_d.values.toList

How to I ensure that all items in an $in clause return a match in MongoDB?

I am working with a set of documents like this:
{
name : "BCC 204",
//etc
}
I have a list of names that I want to map to their DB entries.
For example:
var names = [ "BCC 204", "STEW 101", "SMTH 123" ]
and I want to make a query like this
db.labs.find( { name : { $in: names } } );
But the $in operator does not ensure that each item in the names array matches a result in the db.
(More info, names are unique)
You can't do this in the query. $in will check that a document matches at least one entry in the array given, but it's not going to consider the entire result set. This is a concern you'll need to manage in your application. Given a list of inputs, you will need to retrieve your results then check that given_names - results.map(:name) is empty.
To put it more simply, queries match documents, which compose a result set - they don't match a result set.

Updating multiple MongoDB records in Sails.js

I need to update multiple records in mongodb.
From frontend logic , i got the array of id's as below.
ids: [ [ '530ac94c9ff87b5215a0d6e6', '530ac89a7345edc214618b25' ] ]
I have an array of ids as above , i need to update the folder field for all the records in that array.
I tried passing the id's to mongodb query as below , but still that doesn't work.
Post.native(function(err, collection) {
collection.update({
_id : {
"$in" : ids
}
}, { folder : 'X'}, {
multi : true
}, function(err, result) {
console.log(result);
});
});
Please help.
There seem to be two possible problems.
1) your ids array is not an array of ids, it's an array which has a single element which is itself an array, which has two elements. An array of ids would be `[ 'idvalue1', 'idvalue2']
2) your id values inside of arrays are strings - is that how you are storing your "_id" values? If they are ObjectId() type then they are not a string but a type ObjectId("stringhere") which is not the same type and won't be equal to "stringhere".
There is no reason to use the native method in this case. Just do:
Post.update({id : ids}, {folder : 'X'}).exec(console.log);
Waterline automatically does an "in" query when you set a criteria property to an array, and Sails-Mongo automatically translates "id" to "_id" and handles ObjectId translation for you.
Those strings look like the string representation of mongod ObjectIds, so probably what you want to do is turn them into ObjectIds before querying. Assuming you've corrected your problem with the extra level of nesting in the array, that is:
ids = ['530ac94c9ff87b5215a0d6e6', '530ac89a7345edc214618b25']
Then you want to do something like this:
oids = []
for (var i in ids)
oids.push(ObjectId(ids[i]))
db.c.find({_id: {$in: oids}})
Does that fix your problem?