What wrong with my mongo query to get a specific by nest document? - mongodb

{
"_id" : ObjectId("5dbdacc28cffef0b94580dbd"),
"owner" : {
"image" : "https://lh3.googleusercontent.com/a-/AAuE7mCpG2jzbEdffPgdeVWnkBKwyzCCwEB1HMbU1LAVAg=s50",
"fullname" : "soeng kanel",
"userID" : "5da85558886aee13e4e7f044"
},
"image" : "soeng kanel-1572711618984.png",
"body" : "sdadadasdsadadas sds",
"date" : ISODate("2019-11-02T16:20:05.558Z"),
"comments" : [
{
"user" : "5da85558886aee13e4e7f044",
"fullname" : "soeng kanel",
"username" : "",
"comment" : "sdsfdsfdsfds",
"_id" : ObjectId("5dbdacc78cffef0b94580dbf"),
"replies" : [
{
"likes" : [
"5da85558886aee13e4e7f044"
],
"date" : ISODate("2019-11-02T16:20:05.558Z"),
"_id" : ObjectId("5dbdacd78cffef0b94580dc0"),
"reply" : "r1111111",
"username" : "",
"fullname" : "soeng kanel",
"user" : "5da85558886aee13e4e7f044"
},
{
"likes" : [],
"date" : ISODate("2019-11-02T16:20:05.558Z"),
"_id" : ObjectId("5dbdacdb8cffef0b94580dc1"),
"reply" : "r222222",
"username" : "",
"fullname" : "soeng kanel",
"user" : "5da85558886aee13e4e7f044"
},
{
"likes" : [],
"date" : ISODate("2019-11-03T03:04:23.528Z"),
"_id" : ObjectId("5dbe4749fa751f05afcc1bd6"),
"reply" : "33333333",
"username" : "",
"fullname" : "soeng kanel",
"user" : "5da85558886aee13e4e7f044"
}
],
"date" : ISODate("2019-11-02T16:20:05.558Z"),
"likes" : []
}
],
"likes" : [
"5da85558886aee13e4e7f044"
],
"project" : {},
"__v" : 2
}
My query is
db.getCollection("posts").aggregate([
{ $match: {_id: ObjectId("5dbdacc28cffef0b94580dbd"), "comments._id": ObjectId("5dbdacc78cffef0b94580dbf") }},
{ $unwind: "$comments"},
{ $match: { "comments._id": ObjectId("5dbdacc78cffef0b94580dbf")}},
{ $project: {"replies": "$comments.replies", _id: 0}},
{ $match: { "replies._id": ObjectId("5dbdacd78cffef0b94580dc0")}},
{ $project: {"likes": "$replies.likes", _id: 0}},
])
With this query I get 3 elements ,
{
"likes" : [
[
"5da85558886aee13e4e7f044"
],
[],
[]
]
}
That is not what I want, what I want is to get by specific
replies by this _id 5dbdacd78cffef0b94580dc0.
And My expectation
{
"likes" : [
[
"5da85558886aee13e4e7f044"
]
]
}

Try using $unwind on replies before you $match stage on replies.
db.collection.aggregate([
{
$match: {
_id: ObjectId("5dbdacc28cffef0b94580dbd"),
"comments._id": ObjectId("5dbdacc78cffef0b94580dbf")
}
},
{
$unwind: {
path: "$comments",
preserveNullAndEmptyArrays: false
}
},
{
$match: {
"comments._id": ObjectId("5dbdacc78cffef0b94580dbf")
}
},
{
$project: {
"replies": "$comments.replies",
_id: 0
}
},
{
$unwind: {
path: "$replies",
preserveNullAndEmptyArrays: false
}
},
{
$match: {
"replies._id": ObjectId("5dbdacd78cffef0b94580dc0")
}
},
{
$project: {
"likes": "$replies.likes"
}
}
])
Above query produce output in the following fashion:
[
{
"likes": [
"5da85558886aee13e4e7f044"
]
}
]
I hope that's okay.

Related

Facing a problem with the lookup in the second (student) table that matches all incoming output records mongodb aggregation

