MongoDB
I have data like:-
[
{
review: [
{
title: "Title1",
professor: [
{
id: "1",
accept: false
},
{
id: "2",
accept: false
}
]
}
]
},
{
review: [
{
title: "Title2",
professor: [
{
id: "3",
accept: false
},
{
id: "2",
accept: false
}
]
}
]
}
]
I want title and professor array with filter of specific id (like "1")
My code:-
for match i use below code and it works perfectly fine
$match
{
review: {
$elemMatch: {
professor: {
$elemMatch: {
id: "1"
}
}
}
}
}
for project i use below code
$project
{
review: {
title:1,
professor:{
$filter:{
input: "$review.professor", (I try only professor but give null)
as: "professor",
cond: {$eq:["$$professor.id", "1" ]}
}
}
}
}
Problem is that professor show array but comes with empty data
Playground
Unwind stage could help you.
db.collection.aggregate([
{
"$match": {
review: {
$elemMatch: {
professor: {
$elemMatch: {
id: "1"
}
}
}
}
}
},
{
"$unwind": "$review"
},
{
$project: {
review: {
title: 1,
professor: {
$filter: {
input: "$review.professor",
as: "professor",
cond: {
$eq: [
"$$professor.id",
"1"
]
}
}
}
}
}
}
])
Related
Lets say my documents look like this :
_id: "6285e9a7aff93ead37ec50ad",
date: "2022-04-28T10:51:37.923Z",
devices: {
tablets: [
{
brand: "samsung",
model: "s20"
},
{
brand: "samsung",
model: "s21"
},
{
brand: "apple",
model: "ipad_mini"
},
],
phones: [
{
brand: "samsung",
model: "galaxy_s20"
},
{
brand: "samsung",
model: "galaxy_s20_lite"
}
],
laptops: []
}
}
how would i query and return all documents that contain at least an "apple" value in "brand" property wherever inside the "devices" property ?
When you have more dynamic keys, you can use
db.collection.aggregate([
{
$project: {
"d": {
"$objectToArray": "$devices"
}
}
},
{
"$match": {
"d.v.brand": "apple"
}
}
])
You need to restructure the data if required. It returns if there is atleast one match.
playground
db.collection.aggregate([
{
$project: {
"d": {
"$objectToArray": "$devices"
}
}
},
{
"$match": {
"d.v.brand": "apple"
}
},
{
"$project": {
"devices": { //To reshape back
"$arrayToObject": "$d"
}
}
}
])
Playground
I have a collection product which looks like this.
{
should_show: {
type: String,
enum: ['always', 'never', 'on_date'],
default: 'always',
},
show_start_date: { type: Date },
show_end_date: { type: Date },
categories: [{ type: ObjectId }],
price: number,
}
find condition like this works fine when I find many products.
const condition = {
'$and': [
{ '$and': [ { categories: '61bdd930fb1dfb1f65a21f13' } ] },
{
'$or': [
{ should_show: 'always' },
{
should_show: 'on_date',
show_start_date: { '$lt': 2022-01-03T08:56:19.589Z },
show_end_date: { '$gt': 2022-01-03T08:56:19.589Z }
}
]
}
]
}
Products.find(condition)
But I changed my business logic to use Model.aggregate() instead of `Model.find()
(because I have to use allowdiskuse option)
const condition = {
'$and': [
{ '$and': [ { categories: '61bdd930fb1dfb1f65a21f13' } ] },
{
'$or': [
{ should_show: 'always' },
{
should_show: 'on_date',
show_start_date: { '$lt': 2022-01-03T08:56:19.589Z },
show_end_date: { '$gt': 2022-01-03T08:56:19.589Z }
}
]
}
]
}
Products.aggregate([
{ $match: condition },
])
And returned results is always empty array []
I changed my condition simpler like this
const condition = {
'$and': [ { categories: '61bdd930fb1dfb1f65a21f13' } ],
'$or': [
{ should_show: 'always' },
{
should_show: 'on_date',
show_start_date: { '$lt': 2022-01-03T08:56:50.161Z },
show_end_date: { '$gt': 2022-01-03T08:56:50.161Z }
}
]
}
Products.aggregate([
{ $match: condition },
])
but still returns empty array.
How should I fix this problem?
Self Answer:
I have to pass ObjectId by explicit ObjectId type, not string.
const condition = {
'$and': [
{ '$and': [{
// like this:
categories: Mongoose.Types.ObjectId('61bdd930fb1dfb1f65a21f13'),
}] },
{
'$or': [
{ should_show: 'always' },
{
should_show: 'on_date',
show_start_date: { '$lt': 2022-01-03T08:56:19.589Z },
show_end_date: { '$gt': 2022-01-03T08:56:19.589Z }
}
]
}
]
}
Products.aggregate([
{ $match: condition },
])
sorry it may be silly question, mongodb contains thousands of documents .
hard to be changed manually
original format
{"name" : "aaaa",
"price" : 111,
"ing1" : "abcd",
"ing1Conc" : 50 ,
"ing2" : "wxyz",
"ing2conc": 100}
needed to be converted to
{"name" : "aaaa",
"price" : 111,
"content":[
{ "ing1" : "abcd", "ing1Conc" : 50},
{ "ing2" : "wxyz", "ing2conc": 100}
]
}
The trivial solution would be this one:
db.collection.aggregate([
{
$project: {
name: 1,
price: 1,
content: [
{ ing1: "$ing1", ing1Conc: "$ing1Conc" },
{ ing2: "$ing1", ing2conc: "$ing2conc" }
]
}
}
])
A more generic solution would be this one:
db.collection.aggrega
{
$project: {
name: 1,
price: 1,
data: {
$filter: {
input: { $objectToArray: "$$ROOT" },
cond: { $regexMatch: { input: "$$this.k", regex: "^ing\\d+" } }
}
}
}
},
{ $unwind: "$data" },
{ $set: { i: { $regexFind: { input: "$data.k", regex: "\\d+" } } } },
{ $set: { i: "$i.match" } },
{
$group: {
_id: {
name: "$name",
price: "$price",
i: "$i"
},
content: { $push: "$data" }
}
},
{ $sort: { "_id.i": 1 } },
{ $set: { content: { $arrayToObject: "$content" } } },
{
$group: {
_id: { name: "$_id.name", price: "$_id.price" },
content: { $push: "$content" }
}
},
{ $replaceRoot: { newRoot: { $mergeObjects: [ "$$ROOT", "$_id" ] } } },
{ $unset: "_id" }
])
Mongo Playground
However, I think this structure is still bad. I would suggest something like
content: {
ing: [ "abcd", "wxyz"],
conc: [ 50, 100 ]
}
content: [
{ ing: "abcd", conc: 50 },
{ ing: "wxyz", conc :100 }
]
content: [
{ idx: 1, ing: "abcd", conc: 50 },
{ idx: 2, ing: "wxyz", conc: 100 }
]
I am trying to match nested fields in a document in mongo but my query is not working.
Configuration:
[
{
linenumber: "car",
type: "004",
nested: {
info: {
subinfo: "B"
}
},
},
{
linenumber: "car",
type: "005",
nested: {
info: {
subinfo: "G"
}
},
}
]
query:
db.collection.aggregate([
{
$match: {
$and: [
{
linenumber: "car"
},
{
nested: {
info: {
subinfo: {
$in: [
"B",
"G"
]
}
}
}
}
]
}
},
{
$group: {
_id: null,
types: {
$addToSet: "$type"
}
}
}
])
I am using the mongoplayground to test. Here is the link:
https://mongoplayground.net/p/nND59iO1339
I am getting no documents found.
You are almost there. You need to use dot notation.
play
db.collection.aggregate([
{
$match: {
$and: [
{
linenumber: "car"
},
{
"nested.info.subinfo": { //Dot notation
$in: [
"B",
"G"
]
}
}
]
}
},
{
$group: {
_id: null,
types: {
$addToSet: "$type"
}
}
}
])
I am trying to add a new field to an existing document by using a combination of both $ifnull and $cond but an empty document is always added at the end.
Configuration:
[
{
line: "car",
number: "1",
category: {
FERRARI: {
color: "blue"
},
LAMBORGHINI: {
color: "red"
}
}
},
{
line: "car",
number: "2",
category: {
FERRARI: {
color: "blue"
}
}
}
]
Query approach:
db.collection.aggregate([
{
$match: {
$and: [
{ line: "car" },
{ number: { $in: ["1", "2"] } }
]
}
},
{
"$addFields": {
"category.LAMBORGHINI.number": {
$cond: [
{ "$ifNull": ["$category.LAMBORGHINI", false] },
"$number",
"$$REMOVE"
]
}
}
},
{
$group: {
_id: null,
CATEGORIES: {
$addToSet: "$category.LAMBORGHINI"
}
}
}
])
Here is the link to the mongo play ground:
https://mongoplayground.net/p/RUnu5BNdnrR
I tried the mentioned query but I still get that ugly empty set added at the end.
$$REMOVE will remove last field/key, from your field category.LAMBORGHINI.number the last field is number that is why it is removing number from the end, you can try another approach,
specify just category.LAMBORGHINI, if condition match then it will return object of current category.LAMBORGHINI and number object after merging using $mergeObjects
{
"$addFields": {
"category.LAMBORGHINI": {
$cond: [
{ "$ifNull": ["$category.LAMBORGHINI", false] },
{
$mergeObjects: [
"$category.LAMBORGHINI",
{ number: "$number" }
]
},
"$$REMOVE"
]
}
}
}
Playground