MongoDB query on join - node.js

This is an abstract example, but if someone can help me to solve it, I will be eternally grateful.
I am trying to do a search based on a field in a related record. In this example we have a hierarchy of subjects, that are nested using the parent_subject field. This is the Mongoose definition I have used:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var subjectSchema = new Schema({
url: { type: String, required: true, unique: true },
title: { type: String, required: true, unique: true },
parent_subject: this
});
module.exports = mongoose.model('Subject', subjectSchema);
Some records in my MongoDB:
{
"_id" : ObjectId("5a3857f1afeb498c9533aef1"),
"title" : "Sport",
"url" : "sport",
"__v" : 0
},
{
"_id" : ObjectId("5a3d9a409c0973976f7889c6"),
"title" : "Cycling",
"__v" : 0,
"parent_subject" : ObjectId("5a3857f1afeb498c9533aef1"),
"url" : "cycling"
},
{
"_id" : ObjectId("5a3d9a7e9c0973976f788b48"),
"title" : "Mountain Biking",
"__v" : 0,
"parent_subject" : ObjectId("5a3d9a409c0973976f7889c6"),
"url" : "mountain biking"
}
I would like to know how I can query this data based on a field in the parent record. For example, I would like to find all the subjects that have a parent subject with the title "Sport". In SQL, I would do the following query:
SELECT *
FROM subject
INNER JOIN subject AS parentSubject
ON subject.parent_subject = parentSubject.id
WHERE parentSubject.title = "Sport";
How would I achieve this with a MongoDB?

You can use mongodb aggregate pipeline and lookup for this
`Subject.aggregate([
{
$lookup:
{
from: "parentSubject",
localField: "parent_subject",
foreignField: "id"
as: "parentSubject"
}
},
{$unwind:"$parentSubject"},
{$match:{"$parentSubject.title":"Sport"}}
])`
For more reference you can see https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/index.html

The point of mongoDB is that it is NOSQl, so that you also cannot use joins.
Your best option would be to first acquire the parentSubject and it's id.
And afterwards go for the subjects related to that id.
Since your data already has a subject.parent_subject it should be pretty easy to do.

Related

console.logging data from MongoDB

I am still trying to comprehend Mongoose/Mongo
Now in terminal When I do something like this in terminal
use library
show collections
db.authors.find().pretty()
I get something like this in logged
{
"_id" : ObjectId("5bc8704f3a9828d5513505a2"),
"name" : "Aman",
"age" : "21",
"__v" : 0
}
{
"_id" : ObjectId("5bc870553a9828d5513505a3"),
"name" : "Rohit",
"age" : "20",
"__v" : 0
}
{
"_id" : ObjectId("5bc8704f3a9828d5513505a7"),
"name" : "Aman",
"age" : "21",
"__v" : 0
}
{
"_id" : ObjectId("5bc870553a9828d5513505a5"),
"name" : "Rohit",
"age" : "20",
"__v" : 0
}
Now, I want to have same data in my NodeJs i.e say, I want to find wherever the name is Rohit and want to link it with with some other db or schema.
how can I get the same output which I just obtained by running the above given command in terminal window of mongo in NodeJS
Obviously doing something like this console.log(db.authors.find()) won't work, so how can I get that?
This is how I usually write my database queries.
First create a schema that your model will follow. Don't worry this schema is flexible and you can change it at any time without affecting old data.
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var authorSchema = new Schema({
name: {
type: String,
required: true
},
age: {
type: Number,
default: 20
}
});
Next create a model from your schema.
var authorModel = mongoose.model('authorModel', authorSchema);
Lastly query your model and get the value you need
authorModel.find(
{
name: 'Rohit'
},
function (err, result) {
console.log(result)
});
I put my schema and controller on separate files. How you organise your code structure is up to you.
I pretty much followed this blog when I first learnt to build APIs on NodeJS. You might find this useful as well!

Get Reference in a child object, where parent has array of children with mongoose in NodeJS

