Query filteration in MongoDB - node.js

I have a list of products with the following details:
"products": [{
"product_name": "A",
"product_type": [{
"name": "Metal"
},
{
"name": "Wood"
},
{
"name": "Carbon"
}
],
},
{
"product_name": "B",
"product_type": [{
"name": "Metal"
},
{
"name": "Iron"
}
],
},
{
"product_name": "C",
"product_type": [{
"name": "Metal"
},
{
"name": "Wood"
}
],
},
{
"product_name": "D",
"product_type": [{
"name": "Wood"
}],
}
]
I want to filter this collection with
Product.find(query)
where sending query = ["Wood", "Carbon"] should list me products which has either Wood or Carbon type.
Or works like:
Product.find({product_type: [ { name: query } ]}) list me products with name A, C and D

You can use dot notation with $in to solve your problem:
Product.find({
"product_type.name": {
$in: ["Wood", "Carbon"]
}
})
MongoPlayground

You can write query like,
Product.find({product_name:{$in: [ 'A', 'C', 'D']}}) list me products with name A, C and D
Or if you want to find the documents based on product_type, then you can use following query
Product.find({
"product_type.name": {
$in: [
"Metal"
]
}
})
Refer: https://mongoplayground.net/p/vSfkpUjU494

Related

mongodb pull nested array of objects

I want to pull multiple objects from array.
Here is my sample collection:
Users
{
"_id": "wef324DGSshf",
"userTypes": [
{
"type": "students",
"users": [
{
"name": "John",
"age": 20
},
{
"name": "Mike",
"age": 20
},
{
"name": "Henry",
"age": 30
},
{
"name": "Henry",
"age": 40
}
]
}
]
}
I need to pull those objects where:
type: "students" and ages: [20,40]
So I have these 2 inputs: type & ages
Expected Response:
{
"_id": "wef324DGSshf",
"userTypes": [
{
"type": "students",
"users": [
{
"name": "Henry",
"age": 30
}
]
}
]
}
I have tried this query so far but it is not working:
Users.update({
"userTypes.type": "students",
"userTypes.users.age": {$in: [20, 40]},
},
{
$pull: {
"userTypes": {
"userTypes.users.$.age": {$in: [20, 40]}
}
}
});
Can anyone help me what I am doing wrong here?
Use an arrayFilters to specify the filtering for "type": "students" and normally perform $pull on age
db.collection.update({},
{
"$pull": {
"userTypes.$[ut].users": {
"age": {
$in: [
20,
40
]
}
}
}
},
{
arrayFilters: [
{
"ut.type": "students"
}
],
multi: true
})
Mongo Playground
Explanation: Check out the official doc about arrayFilters. You can think of the entries in arrayFilters as predicates. For a variable ut, it needs to have type: students. Let's go back to the $pull part. The predicate is applied to userTypes. That means for an entry in userTypes, ut, it needs to fit in the predicate of type: students. At the same time, we are $pulling the entries that age is in [20, 40].

MongoDB aggregation lookup array

I have three documents called location, company and vouchers and they structured as follows,
"company": [
{
"_id": "625ae79a51828244cef979d4",
"name": "C1",
"location": "L1",
"category": "A"
},
{
"_id": "625ba41651828244cefa138b",
"name": "C2",
"location": "L2",
"category": "B"
},
{
"_id": "625ba4d651828244cefa1951",
"name": "C3",
"location": "L3",
"category": "B"
}
]
"vouchers":[
{
"_id":"625ae79a51828244cef979d4",
"name":"V1",
"color":"#ad7f7f",
"category":[
"A",
"B"
]
},
{
"_id":"625ba41651828244cefa138b",
"name":"V2",
"color":"#9A348E",
"category":[
"A"
]
},
{
"_id":"625ba4d651828244cefa1951",
"name":"V3",
"color":"#31263E",
"category":[
"B"
]
},
{
"_id":"625ba4d651828244cefa1951",
"name":"V4",
"color":"#31263E",
"category":[
"C"
]
}
]
and the expected result should be
"companies":[
{
"_id":"625ae79a51828244cef979d4",
"name":"C1",
"location":"L1",
"category":"A",
"vouchers":[
{
"_id":"625ae79a51828244cef979d4",
"name":"V1",
"color":"#ad7f7f",
"category":[
"A",
"B"
]
},
{
"_id":"625ba41651828244cefa138b",
"name":"V2",
"color":"#9A348E",
"category":[
"A"
]
}
]
},
{
"_id":"625ba41651828244cefa138b",
"name":"C2",
"location":"L2",
"category":"B",
"vouchers":[
{
"_id":"625ae79a51828244cef979d4",
"name":"V1",
"color":"#ad7f7f",
"category":[
"A",
"B"
]
},
{
"_id":"625ba4d651828244cefa1951",
"name":"V3",
"color":"#31263E",
"category":[
"B"
]
}
]
},
{
"_id":"625ba4d651828244cefa1951",
"name":"C3",
"location":"L3",
"category":"C",
"vouchers":[
{
"_id":"625ba4d651828244cefa1951",
"name":"V4",
"color":"#31263E",
"category":[
"C"
]
}
]
}
]
I am trying to get the expected result by using mongodb aggregation pipeline. First I filtered location documents with $geoWithin and used $lookup to find the related companies. Next I should get all the vouchers which having same category as company. I applied bellow code to previous result to get vouchers.
$lookup: {
from: "vouchers",
let: { category: "$company.category" },
pipeline: [
{
$match: {
$expr: {
$and: [{ $in: ["$category", "$$category"] }],
},
},
},
],
as: "vouchers",
},
But it gives empty result after this stage. Any suggestion would be appreciated.