I'm facing a problem with the lookup in the second (student) table that matches all incoming output records of the first(test) table. I have two collections "tests" and "students". "Test" collection contains all school tests and the "student" table contains student's attended tests. Student table contains "pastTest"(test attended in past with status "pass" or "fail")array. I want to retrieve student who passed all incoming tests (we retrieve from the tests table)
test table: _id (primary ket)
student.pastTests.testId (need to match with test._id)
Test Document:
{
"_id" : ObjectId("5c9b5c1005729b2bf23f3290"),
"testDate" : {
"term" : 1,
"week" : 7
},
"retestDate" : {
"term" : 1,
"week" : 10
},
"testOrder" : "1.1",
"testDateScheduled" : true,
"retestDateScheduled" : true
}
Student Document:
{
"_id" : ObjectId("5c92dd994e8e6b2c1647d0d0"),
"completedYears" : [],
"firstName" : "Andrew",
"lastName" : "Jonhson",
"teacherId" : ObjectId("5bf36b1076696374e65feb4f"),
"yearGroup" : "0",
"schoolId" : 40001,
"currentTest" : ObjectId("5c9b5c1005729b2bf23f3290"),
"pastTests" : [
{
"_id" : ObjectId("5d3570645045863d373f6db1"),
"testId" : ObjectId("5c9b5c1005729b2bf23f3290"),
"status" : "pass"
},
{
"_id" : ObjectId("5d425af07708f5636c3bec1c"),
"testId" : ObjectId("5c9b5fc460e39c2c58e44109"),
"status" : "pass"
},
{
"_id" : ObjectId("5d5e54a875fab079f4d03570"),
"testId" : ObjectId("5c9b6492bb581c2ceb553fef"),
"status" : "fail"
},
],
"createdAt" : ISODate("2019-03-21T00:40:57.401Z"),
"updatedAt" : ISODate("2020-09-24T19:55:38.291Z"),
"__v" : 0,
"holdTests" : [],
"completedTests" : [],
"className" : "dd",
}
Query:
db.getCollection('tests').aggregate([
{
$match: {
yearGroup: '-1',
$or : [
{
$and: [
{'retestDateScheduled': true},
{ 'retestDate.term': { $lt: 4 } },
]
},
{
$and: [
{'testDateScheduled': true},
{ 'testDate.term': { $lt: 4 } },
]
}
]
}
},
{
$lookup: {
from: 'students',
let: {testId: '$_id', schoolId: 49014, yearGroup: '-1'},
pipeline: [
]
}
}
])
Note: Initial match query returns all tests of the term-1, now I have to retrieve students who passed in all tests of the term-1.
Lookup stage is pending - facing problem with lookup in second (student) table who match all incoming output records of first(test) collection
Thanks in advance !!
Try this:
db.tests.aggregate([
{
$match: {
// Your match condition
}
},
{
$group: {
_id: null,
term_1_testIds: { $push: "$_id" },
test_count: { $sum: 1 }
}
},
{
$lookup: {
from: "students",
let: { term_1_testIds: '$term_1_testIds', schoolId: 40001, totalTestCount: "$test_count" },
pipeline: [
{
$match: {
$expr: { $eq: ["$schoolId", "$$schoolId"] }
}
},
{ $unwind: "$pastTests" },
{
$match: {
"pastTests.status": "pass",
$expr: { $in: ["$pastTests.testId", "$$term_1_testIds"] }
}
},
{
$group: {
_id: "$_id",
firstName: { $first: "$firstName" },
yearGroup: { $first: "$yearGroup" },
schoolId: { $first: "$schoolId" },
currentTest: { $first: "$currentTest" },
passedTestCount: { $sum: 1 },
pastTests: { $push: "$pastTests" }
}
},
{
$match: {
$expr: { $eq: ["$passedTestCount", "$$totalTestCount"] }
}
}
],
as: "students"
}
}
]);
Output:
{
"_id" : null,
"term_1_testIds" : [
ObjectId("5c9b5c1005729b2bf23f3290"),
ObjectId("5c9b5fc460e39c2c58e44109"),
ObjectId("5c9b6492bb581c2ceb553fef")
],
"test_count" : 3,
"students" : [
{
"_id" : ObjectId("5c92dd994e8e6b2c1647d0d1"),
"firstName" : "Dheemanth",
"yearGroup" : "0",
"schoolId" : 40001,
"currentTest" : ObjectId("5c9b5c1005729b2bf23f3290"),
"passedTestCount" : 3,
"pastTests" : [
{
"_id" : ObjectId("5d3570645045863d373f6db1"),
"testId" : ObjectId("5c9b5c1005729b2bf23f3290"),
"status" : "pass"
},
{
"_id" : ObjectId("5d425af07708f5636c3bec1c"),
"testId" : ObjectId("5c9b5fc460e39c2c58e44109"),
"status" : "pass"
},
{
"_id" : ObjectId("5d5e54a875fab079f4d03570"),
"testId" : ObjectId("5c9b6492bb581c2ceb553fef"),
"status" : "pass"
}
]
}
]
}
This how my tests collection looks like
/* 1 createdAt:3/27/2019, 5:24:58 PM*/
{
"_id" : ObjectId("5c9b6492bb581c2ceb553fef"),
"name" : "Test 3"
},
/* 2 createdAt:3/27/2019, 5:04:28 PM*/
{
"_id" : ObjectId("5c9b5fc460e39c2c58e44109"),
"name" : "Test 2"
},
/* 3 createdAt:3/27/2019, 4:48:40 PM*/
{
"_id" : ObjectId("5c9b5c1005729b2bf23f3290"),
"name" : "Test 1"
}
This is how my students collection looks like:
/* 1 createdAt:3/21/2019, 6:10:57 AM*/
{
"_id" : ObjectId("5c92dd994e8e6b2c1647d0d1"),
"firstName" : "Dheemanth",
"yearGroup" : "0",
"schoolId" : 40001,
"currentTest" : ObjectId("5c9b5c1005729b2bf23f3290"),
"pastTests" : [
{
"_id" : ObjectId("5d3570645045863d373f6db1"),
"testId" : ObjectId("5c9b5c1005729b2bf23f3290"),
"status" : "pass"
},
{
"_id" : ObjectId("5d425af07708f5636c3bec1c"),
"testId" : ObjectId("5c9b5fc460e39c2c58e44109"),
"status" : "pass"
},
{
"_id" : ObjectId("5d5e54a875fab079f4d03570"),
"testId" : ObjectId("5c9b6492bb581c2ceb553fef"),
"status" : "pass"
}
]
},
/* 2 createdAt:3/21/2019, 6:10:57 AM*/
{
"_id" : ObjectId("5c92dd994e8e6b2c1647d0d0"),
"firstName" : "Andrew",
"yearGroup" : "0",
"schoolId" : 40001,
"currentTest" : ObjectId("5c9b5c1005729b2bf23f3290"),
"pastTests" : [
{
"_id" : ObjectId("5d3570645045863d373f6db1"),
"testId" : ObjectId("5c9b5c1005729b2bf23f3290"),
"status" : "pass"
},
{
"_id" : ObjectId("5d425af07708f5636c3bec1c"),
"testId" : ObjectId("5c9b5fc460e39c2c58e44109"),
"status" : "pass"
},
{
"_id" : ObjectId("5d5e54a875fab079f4d03570"),
"testId" : ObjectId("5c9b6492bb581c2ceb553fef"),
"status" : "fail"
}
]
}
Also:
In your first $match stage, $and operator is redundant inside $or array it should be like this:
{
$match: {
yearGroup: '-1',
$or: [
{
'retestDateScheduled': true,
'retestDate.term': { $lt: 4 }
},
{
'testDateScheduled': true,
'testDate.term': { $lt: 4 }
}
]
}
}

