How to select only field of embeded document in mongodb? - mongodb

I have collection that contains a document like the following:
I just want to get id of quiz
But my result expected is
{"id":1}
How to do that?

This solution works with MongoDB version 4.4 or higher:
Input document:
{ "_id" : 1, "quiz" : { "id" : 1, "time_limit" : 10 } }
The query uses the new functionality in projection:
db.names.find( { }, { "id" : "$quiz.id", "_id": 0 } )
{ "id" : 1 } // desired output
For more information see Projection.

You'll have to use an aggregation $project stage as the query language does not allow restructuring of data.
db.collection.aggregate([
{
$project: {
_id: 0,
id: "$quiz.id"
}
}
])
Mongo Playground

Related

MongoDB - Lookup with pipeline get error: $nin needs an array

Hello everyone I am having some trouble basically I am getting
$nin needs an array
even after passing an array but using a variable when attempting to run the following query.
I want to get documents from a collection numberData that contains a property value which is not assigned to an array in another collection named assignedData.
Example of numbersData docs are given below:
/* 1 */
{
"_id" : ObjectId("61f2b5923327b3f6f4dad27e"),
"value" : 1.0
}
/* 2 */
{
"_id" : ObjectId("61f2b5923327b3f6f4dad27f"),
"value" : 2.0
}
/* 3 */
{
"_id" : ObjectId("61f2b5923327b3f6f4dad280"),
"value" : 3.0
}
/* 4 */
{
"_id" : ObjectId("61f2b5923327b3f6f4dad281"),
"value" : 4.0
}
and the contents of assignedData are given below:
/* 1 */
{
"_id" : ObjectId("61f2b5e43327b3f6f4dad282"),
"name" : "john",
"assigned" : [
1.0,
4.0
]
}
The query I used:
db.assignedData.aggregate([
{
//i only want to get single doc from assigned data
//so using this match
$match: {
_id: ObjectId("61f2b5e43327b3f6f4dad282")
},
},
{
$lookup: {
from: 'numbersData',
let: { aArr: "$assigned" },
pipeline: [
{
$match: {
//here i am using $$maArr
//which is refering to assigned array
value:
{
$nin: "$$aArr"
}
}
}
],
as: 'matchData'
}
}
])
After running this query I got the error:
$nin needs an array
I don't know why please suggest me solution if you can.
From Join Conditions and Subqueries on a Joined Collection section (refer to the table),
let
A $match stage requires the use of an $expr operator to access the variables. The $expr operator allows the use of aggregation expressions inside of the $match syntax.
Instead of using $nin operator, change as below:
{
$match: {
$expr: {
$not: {
$in: [
"$value",
"$$aArr"
]
}
}
}
}
Sample Mongo Playground

How to return Mongodb Aggregate pipeline docs to ONE document?

I know this has got to be simple, but for the life of me I can't seem to generate the correct final stage in my pipeline to get this working. Here are the documents output from a stage that I have in a mongo query:
{ "_id" : ObjectId("61435ceb233ce0118c1d93ec") }
{ "_id" : ObjectId("61435cf29598d31c17f0d839") }
{ "_id" : ObjectId("611e5cf953396d78985d222f") }
{ "_id" : ObjectId("61435cf773b8b06c848af83e") }
{ "_id" : ObjectId("61435cfd7ac204efa857e7ce") }
{ "_id" : ObjectId("611e5cf953396d78985d2237") }
I would like to get these documents into ONE single document with an array as such:
{
"_id" : [
ObjectId("61435ceb233ce0118c1d93ec"),
ObjectId("61435cf29598d31c17f0d839"),
ObjectId("611e5cf953396d78985d222f"),
ObjectId("61435cf773b8b06c848af83e"),
ObjectId("61435cfd7ac204efa857e7ce"),
ObjectId("611e5cf953396d78985d2237")
]
}
My last stage in the pipeline is simply:
{
$group:{_id:"$uniqueIds"}
}
I've tried everything from $push to $mergeObjects, but no matter what I do, it keeps returning the original 6 documents in some shape or form instead of ONE document. Any advice would be greatly appreciated! Thanks in advance.
Test code here
Query
group by null, sees all collection as 1 group
db.collection.aggregate([
{
"$group": {
"_id": null,
"ids": {
"$push": "$_id"
}
}
},
{
"$unset": "_id"
}
])

