Select an object based on the condition in nested array(LINQ ,MongoDB) - c#

{
"Id": 1234,
"CommentType": "project",
"EntityReferenceId": "1345-154-154",
"Members": [{
"MemberId": "1354",
"Name": "a",
"Email": "cdc#df.com"
}],
"Threads": [{
"Id": "233",
"UserReferenceId": "32343",
"UserName": "433434",
"CommentByUserType": "Agent",
"Content": "dfdfdsfs sfdf sdf",
"PostedDate": "0001-01-01T00:00:00",
"Active": true,
"Attachment": [{
"AttachmentName": "ad",
"AttachmentUrl": "http://fdf.jpg"
}]
},
{
"Id": "233",
"UserReferenceId": "32343",
"UserName": "433434",
"CommentByUserType": "Agent",
"Content": "dfdfdsfs sfdf sdf",
"PostedDate": "0001-01-01T00:00:00",
"Active": false,
"Attachment": [{
"AttachmentName": "ad",
"AttachmentUrl": "http://fdf.jpg"
}]
}]
}
I am using MongoDb to linq,
This is my Comment" object format.for each Project there is a comment object.Each Comment object contains a list of "Threads"(you can see this above example).I want to load the "Comment" object with all thread which is Active("Active": true)
var result = _context.Comments
.AsQueryable()
.Where(x => x.EntityReferenceId == EntityReferenceId &&
x.CommentType == Type &&
x.Threads.Any(z=>z.Active==true))
.FirstOrDefault();
I used this query but it loading all threads if Any thread have "Active" value is true.
x.Threads.Any(z=>z.Active==true)
returns only bool values.I need a solution please

You can use x.Threads.Any(z=>z.Active==true) be where condition.
or you will judge Threads collection is contain Active==true data.
A simple way you can do like this.
var result = _context.Comments
.AsQueryable()
.Where(x => x.EntityReferenceId == EntityReferenceId &&
x.CommentType == Type)
.FirstOrDefault();
if (result!=null)
{
result1.Threads = result1.Threads.Where(z => z.Active == true);
}
Or
use select method
var result = _context.Comments
.AsQueryable()
.Where(x => x.EntityReferenceId == EntityReferenceId &&
x.CommentType == Type)
.Select(o => new Comment{
Id = o.Id,
Members = o.Members,
EntityReferenceId = o.EntityReferenceId,
CommentType = o.CommentType,
Threads = o.Threads.Where(z => z.Active == true)
})
.FirstOrDefault();

Related

Get summation of all field in mongodb c#

I have a mongodb which looks like this
[
{
"client_id": "abc",
"product_id": "123",
"weight": {
"value": 100
"unit": "kg"
}
},
{
"client_id": "def",
"product_id": "456",
"weight": {
"value": 200
"unit": "kg"
}
}
]
I need to get summation of weight value for a certain client id and product id using mongodb c# client, how can I do that?
I tried this but it is always returning 0
var total_weight = await Collection.AsQueryable()
.Where(
x => x.client_id == "abc" &&
x => x.product_id== "123")
.SumAsync(x => x.weight.value);
Thanks
I think you are looking for this query so you can try this code:
var total_weight = await Collection.AsQueryable<YourModel>()
.Where(x => x.client_id == "abc" && x.product_id == "123")
.GroupBy(x => new { x.client_id, x.product_id })
.Select(x => x.Sum(y => y.weight.value))
.FirstOrDefaultAsync();

Filter JSON Array with dynamic conditions

