MongoDB query, project nested docs? - mongodb

I was wondering if this is possible, or do I need to use the aggregation pipeline instead?
I have read posts such as this, and intuitively feel like it's possible.
Example of docs:
{
"_id": ObjectID("5143ddf3bcf1bfab37d9c6f"),
"permalink": "btxcacmbqkxbpgtpeero",
"author": "machine",
"title": "Declaration of Independence",
"tags": [
"study",
"law"
],
"comments": [
{
"body": "comment 1",
"email": "email_1#test.com",
"author": "machine_1"
},
{
"body": "comment 2",
"email": "email_2#test.com",
"author": "machine_2"
},
{
"body": "comment 3",
"email": "email_3#test.com",
"author": "machine_3"
},
]
"date": ISODate("2013-03-16T02:50:27.878Z")
}
I am trying to access a particular comment in "comments" by it's index position using dot notation in the projection field, with the following:
db.collection.find({permalink: "btxcacmbqkxbpgtpeero"}, {'comments.0.1.': 1})
Where comments.0 is the first item in the field: the array, and .1 is the second comment in the array.
The result I am getting:
{ "_id" : ObjectID("5143ddf3bcf1bfab37d9c6f"), "comments" : [ { }, { }, { } ] }
If I take away the .1, leaving just comments.0, I get the same result:
{ "_id" : ObjectID("5143ddf3bcf1bfab37d9c6f"), "comments" : [ { }, { }, { } ] }
If I take away the .0, leaving just comments, I get the comments still inside their array:
[
{
"body": "comment 1",
"email": "email_1#test.com",
"author": "machine_1"
},
{
"body": "comment 2",
"email": "email_2#test.com",
"author": "machine_2"
},
{
"body": "comment 3",
"email": "email_3#test.com",
"author": "machine_3"
}
]
Can this be done? If so, how?

without aggregation:
db.collection.find({
permalink:"btxcacmbqkxbpgtpeero"
},
{
comments:{
$slice:[
0,
1
]
}
})
returns
{
"_id":ObjectId("583af24824168f5cc566e1e9"),
"permalink":"btxcacmbqkxbpgtpeero",
"author":"machine",
"title":"Declaration of Independence",
"tags":[
"study",
"law"
],
"comments":[
{
"body":"comment 1",
"email":"email_1#test.com",
"author":"machine_1"
}
]
}
try it online: mongoplayground.net/p/LGbVWPyVkFk
with aggregation:
db.collection.aggregate([
{
$match:{
permalink:"btxcacmbqkxbpgtpeero"
}
},
{
$project:{
comment:{
$arrayElemAt:[
"$comments",
0
]
}
}
}
])
returns
{
"_id":ObjectId("583af24824168f5cc566e1e9"),
"comment":{
"body":"comment 1",
"email":"email_1#test.com",
"author":"machine_1"
}
}

Related

Updating array list in Mongo DB

I want to update the Status field inside array list of Slots where id = "".
Sample data
{
"_id": ObjectId("621e816e7a938400016c5c64"),
"Resource": "abc#gmail.com",
"School": {
"Class": [
{
"Type": "ABC",
"Slots": [
{
"id": "",
"Duration": "1 week",
"Status": "Released",
"Selected": true
},
{
"id": "123",
"Duration": "1 week",
"Status": "Released",
"Selected": true
}
]
}
]
}
}
This is how I am approaching:
db.getCollection("XYZ").update({
"Resource": "abc#gmail.com",
"School.Class": {
"$elemMatch": {
"Type": "ABC",
"Slots.Status": "Released",
"Slots.id": "",
"Slots.Duration": "1 week"
}
}
},
{
$set: {
"School.Class.$[outer].Slots.$[inner].Status": "Confirmed"
}
},
{
"arrayFilters": [
{
"outer.Type": "ABC"
},
{
"inner.Duration": "1 week"
}
]
})
But it is updating the status as confirmed for the both the array list.How can I update the particular field where "Slots.id" : "" . Please forgive me in case of any misalignment or brackets missing in data
If I've understood correctly you are almost there, since you want to update only values where id is "" you have to add that condition into arrayFilters: "inner.id": "".
db.collection.update({
"Resource": "abc#gmail.com",
"School.Class": {
"$elemMatch": {
"Type": "ABC",
"Slots.Status": "Released",
"Slots.id": "",
"Slots.Duration": "1 week"
}
}
},
{
$set: {
"School.Class.$[outer].Slots.$[inner].Status": "Confirmed"
}
},
{
"arrayFilters": [
{
"outer.Type": "ABC"
},
{
"inner.Duration": "1 week",
"inner.id": "" // <--- This line
}
]
})
Example here