Mongodb aggregate add all fields

I have a collection recording impressions (views) of certain tags. I want to see the count of each tag value. In the response I also want to see the whole of the record, in the same way that mysql would.
I'm doing a group using the aggregate pipeline which looks like this
db.tag_impressions.aggregate( [
{ $group : { _id : "$tag_value" , count:{$sum:1} } },
{ $sort : { count: 1 } }
] )
I want to return all of the matched document in tag_impressions.
and I've had some success using $first
db.tag_impressions.aggregate( [
{ $group : { _id : "$tag_value" , "tag_type" : {$first : "Tag_type"} , count:{$sum:1} } },
{ $sort : { count: 1 } }
] )
But I would have to specify each field and it would take away the benefit from mongo being schema-less.
How can I return all of the document in the results?

Does Mongodb have something like SQL "As" feature?

For example, if I want to simplify the result set for a nested doc', get the field score.date as score_date in order to get a flat result
Yes, this is possible through the aggregation framework, in particular you would want to use the $project operator. This reshapes each document in the stream, such as by adding new fields or removing existing fields. For each input document, outputs one document. Using your example, suppose your collection has documents with this schema:
{
"_id" : ObjectId("557330473a79b31f0c805db3"),
"player": "A",
"score": {
"date": ISODate("2015-05-30T15:14:48.000Z"),
"value": 2
}
}
Then you would apply the $project operator in the aggregation pipeline as:
db.collection.aggregate([
{
"$project": {
"player": 1,
"score_date": "$score.date",
"score_value": "$score.value"
}
}
]);
Result:
/* 0 */
{
"result" : [
{
"_id" : ObjectId("557330473a79b31f0c805db3"),
"player" : "A",
"score_date" : ISODate("2015-05-30T15:14:48.000Z"),
"score_value" : 2
}
],
"ok" : 1
}

MongoDB Aggregation Sum implements on Spring Data

The HOUR_COUNTS collections contains {docId, hour, count}
It's very easy for me to get the sum of the count of docId by using the following mongodb query:
db.HOUR_COUNTS.aggregate(
[
{
$match: { hour: { $gte: 10 } }
},
{
$group: { _id: "$docId", total: { $sum: "$count" } }
},
{
$sort: { total: -1, _id: -1 }
},
{
$limit: 20
}
]
)
Then I can get the following result:
{ "_id" : 6831, "total" : 6 }
{ "_id" : 6830, "total" : 6 }
{ "_id" : 6849, "total" : 4 }
{ "_id" : 6848, "total" : 4 }
{ "_id" : 6847, "total" : 3 }
It's the time for me to do it using Spring Data
I tried to do this but it is not going to work:
Aggregation agg = newAggregation(
match(where("hour").gte(0)),
project("docId"),
group("docId").sum("count").as("total"),
project("total").and("docId").previousOperation(),
sort(Sort.Direction.DESC, "total", "docId"),
limit(20)
);
The error is:
java.lang.IllegalArgumentException: Invalid reference 'count'!
Therefore, I would like to know how to make the query work on Spring Data. Thank you.
Why would this be expected to work? Which is the question you should be asking yourself really.
In the aggregation pipeline operations, operators such as $project and $group only ever "return" the fields that you explicitly ask them to. As a "pipeline" concept, only the "output" of the previous piped stage is available to the next stage and those thereafter, until otherwise possibly modified again.
So what you wrote in your Java code is not equal to what you experimented in the shell. You try to refer to a "field" you excluded with a prior $project operation. So don't do that. You seem to have a false view on how things actually optimize in the aggregation pipeline:
Aggregation agg = newAggregation(
match(Criteria.where("hour").gte(10)),
group("docId").sum("count").as("total"),
sort(Sort.Direction.DESC, "total","docId"),
limit(20)
);
So that is actually the "same" as what you wrote before. You don't need the additional "project" operations, and they are detrimental to your intended result.