Use a Linq statement to perform outer join - linq

Starting with the collection below, what Linq statement do I need to return a results set that satisfies the test?
private List<dynamic> _results;
[SetUp]
public void SetUp()
{
_results = new List<dynamic>
{
new {Id = 1, Names = new[] {"n1"}, Tags = new[] {"abc", "def"}},
new {Id = 2, Names = new[] {"n2", "n3"}, Tags = new[] {"ghi"}},
new {Id = 3, Names = new[] {"n1", "n3"}, Tags = new[] {"def", "xyz"}},
new {Id = 4, Names = new[] {"n4"}, Tags = new string[] {}}
};
}
private ILookup<string, string> GetOuterJoinedCollection(IEnumerable<dynamic> results)
{
// ???
}
[Test]
public void Test()
{
ILookup<string, string> list = GetOuterJoinedCollection(_results);
Assert.That(list.Count, Is.EqualTo(4));
Assert.That(list["n1"], Is.EquivalentTo(new [] { "abc", "def", "def", "xyz" }));
Assert.That(list["n2"], Is.EquivalentTo(new [] { "ghi" }));
Assert.That(list["n3"], Is.EquivalentTo(new [] { "ghi", "def", "xyz" }));
Assert.That(list["n4"], Is.EquivalentTo(new string[] { }));
}
Note: this is a follow up from a previous question: Convert Lambda into Linq Statement with nested for loop

Related

NEST (ElasticSearch) search response drops aggregates

Here is a query that works in ElasticSearch.
"query":{
"match_all":{
}
},
"size":20,
"aggs":{
"CompanyName.raw":{
"terms":{
"field":"CompanyName.raw",
"size":20,
"order":{
"_count":"desc"
}
}
}
}
}
The response from ElasticSearch has a property aggregations['CompanyName.raw']['buckets'] which is an array.
I use this code to exeute the same query via NEST
string responseJson = null;
ISearchResponse<ProductPurchasing> r = Client.Search<ProductPurchasing>(rq);
using (MemoryStream ms = new MemoryStream())
{
Client.RequestResponseSerializer.Serialize<ISearchResponse<ProductPurchasing>>(r, ms);
ms.Position = 0;
using (StreamReader sr = new StreamReader(ms))
{
responseJson = sr.ReadToEnd();
}
}
However, in the resulting responseJson this array is always empty.
Where hs it gone?
How can I get it back?
Or is it that NEST doesn't support aggregates?
NEST does support aggregation, you can have a look into docs on how to handle aggregation response with NEST help.
Here you can find a short example of writing and retrieving data from simple terms aggregation:
class Program
{
public class Document
{
public int Id { get; set; }
public string Name { get; set; }
public string Brand { get; set; }
public string Category { get; set; }
public override string ToString() => $"Id: {Id} Name: {Name} Brand: {Brand} Category: {Category}";
}
static async Task Main(string[] args)
{
var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
var connectionSettings = new ConnectionSettings(pool);
connectionSettings.DefaultIndex("documents");
var client = new ElasticClient(connectionSettings);
var deleteIndexResponse = await client.Indices.DeleteAsync("documents");
var createIndexResponse = await client.Indices.CreateAsync("documents", d => d
.Map(m => m.AutoMap<Document>()));
var indexDocument = await client
.IndexDocumentAsync(new Document {Id = 1, Brand = "Tommy", Category = "men"});
var indexDocument2 = await client
.IndexDocumentAsync(new Document {Id = 2, Brand = "Diesel", Category = "men"});
var indexDocument3 = await client
.IndexDocumentAsync(new Document {Id = 3, Brand = "Boss", Category = "men"});
var refreshAsync = client.Indices.RefreshAsync();
var searchResponse = await client.SearchAsync<Document>(s => s
.Query(q => q.MatchAll())
.Aggregations(a => a
.Terms("brand", t => t
.Field(f => f.Brand.Suffix("keyword")))));
var brands = searchResponse.Aggregations.Terms("brand");
foreach (var bucket in brands.Buckets)
{
Console.WriteLine(bucket.Key);
}
}
}
Prints:
Boss
Diesel
Tommy
Hope that helps.

Elasticsearch throw Elasticsearch.Net.UnexpectedElasticsearchClientException

