piątek, 31 sierpnia 2018

Nowy filtr część 2 - testy, konkluzja

Test.

w parametrze umieszczone dwa słowa oddzielone spacją (ciekawe czy przecinek był by lepszy?)
Po stronie bazy sprawdziłem na którym dystansie jest najwięcej wpisów

 "Managment SQL"
select count(dys_id) as ilosc, dys_id from kartoteka2
group by dys_id
order by ilosc desc
Dystans 600 oblegany najbardziej.

Parametr param1 = "100"
Nie stało się nic odkrywczego obie funkcje zwracają taką samą ilość rekordów  = 24. Można było się tego spodziewać.
Parametr param1 = "Zbigniew 100"
Funkcja pierwsza nic nie zwróciła druga 24 rekordy.
Parametr param1 = "100 Brak"
Funkcja pierwsza nic nie zwróciła druga 55 rekordów.
Parametr param1 = "Brak 100"
Funkcja pierwsza zwróciła 24 rekordy druga 55 rekordów.

Dlaczego pierwsza zwróciła 24 rekordy w ostatnim przypadku gdyż zapis danych do kolumny dane
jest w kolejności imię, nazwisko,grupa,dystans,rezerwa,oplacony.
Druga funkcja zwróciła 55 rekordów z powodu budowy zapytania pytam za każdym razem cały zbiór inną wartością parametru param1 gdyż rozbijam go na listę.

Uwaga.
  • Nazwy funkcji są nie znaczące nazwy zmienić trzeba.
  • przed wyszukiwanie uppercase trzeba dać, albo przed zapisem do bazy uppercase na imię, nazwisko, email, grupa.
W kodzie znajdzie się funkcja nr dwa, uprości to nieco paginga w MVC.

Kilka dni temu zauważyłem niepokojące wyniki, zdarza się że filtr zwraca dane, które nie powinny się tam znaleźć albo znajdują się z nie wyjaśnionych przyczyn.
Chciałem wyszukać ludzi jadących na 150 km wartość wpisana "150".
Otrzymałem takie dane:

Nie zgadzał mi się pierwszy rekord dlaczego tam jest dystans 100 km przecież szukam 150.
Robimy inwestigejszyn.

Myślałem że znajdę zapytanie za pomocą SQL Server Profiler ale się nie udało, wydaje mi się (aczkolwiek nie wiem czy dobrze myślę), że ostatnią część filtra czyli porównanie list robię nie na bazie bezpośrednio a na wcześniej zaciągniętych danych.
Nie włączyłem Lazy Lołding, więc jest opcja ta druga.

var tabParam = param1.Split(' ').ToList();

tabParam = tabParam.Where(x => !string.IsNullOrWhiteSpace(x))
.Distinct().ToList();
oneColumn = oneColumn.Where(x => tabParam.Any(y => x.dane.Contains(y)));
                  
result = result.Where(x => oneColumn.
Any(y => y.imie == x.imie && y.nazwisko == x.nazwisko)).ToList();

W wykryciu co się dzieje w środku pomógł mi LinqPad (wersja darmowa).
Kod musiałem trochę dostosować do LinqPad-a

1. string tab = "150 ";
2. List<string> result = tab.Split(' ').ToList();
3. result = result.Where(x => !string.IsNullOrWhiteSpace(x)).Distinct().ToList();
4. List<VOneColumn> oneColumn = VOneColumn.ToList();
5. oneColumn = oneColumn.Where((x => result.Any(y => x.Dane.Contains(y)))).ToList();
6. //oneColumn.Dump();
7. List<VmodelExt> lista = VmodelExt.ToList();
8. lista = lista.Where( x => oneColumn.Any(y => y.Imie == x.Kart_imie && y.Nazwisko == 
9. x.Kart_nazwisko)).ToList();
10. lista.Dump(); //--> zwrocenie rezultatu na ekran

 Wyniki dały do myślenia, po pierwszym zapytaniu linia numer 5, otrzymałem dane zawodników jadących na 150 km, wyniki tej listy służą do wyszukania danych w kolejnym zbiorze linia nr 8 i 9, tu  otrzymałem dodatkowe cztery rekordy.
Dlaczego?
Odpowiedz jest już na powyższym obrazku w ostatnim widocznym wierszu mamy tego samego człowieka na dystansie 150 km. Żeby dokładnie przejrzeć wyniki trzeba było sięgnąć do Managmenta rezultat nie był zaskoczeniem.
Jest dwóch zawodników te same imię i nazwisko ale inny adres email (nie prezentuje go na liście zawodników), tabela ma postawioną unikalność imię, nazwisko i adres email. Więc w tym wypadku unikalność jest zachowana, a wynik poprawny.