check if a field of type array contains an array

Im using mongoose, I have the following data of user collection:
[{
"_id": "1",
"notes": [
{
"value": "A90",
"text": "math"
},
{
"value": "A80",
"text": "english"
},
{
"value": "A70",
"text": "art"
}
]
},
{
"_id": "2",
"notes": [
{
"value": "A90",
"text": "math"
},
{
"value": "A80",
"text": "english"
}
]
},
{
"_id": "3",
"notes": [
{
"value": "A80",
"text": "art"
}
]
}]
and I have as a parameters the following array: [ "A90", "A80" ]
so I want to make a query to use this array to return only the records that have all the array items in the notes (value) table.
So for the example above it will return:
[{
"_id": "1",
"notes": [
{
"value": "A90",
"text": "math"
},
{
"value": "A80",
"text": "english"
},
{
"value": "A70",
"text": "art"
}
]
},
{
"_id": "2",
"notes": [
{
"value": "A90",
"text": "math"
},
{
"value": "A80",
"text": "english"
}
]
}]
I tried the following find query:
{ "notes": { $elemMatch: { value: { $in: valuesArray } } }}
but it returns a record even if just one element in valuesArray exist.
it turned out to be quite easy:
find({ "notes.value": { $all: arrayValues } })

Get a part of document in MongoDB

Suppose i have following document in some collection
{
"name": "Man1",
"Childrens": [
{
"name": "Children 1",
"age": "12"
},
{
"name": "Children 2",
"age": "18"
},
]
}
how to get output of the query as i.e.get child where age of the child is 18
{
"name": "Children 2",
"age": "18"
}
You need to use $elemMatch to find the document and $elemMatch for projection of the data
db.collection.find({
Childrens: {
$elemMatch: {
age: "18"
}
}
},
{
Childrens: {
$elemMatch: {
age: "18"
}
},
_id: 0
})
OutPut --> here
[
{
"Childrens": [
{
"age": "18",
"name": "Children 2"
}
]
}
]

Mongo returning an array element

