In my project I'm using SpringBoot 1.4.0-M2 and MongoDb 3.0
I want to implement sorting.
I have collection of documents:
{
"name":"Tim",
"age":19
}
,{
"name":"Jim",
"age":20
}
,{
"name":"Will",
"age":21
}
,{
"name":"Ed",
"age":22
}
I random number from 19 - 22, lets pretend that a number I have got is 21.
Now I want to order by "age" property using my the 21 value.
I want to have order like this 21,22,19,20 so Will,Ed,Tim,Jim
Do You know how to sort in this way?
Best regards!
I don't know of a way to achieve this "wrapping sort" concept in a single query, but you could do it in two queries. The examples below load everything into memory, so you would just need to change things around a bit if you only wanted to operate on one at a time in the case of very large collections.
If you are using the native MongoDB Java driver 3.x:
int age = 21;
DBObject ageSort = new BasicDBObject("age", 1)
List<Document> results = new ArrayList<>();
mongoCollection.find(new BasicDBObject("age", new BasicDBObject("$gte", age)))
.sort(ageSort)
.forEach((Block<Document>) results::add);
mongoCollection.find(new BasicDBObject("age", new BasicDBObject("$lt", age)))
.sort(ageSort)
.forEach((Block<Document>) results::add);
If you are using Spring Data MongoDB:
int age = 21;
Sort ageSort = new Sort(new Sort.Order(Sort.Direction.ASC, "age"))
List<Map> results = mongoTemplate.findAll(
new Query(Criteria.where("age").gte(age)).with(ageSort),
Map.class
);
results.addAll(mongoTemplate.findAll(
new Query(Criteria.where("age").lt(age)).with(ageSort),
Map.class
));
Related
I have a list with a custom class. I want to take the subSectionRank and the featureRank into the field rankingValue
I can do this with a loop but would like to be more efficient and use streams as the list is 400+
Here is the list
Here is the loop
public List<BaseFeatures> addFeatureRanking(List<BaseFeatures> baseFeatures){
for (int i = 0; i < baseFeatures.size(); i++) {
Integer subSectionRank = baseFeatures.get(i).getSubSectionRank();
Double featureRank = baseFeatures.get(i).getFeatureRank();
int ranking = (int) ((int)subSectionRank + featureRank);
baseFeatures.get(i).setRankingValue(ranking);
}
return baseFeatures;
}
Here is what i am trying.
List<Map.Entry<Double, Integer>> reducedAList = new ArrayList<>(baseFeatures.stream()
.collect(Collectors.groupingBy(BaseFeatures::getFeatureRank, Collectors.summingInt(BaseFeatures::getSubSectionRank)))
.entrySet());
I just dont know if i am on the right track or even remotely close. How can i use java 8 streams to accomplish what i am asking? Any heklp is much appreciated as java 8 streams is not my strong point.
It looks to me like all you want to do is the following:
where
BaseFeature is the class with the getters and setters
baseFeatures is the list of those classes.
for (BaseFeature bf : baseFeatures) {
bf.setRankingValue(bf.getFeatureRank() + bf.getsubSectionRank());
}
I'm not sure of the types so you may need to do some casting.
I'm new to Java so if this has already been answered somewhere else then I either don't know enough to search for the correct things or I just couldn't understand the answers.
So the question being:
I have a bunch of objects in a list:
try(Stream<String> logs = Files.lines(Paths.get(args))) {
return logs.map(LogLine::parseLine).collect(Collectors.toList());
}
And this is how the properties are added:
LogLine line = new LogLine();
line.setUri(matcher.group("uri"));
line.setrequestDuration(matcher.group("requestDuration"));
....
How do I sort logs so that I end up with list where objects with same "uri" are displayed only once with average requestDuration.
Example:
object1.uri = 'uri1', object1.requestDuration = 20;
object2.uri = 'uri2', object2.requestDuration = 30;
object3.uri = 'uri1', object3.requestDuration = 50;
Result:
object1.uri = 'uri1', 35;
object2.uri = 'uri2', 30;
Thanks in advance!
Take a look at Collectors.groupingBy and Collectors.averagingDouble. In your case, you could use them as follows:
Map<String, Double> result = logLines.stream()
.collect(Collectors.groupingBy(
LogLine::getUri,
TreeMap::new,
Collectors.averagingDouble(LogLine::getRequestDuration)));
The Collectors.groupingBy method does what you want. It is overloaded, so that you can specify the function that returns the key to group elements by, the factory that creates the returned map (I'm using TreeMap here, because you want the entries ordered by key, in this case the URI), and a downstream collector, which collects the elements that match the key returned by the first parameter.
If you want an Integer instead of a Double value for the averages, consider using Collectors.averagingInt.
This assumes LogLine has getUri() and getRequestDuration() methods.
I want to use DocumentDB to store roughly 200.000 documents of the same type. The documents each get an integer id field and I would like to retrieve them paged, in reverse order (highest id first).
So recently I found out there is no sorting for DocumentDB (see also DocumentDB - query result order). Perhaps it is better to go for a different database (such as RavenDB) however, time is pressing and I want to avoid the cost of switching to another database.
The question:
I have been looking at implementing my own sorted index of the documents on the client side (ASP Web API 2). I was thinking of creating a SortedList of key(id) and value(document.selflink). Then I could create a Getter with parameters for count, offset and a predicate to filter the documents. Below I added a quick example.
I just have the feeling this is a bad idea; either slow, costing too many resources or can be better done another way. So I am open for implementation suggestions...
public class SortableDocumentDbRepository
{
private SortedList _sorted = new SortedList();
private readonly string _sortedPropertyName;
private DocumentCollection ReadOrCreateCollection(string databaseLink) {
DocumentCollection col = base.ReadOrCreateCollection(databaseLink);
var docs = Client.CreateDocumentQuery(Collection.DocumentsLink)
.AsEnumerable();
lock (_sorted.SyncRoot) {
foreach (Document doc in docs) {
var propVal = doc.GetPropertyValue<string>(_sortedPropertyName);
if (propVal != null) {
_sorted.Add(propVal, doc.SelfLink);
}
}
}
return col;
}
public List<T> GetItems<T>(int count, int offset, Expression<Func<T, bool>> predicate) {
List<T> result = new List<T>();
lock (_sorted.SyncRoot) {
var values = _sorted.GetValueList();
for (int i = offset; i < _sorted.Count; i++) {
var queryable = predicate != null ?
Client.CreateDocumentQuery<T>(values[i].ToString()).Where(predicate) :
Client.CreateDocumentQuery<T>(values[i].ToString());
T item = queryable.AsEnumerable().FirstOrDefault();
if (item == null || item.Equals(default(T))) continue;
result.Add(item);
if (result.Count >= count) return result;
}
}
return result;
}
}
Microsoft has implemented Sorting:
https://learn.microsoft.com/en-us/azure/cosmos-db/sql-api-sql-query-reference#bk_orderby_clause
Example: SELECT * FROM c ORDER BY c._ts DESC
As you mentioned, order by unfortunately isn't implemented yet.
Your approach looks reasonable to me.
I see you are using a predicate to narrow the query result set (pulling 200,000 records for any DB will be costly).
Since it looks like you are looking to order by id - you can also look in to setting up a range index on id allowing you to perform range queries (e.g. < and >) on the id and further narrow the query result set. There is also a range index included by default on the _ts (timestamp) system property on documents that may also be helpful in this context.
See: http://azure.microsoft.com/en-us/documentation/articles/documentdb-indexing-policies/
I have a single table and I need to build a bunch of nested objects based on the single table.
Data:
PointA PointB Month Time Price
1 2 11 11:00 10.99
1 2 12 11:00 9.99
Objects are
POINTS {PointA, PointB, Details}
Details {Month, ExtraDetails}
ExtraDetails {Time, Price}
I want to avoid having loads of loops and if statements, so should be able to use linq to do this. but its beyond my linq experience.
edit: These need grouping aswell
any help would be great.
Thanks
Just tried out a solution:
var nestedObjects = from row in data
select new {row.PointA, row.PointB, Details = new {
row.Month, ExtraDetails = new {
row.Time, row.Price
}
}};
This is assuming that you have already got your data into data.
Group by
If you want to group the Points together, you need 'Group By':
var nestedObjects = from row in data
group row by new { row.PointA, row.PointB } into Points
select new {
Points = Points.Key,
Details = from details in Points
select new { row.Month, ExtraDetails = new {
row.Time, row.Price
}}
};
A little more complicated - of course you might want to group by month as well, in which case, you need to follow the same pattern as for the Points bit. Note, this will not create tables, because the group by doesn't quite do that, but it at least creates the structure for you.
Assuming you got your classes defined for the objects you mentioned, and you have a constructor or properties so you can propery create the object in one line you could have a LINQ query returning a list of a POINTS.
If would go something lik this :
var res =
from item in table.AsEnumerable()
select new Points(){PointA = item["PointA"];
PointB = item["PointB"];
Details = from item2 in table.AsEnumberable()
where item["PointA"] = item2["PointA"] and item["PointB"] = item2["PointB"]
select new Details(){
month=item2["month"],
extraDetails = from item3 in table.AsEnumerable()...
}
};
At the end res will be a IEnumerable of Points
I am sorry for the code, I am not at a computer with .NET 3.5 so I cannot write a proper testable query
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Is it possible to Pivot data using LINQ?
I'm wondering if its at all possible to create crosstab style results with Linq.
I have some data that looks like the following:
var list = new[]
{
new {GroupId = 1, Country = "UK", Value = 10},
new {GroupId = 1, Country = "FR", Value = 12},
new {GroupId = 1, Country = "US", Value = 18},
new {GroupId = 2, Country = "UK", Value = 54},
new {GroupId = 2, Country = "FR", Value = 55},
new {GroupId = 2, Country = "UK", Value = 56}
};
and I'm trying to output to a repeater control something like the following:
GroupId.....UK.....FR.....US
1...........10.....12.....18
2...........54.....55.....56
Its the dynamic columns that are causing my problems. Any solutions to this?
You need a runtimy class to hold these runtimy results. How about xml?
XElement result = new XElement("result",
list.GroupBy(i => i.GroupId)
.Select(g =>
new XElement("Group", new XAttribute("GroupID", g.Key),
g.Select(i => new XAttribute(i.Country, i.Value))
)
)
);
Are you expecting multiple records per result cell? If so there would need to be some Summing (and more grouping) in there.
(this answer is proof of concept, not final result. There's several issues to address, such as: ordering of columns, missing cells, and so on).
After doing a quick search you might want to look at the ModuleBuilder, TypeBuilder, and FieldBuilder classes in System.Reflection.Emit. They allow you to create a class dynamically at runtime. Outside of that you would need to do grouping on your objects and then do something with the hierarchical results you get from LINQ. I am not sure of a way to dynamically create anonymous type fields at runtime, and that sounds like what would need to happen.
You could try using the dynamic linq library provided by MS. They have a number of overloads to extensions methods that take strings as arguments. They also have an expression parser that takes a string an emits a lambda expression. You should be able to create a dynamic select using them.
A word of warning though, you end up with a non-generic IQueryable rather than a generic IQueryable so you are a little bit limited on what you can do with the result, and you give up a bit of type safety, but that may be OK in your application...
The link for the dynamic linq stuff is
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
There is a link where you can download the source code the the dynamic library, plus some nice illustrations of how you can use it.
var labResults = from lab in CoreLabResults
where lab.Patient == 8
group lab by new { lab.Patient, lab.TestNo, lab.CollectedDate }
into labtests
select new
{
labtests.Key.Patient,
labtests.Key.TestNo,
labtests.Key.CollectedDate,
MCHC = labtests.Where(lab => lab.TestVar == "MCHC").FirstOrDefault().Result,
LYABS = labtests.Where(lab => lab.TestVar == "LYABS").FirstOrDefault().Result,
TotalTests = labtests.Count()
}