MongoDB Aggregate Listing by Arrays Value - mongodb

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"
]
}
]

Related

Partition data around a match query during aggregation

What I have been trying to get my head around is to perform some kind of partitioning(split by predicate) in a mongo query. My current query looks like:
db.posts.aggregate([
{"$match": { $and:[ {$or:[{"toggled":false},{"toggled":true, "status":"INACTIVE"}]} , {"updatedAt":{$gte:1549786260000}} ] }},
{"$unwind" :"$interests"},
{"$group" : {"_id": {"iid": "$interests", "pid":"$publisher"}, "count": {"$sum" : 1}}},
{"$project":{ _id: 0, "iid": "$_id.iid", "pid": "$_id.pid", "count": 1 }}
])
This results in the following output:
{
"count" : 3.0,
"iid" : "INT456",
"pid" : "P789"
}
{
"count" : 2.0,
"iid" : "INT789",
"pid" : "P789"
}
{
"count" : 1.0,
"iid" : "INT123",
"pid" : "P789"
}
{
"count" : 1.0,
"iid" : "INT123",
"pid" : "P123"
}
All good so far, but then I had realized that for the documents that match the specific filter {"toggled":true, "status":"INACTIVE"}, I would rather decrement the count (-1). (considering the eventual value can be negative as well.)
Is there a way to somehow partition the data after match to make sure different grouping operations are performed for both the collection of documents?
Something that sounds similar to what I am looking for is
$mergeObjects, or maybe $reduce, but not much that I can relate from the documentation examples.
Note: I can sense, one straightforward way to deal with this would be to perform two queries, but I am looking for a single query to perform the operation.
Sample documents for the above output would be:
/* 1 */
{
"_id" : ObjectId("5d1f7******"),
"id" : "CON123",
"title" : "Game",
"content" : {},
"status" : "ACTIVE",
"toggle":false,
"publisher" : "P789",
"interests" : [
"INT456"
],
"updatedAt" : NumberLong(1582078628264)
}
/* 2 */
{
"_id" : ObjectId("5d1f8******"),
"id" : "CON456",
"title" : "Home",
"content" : {},
"status" : "INACTIVE",
"toggle":true,
"publisher" : "P789",
"interests" : [
"INT456",
"INT789"
],
"updatedAt" : NumberLong(1582078628264)
}
/* 3 */
{
"_id" : ObjectId("5d0e9******"),
"id" : "CON654",
"title" : "School",
"content" : {},
"status" : "ACTIVE",
"toggle":false,
"publisher" : "P789",
"interests" : [
"INT123",
"INT456",
"INT789"
],
"updatedAt" : NumberLong(1582078628264)
}
/* 4 */
{
"_id" : ObjectId("5d207*******"),
"id" : "CON789",
"title":"Stack",
"content" : { },
"status" : "ACTIVE",
"toggle":false,
"publisher" : "P123",
"interests" : [
"INT123"
],
"updatedAt" : NumberLong(1582078628264)
}
What I am looking forward to as a result though is
{
"count" : 1.0, (2-1)
"iid" : "INT456",
"pid" : "P789"
}
{
"count" : 0.0, (1-1)
"iid" : "INT789",
"pid" : "P789"
}
{
"count" : 1.0,
"iid" : "INT123",
"pid" : "P789"
}
{
"count" : 1.0,
"iid" : "INT123",
"pid" : "P123"
}
This aggregation gives the desired result.
db.posts.aggregate( [
{ $match: { updatedAt: { $gte: 1549786260000 } } },
{ $facet: {
FALSE: [
{ $match: { toggle: false } },
{ $unwind : "$interests" },
{ $group : { _id : { iid: "$interests", pid: "$publisher" }, count: { $sum : 1 } } },
],
TRUE: [
{ $match: { toggle: true, status: "INACTIVE" } },
{ $unwind : "$interests" },
{ $group : { _id : { iid: "$interests", pid: "$publisher" }, count: { $sum : -1 } } },
]
} },
{ $project: { result: { $concatArrays: [ "$FALSE", "$TRUE" ] } } },
{ $unwind: "$result" },
{ $replaceRoot: { newRoot: "$result" } },
{ $group : { _id : "$_id", count: { $sum : "$count" } } },
{ $project:{ _id: 0, iid: "$_id.iid", pid: "$_id.pid", count: 1 } }
] )
[ EDIT ADD ]
The output from the query using the input data from the question post:
{ "count" : 1, "iid" : "INT123", "pid" : "P789" }
{ "count" : 1, "iid" : "INT123", "pid" : "P123" }
{ "count" : 0, "iid" : "INT789", "pid" : "P789" }
{ "count" : 1, "iid" : "INT456", "pid" : "P789" }
[ EDIT ADD 2 ]
This query gets the same result with different approach (code):
db.posts.aggregate( [
{
$match: { updatedAt: { $gte: 1549786260000 } }
},
{
$unwind : "$interests"
},
{
$group : {
_id : {
iid: "$interests",
pid: "$publisher"
},
count: {
$sum: {
$switch: {
branches: [
{ case: { $eq: [ "$toggle", false ] },
then: 1 },
{ case: { $and: [ { $eq: [ "$toggle", true] }, { $eq: [ "$status", "INACTIVE" ] } ] },
then: -1 }
]
}
}
}
}
},
{
$project:{
_id: 0,
iid: "$_id.iid",
pid: "$_id.pid",
count: 1
}
}
] )
[ EDIT ADD 3 ]
NOTE:
The facet query runs the two facets (TRUE and FALSE) on the same set of documents; it is like two queries running in parallel. But, there is some duplication of code as well as additional stages for shaping the documents down the pipeline to get the desired output.
The second query avoids the code duplication, and there are much lesser stages in the aggregation pipeline. This will make difference when the input dataset has a large number of documents to process - in terms of performance. In general, lesser stages means lesser iterations of the documents (as a stage has to scan the documents which are output from the previous stage).

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

