How to get all fields of a MongoDB document - javascript

In application I write down in a collection of users documents with separate users. Each document is an object in which there is a name of the user and his category. Categories are an object. How can I take all recorded categories. I try to take them through find (), but there I need to specify the key-value. And I just need to specify the category field and take all the key-values ​​there. How can I get all categories of an individual user?
I need to find them by key.
mongoClient.connect(function (err, client) {
const db = client.db("expensesdb");
const collection = db.collection("users");
if (err) return console.log(err);
collection
.find({ name: "Bob"})
.toArray(function (err, results) {
console.log(results);
client.close();
});
});

Sample data-set
{ "_id" : ObjectId("63050125848392dcf6f3ebba"), "name" : "max", "category" : { "cat1" : "max1", "cat2" : "max2" } }
{ "_id" : ObjectId("63050132848392dcf6f3ebbb"), "name" : "box", "category" : { "cat1" : "box1", "cat2" : "box2" } }
Try this aggregation pipeline :
db.users.aggregate([
{ $match: { "name": "max" } },{$project : {"category":1}}
]);
db.users.aggregate([
{ $match: { "category.cat1": "cat1" } },{$project : {"category":1}}
]);
Output for both:
{ "_id" : ObjectId("630500ec848392dcf6f3ebb9"), "category" : { "cat1" : "cat1", "cat2" : "cat2" } }

Related

How to find data on MongoDB by passing part of array objects

