Filter sub document array - mongodb

db.doc.find({},{"sections.rows.Desk":1}) returns desks list but also empty rows i.e. where desk attribute doesn't exist in rows array...
I would like to eliminate empty results. How to go about it?
Thanks!
My document "doc" has the following format.
"docs":
{
"sections" : [
{
"name" : "Request Details",
"rows" : [
{
"Desk" : "IT4"
}
]
},
{
"name" : "Approval",
"rows" : [
{
"Approval" : ""
}
]
}
]
}

You will have to use the aggregation framework, particularly the $unwind operator to be able to query against individual array elements and get the ones you are after. Currently with the way .find works you will get the entire document if it matches your query.
db.docs.aggregate({$unwind: "$sections"}, {$match: {"sections.rows.Desk": {$exists: 1}}});

Related

How to remove a particular element from a nested array in mongodb

My Document structure is
"MainAccounts" : [
{
"orgs" : "5808ba773fe315441b9e0a9e",
"_id" : ObjectId("5808bc0c3fe315441b9e0b1a"),
"accounts" : [
"5808baf33fe315441b9e0aa7",
"5808baf33fe315441b9e0aa8",
"5808baf33fe315441b9e0aa9",
"5808baf33fe315441b9e0aa1"
]
},
{
"orgs" : "5808ba773fe315441b9e0a9f",
"_id" : ObjectId("5808bc0c3fe315441b9e0b1b"),
"accounts" : [
"5808baf33f35425852s255s7",
"5808baf3sd23s2d3d4w5s2s8",
"5808baf33sd211ds2d2sdsa9",
"5808baf33dssd2d21b9e0aa1"
]
}
],
I want to pull out a particular account say "5808baf33fe315441b9e0aa8" from this, i wrote the query like this.
{ $pull: { "MainAccounts.$.accounts": "5808baf33fe315441b9e0aa8"} }
It gives only error as "The positional operator did not find the match needed from the query. Unexpanded update: MainAccounts.$.accounts"
{ $pull: { "MainAccounts.0.accounts": "5808baf33fe315441b9e0aa8" } }
If i give like this it will remove that value only which gives the expected output.
i need output as
"MainAccounts" : [
{
"orgs" : "5808ba773fe315441b9e0a9e",
"_id" : ObjectId("5808bc0c3fe315441b9e0b1a"),
"accounts" : [
"5808baf33fe315441b9e0aa7",
"5808baf33fe315441b9e0aa9",
"5808baf33fe315441b9e0aa1"
]
},
{
"orgs" : "5808ba773fe315441b9e0a9f",
"_id" : ObjectId("5808bc0c3fe315441b9e0b1b"),
"accounts" : [
"5808baf33f35425852s255s7",
"5808baf3sd23s2d3d4w5s2s8",
"5808baf33sd211ds2d2sdsa9",
"5808baf33dssd2d21b9e0aa1"
]
}
],
here i am not able to delete value from second array i need to give
{ $pull: { "MainAccounts.1.accounts": "5808baf33fe315441b9e0aa8" } }
But i need to loop through, any help is appreciated.
You will get an error:
"Cannot apply $pull to a non-array value"
This should be :
db.collection.update({'MainAccounts.accounts': '5808baf33fe315441b9e0aa8'}, {$pull: {MainAccounts:{ accounts: '5808baf33fe315441b9e0aa8'}}})
Here is a reference to this:
mongodb Cannot apply $pull/$pullAll modifier to non-array, How to remove array element
db.collection.update({someId,{$pull : {"MainAccounts":{"accounts":"5808baf33fe315441b9e0aa8"}}}})
someId could be your _id.
Remember if you have to access the document inside the array you cant access it without . operator only.You have to use the index with it.The other way mongodb can access it is by the use of braces.
This will do what you want:
db.collection.update({'MainAccounts.accounts': '5808baf33fe315441b9e0aa8'}, {$pull: {'MainAccounts.$.accounts': '5808baf33fe315441b9e0aa8'}})

Unwind array for documents that have a specified field