{
"_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.

Return Multiple values from distincts group filters - Mongodb

I want to return two types of group results in one query, but it doen't work.
If you have one idea please share with me.
I have this collection:
[
{
_id: "ABC00001",
results: [
{
_id: "C0001",
status: {
_id: "stj001",
name: "status1"
},
test:{
profession: [
{
"level" : "Pregrado",
"institution" : {
"_id" : "inst006",
"name" : "University 3"
}
},
{
"level" : "Pregrado",
"institution" : {
"_id" : "inst002",
"name" : "University 2"
}
}
]
}
},
{
_id: "C0002",
status: {
_id: "stj002",
name: "status1"
},
test:{
profession: [
{
"level" : "Pregrado",
"institution" : {
"_id" : "inst006",
"name" : "University 3"
}
}
]
}
},
]
},
{
_id: "ABC00002",
results: [
{
_id: "C0001",
status: {
_id: "stj002",
name: "status1"
},
test:{
profession: [
{
"level" : "Pregrado",
"institution" : {
"_id" : "inst002",
"name" : "University 2"
}
},
{
"level" : "Pregrado",
"institution" : {
"_id" : "inst006",
"name" : "University 3"
}
}
]
}
},
{
_id: "C0002",
status: {
_id: "stj003",
name: "status1"
},
test:{
profession: [
{
"level" : "Pregrado",
"institution" : {
"_id" : "inst006",
"name" : "University 3"
}
}
]
}
},
]
},
]
I wanna return only disctincts institutions and status in one group query like this:
institution: [
{"_id" : "inst006","name" : "University 3"},
{"_id" : "inst002", "name" : "University 2"},
]
status: [
{_id: "stj002", name: "status1"},
{_id: "stj003", name: "status1"}
]
I tried with this but doesnt work:
db.collection.aggregate(
[
{'$unwind' : '$results'},
{'$group' : { '_id' : { 'status' : {'_id'=>'$results.status._id', 'name' : '$results.status.name'}, 'count' : { '$sum' : 1 } } } },
{'$group' : { '_id' : { 'institution' : {'_id' :'$results.test.profession.institution._id', 'name':'$results.test.profession.institution.name'},
'count' : { '$sum' 1 } } }
]
)
If I work with two distincts querys with their own group it works but I need only one query returns all values, maybe I'll add more groups
If I understand your requirements correctly then this might work.
db.CollectionName.aggregate([
{"$group" : {_id : {statusid:"$results.status._id", statusname: "$results.status.name", instid:"$results.test.profession.institution._id", instname: "$results.test.profession.institution.name"}},
},
{ "$project": {
"results.status._id": 1,
"results.status.name": 1,
"results.test.profession.institution._id": 1,
"results.test.profession.institution.name": 1
}
},
{ "$sort": { "_id.statusid": 1 }},
])
Note: The JSON data needs to be formatted to make this work.
I found the solution:
db.collection.aggregate([
{'$match' : {'_id' : "CA0001"] ],
{'$unwind' : '$results'],
{'$unwind' : '$results.test'],
{'$unwind' : '$results.test.profession'],
{'$unwind' : '$results.test.skills'],
{'$group' : {
'_id' : {
"status" : {'_id':'$results.status.id', 'name':'$results.status.name'},
"institution" : {'_id':'$results.test.profession.institution._id', 'name':'$results.test.profession.institution.name'},
'profession' => {'_id':'$results.test.profession.education._id', 'description':'$results.test.profession.education.description'},
'availability' : {'_id':'$results.availability._id', 'name':'$results.availability.name'},
'skills' : {'_id':'$results.test.skills._id', 'description':'$results.test.skills.description'}
},
}
},
{'$project' : { 'status' : 1, 'institution': 1, 'profession': 1, 'skills': 1, 'availability': 1} },
{
'$group' : {
'_id' : null,
'status' : {
'$addToSet' : '$_id.status'
},
'institution' : {
'$addToSet' : '$_id.institution'
},
'profession' : {
'$addToSet' : '$_id.profession'
},
'availability' : {
'$addToSet' : '$_id.availability'
},
'skills' : {
'$addToSet' : '$_id.skills'
}
}
}
]);
it returns:
{
"_id": null,
"status": [
{
"_id": 1,
"name": "evaluado"
}
],
"institution": [
{
"_id": "inst078",
"name": "Universidad Privada del Norte"
},
{
"_id": "inst079",
"name": "Universidad San Ignacio de Loyola"
}
],
"profession": [
{
"_id": "fa059",
"description": "Estadística"
},
{
"_id": "fa063",
"description": "Ingeniería Informática"
},
"availability": [
{
"_id": "wo001",
"name": "Inmediata"
}
],
"skills": [
{
"_id": "sk366",
"description": "Pentaho"
}
]
}
All results are distincts.
I reduced time from 550ms to 43ms in programming language, comparing doing with database query and code programming using collections.

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 - Getting #DBRef values from list as separate objects

This is the current object
{
"chargeCodeIds" : [
1,
2
],
"customChargeCode" : [
{
"_id" : 1,
"chargeCodeType" : "Department",
"name" : "IT",
"isBillable" : true,
},
{
"_id" : 2,
"chargeCodeType" : "Task"
"name" : "Development",
"isBillable" : true
}
]
We have the query that searched the charge codes from their collection say "customChargeCode" and listed them in an object "customChargeCode" as array of objects. BUT I don't want it an in array.
Here is the current query:
db.authorizeChargeAssociation.aggregate([
{
$project: {
chargeCodeIds: {
$map: {
input: {
$map: {
input:"$chargeCodes",
in: {
$arrayElemAt: [{$objectToArray: "$$this"}, 1]
},
}
},
in: "$$this.v"
}
},
}
},
{
$lookup: {
from: "customChargeCode",
localField:"chargeCodeIds",
foreignField:"_id",
as:"customChargeCode"
}
},
{$unwind: {path: "$customChargeCodes", preserveNullAndEmptyArrays: true} },
],
{ collation: { locale: "en_US", numericOrdering: true}},
{ allowDiskUse: true}
)
What I want is to get a separate object of each "chargeCodeIds" from the "customChargeCode" collection and name them the field as "chargeCodeType" value.
Expected Result be like:
{
"chargeCodeIds" : [
1,
2
],
"Department" : {
"_id" : 1,
"chargeCodeType" : "Department",
"name" : "IT",
"isBillable" : true,
},
"Task" : {
"_id" : 2,
"chargeCodeType" : "Task"
"name" : "Development",
"isBillable" : true
}