I tried to create an API for filtering the products by sending an array of objects as filters.
this is my Product schema:
const mongoose = require("mongoose");
const { s, rs, rn, rref, ref } = require("../utils/mongo");
let schema = new mongoose.Schema(
{
user: rref("user"),
name: rs,
description: s,
images: [s],
price: rn,
category: ref("category"),
filters: [
{
parent: ref("filter"),
value: s,
name: s,
},
],
subFilter: [
{
parent: s,
value: s,
title: s,
},
],
},
{ timestamps: true }
);
module.exports = mongoose.model("product", schema);
and this one is what I want to send as body to the API
{
category: '62445c3d922d127512867245'
filters: [
{ name: 'filter name 1', value: '62445c3d922d127512861236' },
{ name: 'filter name 2', value: '62445c3d922d127512861458' },
.....
]
}
as you see I want to filter my products based on category Id and an array of filter objects. I tried to write this query but it return an empty array.
this is my query:
filter: async (req, res) => {
try {
const { category, filters } = req.body;
const products = await Product.find({
category,
filters: {
$in: filters,
},
});
res.status(200).json(products);
} catch (err) {
res.status(500).json(err);
}
},
what stored on db
{
"_id" : ObjectId("62643acf19636d7db1804cb3"),
"images" : [
"image-1650735823476۸.jpg"
],
"user" : ObjectId("622606af0f40cb8ea37383dc"),
"name" : "شیر توپی 2 اینچ کلاس 150 پیشگام",
"description" : " برند پیشگام با مدارک و تاییدیه ",
"price" : NumberInt(5000000),
"category" : ObjectId("62445c4d922d127512867246"),
"filters" : [
{
"_id" : ObjectId("62643acf19636d7db1804cb4"),
"parent" : ObjectId("6264307f19636d7db1804b77"),
"value" : "626430bb19636d7db1804b78",
"name" : "Valve Type"
},
{
"_id" : ObjectId("62643acf19636d7db1804cb5"),
"parent" : ObjectId("6264319819636d7db1804b7b"),
"value" : "6264319819636d7db1804b7e",
"name" : "Body Type"
},
{
"_id" : ObjectId("62643acf19636d7db1804cb6"),
"parent" : ObjectId("626431ef19636d7db1804b82"),
"value" : "626431ef19636d7db1804b83",
"name" : "Bore Type"
},
{
"_id" : ObjectId("62643acf19636d7db1804cb7"),
"parent" : ObjectId("6264328519636d7db1804b85"),
"value" : "6264328519636d7db1804b86",
"name" : "Material Type"
},
{
"_id" : ObjectId("62643acf19636d7db1804cb8"),
"parent" : ObjectId("626435de19636d7db1804c10"),
"value" : "626439b619636d7db1804ca7",
"name" : "Trim Material"
},
{
"_id" : ObjectId("62643acf19636d7db1804cb9"),
"parent" : ObjectId("6264367919636d7db1804c17"),
"value" : "6264367919636d7db1804c18",
"name" : "End Conection"
},
{
"_id" : ObjectId("62643acf19636d7db1804cba"),
"parent" : ObjectId("626436a719636d7db1804c1f"),
"value" : "6264378119636d7db1804c28",
"name" : "Size"
},
{
"_id" : ObjectId("62643acf19636d7db1804cbb"),
"parent" : ObjectId("6264389219636d7db1804c6d"),
"value" : "6264389219636d7db1804c6f",
"name" : "Class / Pressure"
}
],
"subFilter" : [
{
"_id" : ObjectId("62643acf19636d7db1804cbc"),
"parent" : "6264328519636d7db1804b85",
"value" : "626433b919636d7db1804b93",
"title" : "Body Material"
}
],
"createdAt" : ISODate("2022-04-23T17:43:43.421+0000"),
"updatedAt" : ISODate("2022-04-23T17:53:29.016+0000"),
"__v" : NumberInt(0)
}
Consider this shrunk down set of inputs that capture the essence of the question. The comments "give away" what we are going to try to find and why. We only show one value for category because matching on that is trivial and not the interesting part of the query.
[
{
"category" : ObjectId("62445c4d922d127512867246"),
"filters" : [
// Matching Valve/value; include this doc
{"name" : "Valve", "value" : "626430bb19636d7db1804b78"},
// ALSO match Body/value; include this doc (but needs only 1 match)
{"name" : "Body", "value" : "6264319819636d7db1804b7e"}
]
}
,{
"category" : ObjectId("62445c4d922d127512867246"),
"filters" : [
// Not target value for Valve name (..79 instead of ...78):
{"name" : "Valve", "value" : "626430bb19636d7db1804b79"},
// ...but correct value for Body, so include this doc
{"name" : "Body", "value" : "6264319819636d7db1804b7e"}
]
}
,{
"category" : ObjectId("62445c4d922d127512867246"),
// No matching Valve or Body so this whole doc is ignored.
"filters" : [
{"name" : "Valve", "value" : "626430bb19636d7db1804b79"},
{"name" : "Body", "value" : "6264319819636d7db1804b7f"}
]
}
,{
"category" : ObjectId("62445c4d922d127512867246"),
// Not even name matches so ignore this too:
"filters" : [
{"name" : "Pipe", "value" : "6264319819636d7db1804eee"}
]
}
]
Assume also we set up inputs coming from the API like this, in their native form i.e. strings NOT ObjectId:
var targ_cat = '62445c4d922d127512867246';
var any_one_of = [
{ name: 'Valve', value: '626430bb19636d7db1804b78' },
{ name: 'Body', value: '6264319819636d7db1804b7e'}
];
We will use $filter as our main function but to do so, we must convert the incoming material into a form required by $filter.
// Convert inbound array of any_one_of into a something designed to work
// in the $filter function by comparing each name/value entry in the
// filters field to the item presented in $$this, meaning take:
// { name: 'Valve', value: '626430bb19636d7db1804b78' },
// and turn it into:
// {$and: [ {$eq:['Valve','$$this.name']}, {$eq:['62643...','$$this.value']} ] }
// Since any one of the entries is considered a hit, we package it all
// into an $or wrapper, not $and.
var or_list = [];
any_one_of.forEach(function(f) {
or_list.push( {$and: [
{$eq:[f['name'], '$$this.name']},
{$eq:[f['value'], '$$this.value']}
]});
});
var or_expr = {$or: or_list};
Now we are ready to query mongoDB:
db.foo.aggregate([
// Get this out of the way quickly; note we must make a new ObjectId!
{$match: {'category': new ObjectId(targ_cat)}}
// The interesting part of the query:
,{$addFields: {filters: {$filter: {input: '$filters', cond: or_expr}}}}
// Only keep those items where $filter found at least one of the
// targets:
,{$match: {$expr: {$gt:[{$size: '$filters'},0]} }}
]);

