Mongoose Query to Find Unique Values - mongodb

In my MongoDB backend I want to create an endpoint that returns all the unique values for a property called department. Now, if I were doing this in IntelliShell I would just do something like:
db.staffmembers.distinct( "department" )
This will return an array of all the values for department.
But how do you return all unique values within a Mongoose find() query like this one?
Staffmember.find({ name: 'john', age: { $gte: 18 }});
In other words, what would the syntax look like if I want to use a find() like above, to return all unique values for department within the "staffmembers" collection?

You can use .aggregate() and pass your condition into $match stage and then use $addToSet within $group to get unique values.
let result = await Staffmember.aggregate([
{ $match: { name: 'john', age: { $gte: 18 }} },
{ $group: { _id: null, departments: { $addToSet: "$department" } } }
]);

We can use find and distinct like this for the above scenario. Aggregate might be a little overkill. I have tried both the solutions below.
Staffmember.find({name: 'john', age: {$gte: 18}}).distinct('department',
function(error, departments) {
// departments is an array of all unique department names
}
);
Staffmember.distinct('department', {name:'john', age:{ $gte: 18 }},
function(error, departments) {
// departments is an array of all unique department names
}
);
This link just nails it with all different possibilities:
How do I query for distinct values in Mongoose?

Related

How to combine $in and $elemMatch properly with MongoDB (PyMongo)

In my database, I've got data stored according to the following scheme:
{'_id': ...,
'names': [{'first': ...,
'last': ...},
{'first': ...,
'last': ...},
...
],
...
}
Now, in my program, I get a list of names according to the following scheme:
name_list = [(first_name1, last_name1), (first_name2, last_name2), ...]
What I want, is to find all documents where any of these combinations of first/last name found in name_list are contained in the names array.
If I'd have just one name to check (instead of a list), I'd use the following query:
query = {'names':
{'$elemMatch':
{'first_name': first_name,
'last_name': last_name}
}}
So I could supply this query for every name in the list, doing something like:
all_results = []
for first_name, last_name in name_list:
rv := # Result from query
# combine all_results and rv
But I feel like there should be a better way to do this.
A multi-elem match query could be created from name_list using $or and $elemMatch which works in a way to find the documents where any of those first/last combination matches.
Query: considering name_list to be
//[(first_name1, last_name1), (first_name2, last_name2), (first_name3, last_name3)]
db.collection.find({
$or: [
{
names: {
$elemMatch: {
first: "first_name1",
last: "last_name1"
}
}
},
{
names: {
$elemMatch: {
first: "first_name2",
last: "last_name2"
}
}
},
{
names: {
$elemMatch: {
first: "first_name3",
last: "last_name3"
}
}
}
]
});

How to update/insert into a mongodb collection with the result of aggregate query on the another collection

I have a below aggregate query and I wanted to insert/update this result into another collection.
db.coll1.aggregate([
{
$group:
{
_id: "$collId",
name: {$first:"$name"} ,
type: {$first:"$Type"} ,
startDate: {$first:"$startDate"} ,
endDate: {$first:"$endDate"}
}
}
])
I have another collection coll2, which has fields in the above query and some additional fields too.
I may already have the document created in Coll2 matching the _id:collId in the above query. If this Id matches, I want to update the document with the field values of the result and keep the values of other fields in the document.
If the Id does not exists, it should just create a new document in Coll2.
Is there a way to do it in the MongoDB query. I want to implement this in my Spring application.
We can use $merge to do that. It's supported from Mongo v4.2
db.collection.aggregate([
{
$group:{
"_id":"$collId",
"name": {
$first:"$name"
},
"type":{
$first:"$Type"
},
"startDate":{
$first:"$startDate"
},
"endDate":{
$first:"$endDate"
}
}
},
{
$merge:{
"into":"collection2",
"on":"_id",
"whenMatched":"replace",
"whenNotMatched":"insert"
}
}
])

mongo: update subdocument's array

