Need MongoDB Query - mongodb

I'm sorry but I'm little confuse with a query , Kindly help. suppose we've one document that contains
{
"_id":100,
"name":"Demarcus Audette",
"scores":[
{
"score":47.42608580155614,
"type":"exam"
},
{
"score":44.83416623719906,
"type":"quiz"
},
{
"score":39.01726616178844,
"type":"homework"
},
"score":89.01726616178844,
"type":"homework"
}
]
}
And I want to write a query that should return only rows which contains homework in that , that means the out put should be like below
{
"_id":100,
"name":"Demarcus Audette",
"scores":[
{
"score":39.01726616178844,
"type":"homework"
},
"score":89.01726616178844,
"type":"homework"
}
]
}
Kindly suggest. Thanks in Advance

Use the $elemMatch operator.
db.collection.find({ "scores": { $elemMatch: { "type": "homework" } } } );
EDIT
What you are asking is not possible. You will need the above query and filter out the rest in whatever language you are programming. You can also use an aggregate function using $unwind and $match.
db.collection.aggregate(
{$unwind: "$messages"},
{$match: {"scores.type": "homework"}}
);
$unwind flattens your array and $match is your actually query which will return matching documents. Please note that $unwind will create a different document for each element in your array. This means you will get two results when you filter on 'homework' according to your example.

Related

MongoDB $elemMatch comparison to field in same document

I'm wanting to create an aggregation step to match documents where the value of a field in a document exists within an array in the same document.
In a very worked example (note this is very simplified; this will be fitting into a larger existing pipeline), given documents:
{
"_id":{"$oid":"61a9085af9733d0274c41990"},
"myArray":[
{"$oid":"61a9085af9733d0274c41991"},
{"$oid":"61a9085af9733d0274c41992"},
{"$oid":"61a9085af9733d0274c41993"}
],
"myField":{"$oid":"61a9085af9733d0274c41991"} // < In 'myArray' collection
}
and
{
"_id":{"$oid":"61a9085af9733d0274c41990"},
"myArray":[
{"$oid":"61a9085af9733d0274c41991"},
{"$oid":"61a9085af9733d0274c41992"},
{"$oid":"61a9085af9733d0274c41993"}
],
"myField":{"$oid":"61a9085af9733d0274c41994"} // < Not in 'myArray' collection
}
I want to match the first one because the value of myField exists in the collection, but not the second document.
It feels like this should be a really simple $elemMatch operation with an $eq operator, but I can't make it work and every example I've found uses literals. What I've got currently is below, and I've tried with various combinations of quotes and dollar signs round myField.
[{
$match: {
myArray: {
$elemMatch: {
$eq: '$this.myField'
}
}
}
}]
Am I doing something very obviously wrong? Is it not possible to use the value of a field in the same document with an $eq?
Hoping that someone can come along and point out where I'm being stupid :)
Thanks
You can simply do a $in in an aggregation pipeline.
db.collection.aggregate([
{
"$match": {
$expr: {
"$in": [
"$myField",
"$myArray"
]
}
}
}
])
Here is the Mongo playground for your reference.

Find whether embedded document exists in array and get the parent document's a property value

I want to find whether embedded document exists in array property of parent document and get the parent document's a property value.
Think, I have a document like this
{
_id:1,
persons:[{name: "Jack", earning: 1000},{name: "Monica", earning: 2000}]
totalDebt:500
}
I want to find whether name=Jamal exists in person array's embedded document and with this the totalDebt of _id=1
The result will be like this:
{
totalDebt:500,
exists:false // as Jamal does not exists
}
How execute this in a single query ?
Yes, you can easily solve this by using one query. I solved this by one query using $cond, Look below code to get a brief understanding.
db.getCollection('collectionName').aggregate([
{
$project: {
_id: 0,
totalDebt: 1,
"exists": {
$cond: {
if: {
$in: [
"Jack",
"$persons.name"
]
},
then: true,
else: false
}
}
}
}
])
Initially, I have used $cond with $eq operator, it always returns false. When I use $in, it solved my problem.

How to query nested arrays in mongoDB to find documents with elements that don't exist

