Projecting specified fields within an array in MongoDB (missing ] error?) - arrays

In MongoDB, I understand that to project only specified fields in a collection, I would execute:
db.collection.find({}, {"field1": 1, "field2": 1})
But what would I do if I wanted to only project specified fields within an array in a collection? The only way I can think would be this:
db.collection.find({field1: [arrayfield1: 1, arrayfield2: 1, arrayfield3: 1]})
But I get "SyntaxError: missing ] after element list (shell):1".
I've tried this as well:
db.collection.find({field1: [arrayfield1: 1], [arrayfield2: 1], [arrayfield3: 1]})
And get the same error. Can anyone tell me what I'm doing wrong? Because I can't figure it out. Or if I'm on the completely wrong track?
(I'm aware that the error says "element list," and I believe it should be a field list or something to that extent, so I suspect this is not the correct way to go about what I'm trying to do.)

I figured it out. The answer was dot notation. This did the trick:
db.colletion.find({}, { "array.arrayfield1" : 1, "array.arrayfield2" : 1, "array.arrayfield3" : 1 })

Related

Mongodb query to find element value type of nested array or object in a field

I have mongodb data model where I have some array fields that contain embedded objects or arrays. I have some inconsistencies in the field in question because I've tweaked my application logic. Initially, my model looked like this:
Initial Setup of Results collection
"competition" : "competition1",
"stats" : [
{
"stat1" : [],
"stat2" : []
}
]
However, I saw that this wasn't the best setup for my needs. So I changed it to the following:
New Setup of Results collection
"competition" : "competition1",
"stats" : [
{
"stat1" : 3,
"stat2" : 2
}
]
My problem now is that documents that have the initial setup cause an error. So what I want is to find all documents that have the initial setup and convert them to have the new setup.
How can I accomplish this in mongodb?
Here is what I've tried, but I'm stuck...
db.getCollection('results').find({"stats.0": { "$exists": true }})
But what I want is to be able to do something like
db.getCollection('results').find({"stats.0".stat1: { "$type": Array}})
Basically I want to get documents where the value of stats[0].stat1 is of type array and override the entire stats field to be an empty array.
This would fix the errors I'm getting.
$type operator for arrays in older versions works little differently than what you might think than $type in 3.6.
This will work in 3.6
db.getCollection('results').find( { "stats.0.stat1" : { $type: "array" } } )
You can do it couple of ways for lower versions and It depends what you are looking for.
For empty arrays you can just check
{"stats.0.stat1":{$size:0}}
For non empty arrays
{"stats.0.stat1": {$elemMatch:{ "$exists": true }}}
Combine both using $or for finding both empty and non empty array.
For your use case you can use below update
db.getCollection('results').update({"stats.0.stat1":{$size:0}}, {$set:{"stats":[]}})

Meteor/Mongo get a specific document in an array

Considering:
{
docs: Array[{field1, field2}]
}
I know how to find the document(s) containing field1 or field2 with $elemMatch, but my question is how can I get the value of field1 knowing field2?
I also know that in MongoDB you would use $elemMatch in the projection parameter. Or that I can do this in JS with something like _.find(), but the goal is to get only one specific document from Mongo.
Let say for example a document:
{
_id: 1
docs: [{a: 42, b:"stringX"}, {a:0, b:"stringY"}]
}
How can I get the value of a (42) knowing b ("stringX")?
Is there something like: MyCollection.findOne({_id: 1}, projection: {docs:{b: "stringX"}}) ?
On the server you can use this projection:
var a = MyCollection.findOne({'docs.b': "stringX"},{fields: {'docs.$': 1}}).a;
However the $ array projection does not work on the client in minimongo (yet).
Hopefully this is sufficient to get you unstuck.
See related question

Referencing arrays that are nested within multiple objects within MongoDB with Express js

