Filtering mongodb data collection filter - c#

I used mongodb in my backend to store some customer personal information and I need to fetch the user who ages 30, 32 and 35.
I tried below ways to get but it returns zero results and I used C# MongoDB.Driver
C# code
Age = new string[] { "26-30", "31-35" }
DateTime today = DateTime.Today;
var filter = Builders<Customer>.Filter.Empty;
foreach (var item in searchFilterBlock.Age)
{
var ageBetween = item.Split('-');
int.TryParse(ageBetween[0], out int startYear);
int.TryParse(ageBetween[1], out int endYear);
var start = today.AddYears(-startYear);
var end = today.AddYears(-endYear);
filter = filter & (Builders<Customer>.Filter.Gte(x => x.Dob, start)
& Builders<Customer>.Filter.Lte(x=>x.Dob, end));
}
// to execute the filter
var searchResult = _context.Customer.Find(filter).ToList(); // it return 0 result
Need to get who has ages 30, 32 and 35.

you can get customers who are aged 30,32 and 35 by using an $or filter like the following:
db.Customer.find({
"$or": [
{
"DOB": {
"$lte": ISODate("1989-06-22T14:57:50.168Z"),
"$gte": ISODate("1988-06-22T14:57:50.168Z")
}
},
{
"DOB": {
"$lte": ISODate("1987-06-22T14:57:50.168Z"),
"$gte": ISODate("1986-06-22T14:57:50.168Z")
}
},
{
"DOB": {
"$lte": ISODate("1984-06-22T14:57:50.168Z"),
"$gte": ISODate("1983-06-22T14:57:50.168Z")
}
}
]
})
here's the c# code that generated the above find query using the convenience library MongoDB.Entities [disclaimer: i'm the author]
using MongoDB.Driver;
using MongoDB.Entities;
using System;
using System.Collections.Generic;
namespace StackOverflow
{
public class Program
{
public class Customer : Entity
{
public string Name { get; set; }
public DateTime DOB { get; set; }
}
private static void Main(string[] args)
{
new DB("test");
(new[] {
new Customer{ Name = "I am 29", DOB = DateTime.UtcNow.AddYears(-29)},
new Customer{ Name = "I am 30", DOB = DateTime.UtcNow.AddYears(-30)},
new Customer{ Name = "I am 32", DOB = DateTime.UtcNow.AddYears(-32)},
new Customer{ Name = "I am 35", DOB = DateTime.UtcNow.AddYears(-35)},
new Customer{ Name = "I am 36", DOB = DateTime.UtcNow.AddYears(-36)}
}).Save();
var ages = new[] { 30, 32, 35 };
var filters = new List<FilterDefinition<Customer>>();
foreach (var age in ages)
{
var start = DateTime.UtcNow.AddYears(-age);
var end = DateTime.UtcNow.AddYears(-age - 1);
filters.Add(DB.Filter<Customer>()
.Where(c => c.DOB <= start && c.DOB >= end));
}
var customers = DB.Find<Customer>()
.Many(f => f.Or(filters))
.ToArray();
}
}
}

Related

Using where in LINQ select new statement for specific columns