I have many JSON array with different types of nodes in it.
Sample Json 1:
[
{
"EmpID": "23",
"EmpName": "Jhon",
"Age": "23"
},
{
"EmpID": "29",
"EmpName": "Paul",
"Age": "25"
},
{
"EmpID": "123",
"EmpName": "Jack",
"Age": "29"
},
{
"EmpID": "129",
"EmpName": "Apr",
"Age": "29"
}
]
Sample Json 2
[
{
"DepID": "2",
"Name": "Sales"
},
{
"DepID": "5",
"Name": "Marketing"
},
{
"DepID": "12",
"Name": "IT"
}
]
I want to filter them based on different conditions such as
1)EmpID=29
This should return
[
{
"EmpID": "29",
"EmpName": "Paul",
"Age": "25",
}
]
2)Age=23 and EmpName=Jhon
This should return
[
{
"EmpID": "23",
"EmpName": "Jhon",
"Age": "23"
}
]
Age=29
This should return
[
{
"EmpID": "123",
"EmpName": "Jack",
"Age": "29"
},
{
"EmpID": "129",
"EmpName": "Apr",
"Age": "29"
}
]
So I need a generic approach to do any number of filters on the JSON array. I am planning to get all the filters using some comma separated string like Age="23",EmpName="Jhon" and this can be converted to any format in the code.
I have tried creating dynamic filter using Json Path such as $.[?(#.Age == '23' && #.EmpName == 'Jhon')].
Also I tried using LINQ like
var result = JsonConvert.DeserializeObject(jsonString);
var res = (result as Newtonsoft.Json.Linq.JArray).Where(x =>
x["Age"].ToString() =="23" && x["EmpName"].ToString()=="Jhon").ToList();
But how I can generate the where conditions dynamically based on any number of conditions I receive
Also there is a plan to include Date filters in case there is some datetime nodes in json such as BirthDate>12051995.
I am not sure how I can dynamically filter using any number of input filter conditions.
To get this working in a traditional way, you'll need to perform 3 steps:
define a class to contain the data
deserialize the json into a list of objects
use linq to query your selection
You can do the same thing for the departments.
If you need to join them in any way, use .Join. If the JSON is mixed, you can create a single class containing all the properties and use that to query.
So for the simple case: first define a class to represent you object:
public class Employee
{
public int EmpID {get;set;}
public string EmpName {get;set;}
public int Age {get;set;}
}
Then deserialize and query:
put at the top:
using System.Text.Json;
public void Main()
{
//deserialize into a list
List<Employee> employees =
JsonSerializer.Deserialize<List<Employee>>(yourJsonString);
//query
var result = employees.Where(c => c.Age == 23 && c.EmpName == "Jhon");
//show results
foreach (var employee in result)
Console.WriteLine(employee.EmpID);
}
As by update:
Depending on your use case you have a couple of options:
a fixed number of dynamic properties
a truly dynamic query
A fixed number of dynamic properties
You can achieve a more dynamic setup with the following:
//define the filterable properties
//note they are nullable
int? age = null;
int? id = null;
string name = null;
//apply them in a query
//
//note: if one of the filter properties is not set,
// that side of the && expression evaluates to "true"
var result = employees.Where(c => (age == null ? true : c.Age == age) &&
(id == null ? true : c.EmpId == id) &&
(name == null ? true : c.EmpName == name));
a truly dynamic query
Now here things start to get tricky. One possible option is to generate a string based query, with the help of a libary like Dynamic Linq
You have almost nailed it. :)
Instead of using DeserializeObject and then converting it to JArray prefer JArray.Parse
var json = File.ReadAllText("sample.json");
var semiParsedJson = JArray.Parse(json);
Instead of using ToList after Where prefer JArray constructor which can work well with an IEnumerable<JToken>
const string IdField = "EmpID", NameField = "EmpName", AgeField = "Age";
const StringComparison caseIgnorant = StringComparison.OrdinalIgnoreCase;
var idEq29 = semiParsedJson.Children()
.Where(token => string.Equals(token[IdField].Value<string>(),"29", caseIgnorant));
Console.WriteLine(new JArray(idEq29).ToString());
The other queries can be implemented in the very same way
var ageEq23AndNameJhon = semiParsedJson.Children()
.Where(token => string.Equals(token[AgeField].Value<string>(), "23", caseIgnorant)
&& string.Equals(token[NameField].Value<string>(), "Jhon", caseIgnorant));
Console.WriteLine(new JArray(ageEq23AndNameJhon).ToString());
var ageEq29 = semiParsedJson.Children()
.Where(token => string.Equals(token[AgeField].Value<string>(), "29", caseIgnorant));
Console.WriteLine(new JArray(ageEq29).ToString());
UPDATE #1: Enhance proposed solution
With the following extension method
public static class JArrayExtensions
{
public static JArray Filter(this JArray array, Func<JToken, bool> predicate)
=> new JArray(array.Children().Where(predicate));
}
you can greatly simplify the filtering
var idEq29 = semiParsedJson
.Filter(token => string.Equals(token[IdField].Value<string>(),"29", caseIgnorant));
var ageEq23AndNameJhon = semiParsedJson
.Filter(token => string.Equals(token[AgeField].Value<string>(), "23", caseIgnorant))
.Filter(token => string.Equals(token[NameField].Value<string>(), "Jhon", caseIgnorant));
var ageEq29 = semiParsedJson
.Filter(token => string.Equals(token[AgeField].Value<string>(), "29", caseIgnorant));
Console.WriteLine(idEq29);
Console.WriteLine();
Console.WriteLine(ageEq23AndNameJhon);
Console.WriteLine();
Console.WriteLine(ageEq29);
Or you can push it even further. If all the fields store string values then you can define the extension method like this:
public static class JArrayExtensions
{
public static JArray Filter(this JArray array, string field, string value)
=> new JArray(array.Children().Where(GenerateFilter(field, value)));
private static Func<JToken, bool> GenerateFilter(string field, string value)
=> (JToken token) => string.Equals(token[field].Value<string>(), value, StringComparison.OrdinalIgnoreCase);
}
The the filter queries are super simple :D
var idEq29 = semiParsedJson
.Filter(IdField,"29");
var ageEq23AndNameJhon = semiParsedJson
.Filter(AgeField, "23")
.Filter(NameField, "Jhon");
var ageEq29 = semiParsedJson
.Filter(AgeField, "29");
Console.WriteLine(ageEq23AndNameJhon);
Console.WriteLine();
Console.WriteLine(idEq29);
Console.WriteLine();
Console.WriteLine(ageEq29);

How to select all from array1 which contains the id from array2?

I have a linq statement which includes a Contain() method. I am using this so that I can select all from an array where name is not null but only select the objects from the array1 that contains the same name in object array2.
I have managed to return the result but its displaying true or false where as I need the object values.
The code
var response = JsonConvert.DeserializeObject<FamilyNames>(result);
List<object> data = new List<object>();
ClassName className = new ClassName();
object [] getNames = className.GetType()
.GetProperties()
.Select(p =>
{
object value = p.Name;
return value == null ? null : value.ToString();
})
.ToArray();
foreach (var obj in response.items.Where(n => n.name != null).DistinctBy(x => x.name).Select(a => getNames.Contains(a.initialName)))
{
data.Add(obj);
}
client.Dispose();
return Json(data, JsonRequestBehavior.AllowGet);
}
The result is :
["True","False","True"]
If I don't use the select statement then I get my objects:
[
{
"initalName": "BD",
"firstName": "Bob",
"LastName": "Dilan"
},
{
"initalName": "HT",
"firstName": "Harry", // the initialName doesn't exist in list so need to remove this object
"LastName": "Thomas"
},
{
"initalName": "LJ",
"firstName": "Lindsey",
"LastName": "Jones"
}
]
The initalName is not present in getNames array so needs to be removed. Any advice would be much appreciated, especially on the approach. The desired result would be:
[
{
"initalName": "BD",
"firstName": "Bob",
"LastName": "Dilan"
},
{
"initalName": "LJ",
"firstName": "Lindsey",
"LastName": "Jones"
]
The problem is, that in this LINQ expression, at the end you are selecting a Bool as an output. (.Contains() returns a bool).
From this reason, your expression will retunr a list of Bool.
response.items
.Where(n => n.name != null)
.DistinctBy(x => x.name)
.Select(a => getNames.Contains(a.initialName))
To acheave what you want, simply replace the .Select() with a .Where(), what will do the intended filtering and keep the original objects as they are, (will not do any projection) and you will get the expected outcome:
response.items
.Where(n => n.name != null)
.DistinctBy(x => x.name)
.Where(a => getNames.Contains(a.initialName))

Elasticsearch - Order search results ASC

having a problem with my elasticsearch.
Setup: Having a Company-Class with the data field "companyName".
My search shall search and response all companys with the searched term.
If I try to sort via
.Sort(x=> x.OnField(x => x.CompanyName).Descending())
The data aren't sorted rightly - reference stackOverflow
I tried the given solution, but if I set my companyName to "not_analyzed" I cant even search anymore for the comnpany name or like a beginning "goo" (google)
So I tried to set up a multifield mapping, with a suffix, which isn't analyzed and one which is analyzed.
My Index is set up like:
client.CreateIndex(IndexName, c => c
.AddMapping<Exhibitor>(m =>m
.MapFromAttributes()
.Properties(o => o
.MultiField(mf=>mf
.Name(x=>x.CompanyName)
.Fields(fs => fs
.String(s=>s.Name(t=>t.CompanyName).Index(FieldIndexOption.Analyzed).Analyzer("standard"))
.String(s=>s.Name(t=>t.CompanyName.Suffix("raw")).Index(FieldIndexOption.NotAnalyzed))))
)
)
)
);
My search looks like:
string SearchTerm ="my search term"
results = GetClient().Search<Company>(s => s
.Query(qa => qa
.MatchPhrasePrefix(m => m
.OnField(f=>f.CompanyName)
.Query(SearchTerm)
))
.Sort(x => x.OnField(x => x.CompanyName.Suffix("raw")).Descending())
.Size(maxResults).Skip(page * pageSize).Take(pageSize)
);
But that still doesn't work.
Any ideas?
Thanks in advance.
Update 1:
For case insensitive sorting I added an custom analyzer:
var companyAnalyzer = new CustomAnalyzer
{
Filter = new List<string> { "standard", "lowercase" },
Tokenizer = "keyword"
};
client.CreateIndex(IndexName, c => c
.Analysis(analysis => analysis
.Analyzers(a => a
.Add("companyanalyzer", companyAnalyzer)
)
)
.AddMapping<Exhibitor>(m => m
.MapFromAttributes()
.Properties(o => o
.MultiField(mf => mf
.Name(x => x.CompanyName)
.Fields(fs => fs
.String(s => s.Name(t => t.CompanyName).Index(FieldIndexOption.Analyzed).Analyzer("standard"))
.String(s => s.Name(t => t.CompanyName.Suffix("raw")).Index(FieldIndexOption.Analyzed).Analyzer("companyanalyzer"))))
)
)
);
This example is working, maybe it will put some light on your issue.
var indicesResponse = client.DeleteIndex(descriptor => descriptor.Index(indexName));
client.CreateIndex(indexName, c => c
.AddMapping<Exhibitor>(m => m
.MapFromAttributes()
.Properties(o => o
.MultiField(mf => mf
.Name(x => x.CompanyName)
.Fields(fs => fs
.String(s => s.Name(t => t.CompanyName).Index(FieldIndexOption.Analyzed).Analyzer("standard"))
.String(s => s.Name(t => t.CompanyName.Suffix("raw")).Index(FieldIndexOption.NotAnalyzed))))
)));
client.Index(new Exhibitor { Id = 1, CompanyName = "a test" });
client.Index(new Exhibitor { Id = 2, CompanyName = "b test" });
client.Index(new Exhibitor { Id = 3, CompanyName = "c test" });
client.Refresh();
string SearchTerm = "test";
var results = client.Search<Exhibitor>(s => s
.Query(qa => qa
.MatchPhrasePrefix(m => m
.OnField(f => f.CompanyName)
.Query(SearchTerm)
))
.Sort(x => x.OnField(f => f.CompanyName.Suffix("raw")).Descending())
);
Result of this query:
{
"took": 2,
"timed_out": false,
"_shards": {..}
"hits": {
"total": 3,
"max_score": null,
"hits": [
{
"_index": "my_index",
"_type": "exhibitor",
"_id": "3",
"_score": null,
"_source": {
"id": 3,
"companyName": "c test"
},
"sort": [
"c test"
]
},
{
"_index": "my_index",
"_type": "exhibitor",
"_id": "2",
"_score": null,
"_source": {
"id": 2,
"companyName": "b test"
},
"sort": [
"b test"
]
},
{
"_index": "my_index",
"_type": "exhibitor",
"_id": "1",
"_score": null,
"_source": {
"id": 1,
"companyName": "a test"
},
"sort": [
"a test"
]
}
]
}
}
Index mapping:
{
"my_index" : {
"mappings" : {
"exhibitor" : {
"properties" : {
"companyName" : {
"type" : "string",
"analyzer" : "standard",
"fields" : {
"raw" : {
"type" : "string",
"index" : "not_analyzed"
}
}
},
"id" : {
"type" : "integer"
}
}
}
}
}
}
Hope it helps.