I'm trying to make an aggregation query to find all documents that do not contain a certain element. It needs to be an aggregation because I want to be able to edit the returned documents. Eg. I only want to return some fields and I also want to be able to do a group on eg. the "producer" element.
I already tried practically all I can think of. I tried unwinding the arrays, but then I created even more documents where the element packagingInformation was mission than originally. I tried using $ne, $eq, $gt, $lte,.. to find the documents needed,... but they always return all documents because of the nested array structure.
$ArrayToObject didn't do the trick either for me.
I'm clueless on how to achieve this. The tripple nested array structure beats my imagination.
The only thing that returns me the wanted result is the following query:
db.product.find({
"json.productData.productInformation.details.packagingInformation": { $exists: false }
})
But this doesn't suffice since it's not an aggregate, thus it doens't allow me to continue to do queries with the results. And the $exists doesn't work in aggregates.
This is the JSON structure which I'm struggling with (dummy data).
{
_id: 5ckflsmdk543klmf543klmtrkmgdfm,
productNumber: 001,
json: {
productData: {
productNumber: 001,
producer: coca-cola,
productInformation: [
{
trackingInformation: {
lastUpdate: 01-01-12,
creationDate: 01-01-11
},
details: [
packagingInformation: [
quantity: 5,
size: 20cm
],
productType: drinks,
otherMeaningLessInformation: whatever,
andEvenMoreInformationInArrays: [
andTheInformationGoesOn: wow,
andOn: nastyArrayStructures
]
]
]
}
}
}
}
The wanted result would be to return all the documents that do not contain the packagingInformation array or the packagingInformation.quantity element.
or even better, to return all documents but with an extra field:
containsPackagingInformation: true/false. With false being the result of all documents that do not contain packagingInformation or packagingInformation.quantity.
$exists DOES WORK in a aggregation.
$exists works the same way it works in .find
you can form a query like:
db.collection.aggregate({ $match: {
$or: [
{
"json.productData.productInformation.details.packagingInformation.quantity": {
$exists: false
}
},
{
"json.productData.productInformation.details.packagingInformation": {
$exists: false
}
}
] } })
Try this query here with dummy data

Query mongodb array of documents

I've a bunch of documents that look like:
{
"ids": [{"name":"aa", "age":1}, {"name":"bb", "age":2}]
}
I'd like to be able to query my documents providing a collection of ids, something like
db.getCollection('Collection').find({"ids":{$in : [{"name":"aa", "age":1}, {"name":"bb", "age":2}]}})
Generally that works, however it breaks when the fields order is changed, so for example I cannot find documents when I execute the following query
db.getCollection('Collection').find({"ids":{$in : [{"age":1,"name":"aa"}, { "age":2, "name":"bb"}]}})
I know that I could try to always execute a query with fields "in order", but from my current task perspective it's not always possible. Any help with that ?
You need $elemMatch when you want to run your query against an array of objects:
db.col.find({ $or: [ { "ids": { $elemMatch: {"age":1,"name":"aa"} } }, { "ids": { $elemMatch: { "age":2, "name":"bb"} } } ] })
Mongo Playground
EDIT: you can decide whether $or or $and should be a top level operator (depending on your use case)

Getting first and last element of array in MongoDB

Mongo DB: I'm looking to make one query to return both the first and last element of an array. I realize that I can do this multiple queries, but I would really like to do it with one.
Assume a collection "test" where each objects has an array "arr" of numbers:
db.test.find({},{arr:{$slice: -1},arr:{$slice: 1}});
This will result in the following:
{ "_id" : ObjectId("xxx"), "arr" : [ 1 ] } <-- 1 is the first element
Is there a way to maybe alias the results? Similar to what the mysql AS keyword would allow in a query?
This is not possible at the moment but will be with the Aggregation Framework that's in development now if I understand your functional requirement correctly.
You have to wonder about your schema if you have this requirement in the first place though. Are you sure there isn't a more elegant way to get this to work by changing your schema accordingly?
This can be done with the aggregation framework using the operators $first and $last as follows:
db.test.aggregate([
{ '$addFields': {
'firstElem': { '$first': '$arr' },
'lastElem': { '$last': '$arr' }
} }
])
or using $slice as
db.test.aggregate([
{ '$addFields': {
'firstElem': { '$slice': [ '$arr', 1 ] },
'lastElem': { '$slice': [ '$arr', -1 ] }
} }
])