mongodb query select multiple subset from a document - mongodb

{
_id: ObjectId("52ca2d45b80de42808000001"),
id: "1111139048239",
name: "Bruce Lim",
first_name: "Bruce",
last_name: "Lim",
friends: [
{
id: "1913681",
name: "John Sim",
icon: "https://fbcdn-profile-a.akamaihd.net/hprofile-ak-ash2/1117702_1913681_1171369396_q.jpg",
photos: [
{
src: "https://scontent-a.xx.fbcdn.net/hphotos-ash2/t1/230718_10150181312510976_7606555_n.jpg",
lat: "38.2289",
lng: "-85.7495"
},
{
src: "https://scontent-b.xx.fbcdn.net/hphotos-frc3/230480_10150181312620976_3864544_n.jpg",
lat: "38.2289",
lng: "-85.7495"
}
]
},
{
id: "31925743892",
name: "Mike Holloway",
icon: "https://fbcdn-profile-a.akamaihd.net/hprofile-ak-prn2/211634_31925743892_1471358831_q.jpg",
photos: [
{
src: "https://scontent-a.xx.fbcdn.net/hphotos-ash2/t1/230718_10150181312510976_7606555_n.jpg",
lat: "38.2289",
lng: "-85.7495"
},
{
src: "https://scontent-b.xx.fbcdn.net/hphotos-frc3/230480_10150181312620976_3864544_n.jpg",
lat: "38.2289",
lng: "-85.7495"
}
]
},
{
id: "1954048",
name: "Christiana Basset",
icon: "https://fbcdn-profile-a.akamaihd.net/hprofile-ak-prn2/211634_1954048_1471358831_q.jpg",
photos: [
{
src: "https://scontent-a.xx.fbcdn.net/hphotos-ash2/t1/230718_10150181312510976_7606555_n.jpg",
lat: "38.2289",
lng: "-85.7495"
},
{
src: "https://scontent-b.xx.fbcdn.net/hphotos-frc3/230480_10150181312620976_3864544_n.jpg",
lat: "38.2289",
lng: "-85.7495"
}
]
}
]
}
when I query a collection of these docs this with
db.mapping.find(
{"id":"1111139048239"},
{"friends":{
$elemMatch:{"id":"1913681"}
}}
)
I get one matching friend subset back.
{
"_id" : ObjectId("52ca2d45b80de42808000001"),
"friends" : [
{
"id" : "1913681",
"name" : "John Sim",
"icon" : "https://fbcdn-profile-a.akamaihd.net/hprofile-ak-ash2/1117702_1913681_1171369396_q.jpg",
"photos" : [
{
"src" : "https://scontent-a.xx.fbcdn.net/hphotos-ash2/t1/230718_10150181312510976_7606555_n.jpg",
"lat" : "38.2289",
"lng" : "-85.7495"
},
{
"src" : "https://scontent-b.xx.fbcdn.net/hphotos-frc3/230480_10150181312620976_3864544_n.jpg",
"lat" : "38.2289",
"lng" : "-85.7495"
}
]
}
]
}
How do I select multiple subsets.
db.mapping.find(
{"id":"1111139048239"},
{"friends":{
$elemMatch:{"id":"1913681", "id":"1954048"}
}}
)
db.mapping.find(
{"id":"1111139048239"},
{"friends":{
$elemMatch:{"id":"1913681"},
$elemMatch:{"id":"1954048"}
}}
)
gets me only the last match, which is 1954048 in this case. How do I get both - 1913681, 1954048?