I'm working on a class assignment and got a bit lost in LINQ.
I have 3 tables, 'oltandok' contains the data of persons, 'preferenciak' contains the preferred vaccine of that person with 3 columns:
an FK for table oltandok
a number indicating the order of preferences (1 is highest, 6 is lowest preferred)
an FK for another table containing the data on the vaccines called 'vakcinak'
I would like to display the data in a DataGridView the following way:
Personal data and the preferred vaccines in different columns:
Pref1 - Name of the vaccine where pref == 1
Pref2 - Name of the vaccine where pref == 2
etc.
This is where I am with my code, but I'm not sure how to select the preferences properly.
manu_rogz.DataSource = ( from x in context.oltandok
join y in context.preferencia on x.TAJ equals y.oltandok_FK
select new
{
TAJ = x.TAJ,
Nev = x.nev,
Szuletesnap = x.birthdate,
Pref1 = ???
Pref2 = ???
}
).ToList();
Because the preferenciak table contains multiple rows per person, you will need to perform some grouping.
Here is some very rough code which illustrates one way to do that.
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main(string[] args)
{
var persons = new List<Person> { new Person { ID = 11, PersonName = "Alice" }, new Person { ID = 22, PersonName = "Bob" } };
var vaccines = new List<Vaccine> { new Vaccine(){ ID = 111, VaccineName= "Pfizer" }, new Vaccine(){ ID = 222, VaccineName = "Moderna" } };
var preferences = new List<VaccPref>
{
new VaccPref() { Person_FK = 11, Preference = 1, Vaccine_FK = 111 },
new VaccPref() { Person_FK = 11, Preference = 2, Vaccine_FK = 222 },
new VaccPref() { Person_FK = 22, Preference = 1, Vaccine_FK = 222 },
new VaccPref() { Person_FK = 22, Preference = 2, Vaccine_FK = 111 }
};
var prefsWithVaccNames = preferences.Join(vaccines, p => p.Vaccine_FK, v => v.ID, (pref, vaccine) => new Tuple<VaccPref, string>(pref, vaccine.VaccineName));
var groupedPrefs = prefsWithVaccNames.GroupBy(p => p.Item1.Person_FK);
var personPrefs = new List<PersonPrefs>();
foreach (var group in groupedPrefs)
{
personPrefs.Add(
new PersonPrefs()
{
Person_FK = group.Key,
Pref1 = group.Single(v => v.Item1.Preference == 1).Item2,
Pref2 = group.Single(v => v.Item1.Preference == 2).Item2,
});
}
var personPrefsWithPersonNames =
personPrefs.Join(
persons,
pp => pp.Person_FK,
p => p.ID,
(pp, p) => new NamedPersonPrefs() { Name = p.PersonName, Pref1 = pp.Pref1, Pref2 = pp.Pref2 }).ToArray();
}
}
class Person
{
public int ID { get; set; }
public string PersonName { get; set; }
}
class VaccPref
{
public int Person_FK { get; set; }
public int Preference { get; set; }
public int Vaccine_FK { get; set; }
}
class Vaccine
{
public int ID { get; set; }
public string VaccineName { get; set; }
}
class PersonPrefs
{
public int Person_FK { get; set; }
public string Pref1 { get; set; }
public string Pref2 { get; set; }
}
class NamedPersonPrefs
{
public string Name { get; set; }
public string Pref1 { get; set; }
public string Pref2 { get; set; }
}
This is a self-contained C# program which should produce a result similar to what you're after. You will of course need to adjust the class definitions (and change the table names) to suit your needs.
I've used LINQ's fluent syntax but you can use the SQL-like version if you prefer.

ListView sorting by two columns

I have a model class Reminder:
public class Reminder
{
public string ReminderName { get; set; }
public ReminderTypes ReminderType { get; set; }
public DateTime ReminderDate { get; set; }
}
and I also have an enum called ReminderTypes:
public enum ReminderTypes
{
REMINDER_NORMAL,
REMINDER_FLAGGED,
REMINDER_IMPORTANT,
REMINDER_WARNING
}
I created my list as follows.
List<Reminder> reminders = new List<Reminder> {
new Reminder{ ReminderName = "Reminder1", ReminderType = ReminderTypes.REMINDER_FLAGGED , ReminderDate = DateTime.Now.AddDays(5)
},
new Reminder{ ReminderName = "Reminder2", ReminderType = ReminderTypes.REMINDER_IMPORTANT, ReminderDate = DateTime.Now.AddDays(10).AddHours(5).AddMinutes(50)
},
new Reminder{ ReminderName = "Reminder3", ReminderType = ReminderTypes.REMINDER_NORMAL, ReminderDate = DateTime.Now.AddHours(5)
},
new Reminder{ ReminderName = "Reminder4", ReminderType = ReminderTypes.REMINDER_WARNING, ReminderDate = DateTime.Now.AddDays(10).AddHours(5).AddMinutes(49)
},
new Reminder{ ReminderName = "Reminder5", ReminderType = ReminderTypes.REMINDER_FLAGGED, ReminderDate = DateTime.Now.AddDays(5).AddHours(5)
},
};
ListViewReminder.ItemsSource = reminders;
The sorting rule that should be:
Always flagged ones must be on top (flagged ones should be sorted by time)
Those that are not flagged should be sorted by current time (those close to current time should be on top)
The view that should be in listview:
Reminder1 ( Becuse flagged and Closer date than the other flagged.)
Reminder5 ( Because flagged )
Reminder3 ( Non-flagged, Closer date than the other non-flagged )
Reminder4
Reminder2
How can I do that?
you can Achieve this using System.Linq,
var Result = reminders
.Where(e => e.ReminderType == ReminderTypes.REMINDER_FLAGGED)
.OrderBy(e => e.ReminderDate).ToList();
Result.AddRange(reminders
.Where(e => e.ReminderType != ReminderTypes.REMINDER_FLAGGED)
.OrderBy(e => e.ReminderDate));
I would recommend two changes.
Assign numbers to your enums so they sort how you want them sorted.
Use OrderBy and ThenBy to sort your list based on ReminderType, then ReminderDate.
public enum ReminderTypes
{
REMINDER_FLAGGED = 0,
REMINDER_NORMAL = 1,
REMINDER_WARNING = 2,
REMINDER_IMPORTANT = 3
}
List<Reminder> ordered = reminders.OrderBy(x => x.ReminderType).ThenBy(x => x.ReminderDate).ToList();
ordered.ForEach(x => Console.WriteLine(x.ReminderName));
Output
Reminder1
Reminder5
Reminder3
Reminder4
Reminder2