How to find data based on sub-field name in MongoDB using Mongoose?

There is a sub-field called 'name' in MongoDB Collection (User):
[
{
"teacher": {
"name": "Alex",
"email": "alex#domain.com"
},
"waiter": {
"name": "Feliks",
"email": "feliks#domain.com"
},
"pilot": [
{
"name": "Sam",
"email": "sam#domain.com"
},
{
"name": "alice",
"email": "alice#domain.com"
}
],
},
{
"teacher": {
"name": "max",
"email": "max#domain.com"
},
"waiter": {
"name": "Sam",
"email": "sam#domain.com"
},
"pilot": [
{
"name": "richard",
"email": "richard#domain.com"
},
{
"name": "alice",
"email": "alice#domain.com"
}
],
}
]
How can I find data based on the field 'name'. For example, when I'm looking for 'Sam', it should return me the all documents since 'Sam' is in "waiter" and "pilot" in first and second documents respectively.
I cannot do something like:
User.find({"teacher.name": "Sam", "waiter.name": "Sam", "pilot.name": "Sam" })
This will return me nothing as it is an AND logic. What I need is an OR logic.
You can use the $or operator.
So the query should look like this:
User.find({ $or: [
{ "teacher.name": "Sam" },
{ "waiter.name": "Sam" },
{ "pilot.name": "Sam" }
]
});
Read here for me details.

How to get an Array of parents and children of categories in mangoose (Materialised Path Category)?

Hello guys I wanted to implement Materialised Path Category Hierarchy to mongodb in nodejs application and if I have those 2 docs and I want to get array of all of them like a tree as I will explain
The collections:
const categoriesCollection = [
{
"_id": ObjectId("54fd7392742abeef6186a68e")
, "name": "electronics"
, "parent": "/"
, "category": "/electronics"
},
{
"_id": ObjectId("54fd7392742abeef6186a68e")
, "name": "embedded"
, "parent": "/electronics"
, "category": "/electronics/embedded"
},
{
"_id": ObjectId("54fd7392742abeef6186a68e")
, "name": "controllers"
, "parent": "/electronics"
, "category": "/electronics/controllers"
},
{
"_id": ObjectId("54fd7392742abeef6186a68e")
, "name": "cases"
, "parent": "/electronics"
, "category": "/electronics/cases"
},
{
"_id": ObjectId("54fd7392742abeef6186a68e")
, "name": "big"
, "parent": "/electronics/cases"
, "category": "/electronics/cases/big"
},
{
"_id": ObjectId("54fd7392742abeef6186a68e")
, "name": "small"
, "parent": "/electronics/cases"
, "category": "/electronics/cases/small"
},
]
const ProductsCollection = [
{
"_id": ObjectId("54fd7392742abeef6186a68e")
, "name": "product1"
, "cost": 125
, "currency": "USD"
, "categories": ["/electronics/embedded"]
},
{
"_id": ObjectId("54fd7392742abeef6186a68e")
, "name": "product2"
, "cost": 134
, "currency": "USD"
, "categories": ["/electronics/controllers"]
},
{
"_id": ObjectId("54fd7392742abeef6186a68e")
, "name": "product3"
, "cost": 133
, "currency": "USD"
, "categories": ["/electronics/cases/big"]
},
{
"_id": ObjectId("54fd7392742abeef6186a68e")
, "name": "product4"
, "cost": 188
, "currency": "USD"
, "categories": ["/electronics/cases/small", "/electronics/cases"]
},
]
I want to have output Array of parents and children and products in a tree alike like that:
const allCategories = [
{
name: "electronics",
categories: [
{
name: "embedded",
categories: [],
products: [{ name: "product1" }]
},
{
name: "controllers",
categories: [],
products: [{ name: "product2" }]
},
{
name: "cases",
categories: [
{
name: "big",
categories: [],
products: [{ name: "product3" }]
},
{
name: "small",
categories: [],
products: [{ name: "product4" }]
}
],
products: [{ name: "product1" }]
},
]
,
products: [{ "name": "product4" }]
}
]
How can I get this array in this array by mongoose queries?
There is no any straight way to handle this situation in MongoDB, You can try $groupLookup and custom logic,
$match filter that records only have parent is /
$graphLookup to get child records and depth number in depthField level
$unwind deconstruct categories array and allow to not remove empty category
$lookup with products collection and join products or categories
$addFields to select only name field in products
$sort by depth level field level in descending order
$group by category field and reconstruct categories array
db.category.aggregate([
{ $match: { parent: "/" } },
{
$graphLookup: {
from: "category",
startWith: "$category",
connectFromField: "category",
connectToField: "parent",
depthField: "level",
as: "categories"
}
},
{
$unwind: {
path: "$categories",
preserveNullAndEmptyArrays: true
}
},
{
$lookup: {
from: "products",
localField: "categories.category",
foreignField: "categories",
as: "categories.products"
}
},
{
$addFields: {
"categories.products": {
$map: {
input: "$categories.products",
in: { name: "$$this.name" }
}
}
}
},
{ $sort: { "categories.level": -1 } },
{
$group: {
_id: "$category",
parent: { $first: "$parent" },
name: { $first: "$name" },
categories: { $push: "$categories" }
}
},
$addFields now find the nested level category and allocate to its level,
$reduce to iterate loop of categories array.
initialize default field level default value is -1, presentChild is [], prevChild is [] for the conditions purpose
$let to initialize fields:
prev as per condition if both level are equal then return prevChild otherwise return presentChild
current as per condition if both level are equal then return presentChild otherwise []
in to return level field and prevChild field from initialized fields
presentChild $filter categories from prev array and return, merge current objects with categories array using $mergeObjects and concat with current array of let using $concatArrays
$addFields to return only presentChild array because we only required that processed array
{
$addFields: {
categories: {
$reduce: {
input: "$categories",
initialValue: { level: -1, presentChild: [], prevChild: [] },
in: {
$let: {
vars: {
prev: {
$cond: [
{ $eq: ["$$value.level", "$$this.level"] },
"$$value.prevChild",
"$$value.presentChild"
]
},
current: {
$cond: [{ $eq: ["$$value.level", "$$this.level"] }, "$$value.presentChild", []]
}
},
in: {
level: "$$this.level",
prevChild: "$$prev",
presentChild: {
$concatArrays: [
"$$current",
[
{
$mergeObjects: [
"$$this",
{
categories: {
$filter: {
input: "$$prev",
as: "e",
cond: { $eq: ["$$e.parent", "$$this.category"] }
}
}
}
]
}
]
]
}
}
}
}
}
}
}
},
{ $addFields: { categories: "$categories.presentChild" } }
])
Playground
NOTE: This will cause the performance issues in huge data!