The general syntax of find in mongodb is
db.collection.find(<criteria>,<projection>)
In your case,
criteria: id should be "1111139048239"
projection: listing friends who have id 1913681, 1954048
elemMatch can get only the first existence of the element and also when multiple values are given for same attribute it will display only the last executed elemMatch within the document.
I would suggest you to go with aggregation. It will help you to get the required output.
db.mapping.aggregate([
{$match:{id:"1111139048239"}}, // STEP 1
{$unwind:"$friends"}, // STEP 2
{$match:{"friends.id":{$in:["1913681","1954048"]}}} // STEP 3
])
Execution:
STEP 1: Selects the document with id "1111139048239"
STEP 2: Unwinds the friends array in the selected document and
create multiple documents as per the size of friends array.
In this case 3 documents.
STEP 3: Select documents which has a friends id "1913681", "1954048".
In this case 2 documents will be selected. Append values to array to get
more documents as output
{"friends.id":{$in:["1913681","1954048",etc]}

Related

Need desired outcome in populate block with customized result in mongodb

I have four collections person , other_details, occupation_details, bank_details as stated below
person =>[
{
id : 1,
name : 'john',
other_details : 1023
},
{
id : 2,
name : 'mark',
other_details : 99
}
]
other_details => [
{
id: 1023,
married: false,
occupation_details: 144,
bank_details : 10
},
{
id: 99,
married: true,
occupation_details: 45,
bank_details : 11
}
]
occupation_details => [
{
id: 144,
comp_name : 'oscorp inc.'
},
{
id: 45,
comp_name : 'tesla inc.'
}
]
bank_details => [
{
id: 10,
bank : 'Bank of canada'
},
{
id: 11,
bank : 'Peoples bank of canada'
}
]
I am using mongoose library with nodejs
// id = 1
person.findById(id).populate({
path: 'other_details',
populate: [
{
path: 'occupation_details'
},
{
path: 'bank_details'
}
]
})
So the result for the above query comes like below
=>
{
id : 1,
name : 'john',
other_details : {
id: 1023,
married: false,
occupation_details: {
id: 144,
comp_name : 'oscorp inc.'
},
bank_details : {
id: 10,
bank : 'Bank of canada'
}
}
}
But for some reasons I want the result like below
{
id : 1,
name : 'john',
other_details : {
id: 1023,
married: false,
occupation_details: {
id: 144,
comp_name : 'oscorp inc.'
},
bank_details : 10,
custom_bank_details : {
id: 10,
bank : 'Bank of canada'
}
}
}
The change I need is the following, The bank_details object should exist with id and the populated bank_details data from other collection should come in other object name as custom_bank_details
bank_details : 10,
custom_bank_details : {
id: 10,
bank : 'Bank of canada'
}
Update:
You could use virtual to populate bank details into a new field. Something like
OtherDetailsSchema.virtual('custom_bank_details', {
ref: 'BankDetails',
localField: 'bank_details',
foreignField: '_id',
justOne: true
});
Person.findById(id).populate({
path: 'other_details',
populate: [
{path: 'occupation_details'},
{path: 'custom_bank_details'}
]})
Original
I'm not a mongoose user so I'm not sure if it is possible to populate to new field name. If feasible you could quite easily achieve using aggregate with lookup.
Something like
person.aggregate([
{"$lookup":{
"from":"other_details",
"let":{"other_details":"$other_details"},
"pipeline":[
{"$match":{"$expr":{"$eq":["$id","$$other_details"]}}},
{"$lookup":{
"from":"occupation_details",
"localField":"occupation_details",
"foreignField":"id",
"as":"occupation_details"
}},
{"$unwind":"$occupation_details"},
{"$lookup":{
"from":"bank_details",
"localField":"bank_details",
"foreignField":"id",
"as":"custom_bank_details"
}},
{"$unwind":"$custom_bank_details"},
],
"as":"other_details"
}},
{"$unwind":"$other_details"}
])
Working example https://mongoplayground.net/p/5Kee0tBQmTd

MongoDB aggregation to check if an element is contained in an array

I am new to MongoDB, my native language is Spanish. So I'm not sure how to find what I need.
I will try to make myself understandable.
my schema:
let noticiaSchema = new Schema({
titulo: {
type: String,
required: [true, 'El titulo de la noticia es requerido']
},
cuerpo_noticia: {
type: String,
required: [true, 'El cuerpo de la noticia es necesario']
},
publicacion_noticia: {
type: Date,
default: new Date()
},
actualizacion_noticia: {
type: Date,
default: new Date()
},
likes: {
type: Array,
default: []
},
foto: {
type: String
},
dislikes: {
type: Array,
default: []
}
})
Sample Doc :
{
"_id" : ObjectId("5e81868faf9d6e084cc60cc8"),
"titulo" : "fdsfsfs",
"cuerpo_noticia" : "fdsfsfs",
"actualizacion_noticia" : ISODate("2020-03-30T05:41:35.144Z"),
"foto" : "14335143.png",
"publicacion_noticia" : ISODate("2020-03-30T05:41:12.997Z"),
"likes" : [
"5e7ffb641650a326dcc1e1c7"
],
"dislikes" : [],
"__v" : 0
}
I'm basically trying to query for an array of elements called likes, if an element is contained in this array I would want to return true / false on a new field.
Below is what I've tried, but it only returns the documents where the element exists in likes.
//5e7ffb641650a326dcc1e1c7 element to search
Noticia.aggregate([
{
$match: {
"likes": { "$in": ["5e7ffb641650a326dcc1e1c7"] },
//likes is the array field with elements to search
}
},
{
$project: {
titulo: "$titulo"
}
}
], (err, trans) => {
})
I want it to return all my docs but with a field that tells me if this element is contained or not.
Finally, I want to know if there is a way to return the result by creation date, that is .sort {_id: -1}
If you wanted to return all docs, you should not be using $match, try below aggregation :
db.collection.aggregate([
/** Add a field across all docs by checking on specific condition */
{
$addFields: {
elementExists: {
$cond: [{ $in: ["5e7ffb641650a326dcc1e1c7", "$likes"] }, true, false]
}
}
},
{
$sort: {
_id: -1 // Sort by '_id' field in descending order
}
}
]);
Test : mongoDB-Playground

Correct way to return from mongo to datatable

I'm using mongoose and returning documents from a collection to be displayed using datatables. I'm having some issues though. The client-side code is
var table = $('#dataTables-example').DataTable( {
"bProcessing" : true,
"bServerSide" : true,
"ajax" : {
"url" : "/mongo/get/datatable",
"dataSrc": ""
},
"columnDefs": [
{
"data": null,
"defaultContent": "<button id='removeProduct'>Remove</button>",
"targets": -1
}
],
"aoColumns" : [
{ "mData" : "name" },
{ "mData" : "price" },
{ "mData" : "category" },
{ "mData" : "description" },
{ "mData" : "image" },
{ "mData" : "promoted" },
{ "mData" : null}
]
});
Then this handled on the server-side using the following
db.once('open', function callback ()
{
debug('Connection has successfully opened');
productSchema = mongoose.Schema({
name: String,
price: String,
category: String,
description: String,
image: String,
promoted: Boolean
});
Product = mongoose.model('Product', productSchema, 'products');
});
exports.getDataForDataTable = function (request, response) {
Product.dataTable(request.query, function (err, data) {
debug(data);
response.send(data);
});
};
If I use the above code the datatable fails to display the documents, claiming no matching records found BUT it does correctly display the number of docs Showing 1 to 2 of 2 entries. If I change the server side code to response with data.data instead of data, the documents are correctly populated in the table BUT the number of records is no longer found, instead saying Showing 0 to 0 of 0 entries (filtered from NaN total entries)
exports.getDataForDataTable = function (request, response) {
Product.dataTable(request.query, function (err, data) {
debug(data);
response.send(data.data);
});
The actual data being returned when querying mongo is
{ draw: '1', recordsTotal: 2, recordsFiltered: 2, data: [ { _id: 5515274643e0bf403be58fd1, name: 'camera', price: '2500', category: 'electronics', description: 'lovely', image: 'some image', promoted: true }, { _id: 551541c2e710d65547c6db15, name: 'computer', price: '10000', category: 'electronics', description: 'nice', image: 'iamge', promoted: true } ] }
The third parameter in mongoose.model sets the collection name which is pluralized and lowercased automatically so it has no effect in this case.
Assuming your Product variable has been declared early on and global, try this:
products = mongoose.model('products', productSchema);
Product = require('mongoose').model('products');
Did you try to remove the dataSrc field in the DataTable configuration:
"ajax" : {
"url" : "/mongo/get/datatable",
},

Get desired output using mongoDB find

I have following documents in my collection
{
_id: ObjectId("5166fefbc482c31052000002"),
contact: [
{
home: 7735734105
},
{
office: 9583866301
}
],
user_name: "moti",
reportsTo: "bikram",
technology: [
{
name: ".net",
rating: 5
},
{
name: "JavaScript",
rating: 2
}
],
project: [
"Agile School",
"Draftmate"
],
type: "developer",
email: "motiranjan.pradhan#ajatus.co.in"
}
and
{
_id: ObjectId("5166fe90c482c31052000001"),
contact: [
{
home: 7735734103
},
{
office: 9583866901
}
],
user_name: "ganesh",
reportsTo: "bikram",
technology: [
{
name: "JavaScript",
rating: 3
},
{
name: ".net",
rating: 4
}
],
project: [
"SLBC",
"Draftmate"
],
type: "developer",
email: "ganesh.patra#ajatus.co.in"
}
Now I need to find the rating of the people who know only JavaScript.Currently if I run
db.users.find(
{
technology: {
$elemMatch: {
name: 'JavaScript'
}
}
},{user_name:1,'technology.name':1,_id:0}
).pretty()
I am getting names of all technologies(.net & JavaScript) and their corresponding ratings. I need only user names,and their respective ratings in JavaScript only. Do I need to use any aggregation techniques?
The positional operator '$' can be used to limit query results to the first matching element. To use in your query above you would change it to:
db.users.find( { technology: { $elemMatch: { name: 'JavaScript' } } },{user_name:1,'technology.$.name':1,_id:0} )

Pivoting data in MongoDB

I have an 'articles' collection, some sample data might look like this:
[
{body: 'Interesting news in Siberia and so on etc. etc. etc. and lolcats too',
author: 'John Doe',
tags: [{tid:24, name: "Siberia"},
{tid: 5231, name: "Lolcats"},]
},
{body: 'Something is going on in Siberia and France',
author: 'Jane Doe',
tags: [{tid:24, name: "Siberia"},
{tid: 6432, name: "France"},]
},
]
And my required ouput is a distinct list of tags:
[
{tid: 24, name: 'Siberia'},
{tid: 5231, name: 'Lolcats'},
{tid: 6432, name: 'France'},
]
I have been struggling with some mapReduce queries and distinct aggregation, but without result.
The simplest way to do this is:
db.articles.distinct("tags")
If you want to use aggregation framework (new in 2.2) it's a little longer:
db.articles.aggregate([{$unwind:"$tags"},
{$group:{_id:"$tags"}},
{$project:{tid:"$_id.tid",name:"$_id.name",_id:0}}
]).result
In mongo v2.2 you can do this with the aggregate function:
db.articles.aggregate([
{
// From each document, emit just the tags
$project: {
tags: 1
}
}, {
// Duplicate each document for each tags element it contains
$unwind: '$tags'
}, {
// Group the documents by the tag's tid and name
$group: {
_id: { tid: '$tags.tid', name: '$tags.name' }
}
}, {
// Reshape the document to exclude the _id and bring tid and name to the top level
$project: {
_id: 0,
tid: '$_id.tid',
name: '$_id.name'
}
}],
function (err, result) {
if (err) {
console.log('aggregation error: %s', err);
} else {
console.dir(result);
}
});
For your documents, this produces the following output:
[ { tid: 6432, name: 'France' },
{ tid: 5231, name: 'Lolcats' },
{ tid: 24, name: 'Siberia' } ]
db.articles.distinct("tags")
gives the following output:
[
{
"tid" : 24,
"name" : "Siberia"
},
{
"tid" : 5231,
"name" : "Lolcats"
},
{
"tid" : 6432,
"name" : "France"
}
]