Some loop with conditions. Specific problem

I have a small problem with mathematics or combinatorics in my C# code. I don't know how to write this easiest.
I have a class Section and TestClass but not a method to return expected result.
public class Section
{
public int Id { get; set; }
public int Pages { get; set; }
public string Name { get; set; }
}
[TestFixture]
public class PermutatorTest
{
private IList<Section> _sections;
private int _targetPage;
[SetUp]
public void SetUp()
{
_targetPage = 30;
_sections = new List<Section>
{
new Section {Id = 1, Pages = 15, Name = "A"},
new Section {Id = 2, Pages = 15, Name = "B"},
new Section {Id = 3, Pages = 10, Name = "C" },
new Section {Id = 4, Pages = 10, Name = "D"},
new Section {Id = 5, Pages = 10, Name = "E"},
new Section {Id = 6, Pages = 5, Name = "F"}
};
}
[Test]
public void GetPermutationsTest()
{
// Code to return list of all combinations
}
}
I want to get each combination which give me 30 as a sum of Pages.
it could be return as a string based on name or Id e.g AA or 11 , AB or 12
Of course, the order is not important ( AB and BA is the same... CCD and CDC and DCC too )
Final result should look like this: (30 correct results)
AA
AB
ACF
ADF
AEF
AFFF
BB
BCF
BDF
BEF
BFFF
CCC
CCD
CCE
CDD
CEE
CDE
CFFFF
CDFF
CCFF
CEFF
DDFF
DEFF
DFFFF
DDD
DDE
EFFFF
EEE
EEFF
FFFFFF
e.g. DDE = 10+10+10 = 30 OK
CFFFF = 10 + 5 +5 +5 +5 = 30 Ok
etc.
I dont have idea for best way to create loops for this, and put records to List
Thank you very much for every attempt to help me.
This was my original idea I was going to post for you, it just returned a list of strings
public List<String> result;
public void GetResultList(int startOffs, String CurNames, int curTotal)
{
for (int newOffs = startOffs; newOffs < _sections.Count; newOffs++)
{
int newTotal = curTotal + _sections[newOffs].Pages;
String newNames = CurNames+ _sections[newOffs].Name;
if (newTotal < _targetPage)
GetResultList(newOffs, newNames, newTotal);
else if (newTotal == _targetPage)
result.Add(newNames);
}
}
called by initialising the result & start parameters :
result = new List<String>();
GetResultList(0,"",0);
This is a version modified to use your Config class
public void GetResultList(int startOffs, Config CurConfig)
{
for (int newOffs = startOffs; newOffs < _sections.Count; newOffs++)
{
Config newConfig = new Config{ Name = CurConfig.Name + _sections[newOffs].Name,
Ids = CurConfig.Ids + _sections[newOffs].Id.ToString(),
Pages = CurConfig.Pages + _sections[newOffs].Pages};
if (newConfig.Pages < _targetPage)
GetResultList(newOffs, newConfig);
else if (newConfig.Pages == _targetPage)
_result.Add(newConfig);
}
}
calling needs the result initialising & a starting Config instance
_result = new List<Config>();
Config s = new Config { Ids = "", Pages=0, Name=""};
GetResultList(0,s);
Only for Information and Searchers.
I know, this code is not so clean
but I put it here as a nUnit Test...
it returns what I wanted ... i think.
using System;
using System.Collections.Generic;
using NUnit.Framework;
[TestFixture]
public class PermutatorTest
{
private IList<Section> _sections;
private int _targetPage;
private IList<Config> _result;
[SetUp]
public void SetUp()
{
_targetPage = 30;
_sections = new List<Section>
{
new Section {Id = 1, Pages = 15, Name = "A"},
new Section {Id = 2, Pages = 15, Name = "B"},
new Section {Id = 3, Pages = 10, Name = "C" },
new Section {Id = 4, Pages = 10, Name = "D"},
new Section {Id = 5, Pages = 10, Name = "E"},
new Section {Id = 6, Pages = 5, Name = "F"}
};
_result = new List<Config>();
}
[Test]
public void GetPermutationsTest()
{
for (var b =0 ; b<=_sections.Count-1; b++)
{
var config = new Config
{
Name = _sections[b].Name,
Ids = _sections[b].Id.ToString(),
Pages = _sections[b].Pages
};
GoDeeperAndAddToResult(config, b);
}
Console.WriteLine(_result.Count);
foreach (var item in _result)
{
Console.WriteLine($"{item.Name} - {item.Ids} - {item.Pages}");
}
}
private void GoDeeperAndAddToResult(Config config, int startIndex)
{
for (var b = startIndex; b <= _sections.Count-1; b++)
{
var section = _sections[b];
var combName = config.Name;
var combIds = config.Ids;
var combPages = config.Pages;
var maxSec = _targetPage / section.Pages;
for (var a = 1; a <= maxSec; a++)
{
combName = combName + section.Name;
combIds = combIds + section.Id.ToString();
combPages = combPages + section.Pages;
var subConfig = new Config
{
Name = combName,
Ids = combIds,
Pages = combPages
};
if (subConfig.Pages == _targetPage)
{
_result.Add(subConfig);
break;
}
else if (subConfig.Pages < _targetPage)
{
GoDeeperAndAddToResult(subConfig, b + 1);
}
else
{
break;
}
}
}
}
public class Config
{
public string Name { get; set; }
public string Ids { get; set; }
public int Pages { get; set; }
}
public class Section
{
public int Id { get; set; }
public int Pages { get; set; }
public string Name { get; set; }
}
}

