I have document like this. How to show only element of array in output.
{
_id: ObjectId("5effaa5662679b5af2c58829"),
email: “email#example.com”,
name: {given: “Jesse”, family: “Xiao”},
age: 31,
addresses: [{label: “home”,
street: “101 Elm Street”,
city: “Springfield”,
state: “CA”,
zip: “90000”,
country: “US”},
{label: “mom”,
street: “555 Main Street”,
city: “Jonestown”,
province: “Ontario”,
country: “CA”}]
}
my query
{ "$project": { _id : 0, "addresses.country": 1 } }
desired output
"country": “...”
actual output
{
"addresses" : [
{
"country" : "..."
}
]
}
You can use aggregation
$unwind to deconstruct the array
$match to match the country
$replaceRoot to make it root
Here is the code
db.collection.aggregate([
{ "$unwind": "$addresses" },
{ "$match": { "addresses.country": "CA" } },
{ "$replaceRoot": { "newRoot": "$addresses" } }
])
Working Mongo playground
short answer is you use exclusion. use field:0 to exclude. so after find statement you would add something like: db.project.find({..}, {_id:0:,label:0,city:0..etc})
It's resolved
{ "$unwind" : "$addresses" },
{
"$project" : {
_id:0.0,
"addresses" : "$addresses._id"
}
}
Related
I have a database of a the employees of a company that looks like this:
{
_id: 7698,
name: 'Blake',
job: 'manager',
manager: 7839,
hired: ISODate("1981-05-01T00:00:00.000Z"),
salary: 2850,
department: {name: 'Sales', location: 'Chicago'},
missions: [
{company: 'Mac Donald', location: 'Chicago'},
{company: 'IBM', location: 'Chicago'}
]
}
I have an exercise in which I need to write the MongoDb command that returns all them employees who did all their missions in Chicago. I struggle with the all because I cannot find a way to check that all the locations of the missions array are equal to 'Chicago'.
I was thinking about doing it in two time: first find the total number of missions the employee has and then compare it to the number of mission he has in Chicago (that how I would do in SQL I guess). But I cannot found the number of mission the employee did in Chicago. Here is what I tried:
db.employees.aggregate([
{
$match: { "missions": { $exists: true } }
},
{
$project: {
name: 1,
nbMissionsChicago: {
$sum: {
$cond: [
{
$eq: [{
$getField: {
field: { $literal: "$location" },
input: "$missions"
}
}, "Chicago"]
}, 1, 0
]
}
}
}
}
])
Here is the result :
{ _id: 7698, name: 'Blake', nbMissionsChicago: 0 }
{ _id: 7782, name: 'Clark', nbMissionsChicago: 0 }
{ _id: 8000, name: 'Smith', nbMissionsChicago: 0 }
{ _id: 7902, name: 'Ford', nbMissionsChicago: 0 }
{ _id: 7499, name: 'Allen', nbMissionsChicago: 0 }
{ _id: 7654, name: 'Martin', nbMissionsChicago: 0 }
{ _id: 7900, name: 'James', nbMissionsChicago: 0 }
{ _id: 7369, name: 'Smith', nbMissionsChicago: 0 }
First of all, is there a better method to check that all the locations of the missions array respect the condition? And why does this commands returns only 0 ?
Thanks!
If all you need is the agents who had all their missions in "Chicago" then you don't need an aggregation pipeline for it, specifically the approach of filtering the array as part of the aggregation can't utilize an index and will make performance even worse.
A simple query should suffice here:
db.collection.find({
$and: [
{
"missions": {
$exists: true
}
},
{
"missions.location": {
$not: {
$gt: "Chicago"
}
}
},
{
"missions.location": {
$not: {
$lt: "Chicago"
}
}
}
]
})
Mongo Playground
This way we can build an index on the missions field and utilize it properly, any documents with a different value other then "Chigaco" will not match as they will fail the $gt or $lt comparion.
Note that an empty array also matches the condition, you can change the generic "missions" exists condition key into "missions.0": {$exists: true}, this will also require at least one mission.
You are unable to get the correct result as it is not the correct way to iterate the element in an array field.
Instead, you need to work with $size operator to get the size of an array and the $filter operator to filter the document.
Updated: You can directly compare the filtered array with the original array.
db.employees.aggregate([
{
$match: {
"missions": {
$exists: true
}
}
},
{
$project: {
name: 1,
nbMissionsChicago: {
$eq: [
{
$filter: {
input: "$missions",
cond: {
$eq: [
"$$this.location",
"Chicago"
]
}
}
},
"$missions"
]
}
}
}
])
Demo # Mongo Playground
I have three documents that looks like this:
_id: ObjectId('61e1312ad435c7124aa883a1')
name: "Brian"
languages: "English,Spanish,French"
_id: ObjectId('52e1312ad435c7124aa883a2')
name: "Max"
languages: "English"
_id: ObjectId('37e1312ad435c7124aa883a9')
name: "Mike"
languages: ""
As you can see, the languages field can either be empty, have one item, or multiple items separated by commas.
I want to create a new field, that is an Array of objects. Each object should consist of a "name" property and an "active" boolean. "name" should be the language and "active" should just be set to false. The end result should look like this:
_id: ObjectId('61e1312ad435c7124aa883a1')
name: "Brian"
languages: "English,Spanish,French"
newLanguages: [
{ name: "English", active: false },
{ name: "Spanish", active: false },
{ name: "French", active: false }
]
_id: ObjectId('52e1312ad435c7124aa883a2')
name: "Max"
languages: "English,Spanish,French"
newLanguages: [
{ name: "English", active: false }
]
_id: ObjectId('37e1312ad435c7124aa883a9')
name: "Mike"
languages: ""
newLanguages: []
I've managed to turn the comma-separated strings into the objects I want, but it doesn't create a new property, it just overwrites the languages property:
db.collection.updateMany({},
[
{
$set: {
languages: {
$filter: {
input: {$split: ["$languages", ","]},
cond: {$gt: [{$strLenCP: "$$this"}, 0]}
}
}
}
},
{
$set: {
languages: {
$map: {
input: "$languages",
as: "item",
in: {name: "$$item", active: false}
}
}
}
}
])
Simply set a new key newLanguages, instead of the existing key languages, which will stay as it was:
db.collection.updateMany({},
[
{
$set: {
newLanguages: {
$filter: {
input: {$split: ["$languages", ","]},
cond: {$gt: [{$strLenCP: "$$this"}, 0]}
}
}
}
},
{
$set: {
newLanguages: {
$map: {
input: "$newLanguages",
as: "item",
in: {name: "$$item", active: false}
}
}
}
}
])
See how it works on the playground example
Hi I have two collection person and department. Those collection have the properties as below.
I want to aggregate and add a inner array in to the root of the document. Please check the result I need below:
person:
{
_id:ObjectID("5ff93b43535bera64de4"),
first_name: some name,
last_name : some name,
dept: department1,
}
department:
{
dept_name: department1,
dept_descp : this is description,
books: [
{
book_name: book1,
subject: subject1,
},
{
book_name: book2,
subject: subject2,
},
]
}
So far I have tried but I didn't get the needed result
person.aggregate([
{
$match:
{
"_id": ObjectId("5ff93b43535bera64de4")
}
},
{
$lookup: {
from: "department",
localField: "dept",
foreignField: "dept_name",
as: "ndept"
}
},
{
$project:{"books": '$ndept.books'}
}
])
the result I would like to get is
Result:
{
_id:ObjectID("5ff93b43535bera64de4"),
first_name: some name,
last_name : some name,
dept: department1,
books: [
{
book_name: book1,
subject: subject1,
},
{
book_name: book2,
subject: subject2,
}
]
}
Try $arrayElemAt to select first element from array
{
$addFields: {
books: { $arrayElemAt: ["$ndept.books", 0] }
}
}
Playground
Sample of my database:
{
name: 'The Perks of Being a Wallflower'
genres: ['Drama', 'Romance']
rating: 8
},
{
name: 'The Edge of Seventeen'
genres: ['Drama', 'Comedy']
rating: 7.3
},
{
name: 'Little Women',
genres: ['Drama', 'Romance']
rating: 7.8
}
I want to project the first element of the genres array if it's Comedy or Romance.
I tried this :
db.shows.find(
{},
{ genres: { $elemMatch: { $or: [{ $eq: 'Drama' }, {$eq: 'Comedy'}] } } }
);
But it gives me this error "unknown top level operator: $eq".
Use $in
The $in operator selects the documents where the value of a field equals any value in the specified array. To specify an $in expression, use the following prototype:
Demo - https://mongoplayground.net/p/MAuWUeP9GIE
db.collection.find({
genres: {
$elemMatch: {
"$in": [ "Drama", "Comedy" ]
}
}
})
Demo - https://mongoplayground.net/p/JJJkoHuz_DZ
db.collection.find({},
{
genres: {
$elemMatch: {
"$in": [ "Drama", "Comedy" ]
}
}
})
For example, with this data:
{id: 1, fname: "Barry", lname: "Sullivan"}
{id: 2, fname: "Sarah", lname: "Bailey"}
{id: 3, fname: "Drake", lname: "Barry"}
Is there a way, with a single query, that I could check to see if anyone had the same lname as id: 1 fname?
You can use $facet to run two separate queries and get the result as one document. This will give you two separate arrays: 1-element with id:1 and the other documents. Then you can simply run $filter to get matching lnames:
db.collection.aggregate([
{
$facet: {
first: [ { $match: { id: 1 } } ],
others: [ { $match: { $expr: { $ne: [ "$id", 1 ] } } } ]
}
},
{
$unwind: "$first"
},
{
$project: {
matches: {
$filter: {
input: "$others",
cond: { $eq: [ "$$this.lname", "$first.lname" ] }
}
}
}
}
])
Mongo Playground