Zastanawiam się nad skutecznością tych filtrów czy lepiej jedno pole czy wybierać z kilku tak jak było na początku? (to tak trochę po za tematem)

Jeszcze jedna uwaga.
Do testów w LinqPad stworzyłem sobie dwa widoki gdyż miałem problem z dołożeniem (poprawnym działaniem) referencji do moich bibliotek.

Nowy filtr część 1 - definicje metod, dane testowe

Jak już wspominałem w części piątej refaktoryzacji zmieniła się koncepcja filtra lub zmieni się muszę się zapytać ludzi czy chcą takiej zmiany (sądzę że i tak nikt nie odpowie, więc testowo uruchomię taki filtr i zobaczymy co będzie się działo).

Krok pierwszy z poprzedniego postu. (ctr+c => ctr+f).

Stworzyłem klasę OneFilter z metodą searchData. Jest to pierwszy krok więc zrobiłem to nie patrząc na solida.
 
public class OneFilter
    {
        public void searchData(string param1, ref List<ExtModelRegistrationList> result)
        {
            if (!string.IsNullOrEmpty(param1))
            {
                using (var db = new EntitiesMaraton())
                {
                    var oneColumn = result.Select(x => new
                    {
                        imie = x.imie,
                        nazwisko = x.nazwisko,
dane = x.imie + " " + x.nazwisko + " " + x.grupa + " " + x.dystans + " " + "rezerwa " + x.rezerwa + " " + "opłacono " + x.oplacony
                    }).ToList();
                    oneColumn = oneColumn
                               .Where(x => x.dane.Contains(param1)).ToList();
result = result
       .Where(x => oneColumn
       .Any(y => y.imie == x.imie && y.nazwisko == x.nazwisko)).ToList();
                }
            }
        }
    }
W czasie testów wyszło, że nie zawsze wyszuka poprawnie zadaną wartość. Pomysłem na to będzie wyszukanie danej frazy osobno w każdej kolumnie i doklejanie wyników (rekordów) do siebie.

Opcja numer dwa
Różnica parametr "param1" rozbity został na listę string-ów, a ta lista została porównana z "OneColumn".


public void searchDataFromFilter(string param1, ref List<ExtModelRegistrationList> result)
        {
            if (!string.IsNullOrEmpty(param1))
            {
                using (var db = new EntitiesMaraton())
                {
                    var oneColumn = result.Select(x => new
                    {
                        imie = x.imie,
                        nazwisko = x.nazwisko,
dane = x.imie + " " + x.nazwisko + " " + x.grupa + " " + x.dystans + " " + "rezerwa " + x.rezerwa + " " + "opłacono " + x.oplacony
                    }).ToList();

                    var tabParam = param1.Split(' ').ToList();
tabParam = tabParam
                             .Where(x => !string.IsNullOrWhiteSpace(x)).Distinct().ToList();
 
oneColumn = oneColumn
                             .Where(x => tabParam.Any(y =>x.dane.Contains(y))).ToList();
 
result = result
.Where(x => oneColumn
.Any(y => y.imie == x.imie && y.nazwisko == x.nazwisko)).ToList();
                }
            }
        }
Różnica w wynikach może być znaczna będzie to widoczne przy większej ilości parametrów (wyrażeń w parametrze oddzielonych spacjami). W metodzie pierwszej szukam w kolumnie dane pasującej wartości parametru, w drugiej metodzie rozbiłem parametr param1 na listę parametrów i przeszukuje każdym elementem z listy osobno.


Miałem mało testerów, a jestem leniwy to nie chciało mi się wpisywać danych więc zrobiłem funkcje generujące imię, nazwisko, email (słowo generujące to trochę nadużycie).

Funkcje generujące nazwisko i adres mail są słabe, trzeba by je dopracować.