Mongoose aggregate query working as a mongodb query but not able to convert to mongoose

Below is my JSON structure
[{
"_id" : ObjectId("626204345ae3d8ec53ef41ee"),
"categoryName" : "Test Cate",
"__v" : 0,
"createdAt" : ISODate("2022-04-22T01:26:11.627Z"),
"items" : [
{
"itemName" : "Cate",
"user" : ObjectId("6260729af547915d9d876c23"),
"itemDescription" : "slkkndanslk",
"itemImage" : "/images/camping-table.jpeg",
"_id" : ObjectId("626204339b24b2ead6c05a70"),
"updatedAt" : ISODate("2022-04-22T01:26:11.627Z"),
"createdAt" : ISODate("2022-04-22T01:26:11.627Z")
}
],
"updatedAt" : ISODate("2022-04-22T01:26:11.627Z")
},
{
"_id" : ObjectId("62620e725ae3d8ec53ef4aa8"),
"categoryName" : "sdsad",
"__v" : 0,
"createdAt" : ISODate("2022-04-22T02:09:54.028Z"),
"items" : [
{
"itemName" : "asdada",
"user" : ObjectId("62620e6299145edb95147482"),
"itemDescription" : "asdsadad",
"itemImage" : "/images/camping-table.jpeg",
"_id" : ObjectId("62620e7299145edb95147486"),
"updatedAt" : ISODate("2022-04-22T02:09:54.028Z"),
"createdAt" : ISODate("2022-04-22T02:09:54.028Z")
},
{
"itemName" : "dsdsa",
"user" : ObjectId("62620e6299145edb95147482"),
"itemDescription" : "adasdad",
"itemImage" : "/images/camping-table.jpeg",
"_id" : ObjectId("62621b9c3662e0b4acabb71f"),
"updatedAt" : ISODate("2022-04-22T03:06:04.727Z"),
"createdAt" : ISODate("2022-04-22T03:06:04.727Z")
}
],
"updatedAt" : ISODate("2022-04-22T03:06:04.727Z")
}]
This is just one document and there would be array of documents. Also there may be multiple items within the same category.
I want to fetch all the items in all category with a particular userid. In MongoDB below is my query which is giving correct output on mongo shell
db.trades.aggregate([
{
$unwind: "$items"
},
{
$match: {
"items.user": ObjectId("6260729af547915d9d876c23")
}
}
]).pretty()
In mongoose I am doing the following thing but not getting the result
tradeModel.aggregate([ { $unwind : "$items" }, { $match : { "items.user" : id } } ])
.then(res => {
console.log(JSON.stringify(res))
})
Let me know what I am missing
Your parameter id is type string but mongodb store type ObjectId
change
tradeModel.aggregate([ { $unwind : "$items" }, { $match : { "items.user" : id } } ])
.then(res => {
console.log(JSON.stringify(res))
})
into
tradeModel.aggregate([ { $unwind : "$items" }, { $match : { "items.user" : {"$oid": id} } } ])
.then(res => {
console.log(JSON.stringify(res))
})
Got the answer we can use ObjectId(id) in search like this
tradeModel.aggregate([ { $unwind : "$items" }, { $match : { "items.user" : ObjectId(id) } } ])
.then(res => {
console.log(JSON.stringify(res))
})

Search for keywords in array and only get the matching elements in MongoDB using NodeJS