I'm working with mongoose in NodeJS and I have two main models, transactions and invoices, both could be inside a balance model, the models are defined like this:
const BalanceSchema = new Schema({
transactions: [{ type: Schema.Types.ObjectId, ref: 'Transaction'}],
invoices: [{ type: Schema.Types.ObjectId, ref: 'Invoice'}],
references: [filesDescription],
});
const TransactionSchema = new Schema({
paybook: PaybookTransaction,
});
I want to get all the transactions and know if they have a reference with Balance, The result that I except after make the consult:
[
{
_id: 'transaction_id'
paybook: {}
balance: 'balance_id'
}
]
Is this possible, or Do I need do this with js?
UPDATE 1
Example Data:
I Have Table Transaction with data:
{
"_id" : ObjectId("5988b1f62442837f5b8b911d"),
"updatedAt" : ISODate("2017-08-09T19:47:14.657Z"),
"createdAt" : ISODate("2017-08-09T19:47:14.657Z"),
"paybook" : {
"id_account" : "59763bc0244283dd798b4a57",
"id_account_type" : "520d3aa93b8e778e0d000000",
"id_credential" : "597a2fda0c212aa7648b58d3",
},
}
And the table Balance with data like:
{
"_id" : ObjectId("598ba359ff32954ffb22da5a"),
"updatedAt" : ISODate("2017-08-10T01:43:31.668Z"),
"createdAt" : ISODate("2017-08-10T00:05:45.491Z"),
"references" : [],
"invoices" : [],
"transactions" : [
ObjectId("5988b1f62442837f5b8b911d")
]
}
I want to get all the Transactions that have a reference in Balance. Like I show you in the json reference.

dont want return children on get list mongodb

I Have A Category Schema With Children Property That Use ObjectId with ref:Category Like This :
const category = new Schema({
title : {type : String},
children : [{type: Schema.Types.ObjectId, ref: 'Category'}]
},{collection : 'Category'});
And I Create Chilrend And Parent And Every Thing is OK...
my Problem is when i get list of category children too returend seprate with parent :
const children = new category({
title : "children",
childrent : []
})
const parent = new category({
title : "parent",
childrent : [children._id]
})
====================================================================
category.find({},(err,result)=>{
console.log("result",result)
})
//output
[
{
_id : "asdasdsadasd", ///my problem is here i dont want return childs that have parent !
title : "children",
childrent : []
},
{
_id : "tttttttttttt",
title : "parent",
childrent : [{
_id : "asdasdsadasd",
title : "children",
childrent : []
}]
}
]
You are using the same schema, so both parents and children are inserted to the same collection - and are thus being returned by your 'find all' query. You should separate your parent categories and child categories in different schemas IMHO.
Another approach would be to set a type:
categoryType: {type: String, enum: ['parent','child'], required: true}
and use that to differentiate your categories, then you would query:
category.find({categoryType: 'child'}) or 'parent' to find only parents or children.!
You have to exclude the field when you are doing query to find category. Try following:
category.find({},{children : 0},(err,result)=>{
console.log("result",result)
});
Refer project-doc for more info.

Mongoose MongoDB: updating objects in a nested array