Search in list of expandoObject

Suppose I have a List of dynamic objects like:
var records = [
{
"Id": 1,
"Name": "sai",
"Age": "4",
"About": "12.02.1991"
},
{
"Id": 2,
"Name": "hjfh",
"Age": "2",
"About": "12.02.1991"
},
{
"Id": 3,
"Name": "hello name",
"Age": "6",
"About": "hi"
},
{
"Id": 4,
"Name": 1,
"Age": "9",
"About": "hello world"
}
]
string searchString = "Hello";
I tried something like:
foreach (var item in records )
{
foreach (var field in item)
{
if (field.Value != null && field.Value.ToString().IndexOf(query.SearchString, StringComparison.OrdinalIgnoreCase) >= 0)
{
count++;
break;
}
}
}
I want the count of records which has searchString at any field in the record. but can I write this using a LINQ query?
This is your code, converted to a linq expression:
You have access to the field and the object in the select statement.
records.Cast<ExpandoObject>().SelectMany(x => x, (obj, field) => new { obj, field })
.Where(x => x.field.Value != null && x.field.Value.ToString().IndexOf(SearchString, StringComparison.OrdinalIgnoreCase) >= 0)
.Select(x => x.field.Key);
To count the number of objects that have at least one field that contains the searchstring:
records.Cast<ExpandoObject>()
.Where(x => x.Any(y => y.Value != null && y.Value.ToString().IndexOf(SearchString, StringComparison.OrdinalIgnoreCase) >= 0))
.Count();
It would be something like this:
logic:
var qry = records.Where(x=>x.Field.Contains(searchedstring)).Count();
real example:
var qry = records.Where(x=>x.Name.Contains("sai")).Count();
Below is the simplest code.
int count = records.Count(x => x.Name.StartsWith("hello") || x.About.StartsWith("hello"));

Categories

Resources