mongodb Grouping fields into a list - mongodb

I have:
{
"name": "A Name"
"ph": {"phone": "1111"}
"alt_ph": {"phone": "2222"}
}
I would like to query mongo in a way the result is like the below:
"A Name": ["1111", "2222"]
and if possible the list to be unique.
Not sure how to go about it

You can use $setUnion to make sure that your result contains unique values along with $replaceRoot combined with $arrayToObject to get your key evaluated dynamically based on other field's value:
db.collection.aggregate([
{
$replaceRoot: {
newRoot: {
$arrayToObject: [[ { k: "$name", v: { $setUnion: [ ["$ph.phone"], ["$alt_ph.phone"] ] } } ]]
}
}
}
])
Mongo Playground

Ok - I tried this & it's working, but not sure how to make it unique:
"$project": {
"_id": 0, "name": 1,
"phones": ["$ph", "$alt_ph"]
}
Gives me desired, but:
I will get null, if field is empty or field does not exist and i can get duplicates as well.

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

Getting concrete elements by element field in mongoDB

I know that by the title it is not very clear what is my problem, so let me explain it with an example.
Let's suppose I have a collection in a mongo database called tweets whose elements look like this:
{
"id": "tweet_id",
"text": "this is the tweet's text",
"user": {
"id": "user_id",
"name": "user_name",
}
}
Let's suppose that we have 100 documents that look like that, and 10 users with diferent ids and names.
How would look a query to know see the different user_id s that exist in the collection and their names?
The result I want would look like this:
{"user_id1": "user_name1"}
{"user_id2": "user_name2"}
...
{"user_id10": "user_name10"}
Thank you for your help
You can use this aggregation query:
First $group by user.id to get all differents user ids with the name.
And then use $replaceRoot with $arrayToObject to get the desired output format.
db.collection.aggregate([
{
"$group": {
"_id": "$user.id",
"name": {
"$first": "$user.name"
}
}
},
{
"$replaceRoot": {
"newRoot": {
"$arrayToObject": [
[
{
"k": "$_id",
"v": "$name"
}
]
]
}
}
}
])
Example here

Mongodb | Check if any other key exist in object field other than given key in object field | length of object field type

We have one use case. Let's suppose I have two documents as given below.
{
"_id": ObjectID('123'),
"test": {
"a":1,
"b":1
}
},
{
"_id": ObjectID('456'),
"test": {
"a":1
}
Now I want those result whose "test" field has property other than "a" or in another way, I want those objects which have multiple keys/properties in a "test" field or to check the size of an object greater than 1
So, the result will be:
{
"_id": ObjectID('123'),
"test": {
"a":1,
"b":1
}
}
I tried to make a query for the above output as shown below and it working as expected
db.col.find({"test": {"$gt": {"a": 1} }})
So, is that the right thing to do? Any downside of doing it? We want to lever-age indexes as well
Please let me know your inputs on this.
Thanks.
Demo - https://mongoplayground.net/p/bluMAU_0Dre
Use $objectToArray { "$objectToArray": "$test" } convert to array
get the size $size os array
$gt check if it's more than 1
db.collection.find({
$expr: {
$gt: [ { $size: { "$objectToArray": "$test" } }, 1 ]
}
})
$expr
Allows the use of aggregation expressions within the query language.

Robomongo query to return a list of ids

I want to query my database in Mongo and then be able to copy and paste the list of ids the query returns.
I know I can project the _id like
db.getCollection('mymodel').find({}}, { _id: 1 })
But I want to be able to copy and paste the result as an array of ids, is there a way to achieve this with Robomongo/Mongo?
Is this query you want?
Using aggregate add all _ids to a set:
db.collection.aggregate([
{
"$group": { "_id": null, "ids": { "$addToSet": "$_id" } }
},
{
"$project": { "_id": 0 }
}
])
And the ouput is similar to this, an array called ids with all id:
"ids": [
ObjectId("5a934e000102030405000000"),
ObjectId("5a934e000102030405000004"),
ObjectId("5a934e000102030405000001"),
ObjectId("5a934e000102030405000005"),
ObjectId("5a934e000102030405000003"),
ObjectId("5a934e000102030405000002")
]
You can use $match to filter the documents you want to get the id like this example.

Is there a way to give order field to the result of MongoDB aggregation?

Is there any way to give order or rankings to MongoDB aggregation results?
My result is:
{
"score":100
"name": "John"
},
{
"score":80
"name": "Jane"
},
{
"score":60
"name": "Lee"
}
My wanted result is:
{
"score":100
"name": "John",
"rank": 1
},
{
"score":80
"name": "Jane"
"rank": 2
},
{
"score":60
"name": "Lee"
"rank": 3
}
I know there is a operator called $includeArrayIndex but this only works with $unwind operator.
Is there any way to give rank without using $unwind?
Using $unwind requires grouping on my collection, and I'm afraid grouping pipeline would be too huge to process.
The other way is to use $map and add rank in document using its index, and don't use $unwind stage because it would be single field array you can directly access using its key name as mention in last line of code,
$group by null and make array of documents in root array,
$map to iterate loop of root array, get the index of current object from root array using $indexOfArray and increment that returned index number using $add because index start from 0, and that is how we are creating rank field, merge object with current element object and rank field using $mergeObjects
let result = await db.collection.aggregate([
{
$group: {
_id: null,
root: {
$push: "$$ROOT"
}
}
},
{
$project: {
_id: 0,
root: {
$map: {
input: "$root",
in: {
$mergeObjects: [
"$$this",
{
rank: { $add: [{ $indexOfArray: ["$root", "$$this"] }, 1] }
}
]
}
}
}
}
}
]);
// you can access result using root key
let finalResult = result[0]['root'];
Playground