I am very new to c# and LINQ, now I need to do some aggregation on the navigation tables nested one after the other, the result was generated as below:
[{
"ForumMain":"General Forum",
"Forums": [{
"Title":"My First Forum",
"TopicCount":4,
"PostCount":[2,0,1,0]
},
{
"Title":"My Second Forum",
"TopicCount":0,
"PostCount":[]
}]
}]
I wanted PostCount also be the sum, but it is giving debug null error when I try to sum PostCount, please see my linq query below:
var data = ForumCategories.Select(f => new
{
ForumMain = f.CategoryName,
Forums = f.Fora.Select(t => new
{
Title = t.ForumName,
TopicCount = t.Topics.Count,
PostCount = t.Topics
.Select(m => m.ForumPosts.Count == null ? 0 : m.ForumPosts.Count)
})
});
From what I understand what you want under PostCount to have the total number of post you have for all the topics in that Forum. Right?
If so:
var data = db.ForumCategories.Select(f => new
{
ForumMain = f.CategoryName,
Forums = f.Fora.Select(t => new
{
Title = t.ForumName,
TopicCount = t.Topics.Count,
PostCount = t.Topics.SelectMany(x => x.ForumPosts).Count()
})
});
Related
I have an entity stored inside an Elastic Index, one of his fields is an Id (int).
I'm currently using this code to retrieve the data:
var searchRequest = new SearchRequest<MyEntity>(indexName)
{
Size = 1,
Query = new MatchAllQuery(),
Sort = new List<ISort>()
{
new FieldSort(){ Field = "_id", Order = SortOrder.Descending }
}
};
var result = await _elasticClient.SearchAsync<MyEntity>(searchRequest);
var highestId = result.Documents.First().Id;
This code does not return the max Id but it returns the id "999999".
What I think its happening is that "_id" in search request it's not the Id inside my entity.
What is the correct way to query that value instead?
Edit:
I tried of course using "id" (or Id) in the code up above but I get an excpetion
Elasticsearch.Net.UnexpectedElasticsearchClientException : expected:'true | false', actual:'"true"', at offset:639
Also I tried this but I get an id that its even lower that the previous one:
var response = await _elasticClient.SearchAsync<MyEntity>(s => s
.Index(indexName)
.Size(1)
.Aggregations(a =>
a.Max("max_id", x => x.Field("id"))
)
if you use id your entity try this:
GET my-index-000001/_search
{
"size": 0,
"aggs": {
"max_id": {
"max": {
"field": "id"
}
}
}
}
In the end here's the solution:
After the response from #rabbitbr I was able to get this request:
var response = await _elasticClient.SearchAsync<MyEntity>(s => s
.Index(indexName)
.Take(1)
.Aggregations(aggr =>
aggr.Max("get_max_id", max =>
max.Field(field => field.Id)
)
)
);
var highestId = result.Documents.First().Id;
Here my error was NOT the query but how I collected the result: the correct way to get the value of "highestId" is to get from the response the field "Aggregations", then using .Max(aggregationName)
var aggregationName = "get_max_id";
var response = await _elasticClient.SearchAsync<MyEntity>(s => s
.Index(indexName)
.Take(0)
.Aggregations(aggr =>
aggr.Max(aggregationName , max =>
max.Field(field => field.Id)
)
)
);
var highestId = response.Aggregations.Max(aggregationName).Value;
I have this sentence linq, with select inside a select using a two viewmodels, the problem is that
in my loginviewmodel not all have a status equal to received, there may be nulls, but when putting them in my view some columns in my grid appear empty, I would like to see N/A appear by default if logviewmodel is = null but I don't know how to do it, I return this as JSON since I'm using datatable on the server side, I don't know if manipulating the json can also be done
I Share my linq sentence
var result = db.document.Select(d => new DocumentViewModel
{
DocumentId = d.DocumentId,
Name = w.name
ReceivedLogs = d.Logs
.Where(l => l.Status == Status.Received)
.Select(l => new LogViewModel
{
CurrentApprover = l.User,
NameApprover = l.User.FullName
}).FirstOrDefault()
}).ToList();
thanks
You can check if FirstOrDefault() returns null and then use a default LogViewModel:
var result = db.document.Select(d => new DocumentViewModel
{
DocumentId = d.DocumentId,
Name = w.name
ReceivedLogs = d.Logs
.Where(l => l.Status == Status.Received)
.Select(l => new LogViewModel
{
CurrentApprover = l.User,
NameApprover = l.User.FullName
}).FirstOrDefault() ?? new LogViewModel {
CurrentApprover = "N/A",
NameApprover = "N/A"
}
}).ToList();
Does anyone know how to do multiple aggregations with nest?
I have found quite a few examples unfortunately none of them work.
Here's what I have:
Vehicles fields = new Vehicles();
//create a terms query
var query = new TermsQuery
{
IsVerbatim = true,
Field = "VehicleOwnerId",
Terms = new string[] { 25 },
};
var aggregations = new Dictionary<string, IAggregationContainer>
{
{ "years", new AggregationContainer
{
Terms = new TermsAggregation(nameof(fields.Year))
{
Field = new Field(nameof(fields.Year))
}
}
}
//,
//{ "makes", new AggregationContainer
// {
// Terms = new TermsAggregation("Make")
// {
// Field = new Field(nameof(fields.Make))
// }
// }
//}
};
//create the search request
var searchRequest = new SearchRequest
{
Query = query,
From = 0,
Size = 100,
Aggregations = aggregations
};
var result = client.SearchAsync<InventoryLiveView>(searchRequest).Result;
var years = result.Aggregations.Terms("years");
Dictionary<string, long> yearCounts = new Dictionary<string, long>();
foreach (var item in years.Buckets)
{
yearCounts.Add(item.Key, item.DocCount ?? 0);
}
If I just execute the code like this it works. Years returns the aggregates as expected. If I try to add another field (like the one commented out above) it fails and I get zero records.
How can I get multiple aggregates in one query? I see examples of it all over, but none of the examples I've tried seem to work and most seem to be outdated (including some in the Nest documentation).
I have also tried this approach which is pretty close to the documentation.
//create the search request
var searchRequest = new SearchRequest
{
Query = query,
From = 0,
Size = 100,
//Aggregations = aggregations
Aggregations = new AggregationDictionary
{
{
"childAgg", new ChildrenAggregation("childAgg", typeof(Vehicles ))
{
Aggregations = new AggregationDictionary
{
{"years", new TermsAggregation(nameof(fields.VehicleYear))},
{"makes", new TermsAggregation(nameof(fields.VehicleMakeName))},
{"models", new TermsAggregation(nameof(fields.VehicleModelName))},
}
}
}
}
};
var result = client.SearchAsync<Vehicles>(searchRequest).Result;
This just produces a null reference exception.
I guess I'll never have too worry about getting to proud as a programmer :)
It's too often that the solution to the problem makes me feel stupid when it reveals itself.
So my issue was that the field I was trying to use in the aggregation was text and couldn't be used. I switched everything to the ID fields and multiple aggregations work as expected.
So this version of the code works like a champ:
Vehicle fields = new Vehicle ();
//create a terms query
var query = new TermsQuery
{
IsVerbatim = true,
Field = "VehicleOwnerId",
Terms = new string[] { "30" },
};
string[] Fields = new[]
{
nameof(fields.Year),
nameof(fields.MakeId),
nameof(fields.ModelId)
};
var aggregations = new Dictionary<string, IAggregationContainer>();
foreach (string sField in Fields)
{
var termsAggregation = new TermsAggregation(sField)
{
Field = sField
};
aggregations.Add(sField, new AggregationContainer { Terms = termsAggregation });
}
//create the search request
var searchRequest = new SearchRequest
{
Query = query,
From = 0,
Size = 10,
Aggregations = aggregations
};
var result = client.SearchAsync<InventoryLiveView>(searchRequest).Result;
var years = result.Aggregations.Terms(nameof(fields.Year));
Dictionary<string, long> yearCounts = new Dictionary<string, long>();
foreach (var item in years.Buckets)
{
yearCounts.Add(item.Key, item.DocCount ?? 0);
}
The exact error from elasticsearch, which I saw using postman was:
Fielddata is disabled on text fields by default. Set fielddata=true on [MakeName] in order to load fielddata in memory by uninverting the inverted index. Note that this can however use significant memory. Alternatively use a keyword field instead.
Here is my example using SearchDescriptors. My only problem is how to serialize returned results into a proper Key Value list. Is Looping through a fields list the best way to return results.
SearchDescriptor<Advert> agghDescriptor = new SearchDescriptor<Advert>();
agghDescriptor.Aggregations(ag => ag.Terms("make", a => a.Field(f => f.Make)) &&
ag.Terms("region", a => a.Field(f => f.Region)) &&
ag.Terms("city", a => a.Field(f => f.City)) &&
ag.Terms("category", a => a.Field(f => f.Category)) &&
ag.Terms("application", a => a.Field(f => f.Application)) &&
ag.Terms("portalId", a => a.Field(f => f.PortalId)) &&
ag.Terms("isActiveAuctionAdvert", a => a.Field(f => f.IsActiveAuctionAdvert)) &&
ag.Terms("isBargainAccount", a => a.Field(f => f.IsBargainAccount)) &&
ag.Terms("condition", a => a.Field(f => f.Condition))
);
agghDescriptor.Size(0);
var json2 = _client.RequestResponseSerializer.SerializeToString(agghDescriptor);
var aggregationResult = _client.Search<Advert>(agghDescriptor);
List<string> fields = new List<string>();
fields.Add("make");
fields.Add("category");
fields.Add("region");
List<Aggregation> aggregations = new List<Aggregation>();
foreach (var field in fields)
{
var aggrs = aggregationResult.Aggregations.Terms(field);
List<AggregateItem> aggregateItems = new List<AggregateItem>();
foreach (var item in aggrs.Buckets)
{
aggregateItems.Add(new AggregateItem()
{
Count = item.DocCount ?? 0,
Key = item.Key
});
}
aggregations.Add(new Aggregation()
{
Name = field,
Aggregates = aggregateItems
});
}
I have a use case where I need to do aggregation on multiple columns using C#.
I am using NEST libraries for this and I am facing the following issue
Query C# :
var searchRequest = new SearchRequest
{
SearchType = SearchType.Count,
Filter = filter,
Aggregations = new Dictionary<string, IAggregationContainer>
{
{ "a", new AggregationContainer
{
ExtendedStats = new ExtendedStatsAggregator()
{
Field = "a"
}
}
},
{ "b", new AggregationContainer
{
ExtendedStats = new ExtendedStatsAggregator()
{
Field = "b"
}
}
}
}
};
When I receive response from NEST, however I am getting only result for one aggregation. I am looking at SearchResult.Agg dictionary but it has only one entry for one aggregation field instead of two.
Let me know if I am missing soemthing or is it some issue with NEST libraries
if you are using term aggregation then you need to use aggregation with filter.
var qryRes1 = client.Search<object>(x => x
.Aggregations(ag => ag
.Filter("filter", (flt => flt
.Filter(f =>
{
FilterContainer filter = null;
filter &= f.Query(qr => qr.Term(wl => wl.OnField("a").Value("the value you need to filter for field a")));
return filter;
})
.Aggregations(agr => agr
.Terms("b", tr =>
{
TermsAggregationDescriptor<object> trmAggDescriptor = new TermsAggregationDescriptor<object>();
trmAggDescriptor.Field("b");
return trmAggDescriptor;
}))))
));
var terms = qryRes1.Aggs.Filter("filter").Terms("b");
I need to preform a query that check if a collection is in given collection, just like the regular in operation but for collections.
class Post
{
public string[] Tags {get;set;}
}
session.Queury<Post>.Where(x=>x.Tags.in(new[]{".net","c#","RavenDB"})).ToList();
so if i have in my DB:
new Post{Tags= new[]{"C#",".net"}};
it will be returned
but if i have:
new Post{Tags= new[]{"C#",".net","SQLServer"}};
it will not be returned.
Update:
what i am trying to do is this:
session.Query<Post>()
.Where(x => x.Tags.All(y => y.In(new[] { "C#", ".net", "RavenDB" })))
.ToList();
but i got System.NotSupportedException.
I manage to find a solution:
static void Main(string[] args)
{
var sessionStore = new EmbeddableDocumentStore
{
RunInMemory = true,
UseEmbeddedHttpServer = true,
Conventions =
{
DefaultQueryingConsistency = ConsistencyOptions.AlwaysWaitForNonStaleResultsAsOfLastWrite
}
};
sessionStore.Initialize();
using (var session = sessionStore.OpenSession())
{
var allTags = new[] {"C#", ".net", "RavenDB", "Linux", "Mac"};
var tagsCollection = new[] {"C#", ".net", "RavenDB"};
var complementTagsCollection = allTags.Except(tagsCollection).ToList();
session.Store(new Post
{
Tags = new List<string>{"C#",".net"}
});
session.SaveChanges();
// Posts where all their tags are in tagsCollection
var result = session.Query<Post>().Where(x => !x.Tags.In(complementTagsCollection)).ToList();
}
}
The way IN works, it matches ANY of them.
If you want to match all you have to do a separate check for each.