I have an application that simply takes and converts XML files to JSON and inserts them into a Mongo database. Inside each document of the collection I have an array VehicleEntry. Some of these VehicleEntries will have a tag called Pre-FlashDTC-IPCand I would like to pull all of those entries. Since I want the individual entries from the array, I used the unwind operator:
db.Vehicles.aggregate( [
{ $unwind : { path : "$VehicleEntry" } },
{ $match : { "$VehicleEntry.Pre-FlashDTC-IPC" : { $exists: true } } }
] );
I have tried this both with unwind first and match first, but neither work. I get an error:
{ "serverUsed": "localhost:27017", "ok": 0.0, "errmsg": "bad query: BadValue unknown top level operator: $VehicleEntry.Pre-FlashDTC-IPC", "code": 16810}
I thought the $exists operator would work to ensure I only returned elements that do have that value, but that doesn't appear to be the case. How can I correct this query?
Consider the following sample document:
{
"VehicleEntry" : [
{
"BatteryStatus" : "GOOD",
"Pre-FlashDTC-IPC" : "U100",
"VehicleStatus" : "PASSED"
},
{
"BatteryStatus" : "GOOD",
"VehicleStatus" : "PASSED"
}
],
"project_id" : "1234"
}
There is some consistent information in the array, such as Battery and Vehicle status, but some have extra information like the first array item. I want to get the individual array items (hence the need for unwind) where this value exists. Therefore my expected results are:
{
"BatteryStatus" : "GOOD",
"Pre-FlashDTC-IPC" : "U100",
"VehicleStatus" : "PASSED"
},
Omit the "$" in your match-clause
{ $match : { "VehicleEntry.Pre-FlashDTC-IPC" : { $exists: true } } }
The $ in front of a field-name is only needed when you want to access the value of a field in an aggregation operation.

Why do I get this result from a multiple criteria query?

On mongo, I issue the following query:
db.usertest.find({ "events.event":"event0", "events.event":"event1", "events.event":"eventX" })
As per the docs here http://docs.mongodb.org/manual/tutorial/query-documents/#id4 I would expect this to only match documents that had all these three elements in an array.
However, it matches this doc:
{ "_id" : "userbruce", "events" : [ { "event" : "eventX" } ] }
Why?
The docs say that this format matches documents that "contain elements that in some combination satisfy the query conditions" What is matching event0 or event1?
EDIT: Is it getting confused because I put in the same field each time?
For AND operation in with this data, please try below query:
db.usertest.find({$and : [{ "events.event":"event0"}, {"events.event":"event1"}, {"events.event":"eventX" }]})
And yes, the confusion is because you given same field each time. IF you try something like below document:
{
"_id" : "userbruce",
"events" : [
{
"eventA" : "eventX"
},
{
"eventB" : "event0"
},
{
"eventC" : "event1"
}
]
}
then, your query would not have given any result. So, your query stands correct, but the data is not appropriate for that query.

Getting only Sub Elements [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.
If I have the following document
{
"_id" : ObjectId("54986d5531a011bb5fb8e0ee"),
"owner" : "54948a5d85f7a9527a002917",
"type" : "group",
"deleted" : false,
"participants" : [
{ "_id": "54948a5d85f7a9527a002917", "name": "user1" },
{ "_id": "5491234568f7a9527a002918", "name": "user2" },
{ "_id": "5491234568f7a9527a002918", "name": "user3" },
{ "_id": "1234567aaaa7a9527a002917", "name": "user2" }
]
}
How would I get all records where name = 'user2'?
I'm trying the followoing:
db.users.find({ _id: ObjectId('54a7103b1a57eee00bc0a9e4') },
{ 'participants.$.name': 'user2') }).pretty()
...and I get the following:
error: {
"$err" : "Can't canonicalize query: BadValue Positional projection 'participants.$.name' does not match the query document.",
"code" : 17287
}
Though the positional operator($) would give you the first matching element from the participant array. If you need all the participants in with the name user2, you need to aggregate the results.
Match the document with the required _id.
Use the redact operator to only keep all the sub documents that have
participants, who have their name as user2.
Code:
var search = "user2";
db.users.aggregate([
{$match:{"_id":ObjectId("54986d5531a011bb5fb8e0ee")}},
{$redact:{$cond:[{$eq:[{$ifNull:["$name",search]},search]},
"$$DESCEND",
"$$PRUNE"]}},
{$project:{"participants":1,"_id":0}} // set _id:1, if you need the _id.
])
o/p:
{
"participants" : [
{
"_id" : "5491234568f7a9527a002918",
"name" : "user2"
},
{
"_id" : "1234567aaaa7a9527a002917",
"name" : "user2"
}
]
}
Coming to your query,
db.users.find({ _id: ObjectId('54a7103b1a57eee00bc0a9e4') },
{ 'participants.$.name': 'user2'}).pretty()
The positional operator can be applied only on the array, that is referred in the query document of the find function. The above query document doesn't have a reference to the array named participants and only refers to the _id field to match a document. Hence you get the error.
From the docs,
The field being limited must appear in the query document
So, changing the query to include the participants array in the query document would fix the error.
db.users.find({ "_id":ObjectId('54a7103b1a57eee00bc0a9e4'),
"participants.name": "user2"
},
{"participants.$.name":"user2"}).pretty()
But it would return you only the first participant that has matched the criteria in the query document.
From the docs,
Use $ in the projection document of the find() method or the findOne()
method when you only need one particular array element in selected
documents.
o/p:
{
"_id" : ObjectId("54986d5531a011bb5fb8e0ee"),
"participants" : [
{
"_id" : "5491234568f7a9527a002918",
"name" : "user2"
}
]
}