MongoDB Aggregate Listing by Arrays Value

How its making listing order by records array's value with subquery
I want best query for perform in mongo side
models.js
new ms.Schema({
name : {type: String,required: true,unique:true},
display_name: {type: String,required: true,unique:true},
url: {type: String,default:'' },
icon: {type: String,default :'no.png' },
assets : {type:Array,default : ['BTCUSDT']},
active: {type: Boolean, default : true},
})
mongodb's record goes here
{
"_id" : ObjectId("5e9e78c477b1c7a1bfc4978c"),
"url" : "https://bitso.com/",
"active" : false,
"name" : "bitso",
"display_name" : "Bitso",
"icon" : "Bitso.png",
"__v" : 0,
"seq" : 888,
"assets" : [
"BTCUSDT",
"ETHUSDT",
"LTCUSDT"
]
},
{
"_id" : ObjectId("5e9e78c377b1c7a1bfc4978a"),
"url" : "https://www.fybsg.com/",
"active" : false,
"name" : "fybsg",
"display_name" : "FYB-SG",
"icon" : "FYB-SG.png",
"__v" : 0,
"seq" : 888,
"assets" : [
"BTCUSDT",
"ETHUSDT"
]
},
{
"_id" : ObjectId("5e9e78c377b1c7a1bfc49789"),
"url" : "https://hitbtc.com/",
"active" : true,
"name" : "hitbtc",
"display_name" : "Hitbtc",
"icon" : "Hitbtc.png",
"__v" : 0,
"seq" : 99,
"assets" : [
"BCCUSDT"
]
},
{
"_id" : ObjectId("5e9e78c077b1c7a1bfc49787"),
"url" : "https://blockchain.io/",
"active" : false,
"name" : "blockchainio",
"display_name" : "Blockchain.io",
"icon" : "Blockchain.io.png",
"__v" : 0,
"seq" : 999,
"assets" : [
"BTCUSDT",
"ETHUSDT"
]
},
db.markets.aggregate([...])
if possible How can i export result like here from mongo records; Else what algorithm must i use
i need data like this
[
BTCUSDT : { which record assets have BTCUSDT listings },
ETHUSDT : { which record assets have BTCUSDT listings},
...
]
You need to flat assets array of each doc using $unwind stage and after that just group them by this field:
db.markets.aggregate([
{
$unwind: "$assets"
},
{
$group: {
_id: "$assets",
recordId: {
$push: "$_id"
}
}
}
])
You output will be:
[
{
"_id": "BCCUSDT",
"recordId": [
"5e9e78c377b1c7a1bfc49789"
]
},
{
"_id": "BTCUSDT",
"recordId": [
"5e9e78c477b1c7a1bfc4978c",
"5e9e78c377b1c7a1bfc4978a",
"5e9e78c077b1c7a1bfc49787"
]
},
{
"_id": "LTCUSDT",
"recordId": [
"5e9e78c477b1c7a1bfc4978c"
]
},
{
"_id": "ETHUSDT",
"recordId": [
"5e9e78c477b1c7a1bfc4978c",
"5e9e78c377b1c7a1bfc4978a",
"5e9e78c077b1c7a1bfc49787"
]
}
]
If it's more convinient for you to get only one object as the result, you can additionally group them by null id, store all the docs in array with k, v properties and replace the root of the single document:
{
$group: {
_id: null,
result: {
$push: {
k: "$_id",
v: "$recordId"
}
}
}
},
{
$addFields: {
result: {
$arrayToObject: "$result"
}
}
},
{
$replaceRoot: {
newRoot: "$result"
}
}
Result for whole query will be:
[
{
"BCCUSDT": [
"5e9e78c377b1c7a1bfc49789"
],
"BTCUSDT": [
"5e9e78c477b1c7a1bfc4978c",
"5e9e78c377b1c7a1bfc4978a",
"5e9e78c077b1c7a1bfc49787"
],
"ETHUSDT": [
"5e9e78c477b1c7a1bfc4978c",
"5e9e78c377b1c7a1bfc4978a",
"5e9e78c077b1c7a1bfc49787"
],
"LTCUSDT": [
"5e9e78c477b1c7a1bfc4978c"
]
}
]

