Getting all unique field-array combinations from the entire collection in MongoDB - mongodb

Lets say I have the following collection
{
_id:1,
item:"cat"
keywords:['A','B']
},
{
_id:2,
item:"cat"
keywords:['B','C']
},
{
_id:3,
item:"dog"
keywords:['C','D']
},
I would like to get the following results:
[{"cat", "A"}, {"cat", "B"}, {"cat", "C"}, {"dog", "C"}, {"dog", "D"}]
Basically creating the combinations between item and keywords and removing duplicates.
Is that possible?
Thanks

You have to use $unwind on array and then you can use $group (by constant value) to get all elements into one array and $addToSet will handle uniqueness of specified pairs:
db.col.aggregate([
{
$unwind: "$keywords"
},
{
$group: {
_id: null,
unique: { $addToSet: { item: "$item", keyword: "$keywords" } }
}
}
])
You can then use onother $unwind on unique field to get a list of documents instead of single document as a result.

Related

How do I sort results based on a specific array item in MongoDB?

I have an array of documents that looks like this:
patient: {
conditions: [
{
columnToSortBy: "value",
type: "PRIMARY"
},
{
columnToSortBy: "anotherValue",
type: "SECONDARY"
},
]
}
I need to be able to $sort by columnToSortBy, but using the item in the array where type is equal to PRIMARY. PRIMARY is not guaranteed to be the first item in the array every time.
How do I set my $sort up to accommodate this? Is there something akin to:
// I know this is invalid. It's for illustration purposes
$sort: "columnToSortBy", {$where: {type: "PRIMARY"}}
Is it possible to sort a field, but only when another field matches a query? I do not want the secondary conditions to affect the sort in any way. I am sorting on that one specific element alone.
You need to use aggregation framework
db.collection.aggregate([
{
$unwind: "$patient.conditions" //reshape the data
},
{
"$sort": {
"patient.conditions.columnToSortBy": -1 //sort it
}
},
{
$group: {
"_id": "$_id",
"conditions": { //re group it
"$push": "$patient.conditions"
}
}
},
{
"$project": { //project it
"_id": 1,
"patient.conditions": "$conditions"
}
}
])
Playground

How to filter array (of objects) inside one document in mongo db based on some condition

I have the below docs collection structure.
I'm able to filter the documnents with various approaches, but not able to filter the array inside the documents.
{
"_id": "",
"employee": {
"EmployeeAttributeValues": {
"EmployeeAttributeValue": [
{.....
},
{.....
},
{.....
},
{.....
}
]
}
}
}
Kindly help me on how to filter the MemberAttributeValue array based on some condition.
you can use $where operator for custom filtering
https://docs.mongodb.com/v4.2/reference/operator/query/where/
db.test.aggregate([
{ $match: {_id: <ID>}},
{ $unwind: '$<ARRAY>'},
{ $match: {'<ARRAY>.a': {$gt: 3}}},
{ $group: {_id: '$_id', list: {$push: '$<ARRAY>.a'}}}
])

MongoDB aggregation pipeline: counting occurrences of words in list field from matching documents?

Here's a simplified example of what I'm trying to do. My documents all have various things and a keywords field with a list of strings as values. (The lists can contain duplicates, which are significant.) Suppose the following documents match the query:
{'original_id': 33, 'keywords': ['dog', 'cat', 'goat', 'dog']},
{'original_id': 34, 'keywords': ['dog', 'kitten', 'goat', 'moose']},
{'original_id': 35, 'keywords': ['moose', 'elk']}
I want to get back a map of the keywords found with the number of occurrences of each in the set of matching documents:
{'dog': 3, 'cat': 1, 'goat':2, 'kitten': 1, 'moose': 2, 'elk': 1}
(Note that dog in document 33 gets counted twice.)
I'm currently doing this from PyMongo by creating a Counter, calling collection_name.find(...) and then iterating through all the documents updating the counter with each keywords field. But I would like to make the process more efficient by doing it within MongoDB.
Is this kind of counting possible in an aggregation pipeline? If so, how?
$unwind deconstruct keywords array
$group by keywords and count total
$group by null and construct array of key-value pair
$arrayToObject convert above array to object key-value format
$replaceRoot to replace above converted object to root
db.collection.aggregate([
{ $unwind: "$keywords"c },
{
$group: {
_id: "$keywords",
count: { $sum: 1 }
}
},
{
$group: {
_id: null,
keywords: {
$push: {
k: "$_id",
v: "$count"
}
}
}
},
{ $replaceRoot: { newRoot: { $arrayToObject: "$keywords" } } }
])
Playground

MongoDB - select distinct values from a collection where values are separated by comma

To get the list of distinct values from DB and the collection called 'names' is as easy as doing this:
db.name.distinct('names')
However, I inherited a MongoDB where names contain values separated by a comma.
So doing db.name.distinct('names') returns JSON that contains values like this:
names
--------
[
"name1,name2",
"name2,name3",
"name4,name1,name3"
]
I need to get the list of distinct values from 'names', so it looks like this:
names
--------
[
"name1",
"name2",
"name3",
"name"
]
Do I need to go about this programmatically?
You can try,
$reduce names array as input, $split value with , and it will return array, $setUnion will join array and get union/unique array from set,
db.collection.aggregate([
{
$project: {
names: {
$reduce: {
input: "$names",
initialValue: [],
in: {
$setUnion: [{ $split: ["$$this", ","] }, "$$value"]
}
}
}
}
}
])
Playground
If you want unique names from all records then try,
$project skipped, its same as above query
$unwind deconstruct names array
$group by null and get unique values from name using $addToSet
// skipped $project from above query
{ $unwind: "$names" },
{
$group: {
_id: null,
names: { $addToSet: "$names" }
}
}
Playground

Remove element from array aggregate

How can I loop through an array and remove a specific element based on a field.
Here is the layout I have - it is in a collection called cases:
** The collection contains a companyID, cases [Array], lastModified **
So I will have to use an aggregate to unwind the cases and then search for the casenumber where it equals '17':
db.cases.aggregate([
{ $match: { companyID: 218}},
{ $unwind: '$cases' },
{ $match: {'cases.casenumber': '17'} }
])
This returns:
But now I want to delete just that specific item.
Thanks.
You can use of an updateMany request. First argument is the matching condition, the second is the action.
$pull is a special keyword that will remove matching elements from arrays.
db.collection.updateMany({
companyID: 218,
}, {
$pull: {
cases: {
casenumber: 17,
},
},
})
https://docs.mongodb.com/manual/reference/method/db.collection.updateMany/
https://docs.mongodb.com/manual/reference/operator/update/pull/
Example from the doc :
db.profiles.update( { _id: 1 }, { $pull: { votes: { $gte: 6 } } } )