I've got the following schema
var UserSchema = new Schema({
emp_no: Number,
skills: [{
skill: {
type: Schema.Types.ObjectId,
ref: 'Skill'
},
startDate: {type: Date},
}]
});
I'm then trying to update the startDate of one particular skill. I've tried several differents ways, one of them being:
User.findOne({emp_no: req.body.emp_no}, function (err, user) {
user.update( {'skills._id': 123}, {'$set': {
'skills.$.startDate': req.body.startDate
}}
}
This particular code gives: err: 'cannot use the part (skills of skills._id) to traverse the element
The actual object looks like
{
"_id" : ObjectId("5469753de27a7c082203fd0a"),
"emp_no" : 123,
"skills" : [
{
"skill" : ObjectId("547d5f3021d99d302079446d"),
"startDate" : ISODate("2014-12-02T06:43:27.763Z")
"_id" : ObjectId("547d5f8f21d99d3020794472")
}
],
"__v" : 108
}
Any ideas what I'm doing wrong?
When you call update on a model instance like you're doing here, the first parameter is the update operation to apply to that document, as the document to update is already uniquely identified by its _id.
Instead, use Model.update to do this all in one operation:
User.update(
{emp_no: req.body.emp_no, 'skills._id': 123},
{'$set': {
'skills.$.startDate': req.body.startDate
}},
function(err, numAffected) {...}
);

NodeJS + Mongoose + Backbone.Marionette nested Collection templating

all
I'm developing an application that store my multimedia catalog, I've JSON collection like this :
{ "_id" : ObjectId( "5142f55394474e2aac000001" ),
"contentType" : "binary/octet-stream",
"length" : 2732376,
"chunkSize" : 262144,
"uploadDate" : Date( 1363342677601 ),
"metadata" : {
"TIT2" : "Chase The Blues (Cameron McVey Remix)",
"TPE1" : "Terranova",
"TRCK" : "1/13",
"TALB" : "!K7",
"TPOS" : "1/1",
"TDRC" : "2000-06",
"TCON" : [
"Electronica",
"Trip-Hop" ],
"COMM" : [
"Chillout",
"Love",
"German",
"Berlin",
"2000s",
"Female Vocalists",
"Male Vocalists" ],
"TMED" : "CD",
"TMOO" : "Chill",
"TDOR" : "2000-06",
"TSO2" : "Various Artists",
"TPE2" : "Various Artists",
"TCMP" : "1",
"TSOP" : "Terranova",
"TIT1" : "Electronica",
"TPUB" : "Sinedín Music",
"TLAN" : "eng",
"TYER" : [
"2000" ],
},
"md5" : "617401af615ac0c6cb1dee9a3f1b99e6",
"origin" : "Chase The Blues.109eb5ab5105a1caa505a26657f7f9a8.mp3",
"evolution" : null,
"insertDate" : Date( 1336662308000 ),
"tagSource" : "MusicBrainz",
"mediainfo" :
{ "Format" : "MPEG Audio",
"Format version" : "Version 1",
"Format profile" : "Layer 3",
"Duration" : "3mn 47s",
"Bit rate mode" : "Constant",
"Bit rate" : "96.0 Kbps",
"Channel(s)" : "1 channel",
"Sampling rate" : "44.1 KHz",
"Compression mode" : "Lossy",
"Stream size" : "2.60 MiB (100%)",
"Language" : "English"
}
}
so, as you can see, there are "metadata" and "mediainfo" array in the document
in the models.js , in the client side, I've rewrite the model parse function like this
var Audio_Model = Backbone.Model.extend({
idAttribute: "_id",
url: 'AudioModel',
urlRoot: 'AudioModel' ,
parse: function(response) {
// Check if response includes some nested collection data...
if (_.has(response, 'metadata')){
// Check if this model has a property called metadata
if (!_.has(this, 'metadata')) { // It does not...
// So instantiate a collection and pass in raw data
this.metadata = new Audio_Collection_Metadata(response.metadata);
} else {
// It does, so just reset the collection
this.metadata.reset(response.metadata);
}
delete response.metadata;
}
// Check if response includes some nested collection data...
if (_.has(response, 'mediainfo')){
// Check if this model has a property called mediainfo
if (!_.has(this, 'mediainfo')) { // It does not...
// So instantiate a collection and pass in raw data
this.mediainfo = new Audio_Collection_Mediainfo(response.mediainfo);
} else {
// It does, so just reset the collection
this.mediainfo.reset(response.mediainfo);
}
delete response.mediainfo;
}
return response;
}
});
so I've created two separate collection of 'metadata' and 'mediainfo'
the problem that I've is how to render 'metadata' and 'mediainfo' in html template because in 'mediainfo' and 'metadata' collection the key, values are not fixed and in 'metadata' some keys are array of values and the number of item in the array are not fixed
I've created backbone.marionette.itemview and compositeview for these two collections but I don't know how to render
Plase, someone have a solutions ?
Best Regards
finally I've fixed the problem myself with a data normalization, this is the new mongoose schema adopted :
var TagSchema = new mongoose.Schema({
value : {type : String, default: '', required: true}
});
var MetadataSchema = new mongoose.Schema({
name : {type: String, default: '', required : true},
values: [TagSchema]
});
var MediainfoSchema = new mongoose.Schema({
name : {type: String, default: ''},
value: {type: String, default: ''}
});
var StreamSchema = new mongoose.Schema({
_id: mongoose.Schema.ObjectId,
TIT2: {type : String, default: '', required: true},
metadata: [MetadataSchema],
mediainfo:[MediainfoSchema]
});
so that with sequence of CollectionView and CompositeView I can browse the entire model
Hope this can help someone

Resources