I am trying to show a statistics count, per month, per manufacturer from a MongoDB collection using Linq.I would like to see my data as:
[{
Manufacturer: '',
Statistics: [{
Month: '',
Counts: 0
}]
}]
So I am trying the following linq query:
var result = _statisticsRepository
.All()
.GroupBy(g => g.ManufacturerId)
.Select(s => new
{
Manufacturer = s.Key,
Statistics = s
.GroupBy(a => a.Values["DATA_MONTH"])
.Select(a => new
{
Month = a.Key,
Counts = a.Sum(d => d.Count)
})
});
When I try this code I get this error:
NotSupportedException: The method GroupBy is not supported in the expression tree: {document}.GroupBy(a => a.Values.get_Item("DATA_MONTH")).
Am I doing this the wrong way or is this a limitation I have in the C# driver? Or is there another way to do this?
You could use the aggregation framework instead LINQ to get the data that you need:
var group1 = new BsonDocument
{
{ "_id", new BsonDocument{{"ManufacturerId", "$ManufacturerId"},{"DATA_MONTH", "$DATA_MONTH"} }},
{ "Count", new BsonDocument
{
{
"$sum", $Count }
} }
};
var group2 = new BsonDocument
{
{ "_id", "$_id.ManufacturerId" },
{ "Statistics ", new BsonDocument { { "$addToSet",new BsonDocument{{"Month", "$_id.DATA_MONTH"},{"Count","$Count"} } } }},
};
var result = database.GetCollection<BsonDocument>("Manufacturers").Aggregate()
.Group(group1)
.Group(group2)
.ToList();
Related
I have my departments data coming from the database. I want to filter this data based on certain criteria.
[
{
"Id":10,
"Name":"Name 10",
"Teachers":[
{
"TeacherId":100,
"TeacherName":null,
"DepartmentId":100,
"Students":[
{
"StudentId":1001,
"StudentName":null,
"TeacherId":10,
"DepartmentId":100
}
]
},
{
"TeacherId":101,
"TeacherName":null,
"DepartmentId":100,
"Students":[
{
"StudentId":1001,
"StudentName":null,
"TeacherId":10,
"DepartmentId":100
}
]
}
]
},
{
"Id":100,
"Name":"Name 10",
"Teachers":[
{
"TeacherId":0,
"TeacherName":null,
"DepartmentId":100,
"Students":[
{
"StudentId":5000,
"StudentName":null,
"TeacherId":50,
"DepartmentId":100
}
]
}
]
},
{
"Id":50,
"Name":"Name 10",
"Teachers":[
{
"TeacherId":0,
"TeacherName":null,
"DepartmentId":100,
"Students":[
{
"StudentId":2000,
"StudentName":null,
"TeacherId":50,
"DepartmentId":100
}
]
}
]
}
]
Now I have to filter the departments based on some values as shown below
var departmenIds = new List<int>() { 10, 20, 30 };
var teachers = new List<int>() { 100, 200, 300 };
var students = new List<int>() { 1000, 2000, 3000 };
I am looking for a query that will return the data in a following fashion
If all department ids exists in the json it will return entire data. If a department with a particular teacher is in the list then only return that teacher and the department. like wise for the student.
I tried this to test if it atleast work at the second level but I am getting all the teachers
var list = allDeplrtments.Where(d => d.Teachers.Any(t => teachers.Contains(t.TeacherId))).ToList();
var list = allDepartments
.Where(d => departmentIds.Contains(d.Id))
.Select(d => new Department() {
Id = d.Id,
Name = d.Name,
Teachers = (d.Teachers.Any(t => teacherIds.Contains(t.TeacherId))
? d.Teachers.Where(t => teacherIds.Contains(t.TeacherId))
: d.Teachers)
.Select(t => new Teacher() {
TeacherId = t.TeacherId,
TeacherName = t.TeacherName,
DepartmentId = d.Id,
Students = t.Students.Any(s => studentIds.Contains(s.StudentId))
? t.Students.Where(s => studentIds.Contains(s.StudentId))
: t.Students
})
})
Would something like this work for you?
I'm trying to get my code to match what the following code does:
var data = new Dictionary<string, object>() {
{ "state", new string[] { "Ohio", "Ohio", "Ohio", "Nevada", "Nevada" } },
{ "year", new int[] { 2000, 2001, 2002, 2001, 2002 } },
{ "pop", new double[] { 1.5, 1.7, 3.6, 2.4, 2.9 } }
};
My current code:
var data = new Dictionary<string, object>() {
{ "targetValue", calc.ListCalculationData.Select(i => i.MRTargetValue).ToArray<decimal>() },
{ "ema12", calc.ListCalculationData.Select(i => i.Ema12).ToArray<decimal>() },
{ "ema26", calc.ListCalculationData.Select(i => i.Ema26).ToArray<decimal>() },
{ "ema", calc.ListCalculationData.Select(i => i.Ema).ToArray<decimal>() }
};
var df1 = DataFrame.FromColumns(data);
var model2 = new LinearRegressionModel(df1, "targetValue ~ ema12 + ema26 + ema"); <= This line gives me the error
I'm getting the following error from the api that I'm using which is the extreme optimization C# api and here is the reference url: http://www.extremeoptimization.com/QuickStart/CSharp/DataFrames.aspx
The variable is not of a kind that is allowed in the group.
I do not see any documentation or examples using the decimal type, but I cannot verify since I do not have access to the library.
There were some examples using the double type. If precision of 15-16 digits is acceptable, that could be an option. Try defining your data variable like this, casting to a double in the Select, then converting to an array:
var data = new Dictionary<string, object>() {
{ "targetValue", calc.ListCalculationData.Select(i => (double)i.MRTargetValue).ToArray() },
{ "ema12", calc.ListCalculationData.Select(i => (double)i.Ema12).ToArray() },
{ "ema26", calc.ListCalculationData.Select(i => (double)i.Ema26).ToArray() },
{ "ema", calc.ListCalculationData.Select(i => (double)i.Ema).ToArray() }
};
Trying to port below mongodb aggregation query to c# but getting the error -
Command aggregate failed: the group aggregate field 'ProductType' must
be defined as an expression inside an object.
Any thoughts on what is wrong with the c# code?
db.collectionName.aggregate([
{ $match: activeTrial},
{ $project: {_id:1, CustomerType: 1, ProductType: 1} },
{ $group: {_id: {"cust": "$CustomerType", "prod" : "$ProductType"}, count: {$sum: 1}}},
]).toArray()
collectionName.Aggregate()
.Match(filter)
.Project(x => new {x.CustomerType, x.SubscriptionId, x.ProductType})
.Group(x => new { x.ProductType, x.CustomerType }, g => new ActiveTrialsByProduct()
{
ProductType = g.Key.ProductType,
CustomerType = g.Key.CustomerType,
ActiveTrials = g.Count()
}).ToList();
Here is the collection..
{
"CustomerType" : "CustA",
"ProductType" : "ProdA",
}
{
"CustomerType" : "CustA",
"ProductType" : "ProdB",
}
{
"CustomerType" : "CustA",
"ProductType" : "ProdB",
}
{
"CustomerType" : "CustA",
"ProductType" : "ProdA",
}
The group method doesn't know about g.Key.ProductType so we have to project the elements of the key in the project method.
collectionName.Aggregate()
.Match(filter)
.Project(x => new {x.CustomerType, x.ProductName, x.SubscriptionId })
.Group(x => new { x.ProductName, x.CustomerType }, g => new
{
Id = g.Key,
ActiveTrials = g.Count()
})
.Project(x => new ActiveTrialsByProduct()
{
CustomerType = x.Id.CustomerType,
Product = x.Id.ProductName,
ActiveTrials = x.ActiveTrials
}).ToList();
I want to group an aggregate by day (not dayOfMonth or Year, just absolute day). Therefore I want to use the $dateToString operator as shown here: $dateToString: { format: "%Y-%m-%d", date: "$date" }. Is there a way to use an expression for this like:
var groups = await collection.Aggregate()
.Match(job => /*...*/)
.Group(job => job.Created.ToString(),
group => /*...*/)
.ToListAsync();
I get this error:
ToString of type System.DateTime is not supported in the expression tree {document}{created}.ToString...
With the help of #Blakes Sevens comment, I solved the original problem with another grouping key.
var groups = await collection.Aggregate()
.Match(job => /*...*/)
.Group(job => new
{
Year = job.Created.Year,
Month = job.Created.Month,
Day = job.Created.Day
},
group => new { Key = group.Key, Value = group.Count() })
.ToListAsync();
Edit
To support other periods than day, I had to use a BsonDocument.
var groupBy = new BsonDocument
{
{
"_id", new BsonDocument
{
{ "$add", new BsonArray
{
new BsonDocument
{
{ "$subtract", new BsonArray
{
new BsonDocument { { "$subtract", new BsonArray
{
"$created",
new DateTime(0) }
}
new BsonDocument { { "$mod", new BsonArray
{
new BsonDocument
{
{ "$subtract", new BsonArray
{
"$created",
new DateTime(0)
}
}
},
msPerPeriod
}
} }
}
}
},
new DateTime(0)
}
}
}
},
{ "count", new BsonDocument("$sum", 1) } };
var groups = await collection.Aggregate()
.Match(job => job.Created >= since && regex.IsMatch(job.Definition))
.Group(groupBy)
.Sort(Builders<BsonDocument>.Sort.Ascending(doc => doc["_id"]))
.ToListAsync();
See this answer: https://stackoverflow.com/a/32137234/498298
I have a collection of BsonDocuments that look like the following:
{
"_id" : ObjectId("5699715218a323101c663b9a"),
"amount" : 24.32,
"color" : false
}
I would like to sum all of the values for "amount" in this particular collection (~1,000 BsonDocuments).
This is what I have so far:
var group = new BsonDocument{
{ "$group", new BsonDocument
{
{ "_id", "$amount" },
}
}
};
var pipeline = new[] { group };
var result = collection.Aggregate(pipeline);`
The last line where the variable "result" is declared gives me the error: "The type arguments for methods Aggregate cannot be inferred from the usage." I really feel like this error comes from how I set up the "group" BsonDocument but in reality I'm not sure if this is even the direction I should be going in. Any help is appreciated.
Thanks for your time.
[EDIT] Maximilianos Rios's answer has worked. Furthermore if one would like to count the documents processed it would be done the following way:
var aggregation = collection.Aggregate<BsonDocument>()
.Group(new BsonDocument
{
{ "_id", BsonNull.Value
},
{
"total_amount", new BsonDocument
{
{
"$sum", "$amount"
}
}
},
{
"sum", new BsonDocument
{
{
"$sum", 1
}
}
}
});
var doc = aggregation.Single();
BsonDocument result = doc.AsBsonDocument;
var total = result["total_amount"].AsDouble;
var count = result["sum"].AsInt32;
First at all, what you want to do based on your question is this:
db.coll.aggregate([ { $group: { _id:null, total_amount:{"$sum":"$amount"}} } ])
Quoting your words
I would like to sum all of the values for "amount" in this particular
collection
The solution is to use the group method of the aggregation framework like this:
var aggregation = collection.Aggregate<BsonDocument>()
.Group(new BsonDocument
{
{ "_id", BsonNull.Value
},
{
"total_amount", new BsonDocument
{
{
"$sum", "$amount"
}
}
}
});
var doc = aggregation.Single();
BsonDocument result = doc.AsBsonDocument;
var total = result["total_amount"].AsDouble;
Remember you can get your value asynchronously too.
var doc = aggregation.SingleAsync();