$lookup nested external id after $group

I want to replace the external user ids with the real user properties after $lookup, my data:
"comments" : [
{
"user_Id" : ObjectId("aaa"),
"content" : "aaaa",
"rep" : [
{
"user_Id" : ObjectId("bbb"),
"comment" : "bbbb",
},
{
"user_Id" : ObjectId("ccc"),
"comment" : "cccc",
}
]
},
{
"user_Id" : ObjectId("ddd"),
"content" : "ddd",
"rep" : [ ]
}
]
User collection:
"users" : [
{
"_id" : ObjectId("aaa"),
"name" : "user1",
"email" : "test1#test.com",
},
{
"_id" : ObjectId("bbb"),
"username" : "user2",
"email" : "test2#test.com",
}
]
What i want to archieve:
"comments" : [
{
"user" : {
"_id" : ObjectId("aaa"),
"name" : "user1",
"email" : "test1#test.com",
}
"content" : "aaaa",
"rep" : [
{
"userId" : {
"_id" : ObjectId("bbb"),
"username" : "user2",
"email" : "test2#test.com",
},
"comment" : "bbbb",
},
{
"user" : {
"_id" : ObjectId("aaa"),
"name" : "user1",
"email" : "test1#test.com",
},
"comment" : "cccc",
}
]
},
{
"user" : {
"_id" : ObjectId("bbb"),
"username" : "user2",
"email" : "test2#test.com",
},
"content" : "ddd",
"rep" : [ ]
}
]
Right now i managed to get my user info from the external id but i'm going crazy trying to get the user object inside the replies too, i tried to group after grouping but nothing to do, this is what i did:
db.pages.aggregate([
{
$match: { _id: ObjectId('5db599f3fffdee1c822269e0b3') }
},
{
$project: {
comments: 1,
}
},
{ $unwind: '$comments' },
{
$lookup:
{
from: 'users',
localField: 'comments.user_Id',
foreignField: '_id',
as: 'us'
}
},
{ $unwind: '$us' },
{
$group: {
_id: {
user: {
id: '$us._id',
name: '$us.username',
email: '$us.email',
},
comments: {
comment: '$comments.comment',
rep: '$comments.rep'
},
}
}
}
]).pretty()

Match documents with their inner array element variables in MongoDB