I have the following schema:
{
_id: objectID('593f8c591aa95154cfebe612'),
name: 'test'
businesses: [
{
_id: objectID('5967bd5f1aa9515fd9cdc87f'),
likes: [objectID('595796811aa9514c862033a1'), objectID('593f8c591ba95154cfebe790')]
}
{
_id: objectID('59579ff91aa9514f600cbba6'),
likes: [objectID('693f8c554aa95154cfebe146'), objectID('193f8c591ba95154cfeber790')]
}
]
}
I need to update "businesses.likes" where businesses._id equal to a center value and where businesses.likes contains certain objectID.
If the objectID exists in the array, I want to remove it.
This is what I have tried and didn't work correctly, because $in is searching in all the subdocuments, instead of the only subdocument where businesses._id = with my value:
db.col.update(
{ businesses._id: objectID('5967bd5f1aa9515fd9cdc87f'), 'businesses.likes': {$in: [objectID('193f8c591ba95154cfeber790')]}},
{$pull: {'businesses.$.likes': objectID('193f8c591ba95154cfeber790')}}
)
Any ideas how how I can write the query? Keep in mind that businesses.likes from different businesses can have the same objectID's.

Aggregate query with no $match

I have a collection in which unique documents from a different collection can appear over and over again (in example below item), depending on how much a user shares them. I want to create an aggregate query which finds the most shared documents. There is no $match necessary because I'm not matching a certain criteria, I'm just querying the most shared. Right now I have:
db.stories.aggregate(
{
$group: {
_id:'item.id',
'item': {
$first: '$item'
},
'total': {
$sum: 1
}
}
}
);
However this only returns 1 result. It occurs to me I might just need to do a simple find query, but I want the results aggregated, so that each result has the item and total is how many times it's appeared in the collection.
Example of a document in the stories collection:
{
_id: ObjectId('...'),
user: {
id: ObjectId('...'),
propertyA: ...,
propertyB: ...,
etc
},
item: {
id: ObjectId('...'),
propertyA: ...,
propertyB: ...,
etc
}
}
users and items each have their own collections as well.
Change the line
_id:'item.id'
to
_id:'$item.id'
Currently you group by the constant 'item.id' and therefore you only get one document as result.

How to use $nin to exclude array of objects in Meteor, MongoDB, React

I want to exclude array of objects from my query when fetching Object.
mixins: [ReactMeteorData],
getMeteorData() {
// Standard stuff
var selector = {};
var handle = Meteor.subscribe('videos', selector);
var data = {};
// Return data = OK!
data.video = Videos.findOne({ _id: this.props._id });
// Fetch objects with $lte _id to exclude, Return id_ field array = OK!
data.excnext = Videos.find({ votes: data.video.votes, _id: {$lt: data.video._id}},{fields: {_id:1}},{sort: {_id: 1},limit:50}).fetch();
// Fetch objects by Votes, Exclude array of objects with $nin = NOT OK!
data.next = Videos.findOne({ _id: { $ne: this.props._id, $nin:data.excnext }, votes: { $gte: data.video.votes}},{ sort: { votes: 1, _id: 1 }});
return data;
},
Why is $nin not working like expected?
Am unsure if am doing something wrong when fetching my array or when returning it using $ini
Logged example = data.excnext
[ { _id: 'A57WgS6n3Luu23A4N' },
{ _id: 'JDarJMxPAnmeTwgK4' },
{ _id: 'DqaeqTfi8RyvPPTiD' },
{ _id: 'BN5qShBJzd6N7cRzh' },
{ _id: 'BSw2FAthNLjav5T4w' },
{ _id: 'Mic849spXA25EAWiP' } ]
Grinding on my first app using this stack. My core setup is Meteor,Flow-router-ssr, React-layout, MongoDB, React. What am trying to do is to fetch next object by votes, problem is that sometimes several objects have the same amount of votes, so then i need to sort it by id and exclude unwanted objects.
First of all am seeking the answer how to use $nin correct in my example above
Second, Suggestions / examples how to do this better are welcome, There could be a much better and simpler way to do this, Information is not easy to find and without any previous experience there is a chance that am complicating this more than needed.
// ❤ peace
In this case $nin needs an array of id strings, not an array of objects which have an _id field. Give this a try:
data.excnext = _.pluck(Videos.find(...).fetch(), '_id');
That uses pluck to extract an array of ids which you can then use in your subsequent call to findOne.