I have the following docs in my DB:
{
Age : 10,
BPM : 20,
Price: 6
},
{
Age : 12,
BPM : 30,
Price: 9
},
{
Age : 15,
BPM : 40,
Price: 6
},
{
Age : 10,
BPM : 46,
Price: 7
},
{
Age : 20,
BPM : 60,
Price: 8
}
I need help to write an aggregate query to find out for groups (age range 10-15 & BPM 20-50), (age range 15 - 20 & BPM 40 - 90), What is the total sum of prices for each group of ranges. There can be few more groups of ranges.
When you have multiple conditions, one option is to use $switch:
db.collection.aggregate([
{$set: {
group: {$switch: {
branches: [
{case: {
$and: [
{$gte: ["$Age", 10]},
{$lt: ["$Age", 15]},
{$gte: ["$BPM", 20]},
{$lt: ["$BPM", 50]}
]
}, then: 1},
{case: {
$and: [
{$gte: ["$Age", 15]},
{$lt: ["$Age", 20]},
{$gte: ["$BPM", 40]},
{$lt: ["$BPM", 90]}
]
}, then: 2}
],
default: "Did not match"
}}
}},
{$group: {_id: "$group", totalSumOfPrices: {$sum: "$Price"}}}
])
See how it works on the playground example
Related
I have a json as data as below :
[
{
"id": 1,
"shopname": "seven up",
"shopkeeper": "John",
"salesbooks": [
{
"bookid": 11,
"bookname": "Tom-story",
"catagories" : 1,
"soldout": false
},
{
"bookid": 12,
"bookname": "Iron-Man",
"catagories" : 2,
"soldout": true
}
]
},
{
"id": 2,
"shopname": "Richmond Shop",
"shopkeeper": "Jame",
"salesbooks": [
{
"bookid": 11,
"bookname": "Tom-story",
"catagories" : 2,
"soldout": false
},
{
"bookid": 13,
"bookname": "PeterPan",
"catagories" : 2,
"soldout": true
}
]
},
{
"id": 3,
"shopname": "Worchester Shop",
"shopkeeper": "Jame",
"salesbooks": [
{
"bookid": 15,
"bookname": "Jurias Park",
"catagories" : 1,
"soldout": false
},
{
"bookid": 16,
"bookname": "Champion",
"catagories" : 1,
"soldout": true
}
]
}
]
My expected result should be:
[
{
"id": 1,
"shopname": "seven up",
"shopkeeper": "John",
"salesbooks": [
{
"bookid": 11,
"bookname": "Tom-story",
"catagories" : 1,
"soldout": false
},
{
"bookid": 12,
"bookname": "Iron-Man",
"catagories" : 2,
"soldout": true
}
]
}
]
Normally, if require to get shop which have book on sales , I can do it
var bookshop = bookshops.where(w=>w.salesbooks.any(a=>a.soldout==false));
But this time, if I also want to filter out if count(salesbooks.catagories) > 1?
That mean I want to filter out id:2 bookshop , as both of salesbook.catagories are same.
How to write the linq in c#?
p.s. I try
var bookshop = bookshops.where(w=>w.salesbooks.any(a=>a.soldout==false) && w.salebooks.GroupBy(g=>g.catagories).count==1);
the result is still show Richmond shop
Thank you
If you just want to filter out bookshops with categories more than 1. In C# You can just group by categories and have a check for >1 rather than ==1 i.e.
C# code
var bookshop = bookshops.Where(w => w.salesbooks.Any(a => a.soldout == false) && w.salesbooks.GroupBy(g => g.catagories).Count() > 1;
you can use another clause like
var bookshop = bookshops.Where(w => w.salesbooks.Any(a => a.soldout == false) && !w.salesbooks.Any(a => a.catagories < 2) );
Hope this helps
In cosmos DB the document structure is like this
[
{
"id": "1",
"Plants": [
{
"PlantId": 3,
"UniqueQualityId": "3_pe55d74fc5f92b11ab3fe"
},
{
"PlantId": 4,
"UniqueQualityId": "3_pe55d74fc5sdfmsdfklms"
},
{
"PlantId": 10,
"UniqueQualityId": "3_pe55d7akjdsj6ysdssdsd"
},
{
"PlantId": 12,
"UniqueQualityId": "5_fdffpe55d7akjdsj6ysds"
}
],
"CompletionTime": 36
},
{
"id": "2",
"Plants": [
{
"PlantId": 3,
"UniqueQualityId": "3_pe55d74fc5f92b11ab3fe"
},
{
"PlantId": 4,
"UniqueQualityId": "3_pe55d74fc5sdfmsdfklms"
},
{
"PlantId": 3,
"UniqueQualityId": "3_pe55d74fc5f92b11ab3fe"
},
{
"PlantId": 5,
"UniqueQualityId": "3_pe55d7akjdsj6ysdssdsd"
}
],
"CompletionTime": 36
},
{
"id": "2",
"Plants": [
{
"PlantId": 10,
"UniqueQualityId": "3_pe55d74fc5f92b11ab3fe"
},
{
"PlantId": 11,
"UniqueQualityId": "3_pe55d74fc5sdfmsdfklms"
}
],
"CompletionTime": 36
}
]
I need to get the collection of plants that meets specific condition:
For example, the query is written as to fetch Plants along with some parent data where PlantId in ("3","4") , then the output am expecting is
[
{
"id": "1",
"Plants": [
{
"PlantId": 3,
"UniqueQualityId": "3_pe55d74fc5f92b11ab3fe"
},
{
"PlantId": 4,
"UniqueQualityId": "3_pe55d74fc5sdfmsdfklms"
}
],
"CompletionTime": 36
},
{
"id": "2",
"Plants": [
{
"PlantId": 3,
"UniqueQualityId": "3_pe55d74fc5f92b11ab3fe"
},
{
"PlantId": 4,
"UniqueQualityId": "3_pe55d74fc5sdfmsdfklms"
}
}
],
"CompletionTime": 36
}
]
Here in the plants array it should only contain the items that meet the filtered condition.
I have tried the following methods
SELECT root["Plants"],root.id FROM root
WHERE EXISTS(select value plant FROM plant in root.Plants WHERE plant.PlantId in ("3","4"))
SELECT root.id,root.Plants FROM root where ARRAY_CONTAINS(c.Plants,{"PlantId": "3"},true)
If any of the plant items meet the condition it is returning the entire plant array instead of specific items.
Is there any method where it will return only the specific array items that meet the condition?
You can use the following query to get the result to get the output you want:
SELECT
c.id,
ARRAY(
SELECT VALUE p
FROM p IN c.Plants
WHERE p.PlantId IN (3, 4)
) AS Plants,
c.CompletionTime
FROM c
Although my personal preference would be the query below that does the same, but creates a seperate item for every plant. From the context I understand that you are looking for specific plants and capture some parent data in the result. In that case it would make sense to have seperate results by the plant.
SELECT
c.id,
p AS Plant,
c.CompletionTime
FROM c
JOIN
(
SELECT VALUE p
FROM p IN c.Plants
WHERE p.PlantId IN (3, 4)
) AS p
I'm currently would like to sort a list of Object on LINQ, where I need to Sort and Remove Duplication if founded. It would be easier if I provide simple example for my scenario.
This is my current list (I have simplified it and sorted):
{
"data": [
{
"userActivityId": 17,
"deviceId": 2,
"name": "Apple Iphone X 2013 32 GB",
"iconResource": "phone_apple_iphone_x.jpg"
},
{
"userActivityId": 16,
"deviceId": 1,
"name": "Apple Iphone X 2013 16 GB",
"iconResource": "phone_apple_iphone_x.jpg"
},
{
"userActivityId": 15,
"deviceId": 1,
"name": "Apple Iphone X 2013 16 GB",
"iconResource": "phone_apple_iphone_x.jpg"
},
{
"userActivityId": 14,
"deviceId": 2,
"name": "Apple Iphone X 2013 32 GB",
"iconResource": "phone_apple_iphone_x.jpg"
},
]
}
Expected Result
What I really wanted:
{
"data": [
{
"userActivityId": 17,
"deviceId": 2,
"name": "Apple Iphone X 2013 32 GB",
"iconResource": "phone_apple_iphone_x.jpg"
},
{
"userActivityId": 16,
"deviceId": 1,
"name": "Apple Iphone X 2013 16 GB",
"iconResource": "phone_apple_iphone_x.jpg"
},
]
}
Here we can see then userActivityId 15 and 14 is removed, because the name already existed there.
So we can sort unique by using name.
What I have tried
First Attempt
This is the original code for the sorting functionality.
var data = new leapserverdbContext().TbUserActivity
.OrderByDescending(id => id.UserActivityId)
.Select(sel => new
{
sel.UserActivityId,
sel.TbDevice.DeviceId,
Name = sel.TbDevice.TbDeviceBrand.Name + " " + sel.TbDevice.Name +
" " + sel.TbDevice.TbDeviceYear.Year + " " + sel.TbDevice.TbDeviceSize.Size,
sel.TbDevice.IconResource
})
.ToList();
Second Attempt
I try to put Distinct(), but it won't work because there is userActivityId and it is unique (because incrementing).
Third Attempt
I try to remove userActivityId, to avoid it being considered while Distinct(), but then my data wont be sorted. Because sorting depends
on userActivityId.
Forth Attempt
I tried using GroupBy (x => x.Name), so focus divide based on unique Name.
But it just start dividing them into array as provided below:
{
"data": [
[
{
"userActivityId": 16,
"deviceId": 1,
"name": "Apple Iphone X 2013 16 GB",
"iconResource": "phone_apple_iphone_x.jpg"
},
{
"userActivityId": 15,
"deviceId": 1,
"name": "Apple Iphone X 2013 16 GB",
"iconResource": "phone_apple_iphone_x.jpg"
}
],
[
{
"userActivityId": 17,
"deviceId": 2,
"name": "Apple Iphone X 2013 32 GB",
"iconResource": "phone_apple_iphone_x.jpg"
},
{
"userActivityId": 14,
"deviceId": 2,
"name": "Apple Iphone X 2013 32 GB",
"iconResource": "phone_apple_iphone_x.jpg"
}
]
]
}
Now it is sorted and divided based on their unique name. But the layout is complete different.
What in my mind would do is get the first array (As it is the latest), but how can I combine and produce the Expected Result (See Above).
Is there anyway to solve this small problem of mine?
Apart from using an EqualityComparer like previously suggested you could also just group your result on name-property since you in this case are using an anonymous type.
var data = new leapserverdbContext().TbUserActivity
.OrderByDescending(id => id.UserActivityId)
.Select(sel => new
{
sel.UserActivityId,
sel.TbDevice.DeviceId,
Name = sel.TbDevice.TbDeviceBrand.Name + " " + sel.TbDevice.Name +
" " + sel.TbDevice.TbDeviceYear.Year + " " + sel.TbDevice.TbDeviceSize.Size,
sel.TbDevice.IconResource
})
.GroupBy(x => x.Name)
.Select(g => g.First())
.ToList();
You can create an Equality Comparer:
public class ActivityComparer : IEqualityComparer<TbUserActivity>
{
public bool Equals(TbUserActivity x, TbUserActivity y)
{
if (x == null && y == null)
return true;
else if (x == null || y == null)
return false;
return x.name == y.name
}
public int GetHashCode(TbUserActivity obj)
{
return obj.userActivityId;
}
}
And then just:
result.Distinct(new ActivityComparer());
From the mongo sample let's say we have a collection like this;
{ "_id" : 1, "semester" : 1, "grades" : [ 70, 87, 90 ] }
{ "_id" : 2, "semester" : 1, "grades" : [ 90, 88, 92 ] }
{ "_id" : 3, "semester" : 1, "grades" : [ 85, 100, 90 ] }
{ "_id" : 4, "semester" : 2, "grades" : [ 79, 85, 80 ] }
{ "_id" : 5, "semester" : 2, "grades" : [ 88, 88, 92 ] }
{ "_id" : 6, "semester" : 2, "grades" : [ 95, 90, 96 ] }
and then the query is like this;
db.students.find( { semester: 1, grades: { $gte: 85 } },
{ "grades.$": 1 } )
which results to this ;
{ "_id" : 1, "grades" : [ 87 ] }
{ "_id" : 2, "grades" : [ 90 ] }
{ "_id" : 3, "grades" : [ 85 ] }
I would like to have a result like ;
{"grades": [87, 90, 85]}
on one array.
c# code I implemented gives me array of LogLists, code is different then above but the operation is quite the same ;
var result = collectionServerName.Find(x => x.LogList.Any(p => p.Ip.Contains("192")))
.Project(Builders<ServerName>.Projection.Exclude("_id").Include("LogList"))
.ToList();
I have tried following code;
var result = collectionServerName.Find(x => x.LogList.Any(p => p.Ip.Contains("192"))).Project(t => t.LogList.SelectMany(k => k)).ToList();
but it gives me following compile error for SelectMany
The type arguments for method 'Enumerable.SelectMany<TSource, TResult>(IEnumerable<TSource>, Func<TSource, IEnumerable<TResult>>)' cannot be inferred from the usage. Try specifying the type arguments explicitly
If I use only Select ;
var result = collectionServerName.Find(x => x.LogList.Any(p => p.Ip.Contains("192"))).Project(t => t.LogList.Select(k => k)).ToList();
The results type is List<IEnumarable<Log>> which I dont intend to have
The reason why I need as one array is I have to paginate the results before retrieving them. I am using c#. Any help would be appreciated.
What about this one for c# ?
For all grades in one Enumerable
collection
.AsQueryable()
.SelectMany(x => x.grades);
And for paging just add Skip(10), Take(10) extension methods.
I want to be able to perform union and then intersection.
My Document strucuture:
{
"_id" : 1,
"items" : [
52711,
201610,
273342,
279449,
511250
]
},
{
"_id" : 2,
"items" : [
246421,
390200
]
}
This collection contains of thousands of Documents of above form.
I want to perform Union on set of documents and then perform intersection on set returned from Union.
For example:
Set 1 contains Id: [1,2,3,4,5]
Set 2 Contains Id: [3,4,5,6,7,8]
Set 3 Contains Id: [12,14,15,16,17]
It should union all list items in set 1 and set 2 and set 3. Then perform Intersection on result of each set.
So far, I have got query that does union of list as following:
db.getCollection('Test').aggregate([
{ "$match": { "_id": { "$in": [1, 2, 3] } } },
{
"$group": {
"_id": 0,
"data": { "$push": "$items" }
}
},
{
"$project": {
"items": {
"$reduce": {
"input": "$data",
"initialValue": [],
"in": { "$setUnion": ["$$value", "$$this"] }
}
}
}
}
])
Also I am doing all this in c# right now:
var group = new BsonDocument
{
{ "_id", 0 },
{
"data", new BsonDocument {{"$push", "$items" } }
}
};
var project = new BsonDocument
{
{"items", new BsonDocument
{
{ "$reduce", new BsonDocument
{
{ "input", "$data"},
{ "initialValue", new BsonArray()},
{ "in", new BsonDocument { {"$setUnion", new BsonArray { "$$value", "$$this" }}}}
}
}
}
}
};
var result = qaCollection.Aggregate()
.Match(Builders<QAList>.Filter.In(x => x.Id, list))
.Group(group)
.Project(project)
.FirstOrDefault();
This query takes some time since it could return large data. So it would really nice if i can pass multiple sets and it would union separate sets and intersect them so data is not to big to return.
thanks in advance..
Answer based on the answer given to question 24824361:
There is no function to automatically do an intersection in MongoDB across several different documents. However, it is possible to calculate the intersection by taking this approach:
note the number of documents you are intersecting
unwind the items array
count the occurrence of each item
match only those items whose occurrence count matches the number of documents from step 1
So for example if you are taking the intersection of items in 3 documents, then you unwind the items, count the number of times each item comes up, and finish with just the items which come up 3 times.
This will only work if each document's items array has no duplicates, of course.
So for example, if the source data is like this:
db.test_unionintersection_stackoverflow_42686348.insert([
{ "_id" : 1,
"items" : [ 10, 20, 30, 40, 50 ]},
{ "_id" : 2,
"items" : [ 20, 30, 40, 50, 60, 70, 80 ]},
{ "_id" : 3,
"items" : [ 10, 40, 50, 60, 80 ]},
{ "_id" : 4,
"items" : [ 20, 30, 40, 70, 80 ]}
])
Then if you want the intersection of documents 1,2,3 (for example), you want the result [40, 50].
You can calculate it like this:
var document_ids = [1, 2, 3];
var number_documents = document_ids.length;
db.test_unionintersection_stackoverflow_42686348.aggregate([
{ "$match": { "_id": { "$in": document_ids } } },
{ "$unwind": "$items"},
{ "$project" : { "_id" : 0, "item" : "$items"}},
{ "$group" : { _id: "$item", "count" : {$sum: 1}}},
{ "$match" : { "count" : number_documents}},
{ "$group" : { _id: "intersection", "items" : {$push: "$_id"}}},
]);
which gives you the result:
{
"_id" : "intersection",
"items" : [
50.0,
40.0
]
}