'List<Choice>' does not contain a definition for 'ChoiceId ' and no extension...are you missing a using directive or an assembly reference?)

Title is just a part of the error, here is one of many error:
'List' does not contain a definition for 'ChoiceId' and no extension method 'ChoiceId' accepting a first argument of type 'List' could be found (are you missing a using directive or an assembly reference?)
I am trying to unit test a list of RepaymentPlan as stated below. But I can not seem to fetch the rest of the values in that list since I get red underline error below those keys (I have wrapped those keys with ** ** in unit test below. Something wrong I am doing and not sure how to solve it.
Still learning and fresh new to programming so hopefully you guys can guide me in the right way. Really appreciate your help.
Here is what I have:
Models:
using System.Collections.Generic;
namespace Project.Models
{
public class Repayment
{
public int Amount { get; set; }
public string DueDate { get; set; }
public string RepaymentId { get; set; }
}
public class Choice
{
public string ChoiceId { get; set; }
public List<Repayment> Repayments{ get; set; }
}
public class RepaymentPlan
{
public List<Choice> Choices{ get; set; }
}
}
Repositories:
using System.Collections.Generic;
using System.Linq;
using Project.Models;
using System.Net;
using System.Collections.Specialized;
using System.Text;
namespace Project.Repositories
{
public class OrderRepository
{
private static List<RepaymentPlan> _RepaymentPlan;
static OrderRepository()
{
_RepaymentPlan = new List<RepaymentPlan>();
_RepaymentPlan.Add(new RepaymentPlan
{
Choices = new List<Choice>
{
new Choice
{
ChoiceId = "cho1",
Repayments = new List<Repayment>
{
new Repayment
{
Amount = 200,
DueDate = "2018-06-01"
},
new Repayment
{
Amount = 100,
DueDate = "2018-08-01",
}
}
},
new Choice
{
ChoiceId = "cho2",
Repayments = new List<Repayment>
{
new Repayment
{
RepaymentId = "Choice1",
Amount = 300,
DueDate = "2018-10-01"
},
new Repayment
{
RepaymentId = "Choice2",
Amount = 150,
DueDate = "2018-11-01"
},
}
}
},
});
}
* Unit test *
[Test]
public void ListRepaymentPlan()
{
// Arrange
var controller = new CaseController();
var expectedResult = new List<RepaymentPlan>();
var repaymentPlan = new RepaymentPlan()
{
Choices = new List<Choice>
{
new Choice
{
ChoiceId = "cho1",
Repayments = new List<Repayment>
{
new Repayment
{
Amount = 200,
DueDate = "2018-06-01"
},
new Repayment
{
Amount = 100,
DueDate = "2018-08-01"
},
}
},
new Choice
{
ChoiceId = "cho2",
Repayments = new List<Repayment>
{
new Repayment
{
RepaymentId = "Choice1",
Amount = 300,
DueDate = "2018-10-01"
},
new Repayment
{
RepaymentId = "Choice2",
Amount = 150,
DueDate = "2018-11-01"
},
}
}
},
};
expectedResult.Add(RepaymentPlan );
// Act
var actionResult = controller.FindRepaymentPlan("578", "156");
var result = (List<RepaymentPlan >)actionResult;
// Assert
for (var i = 0; i < result.Count; i++)
{
Assert.AreEqual(expectedResult[i].Choices , result[i].Choices);
Assert.AreEqual(expectedResult[i].Choices.**ChoiceId**, result[i].Choices.**ChoiceId**);
Assert.AreEqual(expectedResult[i].Choices.**ChoiceId**.Repayments, result[i].Choices.**ChoiceId**.Repayments);
Assert.AreEqual(expectedResult[i].Choices.**Repayments**.Amount, result[i].Choices.**Repayments**.Amount);
Assert.AreEqual(expectedResult[i].Choices.**Repayments**.DueDate, result[i].Choices.**Repayments**.DueDate);
}
}
** Updated unit test **
// Assert
for (var i = 0; i < result.Count; i++)
{
CollectionAssert.AreEqual(expectedResult[i].Choices, result[i].Choices);
CollectionAssert.AreEqual(expectedResult[i].Choices[0].ChoiceId, result[i].Choices[0].ChoiceId);
CollectionAssert.AreEqual(expectedResult[i].Choices[0].Repayments, result[i].Choices[0].Repayments);
CollectionAssert.AreEqual(expectedResult[i].Choices, result[i].Choices);
CollectionAssert.AreEqual(expectedResult[i].Choices[1].ChoiceId, result[i].Choices[1].ChoiceId);
CollectionAssert.AreEqual(expectedResult[i].Choices[1].Repayments, result[i].Choices[1].Repayments);
}
Choices is a List<> so you need to index it too, something like this:
expectedResult[i].Choices[0].ChoiceId
However, you'd have to loop around those inner values too. It's much easier to use CollectionAssert:
CollectionAssert.AreEqual(expectedResult, result);

ForEach on IEnumerable<dynamic> type data

I am getting data in the form of :
[0]: { rkey = "100asb", pkey = "100ap0", ckey = {1/6/2013 3:28:09 AM} }
[1]: { rkey = "yxq4c", pkey = "100iis", ckey = {1/6/2013 6:38:48 PM} }
.
.
.
I have to write another method that has access to the values rkey, pkey and ckey. Unfortunately I can't access them in a linear ForEach or Parallel.ForEach. I have searched, but I have not found a solution to access my parameters. Some people suggested to convert this to a Dictionary, but I am not sure if that is a good path to take. It has to be much simpler than that.
the code that I have written is like this:
var films = myrollup.GetCompletedMovies(dtstart, dtend).Result;
using (var session = factory.OpenSession())
{
using (var transaction = session.BeginTransaction())
{
Parallel.ForEach(films, currentFilm =>
{
dynamic f = currentFilm;
lock (myrollup)
{
var user = User.GetAsync(f.pkey).Result;
var record = new FilmAnalytics()
{
UserID = currentFilm.pkey,
FilmID = currentFilm.rkey,
UserName = user.FirstName,
UserLastName = user.LastName,
UserAlias = user.Alias,
UserEmail = user.Email,
UserFacebook = user.FbEmail,
Dateofcompletion = currentFilm.ckey
};
session.SaveOrUpdate(record);
}
});
transaction.Commit();
}
}
The variable films produces the IEnumerable<dynamic> type data.
Works for me:
static void Main(string[] args) {
IEnumerable<dynamic> films = new dynamic[] { new { rkey = 1, rval = "val1" }, new { rkey = 2, rval = "val2" } };
foreach (var film in films.Select(f => new { RKey = (int)f.rkey,RValue = (string)f.rval }))
Console.WriteLine(film.RKey + ":" + film.RValue);
}
This way I can transform dynamics to strongly typed objects and then I can do whatever I want with them
You have to create a class like
public class Thing
{
public string rkey { get; set; }
public string pkey { get; set; }
public DateTime ckey { get; set; }
}
and then make the task like this:
public Task<IEnumerable<Thing>> ....
then I can have access to pkey, rkey, and ckey.

Categories