So in my MongoDB Collection I have this structure:
"_id" : "Object("-----------")
"name" : "John Doe"
"tool" : {
"hammer" : {
"name" : "hammer 1",
"characteristics" : [
{
"length" : "9 inches"
},
{
"weight" : "4 pounds"
}
]
I know the data may seem a little strange but I can't put the actual data online so I had to input some dummy data. So essentially what I would like to do is be able to update the array that is nested within those objects. So I would like to be able to update the weight or add a new characteristic that I haven't previously entered into it. So for example, add in "metal" : "steel" as a new entry into the array. Currently I'm using a Rest API built in Node.js and Express.js to edit the db. When I was trying to figure out how to dig down this deep I was able to do it with an array at the highest level, however I haven't been able to figure out how to access an array when its embedded like this. So what I was wondering if anybody knew if it was even possible to edit an array this far down? I can post code from controller.js and server.js file if needed but I figured I'd see if it's even possible to do before I start posting it. Any help would be greatly appreciated!
You can use findAndModify to $push it into the array. You have to specify the path precisely though:
db.tools.findAndModify( {
query: { name: "John Doe"},
update: { $push: { tool.hammer.characteristics: {metal: "steel"} }
} );

How to project only matching fields of nested array in mongo shell query

I am fairly new to mongodb, and I have what is hopefully a simple question:
I have a nested schema where I have a field that is an array, where each item of that array is an object that itself has an array field.
For example:
> db.mytest.insert({
name: 'a',
top: [
{x:1, y:2, nest: [{p:1, q:2}, {p:2, q:3}]},
{x:2, y:3, nest: [{p:4, q:5}, {p:6, q:7}]}
]
})
I can query for certain values of p just fine, and can even limit my result to the first matching element of top:
> db.mytest.findOne({'top.nest': {$elemMatch: {p:6}}}, {'top.nest.$': 1})
{"_id":ObjectId(...), top: [{x:2, y: 3, nest: [{p:4, q:5}, {p:6, q:7}]}]}
Which brings me to my question: {'top.nest.$': 1} and {'top.$': 1} as my projection document both return the same result. How can I limit my search results to only include the first matching element of nest?
Do I need a second pass that iterates over the result of this style of query?
Ok, the trick was the aggregation framework, specifically unwind.
> db.mytest.aggregate({$unwind: '$top'},
{$unwind: '$top.nest'},
{$match: {'top.nest.p': 6}}
)
Though in the case that I had multiple sub matches in a single object, this would return multiple results instead of in their original grouped form. I suppose I can put a $group into the pipeline, though.
Though the related links I found suggested schema redesign as the only complete fix right now, so this is definitely better than nothing.

Using CouchDB-lucene how can I index an array of objects (not values)

Hello everyone and thanks in advance for any ideas, suggestions or answers.
First, the environment: I am using CouchDB (currently developing on 1.0.2) and couchdb-lucene 0.7. Obviously, I am using couchdb-lucene ("c-l" hereafter) to provide full-text searching within couchdb.
Second, let me provide everyone with an example couchdb document:
{
"_id": "5580c781345e4c65b0e75a220232acf5",
"_rev": "2-bf2921c3173163a18dc1797d9a0c8364",
"$type": "resource",
"$versionids": [
"5580c781345e4c65b0e75a220232acf5-0",
"5580c781345e4c65b0e75a220232acf5-1"
],
"$usagerights": [
{
"group-administrators": 31
},
{
"group-users": 3
}
],
"$currentversionid": "5580c781345e4c65b0e75a220232acf5-1",
"$tags": [
"Tag1",
"Tag2"
],
"$created": "/Date(1314973405895-0500)/",
"$creator": "administrator",
"$modified": "/Date(1314973405895-0500)/",
"$modifier": "administrator",
"$checkedoutat": "/Date(1314975155766-0500)/",
"$checkedoutto": "administrator",
"$lastcommit": "/Date(1314973405895-0500)/",
"$lastcommitter": "administrator",
"$title": "Test resource"
}
Third, let me explain what I want to do. I am trying to figure out how to index the '$usagerights' property. I am using the word index very loosely because I really do not care about being able to search it, I simply want to 'store' it so that it is returned with the search results. Anyway, the property is an array of json objects. Now, these json objects that compose the array will always have a single json property.
Based on my understanding of couchdb-lucene, I need to reduce this array to a comma separated string. I would expect something like "group-administrators:31,group-users:3" to be a final output.
Thus, my question is essentially: How can I reduce the $usagerights json array above to a comma separated string of key:value pairs within the couchdb design document as used by couchdb-lucene?
A previous question I posted regarding indexing of tagging in a similar situation, provided for reference: How-to index arrays (tags) in CouchDB using couchdb-lucene
Finally, if you need any additional details, please just post a comment and I will provide it.
Maybe I am missing something, but the only difference I see from your previous question, is that you should iterate on the objects. Then the code should be:
function(doc) {
var result = new Document(), usage, right;
for(var i in doc.$usagerights) {
usage = doc.$usagerights[i];
for(right in usage) {
result.add(right + ":" + usage[right]);
}
}
return result;
}
There's no requirement to convert to a comma-separated list of values (I'd be intrigued to know where you picked up that idea).
If you simply want the $usagerights item returned with your results, do this;
ret.add(JSON.stringify(doc.$usagerights),
{"index":"no", "store":"yes", "field":"usagerights"});
Lucene stores strings, not JSON, so you'll need to JSON.parse the string on query.

Resources