Convert flat data into hierarchy in mongodb using Mongoose - node.js

I have created a model which has following properties:
id
children
parent
If the parent is set to null the document will be treated as top level parent. Each document may or may not have list of children which just contains the id's of other document.
db.categories.insert( { _id: "MongoDB", children: [],name: '', parent: 'Databases'} )
db.categories.insert( { _id: "dbm", children: [], parent: 'Databases' } )
db.categories.insert( { _id: "Databases", children: [ "MongoDB", "dbm" ], parent: 'Programming' } )
db.categories.insert( { _id: "Languages", children: [], parent: 'Programming' } )
db.categories.insert( { _id: "Programming", children: [ "Databases", "Languages" ],parent: 'books' } )
db.categories.insert( { _id: "Books", children: [ "Programming" ],parent: null } )
from the above model, I need to generate an output like this:
{
name: "books",
nodes: [
{
name: "Programming",
nodes: [
{
name: "Databases",
nodes: [
{
name: "MonogoDB"
},
{
name: "dbm"
}
]
},
{
name: "Languages"
}
]
}
]
}
So far I have tried this, but because of async nature of findByID(), I am not getting the desired result.Please suggest changes.
myDoc.find({
parent: null
}, function (err, topics) {
if (err) {
return handleError(res, err);
}
var docTree = [];
_.each(document, function(parentDoc) {
var doc = {};
doc.text = parentDoc.name;
doc.nodes = [];
_.each(parentDoc.children, function(child) {
doc.nodes.push(processChildren(child));
});
docTree.push(doc);
});
function processChildren(child) {
myDoc.findById(child, function (err,item) {
var doc = {};
doc.name = item._doc.name;
doc.nodes = [];
if (item._doc.children === null) {
return topic;
} else {
_.each(item._doc.children, function (chld) {
processChildren(chld);
});
}
return doc;
});
};

You can try calling the lean() method after the find() method since documents returned from queries with the lean option enabled are plain JavaScript objects. You can then manipulate those objects to get the hierarchical structure you want. The following may not give the exact desired result but will push you into the right direction:
Model.find({}).lean().exec(function (err, docs) {
if (err) {
return handleError(res, err);
}
var i, len, temp, top_level, id, parent, hierarchical, nodePendingObj, doc, _id, _parent, _children;
i = 0;
top_level = [];
temp = {};
obj = {};
nodePendingObj = {};
_id = '_id';
_parent = 'parent';
_children = 'children';
_.each(docs, function(doc) {
id = doc['_id'];
parent = doc[_parent];
temp[id] = doc;
if (parent === undefined || parent === null) {
obj["name"] = id;
obj["nodes"] = doc[_children];
top_level.push(obj);
}
else {
if (temp[parent] !== undefined) {
if (temp[parent][_children] === undefined) {
temp[parent][_children] = [];
}
var o = {};
o["name"] = id;
if (doc[_children].length !== 0) o["nodes"] = doc[_children];
temp[parent][_children].push(o);
} else {
if (nodePendingObj[parent] === undefined) {
nodePendingObj[parent] = [];
}
var o = {};
o["name"] = id;
if (doc[_children].length !== 0) o["nodes"] = doc[_children];
nodePendingObj[parent].push(o);
}
delete doc[_parent];
}
if (nodePendingObj[id] !== undefined) {
var len = nodePendingObj[id].length;
if (doc[_children] === undefined) {
doc[_children] = [];
}
while (len-- > 0) {
doc[_children].push(nodePendingObj[id].shift());
}
}
});
if (top_level.length === 1) {
hierarchical = top_level[0];
} else if (top_level.length > 1) {
hierarchical = {};
hierarchical[_children] = top_level;
} else {
hierarchical = {};
}
console.log(JSON.stringify(hierarchical));
});
Check the demo below.
var documents = [
{
"_id" : "MongoDB",
"children" : [],
"name" : "",
"parent" : "Databases"
},{
"_id" : "dbm",
"children" : [],
"parent" : "Databases"
},{
"_id" : "Databases",
"children" : [
"MongoDB",
"dbm"
],
"parent" : "Programming"
},{
"_id" : "Languages",
"children" : [],
"parent" : "Programming"
},{
"_id" : "Programming",
"children" : [
"Databases",
"Languages"
],
"parent" : "books"
},{
"_id" : "books",
"children" : [
"Programming"
],
"parent" : null
}
];
var i, len, temp, top_level, id, parent, hierarchical, nodePendingObj, doc, _id, _parent, _children;
i = 0;
top_level = [];
temp = {};
obj = {};
nodePendingObj = {};
_id = '_id';
_parent = 'parent';
_children = 'children';
_.each(documents, function(doc) {
id = doc['_id'];
parent = doc[_parent];
temp[id] = doc;
if (parent === undefined || parent === null) {
obj["name"] = id;
obj["nodes"] = doc[_children];
top_level.push(obj);
}
else {
if (temp[parent] !== undefined) {
if (temp[parent][_children] === undefined) {
temp[parent][_children] = [];
}
var o = {};
o["name"] = id;
if (doc[_children].length !== 0) o["nodes"] = doc[_children];
temp[parent][_children].push(o);
} else {
if (nodePendingObj[parent] === undefined) {
nodePendingObj[parent] = [];
}
var o = {};
o["name"] = id;
if (doc[_children].length !== 0) o["nodes"] = doc[_children];
nodePendingObj[parent].push(o);
}
delete doc[_parent];
}
if (nodePendingObj[id] !== undefined) {
var len = nodePendingObj[id].length;
if (doc[_children] === undefined) {
doc[_children] = [];
}
while (len-- > 0) {
doc[_children].push(nodePendingObj[id].shift());
}
}
});
if (top_level.length === 1) {
hierarchical = top_level[0];
} else if (top_level.length > 1) {
hierarchical = {};
hierarchical[_children] = top_level;
} else {
hierarchical = {};
}
pre.innerHTML = JSON.stringify(hierarchical, null, 4);
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.7.0/underscore-min.js"></script>
<pre id="pre"></pre>

Related

How to pass the timestamp in the query param of NodeJS (Express + Mongoose)

I have MongoDB command like this:
db.transaction.aggregate([
{
"$match":
{
$and:[{
'is_deleted': false,
'createdAt':{'$gte': ISODate('2020-09-01T00:00:00.000Z'),'$lte':ISODate('2020-12-15T00:00:00.000Z')},
'type': 'deposit'
}
]
}
},
{
$group: {
_id: null ,
depositRevenue: {$sum: "$amount"}
}
}
]).pretty();
And the output like this:
{
"_id" : null,
"depositRevenue" : 324
}
The query is fine, but when I pass in NodeJS it's not work.
Bellow is my code in ExpressJS and Mongoose.
let revenueData = await Transaction.aggregate([
{
$match: {
$and: [{objFind}]
}
},
{
$group: {
_id: null,
totalRevenue: {$sum: "$amount"}
}
}
]);
with objFind like this:
var startDate = new Date(req.query.startDate).toISOString();
var endDate = new Date(req.query.endDate).toISOString();
var objFind = {};
objFind["is_deleted"] = false;
if(startDate != undefined || endDate != undefined){
objFind["createdAt"] = {};
if(startDate != undefined){
objFind["createdAt"]["$gte"] = startDate;
}
if(endDate != undefined){
objFind["createdAt"]["$lte"] = endDate;
}
}
objFind['type'] = 'deposit';
Please take a look. Thanks you
You need to simplify your code,
create an object with required conditions
var objFind = {
is_deleted: false,
type: 'deposit'
};
check start date and end date available
if (req.query.startDate || req.query.endDate) {
objFind.createdAt = {};
if (req.query.startDate) objFind.createdAt.$gte = new Date(req.query.startDate);
if (req.query.endDate) objFind.createdAt.$lte = new Date(req.query.endDate);
}
by default root fields condition would be and condition, so no need to use $and
let revenueData = await Transaction.aggregate([
{ $match: objFind },
{
$group: {
_id: null,
totalRevenue: { $sum: "$amount" }
}
}
]);

how to update an object of an element in array in mongodb?

This is the structure i have, i want to update the nested array element if an object key matches for example - i want to match grnno :"10431000" and update the other keys of that object like vehicle_no,invoice_no etc.
{
"_id" : ObjectId("5f128b8aeb27bb63057e3887"),
"requirements" : [
{
"grns" : [
{
"invoice_no" : "123",
"vehicle_no" : "345",
"req_id" : "5f128c6deb27bb63057e388a",
"grnno" : "10431000"
},
{
"invoice_no" : "abc",
"vehicle_no" : "def",
"req_id" : "5f128c6deb27bb63057e388a",
"grnno" : "10431001"
}
]
}
]
}
I have tried this code
db.po_grn.update({
"requirements.grns.grnno":"10431001"
}, {
$set: {
"requirements.$.grns": {"invoice_no":"test",vehicle_no:"5455"}
}
})
But this is changing the structure i have like this
"requirements" : [
{
"grns" : {
"invoice_no" : "test",
"vehicle_no":"5455"
},
"req_id" : ObjectId("5f128b8aeb27bb63057e3886")
}
],
grns key should be array, and update should be of the particular object which matches the key "grnno". Please help me out. Thanks.
==Edit==
var grnno = req.body.grnno;
db.po_grn.find({
"requirements.grns.grnno":grnno
}).toArray(function(err, po_grn) {
console.log("po_grn",po_grn);
if (po_grn.length > 0) {
console.log("data.grn.grnno ", grnno);
var query = {
requirements: {
$elemMatch: {
"grns.grnno": grnno
}
}
};
var update = {
$set: {
'requirements.$[].grns.$[inner].invoice_no': data.invoice_no,
'requirements.$[].grns.$[inner].vehicle_no': data.vehicle_no,
}
};
var options = {
arrayFilters: [
{ "inner.grnno" : grnno }
]
};
db.po_grn.update(query, update, options
, function(er, grn) {
console.log("grn",grn,"er",er)
res.send({
status: 1,
message: "Grn updated successfully"
});
}
);
} else {
res.send({
status: 0,
message: "Grn not found "
});
}
})
Use a combination of $[] positional-all operator with array filters to update your inner nested document.
var query = {
requirements: {
$elemMatch: {
"grns.grnno": "10431001"
}
}
};
var update = {
$set: {
'requirements.$[].grns.$[inner].invoice_no': "test",
'requirements.$[].grns.$[inner].vehicle_no': "5455",
}
};
var options = {
arrayFilters: [
{ "inner.grnno" : "10431001" }
]
};
db.collection.update(query, update, options);
Update -
NodeJS native MongoDb driver code attached, which is working fine
const { MongoClient } = require('mongodb');
const url = "mongodb://localhost:27017/";
MongoClient.connect(url, function(err, db) {
if (err) {
throw err;
}
const dbo = db.db("test");
(async() => {
const query = {
requirements: {
$elemMatch: {
"grns.grnno": "10431001"
}
}
};
const update = {
$set: {
'requirements.$[].grns.$[inner].invoice_no': "test",
'requirements.$[].grns.$[inner].vehicle_no': "5455",
}
};
const options = {
arrayFilters: [
{ "inner.grnno" : "10431001" }
],
multi: true
};
try {
const updateResult = await dbo.collection("collection").update(query, update, options);
} catch (err) {
console.error(err);
}
db.close();
})();
});

Adding array of objects to mongodb issue

I'm having issues adding an array of objects to mongodb. The problem is related to when I get the posts ngOnInit() and there is an entry for _id like this before I start adding anything to inviteGroup
If I add this.inviteGroup = [] to get rid of _id first entry, then I can successfully add to the database my invite like in this image. Is there a way to not have that _id that's related to my mongoose schema?
but naturally the this.inviteGroup = [] makes it so I can have only one entry at a time since it erases everything on page load. How can I make that _id entry not there anymore so that when I do a .push() it doesn't cause a page reload because it throws off the .push(). I want to have multiple entries in db for each invite. Is it my mongoose model that's the issue? I appreciate any help!
mongoose schema definition
inviteGroup: {
bidderId: { type: String, lowercase: true, trim: true },
username: { type: String, lowercase: true, trim: true }
}
app.js
app.patch("/api/listings/:id", (req, res) => {
console.log("INVITE GRdddOUP IS");
console.log(req.body);
console.log(req.body[0].biddingUserId);
let invites;
if (req.body[0].biddingUserId) {
invites = req.body;
console.log("INVITE IS");
}
console.log(invites);
if (invites) {
console.log("INVITE GROUP IS");
console.log(req.params.id);
Post.findByIdAndUpdate(
{ _id: req.params.id },
{
inviteGroup: invites
},
function(err, docs) {
if (err) {
console.log(err);
res.json(err);
} else {
return true;
console.log(docs);
}
}
);
component.ts
import {
Component,
OnInit,
ViewChild,
OnDestroy,
AfterViewInit
} from "#angular/core";
import { Router } from "#angular/router";
import {
MatTableDataSource,
MatPaginator,
MatSort,
MatDialog
} from "#angular/material";
import { NgForm, FormControl } from "#angular/forms";
import { SubmitListingService } from "../submit-listing/submit-auction.service";
import { BidderInvite } from "./bidder-invite.model";
import { Observable, Subject } from "rxjs";
import { startWith, map, takeUntil } from "rxjs/operators";
import { Page } from "ngx-pagination/dist/pagination-controls.directive";
import { BidderInviteRetrieved } from "./bidder-invite-retrieved";
#Component({
selector: "app-private-auction-invite",
templateUrl: "./private-auction-invite.component.html",
styleUrls: ["./private-auction-invite.component.css"]
})
export class PrivateAuctionInviteComponent
implements OnInit, AfterViewInit, OnDestroy {
allMyPeopleAreInvited: boolean;
auctionId: string;
dataSource: MatTableDataSource<any> = new MatTableDataSource();
timeout: any = null;
posts: BidderInviteRetrieved[];
artistId: string;
bidderId: string;
inviteGroup: BidderInvite[] = [];
test: any[] = [];
value: string;
usernameFound: string;
userSearched: string;
invites: BidderInvite[] = [];
destroy = new Subject();
inviteName: string;
filteredOptions: Observable<string[]>;
myControl = new FormControl();
selectedValue: string;
url: string;
displayedColumnsInvites: string[] = ["User", "revokeInvite"];
options: string[] = [];
#ViewChild(MatSort, { static: false }) set sort(sort: MatSort) {
this.dataSource.sort = sort;
}
#ViewChild(MatPaginator, { static: false }) set paginator(
paginator: MatPaginator
) {
this.dataSource.paginator = paginator;
}
constructor(
private router: Router,
private submitListingService: SubmitListingService
) {}
ngOnInit() {
this.inviteGroup = [];
this.dataSource.paginator = this.paginator;
this.dataSource.sort = this.sort;
this.allMyPeopleAreInvited = false;
this.url = this.router.url;
const value = this.router.url.split("/");
this.auctionId = value[2];
this.artistId = value[3];
this.submitListingService
.getPrivateAuctionInviteList(this.auctionId)
.pipe(takeUntil(this.destroy))
.subscribe(res => {
this.inviteGroup = res.posts;
console.log("res");
console.log(res);
console.log(this.inviteGroup);
if (this.inviteGroup["_id"].length > 2) {
this.inviteGroup = [];
console.log(this.inviteGroup);
}
});
this.filteredOptions = this.myControl.valueChanges.pipe(
startWith(""),
map(value => this._filter(value))
);
}
ngAfterViewInit() {
this.dataSource.paginator = this.paginator;
this.dataSource.sort = this.sort;
this.dataSource = new MatTableDataSource(this.inviteGroup);
this.dataSource.data = this.inviteGroup;
}
sendInvite(form: NgForm) {
if (form.invalid) {
return;
}
let counter: number;
counter = 0;
console.log("USER " + this.value);
console.log("POST LEGNTH: " + this.posts.length);
for (let i = 0; i < this.posts.length; i++) {
counter = counter++;
console.log("post");
console.log(form.value.username);
let user = this.posts[i].username.trim().toLowerCase();
let enteredUser = form.value.username.trim().toLowerCase();
console.log("COUNTER LOOP NUMBER: " + counter);
if (enteredUser === user) {
this.bidderId = this.posts[i].id;
console.log(this.inviteGroup);
let invites = this.inviteGroup;
console.log("INVITE LENGTH = " + this.inviteGroup.length);
console.log(invites.indexOf);
this.inviteGroup.push({
biddingUserId: this.bidderId,
username: this.posts[i].username
});
console.log(this.inviteGroup);
console.log("invite group");
console.log(this.inviteGroup);
//this.posts = [];
this.dataSource.data = this.inviteGroup;
console.log("invite group");
}
}
console.log("BIDDER ID " + this.bidderId);
if (this.bidderId === null || this.bidderId === undefined) {
console.log("SOMETHING WENT WRONG");
}
console.log("made it to next section");
let invites = this.inviteGroup;
console.log("invites[0].username");
console.log("filtering....");
invites = invites.filter((obj, pos, arr) => {
return (
arr.map(mapObj => mapObj["bidderId"]).indexOf(obj["bidderId"]) === pos
);
});
console.log("invites");
console.log(invites);
this.submitListingService
.sendPrivateAuctionInvite(this.auctionId, invites)
.pipe(takeUntil(this.destroy))
.subscribe(res => {
console.log("res");
console.log(res);
});
}
private onKeySearch(event: any) {
console.log("EVENT IS ");
console.log(event);
clearTimeout(this.timeout);
var $this = this;
this.timeout = setTimeout(function() {
if (event.keyCode !== 13) {
$this.executeListing(event.target.value);
}
}, 1000);
}
private executeListing(bidderName: string) {
console.log("BIDDERNAME");
console.log(bidderName);
if (bidderName === "[Object object]") {
return;
}
if (bidderName.length < 4) {
return;
}
if (bidderName.length > 3) {
this.submitListingService
.getUserIdAutoComplete(bidderName)
.pipe(takeUntil(this.destroy))
.subscribe(res => {
console.log("res");
console.log(res);
this.posts = res.posts;
console.log(this.posts);
// this.artists = res.posts;
});
}
}
private _filter(value: string): string[] {
const filterValue = value.toLowerCase();
return this.options.filter(
option => option.toLowerCase().indexOf(filterValue) === 0
);
console.log("OPTION IS " + filterValue);
}
storeUserPrivaeAuctionInvite(user: Page) {
console.log("USER VALUE I S" + user);
}
ngOnDestroy() {
this.destroy.next();
this.destroy.complete();
}
}
angular service
sendPrivateAuctionInvite(id: string, inviteGroup1: BidderInvite[]) {
// console.log(inviteGroup1);
return this.http.patch(
`http://localhost:3000/api/listings/${id}/`,
inviteGroup1
);
}
BidderInvite model
export interface BidderInvite {
biddingUserId: string;
username: string;
}
Is your schema definition supposed to be:
inviteGroup: {
type: [inviteSchema]
default: undefined //if you want to unset []
}
invite: {
bidderId: { type: String, lowercase: true, trim: true },
username: { type: String, lowercase: true, trim: true }
}
(See https://mongoosejs.com/docs/schematypes.html#arrays)
Try caching to prevent a reload. (See https://github.com/isaacs/node-lru-cache) and How to stop MongoDB from reloading data every time I refresh a page?
Try projection to exclude _id in a query output with _id: 0. See https://docs.mongodb.com/v3.2/tutorial/project-fields-from-query-results/#return-all-but-the-excluded-field
Try this. Hope it will help you.
Post.findByIdAndUpdate(
{ _id: req.params.id },
{
inviteGroup: invites
},
{select: {_id: 0}}, // sets the document fields to return
function(err, docs) {
if (err) {
console.log(err);
res.json(err);
} else {
return true;
console.log(docs);
}
}
);
If I understood your question right, the problem is related to the lack of operator at the update method.
When you use something like:
Post.findByIdAndUpdate(
{ _id: req.params.id },
{
inviteGroup: invites // <-- Update statement
},
function(err, docs) {
//...
}
);
It will replace the full value of the inviteGroup field.
In order to add an item to an existent array on the database, you will need to use $push or $addToSet operator, along with $each operator.
The $push and $addToSet operators, only add/append one item per time, so the use of $each is necessary to interact with every item present on the invites array. In the following examples, I will include that because I believe is what you will need. But please, take the time to read the linked documentation of every operator so you can find more samples.
The $push operator appends a specified value to an array, making no extra verification if the value that is been added already exists on the field or not. As:
//document on mongodb, before the update
// { _id : "1", inviteGroup : [] }
//Invites from the request
// invites = [ { bidderId:"5e2350c7f88cfb331c4f67de", username:"artist1"} ];
//update method
Post.findByIdAndUpdate(
{ _id: req.params.id }, //req.params.id = "1"
{ $push : { inviteGroup: { $each : invites } } },
function(err, docs) {
//...
}
);
//document on mongodb, after the update
/*
{
_id : "1",
inviteGroup : [ { bidderId:"5e2350c7f88cfb331c4f67de", username:"artist1"} ]
}
*/
If you call the update method again with the same values:
Post.findByIdAndUpdate(
{ _id: req.params.id }, //req.params.id = "1"
{ $push : { inviteGroup: { $each : invites } } },
function(err, docs) { }
);
// the end document will be like:
/*
{
_id : "1",
inviteGroup : [
{ bidderId:"5e2350c7f88cfb331c4f67de", username:"artist1"},
{ bidderId:"5e2350c7f88cfb331c4f67de", username:"artist1"}
]
}
*/
In the same way, the $addToSet operator adds a value to an array unless the value is already present, in which case $addToSet does nothing to that array. Like:
//document on mongodb, before the update
// { _id : "1", inviteGroup : [] }
//Invites from the request
// invites = [ { bidderId:"5e2350c7f88cfb331c4f67de", username:"artist1"} ];
//update method
Post.findByIdAndUpdate(
{ _id: req.params.id }, //req.params.id = "1"
{ $addToSet : { inviteGroup: { $each : invites } } },
function(err, docs) {
//...
}
);
//document on mongodb, after the update
/*
{
_id : "1",
inviteGroup : [ { bidderId:"5e2350c7f88cfb331c4f67de", username:"artist1"} ]
}
*/
If you call the update method again with the same values:
Post.findByIdAndUpdate(
{ _id: req.params.id }, //req.params.id = "1"
{ $addToSet : { inviteGroup: { $each : invites } } },
function(err, docs) { }
);
//the end document will be the same because the same value was already on the list:
/*
{
_id : "1",
inviteGroup : [ { bidderId:"5e2350c7f88cfb331c4f67de", username:"artist1"} ]
}
*/
Well, I hope that was what you looking for. =]

Error: where: "raw query" has been removed, please use where ["raw query", [replacements]]

Unhandled rejection Error: where: "raw query" has been removed, please
use where ["raw query", [replacements]]
I encountered this error while rendering the following code. It's a dynamic where clause, generated by the value entered by user in the search field:
var queryWhere = {id: {$ne: null}};
if (req.query) {
if (req.query.gender && req.query.gender !== '') {
searchGender = "gender = '" + req.query.gender + "'";
} else if (req.query.gender && req.query.gender === '') {
searchGender = "gender IS NOT NULL ";
}
if (req.query.experience && req.query.experience !== '') {
searchExperience = "experience = '" + req.query.experience + "'";
} else if (req.query.gender && req.query.experience === '') {
searchExperience = "experience IS NOT NULL ";
}
queryWhere = {
$and: [
{$or: [
searchGender,
searchExperience]}
]
};
models.Users.findAll({
offset: numPerPage * 50,
limit: 50,
where: queryWhere,
include: [
{model: models.Users_Answers}
],
order: [
[models.Sequelize.col('id'), 'ASC'],
[models.Users_Answers, 'id', 'ASC']
]
}).then(function(answers) {
res.render('answers', {answers: answers, search: req.query.searchParam, moment: moment, pagesize: (numPerPage+1), total: totalCount / 50});
});
} else {
res.render('answers');
}
How can I resolve this?
Raw query should be array
queryWhere = {
$and: [
{$or: [
[searchGender],
[searchExperience]]}
]
};
Recommended way is using Sequelize's own operators
if (req.query.gender && req.query.gender !== '') {
searchGender = {'gender': req.query.gender};
} else if (req.query.gender && req.query.gender === '') {
searchGender = {'gender': {$not: null}};
}
if (req.query.experience && req.query.experience !== '') {
searchExperience = {'experience': req.query.experience};
} else if (req.query.gender && req.query.experience === '') {
searchExperience = {'experience': {$not: null}};
}
Recommended using Sequelize's own operators
var defered = Q.defer();
const offset = queryString.offset * queryString.limit;
const limit = queryString.limit;
var queryWhere = { class_id: { $ne: null }, section_id: { $ne: null } };
var searchClass = {};
var searchSection = {};
if (queryString) {
if (queryString.class && queryString.class !== "") {
searchClass = { class_id: { $eq: queryString.class } };
} else if (queryString.class && queryString.class === "") {
searchClass = { class_id: { $ne: null } };
}
if (queryString.section && queryString.section !== "") {
searchSection = { section_id: { $eq: queryString.section } };
} else if (queryString.section && queryString.section === "") {
searchSection = { section_id: { $ne: null } };
}
}
queryWhere = {
$and: [[searchClass], [searchSection]]
};
const schoolDB = require("../../db/models/tenant")(schema);
const Student = schoolDB.model("Student");
Student.findAll({
attributes: [
"id",
"first_name",
"last_name",
"profile_image_url",
"roll_number",
"emergency_contact_number"
],
offset: offset,
limit: limit,
where: queryWhere,
order: [["roll_number", "ASC"]]
})
.then(result => {
defered.resolve(result);
})
.catch(err => {
defered.reject(err);
});

Mongo + check multiple fields existing

I am working mongo with nodejs.
I have array list:
var checkFields = ["field1","field2","field3"];
I try to get the count of records having the array list fields and user field is equal to admin.
Sample data:
[
{
"checkFields": {
"field1": "00124b3a5c31",
"user": "admin"
}
},
{
"checkFields": {
"field2": "00124b3a5c31",
"user": "admin"
}
},
{
"checkFields": {
"field1": "00124b3a5c31",
"user": "regular"
}
}
]
Query:
db.collection_name.find(
{"checkFields.user" : "admin"}
{ "checkFields.field1": { $exists: true} }
)
Expected Result:
Result is to get rows of count of matching the field in array list(checkFields).
Building up an $or array for the list of field existence checks is the right approach, but assuming you're on a current node.js build you can simplify the query creation to:
var checkFieldsLists = checkFields.map(field => ({
['checkFields.' + field]: {$exists: true}
}));
var query = {
$or: checkFieldsLists,
'checkFields.user': 'admin'
}
This removes the superfluous $or for the "user is admin" check which lets you also remove the outer $and, so that the generated query is:
{ '$or':
[ { 'checkFields.field1': { '$exists': true } },
{ 'checkFields.field2': { '$exists': true } },
{ 'checkFields.field3': { '$exists': true } } ],
'checkFields.user': 'admin' }
I tried the following code. Its working but don't know its good solution and perfomance. Please anyone have better answer means please post it.
var checkFields = ["field1", "field2", "field3"];
var checkFieldsLists = [];
for ( i = 0; i < checkFields.length; i++) {
var jsObj = {};
jsObj['checkFields.' + checkFields[i]] = {};
jsObj['checkFields.' + checkFields[i]].$exists = true;
checkFieldsLists.push(jsObj);
}
var query = {
"$and" : [{
"$or" : checkFieldsLists
}, {
"$or" : [{
"checkFields.user" : "admin"
}]
}]
};
console.log(JSON.stringify(query));
//console log will return
/*
{"$and":[{
"$or" : [{
"checkFields.field1" : {
"$exists" : true
}
}, {
"checkFields.field2" : {
"$exists" : true
}
}, {
"checkFields.field3" : {
"$exists" : true
}
}]
}, {
"$or" : [{
"checkFields.user" : "admin"
}]
}]
}
*/
collection.find(query);
Here is the solution using aggregate query.
var Db = require('mongodb').Db, Server = require('mongodb').Server, assert = require('assert');
var db = new Db('localhost', new Server('localhost', 27017));
var checkFields = ["field1", "field2", "field3"];
var checkFieldsLists = [];
for (var i = 0; i < checkFields.length; i++) {
var jsObj = {};
jsObj['checkFields.' + checkFields[i]] = {};
jsObj['checkFields.' + checkFields[i]].$exists = true;
checkFieldsLists.push(jsObj);
}
var query = {
"$and" : [{
"$or" : checkFieldsLists
}, {
"$or" : [{
"checkFields.user" : "admin"
}]
}]
};
var matchQuery = {
"$match" : {
"checkFields.user" : "admin",
"$or" : checkFieldsLists
}
};
var groupQuery = {
$group : {
_id : null,
count : {
$sum : 1
}
}
};
var aggregateCheckFields = function(db, callback) {
console.log("Match query is ====>" + JSON.stringify(matchQuery));
console.log("Group query is ====>" + JSON.stringify(matchQuery));
db.collection('checkfields').aggregate([ matchQuery, groupQuery ]).toArray(
function(err, result) {
assert.equal(err, null);
console.log("Result is ===>" + JSON.stringify(result));
if (result.length > 0) {
console.log("Count is ===>" + result[0].count);
}
callback(result);
});
};
db.open(function(err, db) {
aggregateCheckFields(db, function() {
db.close();
});
});
Output:-
Result is ===>[{"_id":null,"count":3}]
Count is ===>3

Resources