I have a collection asset and This is my Data
{
"_id" : ObjectId("5e71d235a3b5401685a058"),
"company" : ObjectId("5e6b834b5991d70945840"),
"asset_name" : "LG-OLED-55-Inch",
"installedAt" : ["lobby", "storeroom", "f105"],
}
{
"_id" : ObjectId("5e71d235a3b540168475d8"),
"company" : ObjectId("5e6b834b5991d70945840"),
"asset_name" : "LG-OLED-32-Inch",
"installedAt" : ["lobby", "f108"],
}
{
"_id" : ObjectId("5eb3d53a7e16dc70244d6578"),
"company" : ObjectId("5e6b834b5991d70945840"),
"asset_name" : "LG-OLED-68-Inch",
"installedAt" : ["tvroom", "f105"],
}
{
"_id" : ObjectId("5eb3d53a7e16dc7024474a12"),
"company" : ObjectId("5e6b834b5991d70945840"),
"asset_name" : "LG-OLED-22-Inch",
"installedAt" : ["tvroom"],
}
So for the above data my requirement is to search for keyword in installedAt and return all the elements that match the keyword which user provides.
For Example, if the user searches for f10 then we should search all the installedAt arrays in assests and return like below
"installedAt": ["f105","f108"]
And I have tried using $in for getting similar elements but it is not working as I have expected.
This is my query
var autoRecords =[];
key = [searchString];
key.forEach(function(opt){
autoRecords.push(new RegExp(opt,"i"));
});
Assets.find({ "installedAt" : {"$in" : autoRecords},"company": companyId},{"installedAt" : 1})
So for the above query when I try to send search text which is f10 the result is as below
[
{"installedAt":["lobby", "storeroom", "f105"],"_id":"5e71d235a3b5401685a058"},
{"installedAt":["lobby", "f108"],"_id":"5e71d235a3b540168475d8"},
{"installedAt":["tvroom", "f105"],"_id":"5eb3d53a7e16dc70244d6578"},
]
It is getting all elements in the installedAt array even if it finds one. So Can anyone help me in getting only matched elements in the array and try to obtain this format
"installedAt": ["f105","f108"]
You can use below aggregation
const data = await Assets.aggregate([
{ $match: { installedAt: { $regex: "f10", $options: "i" }}},
{ $unwind: "$installedAt" },
{ $match: { installedAt: { $regex: "f10", $options: "i" }}},
{ $group: {
_id: null,
data: { $addToSet: "$installedAt" }
}}
])
MongoPlayground

MongoDB .find() only ObjectId's embedded in an Array.

I am trying to get only the ObjectId's from One specific Document that is embedded in the projects Array.
Basically I am trying to make a database that will have users and each user will have there own projects.
Thank you !
db.users.find().pretty()
{
"_id" : ObjectId("5762c0cf2b9a78006373a684"),
"name" : "seq",
"pass" : "seq",
"projects" : [
{
"pid" : ObjectId("5762c0ba2b9a78006373a682"),
"name" : "aaa"
},
{
"pid" : ObjectId("5762c0ba2b9a78006373a683"),
"name" : "bbb"
}
]
}
{
"_id" : ObjectId("5762c28d2b9a78006373a687"),
"name" : "leq",
"pass" : "leq",
"projects" : [
{
"pid" : ObjectId("5762c2892b9a78006373a685"),
"name" : "ccc"
},
{
"pid" : ObjectId("5762c2892b9a78006373a686"),
"name" : "ddd"
}
]
}
let say we want two pids
{"pid" : ObjectId("5762c0ba2b9a78006373a682")} and
{"pid" : ObjectId("5762c2892b9a78006373a686"),}
and only inner documents
so required response should look like:
{
"_id" : ObjectId("5762c0ba2b9a78006373a682"),
"name" : "aaa"
},{
"_id" : ObjectId("5762c2892b9a78006373a686"),
"name" : "ddd"
}
Aggregation framework can manipulate documents, match only needed ones and transform inner structure by project phase:
var match = {
$match : {
"projects.pid" : {
$in : [ObjectId("5762c0ba2b9a78006373a682"),
ObjectId("5762c2892b9a78006373a686")]
}
}
}
var unwind = {
$unwind : "$projects"
};
// now move array objet as top level object
var project = {
$project : {
_id : "$projects.pid",
name : "$projects.name",
// list other fields here
}
}
db.vic.aggregate([match, unwind, match, project])

