Pokazywanie postów oznaczonych etykietą filter. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą filter. Pokaż wszystkie posty

piątek, 27 września 2013

[FullTextSearch] ElasticSearch: Filtrowanie, Sortowanie, Paginacja

Filtrowanie

Filtrowanie zawęża zbiór dokumentów, na których odbywa się przeszukiwanie pełnotekstowe. Podejście takie znacznie poprawia wydajność zapytań. Filtr nie wpływa także na wynik wyszukiwania (pole _score) w zwracanej kolekcji wyników. Przykład poprawnego filtrowanego zapytania, gdzie kolekcja jest najpierw filtrowana, a później odpytana po indeksie:

POST http://localhost:9200/books/book/_search HTTP/1.1
User-Agent: Fiddler
Content-Type: application/json
Host: localhost:9200
Content-Length: 160

{
 "query": {
  "filtered" : {
   "query" : {
    "field" : { "author" : "Kaku" }
   },
    "filter" : {
    "term" : { "year" : 1993 }
   }
  }
 }
}

Filtrować można także zawężając przeszukiwany zbiór

POST http://localhost:9200/books/book/_search HTTP/1.1
User-Agent: Fiddler
Content-Type: application/json
Host: localhost:9200
Content-Length: 331

{
   "query":{
      "filtered":{
         "query":{
            "term":{
               "title":"worlds"
            }
         },
         "filter":{
            "range":{
               "year":{
                  "from":2000,
                  "to":2013
               }
            }
         }
      }
   }
}

Sortowanie

Ponieważ ElasticSearch może działać jako zwykła dokumentowa baza danych, musi także wspierać sortowanie zwracanych elementów. Wykorzystujemy do tego pole sort.

POST http://localhost:9200/books/book/_search HTTP/1.1
User-Agent: Fiddler
Content-Type: application/json
Host: localhost:9200
Content-Length: 156

{
"query" : {
 "match" : {
  "author" : {
   "query" : "penrose kaku",
   "operator" : "or"
  }
 }
},
  "sort" : [
    { "price" : "asc" }
  ]
}

Paginacja

Stronnicowanie jest dziś powszechnie stosowaną techniką umożliwiającą przesyłanie danych małymi porcjami. Również ElasticSearch wspiera stronnicowanie za pomocą pól from oraz size. 

POST http://localhost:9200/books/book/_search HTTP/1.1
User-Agent: Fiddler
Content-Type: application/json
Host: localhost:9200
Content-Length: 184

{
"query" : {
 "match" : {
  "author" : {
   "query" : "penrose kaku",
   "operator" : "or"
  }
 }
},
  "sort" : [
    { "price" : "asc" }
  ],
 "from" : 2,
 "size" : 2
}

czwartek, 29 sierpnia 2013