I have a problem with Elasticsearch.Net.UnexpectedElasticsearchClientException:
I tried change nest edition(eg:5.xxx,2.xxx,6.xxx) and can not fixed this problem,I can't found 7.xxx in nuget,so what should I try again to fixed this?
Exception is:
Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Int64' because the type requires a JSON primitive value (e.g. string, number, boolean, null) to deserialize correctly.
To fix this error either change the JSON to a JSON primitive value (e.g. string, number, boolean, null) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object.
Path 'hits.total.value', line 1, position 115.”
var settings = new ConnectionSettings(new Uri("http://localhost:9200")).DefaultIndex("my-application");
var client = new ElasticClient(settings);
IEnumerable<Person> persons = new List<Person>
{
new Person()
{
Id = "4",
Firstname = "aaa",//Boterhuis-040
Lastname = "Gusto-040",
Chains = new string[]{ "a","b","c" },
},
new Person()
{
Id = "5",
Firstname = "sales#historichousehotels.com",
Lastname = "t Boterhuis 1",
Chains = new string[]{ "a","b","c" },
},
new Person()
{
Id = "6",
Firstname = "Aberdeen #110",
Lastname = "sales#historichousehotels.com",
Chains = new string[]{ "a","b","c" },
},
new Person()
{
Id = "7",
Firstname = "Aberdeen #110",
Lastname = "t Boterhuis 2",
Chains = new string[]{ "a","b","c" },
},
new Person()
{
Id = "8",
Firstname = "Aberdeen #110",
Lastname = "t Boterhuis 2",
Chains = new string[]{ "a","b","c" },
},
};
client.IndexMany<Person>(persons, "test");
var rs = client.Search<Person>(s => s.Index("test"));
var response = rs.Documents;

C# Linq how to use group by to get the desired output

I have the following classes
public class Product
{
public string PNo {get;set;}
public String GCode {get;set;}
public IList<Detail> Details {get;set;}
}
public class Detail{
public string Color {get;set;}
public string Size {get;set;}
}
And i have the data in an array as below
PNO GCode Color Size Amount
12345 GCC A L 1
12345 GCC V M 2
12345 GCC C S 3
12345 GCC D XL 4
12345 GCC E XS 5
How do i get the following output using groupby so that my output could like shown below
Expected output:
{
PNO: 12345,
GCode: GCC,
options:[
{Color:A, Size:L, Amount: 1},
{Color:V, Size:M, Amount: 2},
{Color:C, Size:S, Amount: 3},
{Color:D, Size:XL, Amount: 4},
{Color:E, Size:XS, Amount: 5}
]
}
Thanks
Given the contract you defined. I think below code snippet is OK to generate the JSON you want, I use anonymous object a lot, you can replace with you entity class.
products.GroupBy(product => new
{
product.PNo,
product.GCode
})
.Select(productGroup => new
{
productGroup.Key.PNo,
productGroup.Key.GCode,
options = productGroup.SelectMany(product => product.Details.Select(detail => new
{
detail.Color,
detail.Size,
detail.Amount
}))
});
Hope it helps.
This will get you that expected output:
Query Syntax:
var result = (from item in collection
group item by new { item.PNO, item.GCode } into grouping
select new Product
{
PNo = grouping.Key.PNO,
GCode = grouping.Key.GCode,
Details = grouping.Select(item => new Detail { Color = item.Color, Size = item.Size, Amount = item.Amount }).ToList()
}).ToList();
Method Syntax:
var result = collection.GroupBy(item => new { item.PNO, item.GCode })
.Select(grouping => new Product
{
PNo = grouping.Key.PNO,
GCode = grouping.Key.GCode,
Details = grouping.Select(item => new Detail { Color = item.Color, Size = item.Size, Amount = item.Amount }).ToList()
}).ToList();
Test Data:
List<dynamic> collection = new List<dynamic>()
{
new { PNO = "12345", GCode = "GCC", Color = "A", Size="L", Amount=1 },
new { PNO = "12345", GCode = "GCC", Color = "V", Size="M", Amount=2 },
new { PNO = "12345", GCode = "GCC", Color = "C", Size="S", Amount=3 },
new { PNO = "12345", GCode = "GCC", Color = "D", Size="XL", Amount=4 },
new { PNO = "12345", GCode = "GCC", Color = "E", Size="XS", Amount=5 },
};

Not able to get TermVector results properly in SolrNet