How to get count of elements in document array - MongoDB?

I have the following MongoDB collection (JSON):
{
"_id" : ObjectId("570185458351bbac27bc9a20"),
"email" : "test#gmail.com",
"applicants" : [
{
"id" : "570724e4ae4f8a5026156999",
"email" : "a#gmail.com",
},
{
"id" : "570724e4ae4f8a5026156333",
"email" : "a2#gmail.com",
},
{
"id" : "570724e4ae4f8a5026156111",
"email" : "a3#gmail.com",
},
{
"id" : "570724e4ae4f8a5026156222",
"email" : "a4#gmail.com",
}
],
},
{
"_id" : ObjectId("570185458351bbac27bc9a20"),
"email" : "test#gmail.com",
"applicants" : [
{
"id" : "570724e4ae4f8a5026156555",
"email" : "a#gmail.com",
},
{
"id" : "570724e4ae4f8a5026156666",
"email" : "a2#gmail.com",
},
],
},
{
"_id" : ObjectId("570185458351bbac27bc9a20"),
"email" : "test2#gmail.com",
"applicants" : [
{
"id" : "570724e4ae4f8a5026156555",
"email" : "a#gmail.com",
},
{
"id" : "570724e4ae4f8a5026156666",
"email" : "a2#gmail.com",
},
],
}
I would like to get the count of the elements in all arrays of the of the document where the email = test#gmail.com. How can I go about getting that count?
I am using the following to get the number of documents with email test#gmail.com using this:
collection.count({"email" : tmpEmail}, function (err, count) {
res.json(count);
console.log("Number: " + count);
});
How can I go ahead and count the number of elements in all applicant arrays for the documents where the email is test#gmail.com? The could for the example above would be: 6.
EDIT:
As per one of the answers I modified my query to the following:
Answer 1:
collection.aggregate(
{$match: {"email": req.user.username, "status" : "true"}},
{$unwind: "$applicants"},
{$group: {_id:null, count: {$sum :1}}, function (err, count) {
res.json(count);
console.log("Number of New Applicants: " + count);
}
});
Answer 2:
collection.aggregate(
[{$match:{"email" : req.user.username, "status" : "true"}},
{$project:{_id:0, email:1, totalApplicants:{$size:"$applicants"}}},
{$group:{_id:"$employer", count:{$sum:"$totalApplicants"}}}],
function (err, count){
res.json(count);
console.log("Number of New Applicants: " + count);
});
You can use an aggregate query instead:
collection.aggregate(
[{$match: {"email": req.user.username, "status" : "true"}},
{$unwind: "$applicants"},
{$group: {_id:null, count: {$sum :1}}}], function (err, result) {
console.log(result);
console.log("Number of New Applicants: " + result[0].count);
if(result.length > 0)
res.json(result[0]);
else
res.json({count:0});
}
});
This will result you in one document where count will have your required result
This may require to write a aggregation since you need to count the size of applicants array grouped by email:
Here is the equivalent mongodb query that returns the expected email with count:
db.yourCollection.aggregate(
[{$match:{"email" : "test#gmail.com"}},
{$project:{_id:0, email:1,totalEmails:{$size:"$applicants"}}},
{$group:{_id:"$email", count:{$sum:"$totalEmails"}}}])
This returns { "_id" : "test#gmail.com", "count" : 6 }
You may need to change this according to your code.

Categories