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

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"
}
}
}
]
});

Related

MongoDB query for two input arrays at same index?

I have two arrays A and B of length n defined by the input,
fruit_ids = [{id: "id1"}, {id: "id2"}, {id:"id3"}];
fruit_names = [{name: "Orange"},{name: "Kiwi"},{name: "Banana"}]
and MongoDB documents
{ farm_id: "3344", fruits: [{name: "Orange", id:"id1"}, {name: "Kiwi", id:"id67"}]}
Now I want to write a Mongo query such that it pulls items from particular farm_id specified at array fruit_ids and fruit_names but at same index,
for example for the above input, I want for farm_id: 3344 {name: "Orange", id:"id1"} to get deleted.
Can anyone please help me.
You can use $pullAll operator to remove all the matching elements and build your update statement dynamically using below code:
var fruit_ids = [{id: "id1"}, {id: "id2"}, {id:"id3"}];
var fruit_names = [{name: "Orange"},{name: "Apple"},{name: "Banana"}];
var pullAll = {
$pullAll: { fruits: fruit_ids.map((id, index) => Object.assign(fruit_names[index], id)) }
}
db.col.update({ farm_id: 3344 }, pullAll)
This will only try to update the farm_id: 3344.
I was trying $pullAll as suggested by #mickl in his answer, but the thing is I had other fields inside my embedded documents and because $pullAll works only for exact matches, that's why I currently I am using $pull with $or on the array of embedded docs. I found this solution from this answer how-to-force-mongodb-pullall-to-disregard-document-order.
let arr = [{name: "Orange", id:"id1"}, {name: "Kiwi", id:"id67"}];
db.col.update(
{ farm_id: 3344 },
{ "$pull": { "fruits": { "$or": arr } }}
)

Mongoose Query to Find Unique Values

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?

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.

MongoDB Regex $and $or Search Query

I am trying to construct a query that will accept multiple fields that can be searched over using regex for partial field matching that also has a hard constraint on other fields.
Example:
Collection: "Projects"
Required Information: { propertyId: "abc", clientId: "xyz" }
Fields to be Searched: name, serviceType.name, manager.name
Currently, I have a query like this, but if there are no results it returns all the results, which isn't helpful.
{
'$and': [
{ propertyId: '7sHGCHT4ns6z9j6BC' },
{ clientId: 'xyz' },
{ '$or':
[
{ name: /HVAC/gi },
{ 'serviceType.name': /HVAC/gi },
{ 'manager.name': /HVAC/gi }
]
}
]
}
If anyone has any insight into this it would be much appreciated.
Example Document:
{
_id: "abc",
propertyId: "7sHGCHT4ns6z9j6BC",
clientId: "xyz"
name: "16.000.001",
serviceType: {
_id: "asdf",
name: "HVAC"
},
manager: {
_id: "dfgh",
name: "Patrick Lewis",
}
}
The expected result is to only find documents where propertyId = 7sHGCHT4ns6z9j6BC AND one at least one of the following keys: name, serviceType.name, or manager.name match an inputted string, in this case, it's HVAC and if none of the regex fields match, then return nothing.
UPDATE
The issue was with MongoDB, after restarting it, everything worked.
Try following script:
db.collection.find({
$and:[
{propertyId:"7sHGCHT4ns6z9j6BC"},
{
$or:[
{name: /HVAC/i},
{"serviceType.name": /HVAC/i},
{"manager.name": /HVAC/i}
]
}]
})
Query above will return a document or documents if and only if propertyId matches and either of name, serviceType.name or manager.name matches desired regex.

JugglingDB MongoDB Array of IDs

I'm using MongoDB adapter of Juggling-DB.
How can I run the equivalent of the following SQL query (IDs are fake)
SELECT * FROM APPS
WHERE
active = 1
AND (
user_id = '12345'
OR
id IN ('456', '789', '012')
)
that is "active apps that either belong to the given user or are available to all".
I tried the following:
{
where: {
active: true,
or: [
{
user_id: '12345'
},
{
_id: {
in: ['456', '789', '012']
}
}
]
}
}
But it returns nothing. Looking at the source code my wild guess is that it only converts strings to MongoDB.ObjectID when _id is a single string, not an array.
The mongodb query should be like:
db.apps.find({active: true, "$or": [{"user_id": "12345"}, {"_id": { $in: ['1', '2', '3']}}]})