Mongoose aggregate group items by tag

I'm trying to group items by tag. But I do not get what exactly I want to do.
There is schema Item (_id, name, tags) with tags is array of tags_id ( was referenced from Tag schema )
Item = new Schema({
id: Schema.ObjectId,
name: String,
tags: [ { type: Schema.ObjectId, ref: 'Tag' }]
});
Tag = new Schema({
id: Schema.ObjectId,
name: String
});
This is what I have done so far:
Item.aggregate([
{
"$lookup": {
"from": Tag.collection.name,
"let": { "tags": "$tags" },
"pipeline": [
{
"$match": {
"$expr": { "$in": ['$_id', '$$tags'] }
}
},
{
"$project": {
"_id": 1,
"name": 1
}
}
],
"as": 'tags'
}
},
{
"$group": {
"_id": "$tags._id",
"items": {
"$push": {
"id": "$_id",
"name": "$name",
}
}
}
}
]);
This is what I get
{
"data": [
{
"_id": [
"5eb95e8dcae79713f1de0a27"
],
"items": [
{
"id": "5eb95e9fcae79713f1de0a28",
"name": "My Item 1"
}
]
},
{
"_id": [
"5eb9564dc4317411fe79e1bf"
],
"items": [
{
"id": "5eb95b1430f138131ed90f4f",
"name": "My Item 2"
},
{
"id": "5eb95ed0cae79713f1de0a29",
"name": "My Item 3"
}
]
}
]
}
I would like to get the name of each tag, and not only the _id possibly not within an array. Below the result that I would like to receive:
{
"data": [
{
"_id": "5eb95e8dcae79713f1de0a27",
"name": "My Tag name 1",
"items": [
{
"id": "5eb95e9fcae79713f1de0a28",
"name": "My Item 1"
}
]
},
{
"_id": "5eb9564dc4317411fe79e1bf",
"name": "My Tag name 2",
"items": [
{
"id": "5eb95b1430f138131ed90f4f",
"name": "My Item 2"
},
{
"id": "5eb95ed0cae79713f1de0a29",
"name": "My Item 3"
}
]
}
]
}
What am I doing wrong?
One way to to this is to add the tag-name to your $group operator:
...
{
"$group": {
"_id": "$tags._id",
"tagName": {$first: "$tags.name"},
"items": {
"$push": {
"id": "$_id",
"name": "$name",
}
}
}
}
...
Note that this will take the first matching entry for each group. Another option would be to use $replaceRoot and the $mergeObjects operator, like it was done here.

Resources