I have the following JSON document in my mongoDB which I added with mingoimport.
I am trying to return a single element from the questions array where theQuestion equals "q1".
{
"questions": [
{
"questionEntry": {
"id": 1,
"info": {
"seasonNumber": 1,
"episodeNumber": 1,
"episodeName": "Days Gone Bye"
},
"questionItem": {
"theQuestion": "q1",
"attachedElement": {
"type": 1,
"value": ""
}
},
"options": [
{
"type": 1,
"value": "o1"
},
{
"type": 1,
"value": "o1"
}
],
"answer": {
"questionId": 1,
"answer": 1
},
"metaTags": [
"Season 1",
"Episode 1",
"Rick Grimmes"
]
}
},
{
"questionEntry": {
"id": 1,
"info": {
"seasonNumber": 1,
"episodeNumber": 1,
"episodeName": "Days Gone Bye"
},
"questionItem": {
"theQuestion": "q2",
"attachedElement": {
"type": 1,
"value": ""
}
},
"options": [
{
"type": 1,
"value": "o2"
},
{
"type": 1,
"value": "o2"
}
],
"answer": {
"questionId": 1,
"answer": 1
},
"metaTags": [
"Season 1",
"Episode 1",
"Rick Grimmes",
"Glenn Rhee"
]
}
}
]
}
I ran the query db.questions.find({"questions.questionEntry.questionItem.theQuestion" : "q1"}) but this retruned the whole document (both questionEntry's in question array!
I have tried db.questions.find({"questions.questionEntry.questionItem.theQuestion" : "q1"}, _id:0," questions.questionItem": {$elemMatch : {theQuestion: "q1"}}})
But get the following error:
Error: error: {
"$err" : "Can't canonicalize query: BadValue Cannot use $elemMatch projection on a nested field.", "code" : 17287
Is there a way I could limit the result to just the array element which contains it?
Thanks
db.questions.find({},{"questions.questionEntry.questionItem.theQuestion" : "q1"});
or
db.questions.find({"questions.questionEntry.questionItem.theQuestion" : "q1"},{'questions.$':1});
please try these.
If you want to use $elemMatch the query should be:
db.questions.find(
{"questions.questionEntry.questionItem.theQuestion" : "q1"},
{
'_id':0,
"questions": {
$elemMatch : {"questionEntry.questionItem.theQuestion": "q1"}
}
}
)

MongoDB - Query within an in-memory BsonDocument

I am reading a single document into a BsonDocument object. Having read the document from MongoDB I'd like to query the document in-memory.
My doc looks like this:
{
"_id": {
"$binary": "DYibd4bSz0SFXTTmY46gOQ==",
"$type": "03"
},
"title": "XYZ 2011",
"pages": [
{
"pagetype": "contactcapture",
"pagetitle": "Contact",
"questions": [
{
"qtype": "text",
"text": "Firstname",
"name": "firstname"
},
{
"qtype": "text",
"text": "Surname",
"name": "surname"
},
{
"qtype": "text",
"text": "Company",
"name": "companyname"
}
]
},
{
"pagetype": "question",
"pagetitle": "Question 1",
"questions": [
{
"qtype": "radio",
"text": "What drink?",
"name": "drink",
"answers": [
{
"text": "Tea"
},
{
"text": "Coffee"
},
{
"text": "Hot chocolate"
},
{
"text": "Water"
}
]
}
]
},
{
"pagetype": "question",
"pagetitle": "Question 2",
"questions": [
{
"qtype": "check",
"text": "Accompaniments?",
"name": "accompaniments",
"answers": [
{
"text": "Nuts"
},
{
"text": "Crisps"
},
{
"text": "Biscuits"
}
]
}
]
},
{
"pagetype": "question",
"pagetitle": "Question 3",
"questions": [
{
"qtype": "radio",
"text": "When would you like that?",
"name": "when",
"answers": [
{
"text": "Immediately"
},
{
"text": "10 minutes"
},
{
"text": "Half-an-hour"
}
]
},
{
"qtype": "text",
"text": "Anything else with that?",
"name": "anythingelse"
}
]
}
]
}
I want to get all pages that have a pagetype="question". I'm currently doing it as follows:
BsonDocument profileDocument= profilesCollection.FindOneByIdAs<MongoDB.Bson.BsonDocument>(binaryId);
BsonElement pagesElement = profileDocument.GetElement("pages");
BsonArray pages=profileDocument.GetElement("pages").Value.AsBsonArray;
foreach (BsonValue pageV in pages.Values)
{
BsonDocument page = pageV.AsBsonDocument;
if (page["pagetype"].AsString == "question")
{
sb.Append("<br />Question Page:" + page.ToJson());
}
}
The code seems a little verbose and complex - I just wondered if there is a better way of doing this? Thanks
Assuming that the data type of profilesCollection is MongoCollection<BsonDocument>, you could shorten the code so something like this:
var profileDocument = profilesCollection.FindOneById(binaryId);
foreach (BsonDocument page in profileDocument["pages"].AsBsonArray) {
if (page["pagetype"].AsString == "question") {
sb.Append("<br />Question Page:" + page.ToJson());
}
}