MongoDB : projection matches all element corresponding to query - mongodb

Supposing the following data format
{
_id: "1234",
tag: "MyTag",
members: [{
name: "James", age: 54
}, {
name: "John", age: 22
}, {
name: "Eric", age: 36
}],
},
{
_id: "7896",
tag: "MyTag2",
members: [{
name: "Philip", age: 6
}, {
name: "Mark", age: 14
}, {
name: "Maya", age: 64
}],
}
How can I request all the person over 30 to get that result
{
_id: "1234",
tag: "MyTag",
members: [{
name: "James", age: 54
}, {
name: "Eric", age: 36
}],
},
{
_id: "7896",
tag: "MyTag2",
members: [{
name: "Maya", age: 64
}],
}
Basically this request
db.MyDB.find({'members':{'$elemMatch':{'age':{'$gt':30}}}}, {'tag:1', 'members.$':1})
but where the .$ operator does not return only the first or each list
(I'm using python api but I think the question applies more generally)
Thanks a lot!

The positional operator $ and the $elemMatch projection operator both only match the first element that matches the query.
If you want to get all elements that match, use aggregation and $filter the array.

Related

Filter documents that do not have a property set - Prisma

I have this example data:
const example = [
{
productId: 1,
name: "Husdady",
age: 21
},
{
name: "Vereth",
age: 19
},
{
name: "Mikaela",
age: 17
},
{
name: "Richard",
age: 20
},
{
productId: 4,
name: "Markus",
age: 24
},
{
productId: 8,
name: "Cecilia",
age: 18
},
{
productId: 1,
name: "Anton",
age: 16
}
]
console.log(example)
I have a 'User' model in prisma. So what I must achieve is to filter all those users that do not have a defined productId and also filter by productId.
For example, I want to filter all users that have product Id 1 and also all users that do not have a productId defined
I have tried with the following filter
this.prisma.users.findMany({
where: {
OR: [
{ productId: undefined },
{ productId: 1 }
]
}
})
This query gets me all the users that have '1' as the value of their 'productId' but it does not bring me the users that do not have a defined productId.
The expected result of the above query would be:
const result = [
{
productId: 1,
name: "Husdady",
age: 21
},
{
name: "Vereth",
age: 19
},
{
name: "Mikaela",
age: 17
},
{
name: "Richard",
age: 20
},
{
productId: 1,
name: "Anton",
age: 16
}
]
console.log(result)
I only get the users 'Husdady' and 'Anton'
const resultObtained = [
{
productId: 1,
name: "Husdady",
age: 21
},
{
productId: 1,
name: "Anton",
age: 16
}
]
console.log(resultObtained)
How I can resolve this problem?

How can I have array filter count from nested array

Data:
_id: ObjectId(''),
restaurantId: ObjectId(''),
orderId: ObjectId(''),
reviews: [
{
type: "food",
tags: ["good food", "nice food"]
},
{
type: "pricing",
tags: ["best price", "good price"]
}
]
Group by restaurant id
Get total reviews by type: Ex: food: 3, pricing: 2, ambience: 1
Group by type and tags and get counts:
Ex: food-superb: 1, food-loudt:2, pricing-superb: 2, ambience-superb: 1
Expected result:
{
_id: restaurantId,
types: {
food: 4,
....
},
tags: {
nicePrice: 20
}
}

How to make and requests in mongodb queries

I've worked on this for about an hour now and I can't figure anything out that works so sorry if this is obvious.
I want my query to only return results where every result matches, but right now it returns a result if at least one match is found.
My document looks like this...
{
country: 'Great Britain',
data: [
{
school: 'King Alberts',
staff: [
{
name: 'John',
age: 37,
position: 'Head Teacher'
},
{
name: 'Grace',
age: 63,
position: 'Retired'
},
{
name: 'Bob',
age: 25,
position: 'Receptionist'
}
]
},
{
school: 'St Johns',
staff: [
{
name: 'Alex',
age: 22,
position: 'Head of Drama'
},
{
name: 'Grace',
age: 51,
position: 'English Teacher'
},
{
name: 'Jack',
age: 33,
position: 'Receptionist'
}
]
},
// ... more schools
]
}
The query I'm currently making looks like...
{ 'data.staff.name': { $in: names } }
and the 'names' array that is being provided looks like ['Bob', 'Grace', 'John', 'Will', 'Tom']. Currently both schools are being returned when I make this query, I think it's because the 'names' array contains 'Grace' which is a name present at both schools and so the document it matching. Does anyone know if there's a query I could make so mongodb only returns the school object if every name in the 'names' array is a member of staff at the school?
You need to use the aggregation pipeline for this, after matching the document we'll just filter out the none matching arrays, like so:
db.collection.aggregate([
{
$match: {
"data.staff.name": {
$in: names
}
}
},
{
$addFields: {
data: {
$filter: {
input: "$data",
cond: {
$eq: [
{
$size: {
"$setIntersection": [
"$$this.staff.name",
names
]
}
},
{
$size: "$$this.staff"
}
]
}
}
}
}
}
])
Mongo Playground

How to get documents from collection compared with object? custom filtering function?

I have an object:
{
name: "John",
size: 10,
volume: 20
}
Documents on collection:
[{
id:1,
name: "Sara",
size: 10
},
{
id:2,
volume: 20
},
{
id:3,
name: "John",
size: 10
},
{
id:4,
size: 20
}]
Now I need to filter my collection using object - if every field(except id) from each document exists in object and the values are equal - then this document should be in query results:
[{
id:1, //!EXCLUDE - name not Sara
name: "Sara",
size: 10
},
{
id:2, //OK - volume matches volume in object - return document in results
volume: 20
},
{
id:3, //OK - name and size matches object - return in results
name: "John",
size: 10
},
{
id:4, //!EXCLUDE - size don't match size on object
size: 20
}]
So the final response would be:
[{
id:2, //OK - volume matches volume in object - return document in results
volume: 20
},
{
id:3, //OK - name and size matches object - return in results
name: "John",
size: 10
}]
How can I do it with mongo find or others? Maybe I should write my custom filtering function?
You can try something like this.
The query will select all documents where each field when exists and is equal to the value.
db.collection.find({
$and: [{
$or: [{
name: {
$exists: false
}
}, {
name: "John"
}]
}, {
$or: [{
size: {
$exists: false
}
}, {
size: 10
}]
}, {
$or: [{
volume: {
$exists: false
}
}, {
volume: 20
}]
}]
})

Pivoting data in MongoDB

I have an 'articles' collection, some sample data might look like this:
[
{body: 'Interesting news in Siberia and so on etc. etc. etc. and lolcats too',
author: 'John Doe',
tags: [{tid:24, name: "Siberia"},
{tid: 5231, name: "Lolcats"},]
},
{body: 'Something is going on in Siberia and France',
author: 'Jane Doe',
tags: [{tid:24, name: "Siberia"},
{tid: 6432, name: "France"},]
},
]
And my required ouput is a distinct list of tags:
[
{tid: 24, name: 'Siberia'},
{tid: 5231, name: 'Lolcats'},
{tid: 6432, name: 'France'},
]
I have been struggling with some mapReduce queries and distinct aggregation, but without result.
The simplest way to do this is:
db.articles.distinct("tags")
If you want to use aggregation framework (new in 2.2) it's a little longer:
db.articles.aggregate([{$unwind:"$tags"},
{$group:{_id:"$tags"}},
{$project:{tid:"$_id.tid",name:"$_id.name",_id:0}}
]).result
In mongo v2.2 you can do this with the aggregate function:
db.articles.aggregate([
{
// From each document, emit just the tags
$project: {
tags: 1
}
}, {
// Duplicate each document for each tags element it contains
$unwind: '$tags'
}, {
// Group the documents by the tag's tid and name
$group: {
_id: { tid: '$tags.tid', name: '$tags.name' }
}
}, {
// Reshape the document to exclude the _id and bring tid and name to the top level
$project: {
_id: 0,
tid: '$_id.tid',
name: '$_id.name'
}
}],
function (err, result) {
if (err) {
console.log('aggregation error: %s', err);
} else {
console.dir(result);
}
});
For your documents, this produces the following output:
[ { tid: 6432, name: 'France' },
{ tid: 5231, name: 'Lolcats' },
{ tid: 24, name: 'Siberia' } ]
db.articles.distinct("tags")
gives the following output:
[
{
"tid" : 24,
"name" : "Siberia"
},
{
"tid" : 5231,
"name" : "Lolcats"
},
{
"tid" : 6432,
"name" : "France"
}
]