I can't understand how to compare a document variable to another document variable. My goal is to match all Authors who have at least one book written in their mothertongue (native language).
However, after unwinding the books array, My $match: { mothertongue: "$bookLang"}} doesn't return return anything, eventhough they're the same in the $project stage.
Can you help me without javascript?
This is my current query:
db.author.aggregate([
{
$unwind: "$books"
},
{
$project: {
books: true,
mothertongue: true,
bookLang: "$books.lang"
}
},
{
$match: { mothertongue: "$bookLang"}
}
])
And here is a sample of the dataset
{
"_id" : ObjectId("5aa7b34a338571a7470be0eb"),
"fname" : "Minna",
"lname" : "Canth",
"mothertongue" : "Finnish",
"birthdate" : ISODate("1844-03-19T00:00:00Z"),
"deathdate" : ISODate("1897-05-12T00:00:00Z"),
"books" : [
{
"title" : "Anna Liisa",
"lang" : "Finnish",
"language" : "finnish",
"edition" : 1,
"cover" : "Hard",
"year" : 1895,
"categorytags" : [
"Finland"
],
"publisher" : [
{
"name" : "Tammi",
"pubId" : ObjectId("5aa7b34a338571a7470be0e4")
}
]
},
{
"title" : "The Burglary and The House of Roinila",
"lang" : "English (UK)",
"translator" : ObjectId("5aa7b34a338571a7470be0ee"),
"cover" : "Soft",
"year" : 2010,
"categorytags" : [
"Finland"
],
"publisher" : [
{
"name" : "Jonathan Cape",
"pubId" : ObjectId("5aa7b34a338571a7470be0e7")
}
]
},
{
"title" : "Anna Liisa 2 ed.",
"lang" : "Finnish",
"language" : "finnish",
"edition" : 2,
"cover" : "hard",
"year" : 1958,
"categorytags" : [
"Finland"
],
"publisher" : [
{
"name" : "Otava",
"pubId" : ObjectId("5aa7b34a338571a7470be0e9")
}
]
}
]
}
End goal. note I'm not interested in formatting just yet, just the filtering
{
"Author" : "Charles Bukowski",
"BooksInMothertongue" : [
"Love Is a Dog from Hell"
]
}
{
"Author" : "Minna Canth",
"BooksInMothertongue" : [
"Anna Liisa",
"Anna Liisa 2 ed."
]
}
...
Try this
db.author.aggregate([{
$match: {
books: {
$ne: []
}
}
},
{
$project: {
books: {
$filter: {
input: "$books",
as: "book",
cond: {
$eq: ["$$book.lang", "$mothertongue"]
}
}
},
fname: 1
}
}, {
$unwind: "$books"
},
{
$group: {
_id: "$_id",
Author: {
$first: '$fname'
},
BooksInMothertongue: {
$push: "$books.title"
}
}
}
])

mongodb and aggregate multiple items

i have this code
> db.attributes.aggregate([{ $match: {"name.en-UK": "Size"} },{ $unwind : "$values" },{ $project: { "code" : "$values.code", "name" : "$values.name", "_id": "$values._id"} },{ $match: {"name.en-UK": "S"}}])
{
"result" : [
{
"_id" : ObjectId("53b2c644d901f244965f74e3"),
"code" : 0,
"name" : {
"en-UK" : "S"
}
},
{
"_id" : ObjectId("53b2c644d901f244965f74e4"),
"code" : 1,
"name" : {
"en-UK" : "S"
}
}
],
"ok" : 1
}
how do i change it so that it returns only "code" == 0 with "name" == "en-UK" : "S"
i tried this:
> db.attributes.aggregate([{ '$match': {"name.en-UK": "Size"} },
... { '$unwind' : "$values" },
... { '$project': { "code" : "$values.code", "valueId": "$values._id"} },
... { '$match': {
... $and: [
... {"name.en-UK": "S"},
... {"code": 0}
... ]
... }
... }])
{ "result" : [ ], "ok" : 1 }
but is not working!
and advice much appreciated
it seems i need to add what to select, so { $project: { "code" : "$values.code", "name" : "$values.name", "_id": "$values._id"} } must include the name
> db.attributes.aggregate([{ '$match': {"name.en-UK": "Size"} }, { '$unwind' : "$values" }, { $project: { "code" : "$values.code", "name" : "$values.name", "_id": "$values._id"} }, { '$match': { $and: [ {"name.en-UK": "S"}, {"code": 0} ] } }])
{
"result" : [
{
"_id" : ObjectId("53b2c644d901f244965f74e3"),
"code" : 0,
"name" : {
"en-UK" : "S"
}
}
],
"ok" : 1
}