public void testAdd()
        {
            try
            {
                using (var db = new EntitiesMaraton())
                {
                    Random _gr = new Random();
                    Random _ds = new Random();


                    kartoteka2 test1 = new kartoteka2();
                    for (int i = 0; i < 30; i++)
                    {
                        test1.grup_id = _gr.Next(1, 3);
                        test1.kart_imie = RandomStringNameDictionary();
                        test1.kart_nazwisko = RandomStringSurnameDictionary(6, "");
                        test1.kart_email = RandomStringEmailDictionary(6, "");
                        test1.dys_id = _ds.Next(1, 7);
                        test1.plec_id = 1;
                        test1.kart_dataUr = DateTime.Parse("1981-09-01");
                        test1.kart_telefon = "123456789";
                        test1.kart_uwagi = "test";
                        test1.kart_dataRej = DateTime.Now;
                        test1.kart_wpis_rejestacja = true;
                        test1.kart_wpis_oplata = false;
                        test1.kart_wpis_rezerwowa = true;
                        db.kartoteka2.Add(test1);
                        db.SaveChanges();
                    }
                }
            }
            catch (Exception ex)
            {
                string er = ex.InnerException.Message;
            }
        }


Prawie Generator losowych emaili.



public string RandomStringEmailDictionary(int lengthSName, string Sname)
        {
            Random random = new Random();
            Dictionary<int, string> lista = new Dictionary<int, string>();
            lista.Add(1, "ma");
            lista.Add(2, "sza");
            lista.Add(3, "ko");
            lista.Add(4, "wa");
            lista.Add(5, "pie");
            lista.Add(6, "ra");
            lista.Add(7, "ka");
            lista.Add(8, "ju");
            lista.Add(9, "nek");
            lista.Add(10, "po");
            lista.Add(11, "go");
            lista.Add(12, "tka");
            lista.Add(13, "ra");
            lista.Add(14, "ka");
            int key = random.Next(1, 13);
            System.Threading.Thread.Sleep(250);
            if (Sname.Length >= lengthSName)
            {
                Sname += "@.hw7.pl";
                return Sname;
            }
            else
            {
                Sname += lista.Where(x => x.Key == key).FirstOrDefault().Value;
                return RandomStringEmailDictionary(lengthSName, Sname);
            }
        }

Generator losowych nazwisko (prawie). Metoda jest bardzo słaba jak ktoś chce mieć bajzel to może być ale żeby generować mądrzejsze nazwisko trzeba by się bardziej napocić, z drugiej strony ktoś  się może obrazić odnajdując swoje nazwisko. Funkcja ta stworzona z lenistwa żeby nie musieć insertów robić ręcznie, to samo tyczy się emaila.


public string RandomStringSurnameDictionary(int lengthSName,string Sname)
        {
          
            Random random = new Random();
            Dictionary<int, string> lista = new Dictionary<int, string>();
            lista.Add(1, "ma");
            lista.Add(2, "sza");
            lista.Add(3, "ko");
            lista.Add(4, "wa");
            lista.Add(5, "pie");
            lista.Add(6, "ra");
            lista.Add(7, "ka");
            lista.Add(8, "ju");
            lista.Add(9, "nek");
            lista.Add(10, "po");
            lista.Add(11, "go");
            lista.Add(12, "tka");
            lista.Add(13, "ra");
            lista.Add(14, "ka");

            int key = random.Next(1, 13);
            System.Threading.Thread.Sleep(200);
            if (Sname.Length >= lengthSName)
            {
                return Sname;
            }
            else
            {
                Sname += lista.Where(x => x.Key == key).FirstOrDefault().Value;
                return RandomStringSurnameDictionary(lengthSName, Sname);
            }
        }

Wybieranie imion:

public string RandomStringNameDictionary()
        {
            Random random = new Random();
            Dictionary<int, string> lista = new Dictionary<int, string>();
            lista.Add(1, "Marcin");
            lista.Add(2, "Grzegorz");
            lista.Add(3, "Zbgniew");
            lista.Add(4, "Mateusz");
            lista.Add(5, "Monika");
            lista.Add(6, "Magda");
            lista.Add(7, "Sylwester");
            lista.Add(8, "Michał");
            lista.Add(9, "Tomasz");
            lista.Add(10, "Sylwia");
            lista.Add(11, "Irena");
            lista.Add(12, "Antoni");
            lista.Add(13, "Marysia");
            lista.Add(14, "Jan");
            int key = random.Next(1, 13);
           
            string sname = lista.Where(x => x.Key == key ).FirstOrDefault().Value;
            if (string.IsNullOrEmpty(sname))
            {
                RandomStringNameDictionary();
                return sname;
            }
            else
            {
                return sname;
            }
        }




Testy. cdn...

 Po wielu miesiącach przerwy Czteroletnim exodusie do bloga ismartdev, który zdechł w zeszłym roku w listopadzie, na powrót wstąpiłem w ten ...