I'm not able to get TermVector results properly thru SolrNet. I tried with the following code.
QueryOptions options = new QueryOptions()
{
OrderBy = new[] { new SortOrder("markupId", Order.ASC) },
TermVector = new TermVectorParameters
{
Fields = new[] { "text" },
Options = TermVectorParameterOptions.All
}
};
var results = SolrMarkupCore.Query(query, options);
foreach (var docVectorResult in results.TermVectorResults)
{
foreach (var vectorResult in docVectorResult.TermVector)
System.Diagnostics.Debug.Print(vectorResult.ToString());
}
In the above code, results.TermVectorResults in the outer foreach gives the proper count whereas docVectorResult.TermVector in the inner foreach is empty.
I've copied the generated solr query of the above code and issued against solr admin and I'm properly getting the termVectors values. The actual query I issued is below
http://localhost:8983/solr/select/?sort=markupId+asc&tv.tf=true&start=0&q=markupId:%2823%29&tv.offsets=true&tv=true&tv.positions=true&tv.fl=text&version=2.2&rows=50
First you should check HTTP query to sure termvector feature is set property.
If it's not OK, change your indexing based on:
The Term Vector Component
If it is OK,You can use "ExtraParams" by changing the handler to termvector handler. Try this:
public SolrQueryExecuter<Product> instance { get; private set; }
public ICollection<TermVectorDocumentResult> resultDoc(string q)
{
string SERVER="http://localhost:7080/solr/core";//change this
var container = ServiceLocator.Current as SolrNet.Utils.Container;
instance = new SolrQueryExecuter<Product>(
container.GetInstance<ISolrAbstractResponseParser<Product>>(),
new SolrConnection(SERVER),
container.GetInstance<ISolrQuerySerializer>(),
container.GetInstance<ISolrFacetQuerySerializer>(),
container.GetInstance<ISolrMoreLikeThisHandlerQueryResultsParser<Product>>());
instance.DefaultHandler = "/tvrh";
SolrQueryResults<Product> results =
instance.Execute(new SolrQuery(q),
new QueryOptions
{
Fields = new[] { "*" },
Start = 0,
Rows = 10,
ExtraParams = new Dictionary<string, string> {
{ "tv.tf", "false" },
{ "tv.df", "false" },
{ "tv.positions", "true" },
{ "tv", "true" },
{ "tv.offsets", "false" },
{ "tv.payloads", "true" },
{ "tv.fl", "message" },// change the field name here
}
}
);
return results.TermVectorResults;
}

Linq "group by" values in a Dictionary property with a list of keys

I have the following list of objects
List<Obj> source = new List<Obj>();
source.Add(new Obj() { Name = "o1", Attributes = new Dictionary<string, string> { { "attA", "1" }, { "attB", "1" }, { "attC", "1" } } });
source.Add(new Obj() { Name = "o2", Attributes = new Dictionary<string, string> { { "attA", "1" }, { "attB", "2" }, { "attC", "1" } } });
source.Add(new Obj() { Name = "o3", Attributes = new Dictionary<string, string> { { "attA", "1" }, { "attB", "3" }, { "attC", "2" } } });
source.Add(new Obj() { Name = "o4", Attributes = new Dictionary<string, string> { { "attA", "1" }, { "attB", "4" }, { "attC", "2" } } });
source.Add(new Obj() { Name = "o5", Attributes = new Dictionary<string, string> { { "attA", "2" }, { "attB", "5" }, { "attC", "3" } } });
source.Add(new Obj() { Name = "o6", Attributes = new Dictionary<string, string> { { "attA", "2" }, { "attB", "6" }, { "attC", "3" } } });
source.Add(new Obj() { Name = "o7", Attributes = new Dictionary<string, string> { { "attA", "2" }, { "attB", "7" }, { "attC", "4" } } });
source.Add(new Obj() { Name = "o8", Attributes = new Dictionary<string, string> { { "attA", "2" }, { "attB", "8" }, { "attC", "4" } } });
so i need to group it by the values of a specific attribute(s), furthermore the names of these attributes are kept in a separate list, like:
List<string> groupBy = new List<string>() { "attA", "attC" };
i tried using
var groups =
from s in source
group s by s.Attributes["attA"];
this works fine, returning 2 groups:
"1" - "o1 o2 o3 o4"
"2" - "o5 o6 o7 o8"
but what actually I need to do is to group by "attA" and "attC" (or whatever is in the groupBy variable) and get the following four groups:
"1_1" - "o1 o2"
"1_2" - "o3 o4"
"2_3" - "o4 o5"
"2_4" - "o7 o8"
from c in source
group c by String.Join("_",groupBy.Select(gr=>c.Attributes[gr]).ToArray()) into gr
select new
{
AttrValues = gr.Key,
//Values = gr.Key.Split('_'),
Names = gr.Select(c=>c.Name).ToList()
};
The group key is the concatenated projection of the dictionary values obtained from groupBy list of keys.
You can group by multiple properties:
var groups = from s in source
group s by new
{
AttributeA = s.Attributes["attA"],
AttributeC = s.Attributes["attC"]
};
//shows 4 groups
foreach (var group in groups)
Console.WriteLine(group.Key.AttributeA + "_" + group.Key.AttributeC);

Resources