In a Mongo collection, how do you query for a specific object in an array?

I'm trying to retrieve an object from an array in mongodb. Below is my document:
{
"_id" : ObjectId("53e9b43968425b29ecc87ffd"),
"firstname" : "john",
"lastname" : "smith",
"trips" : [
{
"submitted" : 1407824585356,
"tripCategory" : "staff",
"tripID" : "1"
},
{
"tripID" : "2",
"tripCategory" : "volunteer"
},
{
"tripID" : "3",
"tripCategory" : "individual"
}
]
}
My ultimate goal is to update only when trips.submitted is absent so I thought I could query and determine what the mongo find behavior would look like
if I used the $and query operator. So I try this:
db.users.find({
$and: [
{ "trips.tripID": "1" },
{ "trips": { $elemMatch: { submitted: { $exists: true } } } }
]
},
{ "trips.$" : 1 } //projection limits to the FIRST matching element
)
and I get this back:
{
"_id" : ObjectId("53e9b43968425b29ecc87ffd"),
"trips" : [
{
"submitted" : 1407824585356,
"tripCategory" : "staff",
"tripID" : "1"
}
]
}
Great. This is what I want. However, when I run this query:
db.users.find({
$and: [
{ "trips.tripID": "2" },
{ "trips": { $elemMatch: { submitted: { $exists: true } } } }
]
},
{ "trips.$" : 1 } //projection limits to the FIRST matching element
)
I get the same result as the first! So I know there's something odd about my query that isn't correct. But I dont know what. The only thing I've changed between the queries is "trips.tripID" : "2", which in my head, should have prompted mongo to return no results. What is wrong with my query?
If you know the array is in a specific order you can refer to a specific index in the array like this:-
db.trips.find({"trips.0.submitted" : {$exists:true}})
Or you could simply element match on both values:
db.trips.find({"trips" : {$elemMatch : {"tripID" : "1",
"submitted" : {$exists:true}
}}})
Your query, by contrast, is looking for a document where both are true, not an element within the trips field that holds for both.
The output for your query is correct. Your query asks mongo to return a document which has the given tripId and the field submitted within its trips array. The document you have provided in your question satisfies both conditions for both tripIds. You are getting the first element in the array trips because of your projection.
I have assumed you will be filtering records by the person's name and then retrieving the elements inside trips based on the field-exists criteria. The output you are expecting can be obtained using the following:
db.users.aggregate(
[
{$match:
{
"firstname" : "john",
"lastname" : "smith"
}
},
{$unwind: "$trips"},
{$match:
{
"trips.tripID": "1" ,
"trips.submitted": { $exists: true }
}
}
]
)
The aggregation pipeline works as follows. The first $match operator filters one document (in this case the document for john smith) The $unwind operator in mongodb aggregation unwinds the specified array (trips in this case), in effect denormalizing the sub-records associated with the parent records. The second $match operator filters the denormalized/unwound documents further to obtain the one required as per your query.