[C#|Visual Studio] ServiceStack: Filtry

Filtrami w ServiceStacku nazywane są fragmenty kodu wywoływane przed lub po każdym zapytaniu. Można ustawiać im priorytety, które odpowiadają później kolejności, w jakiej filtry się wykonują. Aby stworzyć filtr wystarczy dziedziczyć po klasie RequestFilterAttribute lub ResponseFilterAttribute.

public class IpRecordFilter : RequestFilterAttribute
{
    public ICacheClient Cache { get; set; }

    public override void Execute(IHttpRequest req, IHttpResponse res, object requestDto)
    {
        Cache.Add(req.UserHostAddress, req.RemoteIp);
    }
}

Użycie filtra sprowadza się do dodania atrybutu nad DTO lub serwisem.

public class EntryService : Service
{
    public int Sum { get; set; }

    [IpRecordFilter]
    public object Post(Entry entry)
    {
        Sum += entry.Count;
        return new EntryResponse() {Total = Sum};
    }
}

sobota, 1 grudnia 2012

[SQL|ORM] Entity Framework : Database First

Database First to podejście, w którym mamy już stworzoną bazę danych i chcemy ją jedynie zmapować na encje w naszej aplikacji. Przy takiej okazji pojawia się kilka ciekawych zagadnień.

Modelowanie relacji wiele do wielu

W relacyjnych bazach danych do tworzenia tego typu relacji stosuje się tzw. junction tables. Są to tabele, które zawierają przeważnie dwie kolumny - klucze obce do obu "stron" relacji. Prosty skrypt tworzący taką relację poniżej

CREATE TABLE Album (
  AlbumId INT IDENTITY PRIMARY KEY,
  AlbumName VARCHAR(50)
  )
 
CREATE TABLE Artist (
  ArtistId INT IDENTITY PRIMARY KEY,
  FirstName VARCHAR(50),
  MiddleName VARCHAR(50),
  SecondName VARCHAR(50)
  )
 
CREATE TABLE LinkTable (
  ArtistId INT NOT NULL ,
  AlbumId INT NOT NULL ,
  PRIMARY KEY (ArtistId, AlbumId) ,
  CONSTRAINT fk_Artist_has_Album
    FOREIGN KEY (ArtistId)
    REFERENCES Album (AlbumId),
  CONSTRAINT fk_Album_has_Artist
    FOREIGN KEY (AlbumId)
    REFERENCES Artist (ArtistId)
    );

Po dodaniu ADO .NET Entity Data Model i wybraniu powyższych tabel, otrzymamy w designerze dwie encje połączone relacją wiele do wielu.

Obie encje otrzymały dodatkowe Navigation Properties mapowane do LinkTable. Dzięki temu w łatwy sposób można dodawać Artystów do albumu (i na odwrót) przez dodanie referencji do kolekcji.

Nieco inaczej sytuacja wygląda w przypadku, gdy tabela łącząca zawiera dodatkowe dane.Wtedy Entity Framework utworzy trzy encje danych, jak w przykładzie poniżej:
Dane dodajemy zatem do tabel poprzez stworzenie trzech obiektów.

Modelowanie tabel zawierających referencje do samych siebie

Tabele zawierające klucz główny, oraz klucz obcy będący referencją do własnego klucza głównego są w relacji do samych siebie. Dodając taką tabelę do EDM dostaniemy encję z dwoma dodatkowymi Navigation Properties. Możemy zmienić ich nazwy, na takie, które będą odpowiadały ich reprezentacji. Jedna z nich będzie właściwością skalarną (relacja 0..1), drugą kolekcją (relacja 1..*). W ten sposób modelowane mogą być dane reprezentowane np. jako drzewa.

Encja taka może mieć jedną nadkategorię (lub nie mieć nadkategorii - relacja 0..1) oraz wiele podkategorii.

Jedna encja - wiele tabel

Mamy dwie tabele dzielące ten sam klucz główny. Z punktu widzenia programu możemy połączyć zatem dwie encje w jedną.



Po wybraniu obu tabel w okienku Entity Data Model, dostaniemy dwie encje w designerze. Aby je połączyć, należy:
  • Przekopiować właściwości z encji ProductWebInfo do Product
  • Usunąć ProductWebInfo, wybierając opcję No przy pytaniu o usuwanie tabel z store model
  • W widoku Mapping Details w VS dla encji Product należy dodać tabelę ProductWebInfo
  • Dla tabeli ProductWebInfo ustawić mapowania (SKU na property SKU, ImageURL na property ImageURL)
Podczas zapisywania obiektu encji Product do bazy danych zostaną dodane wpisy do obu tabel.

Jedna tabela - wiele encji

Sytuacja taka może być przydatna, gdy mamy w tabeli pewne pola dużych rozmiarów, z których rzadko korzystamy. Aby nie ładować ich nie potrzebnie za każdym razem możemy dodać tabelę do EDM, a następnie odpowiednio zmapować ją do dwóch encji:
  • Dodać nową encję do designera, zmieniając w polu klucza wartość na nazwę kolumny klucza głównego  z encji głównej
  • Przenieść rzadko używane właściwości do nowej encji
  • W Mapping Window na nowej encji dodać tabelę i ustawić mapowanie właściwości na odpowiednie kolumny
  • Dodać asocjację (PPM na jednej z encji) między dwoma encjami, ustawioną na 1 do 1, odznaczając dodawanie nowego klucza obcego
  • Ostatecznie klikając dwukrotnie na połączeniu należy dodać ograniczenia (Constraint) w Referential Constraint ustawiając Principal na Photograph, a właściwości klucza na PhotoId
Po poprawnej konfiguracji otrzymujemy Lazy Loading dla pól w dodatkowej encji. Warto pamiętać, że usuwając encję główną, usuwane także encję dodatkową, oraz, że aby dodać encję dodatkową do zbioru, musi istnieć odpowiadająca jej encja główna.

Modelowanie dziedziczenia "tabela per typ"

Dysponujemy jedną tabelą główną, oraz dwoma powiązanymi z nią relacjami kluczy obcych. Dwie dodatkowe tabele stanowią uzupełnienie informacji o obiekcie z tabeli głównej.



Z punktu widzenia aplikacji pisanej w języku obiektowym, dwie tabele dziedziczą z tabeli głównej. Aby zamodelować dziedziczenie w EF, należy po dodaniu trzech tabel do Entity Data Model:
  • usunąć wszystkie asocjacje
  • na encji odpowiadającej tabeli głównej wybrać Add...Inheritance. W dialog boxie wybrać tabelę bazową jako encję bazową oraz jedną z tabel uzupełniających, jako encję dziedziczącą
  • czynność powtórzyć dla drugiej tabeli
  • w oknie mapowania usunąć property odpowiadające kolumnie z kluczem obcym z encji dziedziczących i dodać je, jako mapowane do kolumny z tabeli głównej
W efekcie uzyskamy hierarchę obiektów jak poniżej:


Filtrowanie kolekcji obiektów

Podczas mapowania tabeli na encję możemy założyć filtr odrzucający pewne rekordy. Przykładowo możemy odrzucać wszystkie wpisy, gdzie pola z pewnej kolumny są różne od NULL. Po dodaniu tabeli do EDM, należy przejść do widoku Mapping Details, a następnie kliknąć w opcję <Add a Condition> . W dodanym warunku wybieramy kolumnę, rodzaj warunku (is lub =) i wartość (w tym przypadku is Null. Nie będziemy potrzebowali więcej property, które służy za warunek, zatem można je usunąć z listy właściwości na encji. Pozostałe możliwości warunków
  • <column> Not Null
  • <integer> = <value>
  • <String> = <value>

Modelowanie dziedziczenia "tabela per hierarchia"

Mamy tabelę, zawierającą jedną kolumnę, która determinuje typ (rodzaj) obiektu. Chcemy tak zmapować ją do encji, aby uzyskać jeden bazowy typ abstrakcyjny i dwa dziedziczące z niego. Po dodaniu naszej tabeli do Entity Data Model należy:
  • Dodać dwie dodatkowe encje w designerze, które będą reprezentowały typy dziedziczące. Podczas dodawania należy ustalić typ bazowy na encję mapowaną bezpośrednio do tabeli
  • Przenieść properties charakterystyczne dla danego typu z encji bazowych do odpowiednich encji dziedziczących
  • W widoku Mapping Details wybrać dla obu typów dziedziczących tabelę, do której będą mapowane, oraz sprawdzić poprawność mapowania properies nowej encji do odpowiednich kolumn tabeli
  • W tym samym widoku w polu Add a Condition należy dodać warunek na podstawie kolumny z tabeli głównej, np. 


W opcjach encji bazowej można ustawić pole abstract na true, oraz usunąć property decydujące o typie encji.



Modelowanie relacji typu "Is-a" oraz "Has-a"

Może się zdarzyć taka sytuacja że dwie tabele są w relacji przedstawionej poniżej:


Obie tabele posiadają ten sam klucz główny, a dodatkowo jedna posiada asocjację z drugą poprzez klucz obcy.

Aby uprościć dostęp do danych po stronie programu, możemy dodać je do EDM, a następnie:
  • usunąć asocjację 1...0.1
  • na tabeli bez klucza obcego dodać Inheritance i jako encję dziedziczącą dodać tą odpowiadającą drugiej tabeli
  • z encji dziedziczącej usunąć property odpowiadające kluczowi głównemu, a w szczegółach mapowania ustawić mapowanie tej kolumny (ParkId) na Id encji bazowej (LocationId)
  • zmienić nazwę Navigation Property reprezentującego klucz obcy
Uproszone encje wyglądają następująco:


Typy złożone

Jedna tabele reprezentuje kilka logicznych obiektów, np osobę i jej adres jednocześnie.

Po dodaniu do EDM tabeli, należy:
  • zaznaczyć properties z danej grupy i kliknąć opcję Refactor into New Complex Type dostępną pod prawym przyciskiem myszy
  • w widoku Model Browser możemy zmienić typ np. na Name oraz w Designerze możemy zmienić nazwę property
  • Complex property można także dodawać poprzez kliknięcie prawym przyciskiem myszy na encji i wybranie Add...ComplexProperty. 
  • W oknie szczegółów mapowania wszystkie properties powinny być mapowane na propercje typów złóżonych


niedziela, 7 października 2012

[HTML|JS|CSS] AngularJS: Filters

Filtry są bardzo użytecznym narzędziem pozwalającym na wyświetlanie danych w sposób sformatowany do naszych potrzeb. Angular JS udostępnia kilka przydatnych filtrów, z których możemy w każdej chwili skorzystać podając ich nazwę w expression, w konwencji  {{text | filtr}}. Poniżej przedstawiono najważniejsze filtry. Dla każdego property z poniższego kontrolera można zastosować filtry z widoku.

function filtersController($scope){
 $scope.text = "hello world";
 $scope.cost = 1000000000;
 $scope.population = 38276149;
 $scope.time = new Date();
 $scope.entity = {
  name : "Johnny",
  surname : "Depp",
  country : "USA"
 }
 $scope.numbers = [1,2,3,4,5,6,7,8,9];
 $scope.leet = "HeLLo WoRLd"; 
}


<!doctype html>
<html ng-app>
<head>
<meta charset="utf-8">
 <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.min.js"></script>
 <script src="scripts/myscript.js"></script>
</head>
<body ng-controller="filtersController"> 
 <div>{{text | uppercase}}</div>
 <div> {{cost | currency:"PLN" }} </div>
 <div> {{population | number}} </div>
 <div> {{time | date:'yyyy-MM-dd HH:mm:ss'}}</div>
 <div> {{entity | json}} </div>
 <div> {{numbers | limitTo:4}} </div>
 <div> {{numbers | limitTo:-4}} </div>
 <div> {{leet | lowercase}} </div>
</body>
</html>

Po uruchomieniu strony otrzymano poniższy widok:


Jeżeli dostarczone filtry nam nie wystarczają, możemy stworzyć własny filtr. Deklarujemy go w podobny sposób jak dyrektywę:

angular.module('filters', []).
 filter('oddUpper',function(){
 return function(text){
  var text = text.toUpperCase();
  var text2 = text.toLowerCase();
  var oddUpper = "";
  for (var i = 0, length = text.length; i < length; i+=1) {
   oddUpper += i % 2 == 0 ? text.charAt(i) : text2.charAt(i);
  };
  return oddUpper;
 }
});

Widok korzysta z filtrów użytkownika w identyczny sposób jak z normalnych filtrów.

<p>My custom filter</p>
<div>{{title | oddUpper}}</div>

wtorek, 25 września 2012

[HTML|JS|CSS] AngularJS: Templates

Poniewż aplikacje tworzone w AngularJS powinny wykorzystywać wzorzec MVC, należy wydzielić komponenty widoku, kontrolera oraz modelu danych. Za widok odpowiada szablon html, który zostaje przekompilowany do ostatecznej postaci za pomocą dyrektyw biblioteki. Kontrolerem będzie funkcja w pliku .js, natomiast model danych zostanie zaaplikowany do obiektu $scope.

Przykładowy widok poniżej prezentuje działanie dyrektyw ng-repeat, ng-controller oraz ng-click.

<body ng-controller="CountriesController"> 
<ul>
   <li ng-repeat="country in countries">
      <div>
 <span>
 Name : <strong>{{country.name}}</strong>
 </span>
 <span>
 Capital City : <strong>{{country.capital}}</strong>
 </span>
      </div>
   </li>
</ul>
<p> Total countries in Benelux : {{countries.length}} </p>
<button ng-click="removeLast()">Remove last item</button> 
</body>

Aby wykonać data-binding, należy podać nazwę kontrolera, za pomocą atrybutu ng-controller umieszczanego wewnątrz tagu body. AngularJS po napotkaniu takiej dyrektywy zacznie poszukiwanie funkcji o identycznej nazwie w plikach js. Kolejna instrukcja to ng-repeat, czyli odpowiednik pętli foreach z języka C#, Umożliwia ona binding do kolekcji obiektów i stworzenie szablonu dla pojedynczego obiektu. Za pomocą ng-click, możemy podać jaka akcja ma się wykonać po kliknięciu w element. W powyższym przykładzie będzie to funkcja removeLast zdefiniowana w kontrolerze.

//funkcja konstruktora dla country
var Country = function(name,capital)
{
 this.name = name;
 this.capital = capital;
}

function CountriesController($scope) {
 $scope.countries = [
  new Country("Netherlands","The Hague"),
  new Country("Belgium","Brussels"),
  new Country("Luxembourg","Luxembourg")
  ];
 $scope.removeLast = function(){
  var index = $scope.countries.length - 1;
  $scope.countries.splice(index, 1);
 }
} 

Funkcja kontrolera wywoływana z parametrem $scope, definiuje model danych oraz zachowania poprzez odpowiednie funkcje. Zmiany przeprowadzane na kolekcji (np. usunięcie elementu), spowodują natychmiastowe odświeżenie widoku.

Kolejną prostą do dodania funkcjonalnością jest przeszukiwanie pełno-tekstowe (Full Text Search), które nie korzysta w żaden sposób z kontrolera (!).
Aby móc przeszukiwać kolekcję, do której wpięliśmy data-binding, należy zmodyfikować ng-repeat o dyrektywę filtru.

<li ng-repeat="country in countries | filter:query">

Dodatkowo wystarczy dodać element input, do którego użytkownik będzie mógł wpisać swoje zapytanie.

<input type="text" ng-model="query">

W przypadku kolekcji obiektów przeszukane zostaną wszystkie properties i element zostanie zwrócony, jeżeli jakakolwiek zawiera podaną przez użytkownika frazę.

Aby zastosować binding w dwie strony, wystarczy dodać property query do elementu $scope w kontrolerze. Dzięki temu możemy na przykład wypisywać i resetować zapytanie.
<label>{{query}}</label>
<!-- ... -->
<button ng-click="resetQuery()">Reset query</button> 

function CountriesController($scope) {
 $scope.query = '';
 $scope.resetQuery = function(){
  $scope.query = '';
 }
} 

Binding automatycznie odświeży zarówno UI, jak i filtr wyszukiwania.

sobota, 25 sierpnia 2012

[WPF] ICollectionView

WPF oferuje mechanizm wspierający podstawowe standardy dotyczące wyświetlania kolekcji, takie jak filtrowanie, sortowanie czy grupowanie. Mechanizm jest następujący : w momencie, gdy następuje data binding do obiektu typu IEnumerable, tworzony jest pomiędzy źródłem a celem tworzony jest niejawnie widok. Jest nim obiekt implementujący interfejs ICollectionView. Wspiera on wspomniane wcześniej operacje.
  • Sortowanie
Tworzymy obiekt klasy SortDescription, gdzie jako parametry konstruktora podawane jest property, po którym ma się odbyć sortowanie, oraz kierunek (rosnąco lub malejąco).


var sort1 = new SortDescription("Surname", ListSortDirection.Descending);   
c1.Items.SortDescriptions.Add(sort1);

  • Grupowanie
Aby móc w pełni wykorzystać grupowanie, należy nieco rozszerzyć wygląd ListBoxa o to, jak prezentowany będzie nagłówek grupowania.

<ListBox.GroupStyle>
    <GroupStyle>
        <GroupStyle.HeaderTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal">
                    <Label Content="Age = " />
                    <Label Content="{Binding}"/>
                </StackPanel>                                
            </DataTemplate>
        </GroupStyle.HeaderTemplate>
    </GroupStyle>
</ListBox.GroupStyle>

Po dodaniu wyglądu nagłówka, można przejść do dodania mechanizmu grupującego. Najbardziej przydatne wydaje się być grupowanie po property.


var group1 = new PropertyGroupDescription("Age");
c2.Items.GroupDescriptions.Add(group1);

  • Filtrowaanie
 Aby móc filtrować kolekcję należy stworzyć delegata zwracającego wartość logiczną.


c3.Items.Filter = o =>
                        {
                            var p = o as Person;
                            if (p == null)
                                return false;
                            return p.Age > 27;
                        };