4Developers 2012 – prezentacje i krótka relacja

W poprzednim wpisie zachęcałem Was do uczestnictwa w 4Developers 2012, minął miesiąc od imprezy, więc czas najwyższy na krótkie podsumowanie.

Ogólne wrażenia

Konferencja odbywała się w centrum konferencyjnym Concordia Design w centrum Poznania, dojazd dogodny autem, niedaleko dworzec PKP. Warunki konferencyjne bardzo dobre – akurat o to się nie obawiałem, gdyż organizatorzy 4Developers zdążyli nas przyzwyczaić do pewnego standardu.

Zero problemów z nagłośnieniem, terminowością rozpoczęcia i zakończenia prezentacji, szwedzki stół z napojami, słodyczami i owocami, drobne konkursy oraz stanowiska sponsorów IT z darmowymi energetykami oraz wifi… czyli wszystko co dobra konferencja branżowa powinna posiadać. Czytaj dalej 4Developers 2012 – prezentacje i krótka relacja

4Developers 2012

Już niedługo, 18 kwietnia startuje kolejna (czwarta) edycja konferencji 4Developers, która tym razem odbędzie się w Poznaniu.

4Developers to jednodniowa konferencja technologiczna kierowana głównie do co najmniej średnio zaawansowanych developerów aplikacji internetowych oraz project managerów.

Konferencja trwa około 7 godzin, w tym czasie prowadzone są jednoczesne wykłady w 4 tematykach, każde wystąpienie trwa około 45-60 min, nie musimy deklarować tematyki, na którą się wybieramy, w każdym momencie możemy zmienić salę. Czytaj dalej 4Developers 2012

Jak w Google Analytics zliczać kliknięcia ‚like it’?

Tworząc aplikacje czy konkursy na Facebooku z pewnością chcielibyśmy wiedzieć jak przekładają się one na przyrost fanów fanpage marki.

  1. W sekcji <head> podpinamy kod Google Analytics.
  2. Wstawiamy like button <fb:like></fb:like>
  3. Osadzamy asynchroniczny kod biblioteki Facebooka.
  4. Podpinamy funkcje pod zdarzenia facebooka ‚edge.create’ i ‚edge.remove’, w tych funkcjach wywołujemy handler Google Analytics z pożądanymi parametrami.

 

<head>
 <script type="text/javascript">
      var _gaq = _gaq || [];
      _gaq.push(['_setAccount', 'UA-XXXXX-XX']);
      _gaq.push(['_trackPageview']);
    
      (function() {
        var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
        ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
        var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
      })();  
 </script>
</head>

<body>
<div id="fb-root"></div>
<fb:like href="http://www.facebook.com/FANPAGE" send="false" show_faces="false" font=""></fb:like>

<script type="text/javascript">
    window.fbAsyncInit = function() {
        FB.init({appId: 'YOUR_APP_ID', status: true, cookie: true, xfbml: true});
        FB.Canvas.setAutoResize();
        FB.Event.subscribe('edge.create',
            function(response) {
                alert('like');
                _gaq.push(['_trackPageview', '/like_it']);
            }
        );
        FB.Event.subscribe('edge.remove',
            function(response) {
                alert('unlike');
                _gaq.push(['_trackPageview', '/unlike_it']); 
            }
        );
    };

    //Load the SDK asynchronously
    (function() {
        var e = document.createElement('script'); e.async = true;
            e.src = document.location.protocol +
              '//connect.facebook.net/pl_PL/all.js';
            document.getElementById('fb-root').appendChild(e);
    }());
</script>
</body>

Z powyższego kodu należy usunąć alerty, które są wstawione jedynie dla testu działania mechanizmu.

SEO Auto Linker

SEO Auto Linker to niesamowicie prosty w obsłudze, skuteczny w działaniu plugin do wordpress’a automatycznie linkujący zdefiniowane przez frazy do zewnętrznych i wewnętrznych stron.

Po instalacji definiujemy pary fraza – link, jeżeli fraza pojawi się w treści posta zostanie automatycznie podlinkowana. Dodatkowo można ograniczyć liczbę podlinkowanych fraz w poście oraz ograniczyć linkowanie do typu posta (post/strona). Dla jednego linku można podać wiele fraz oddzielając je przecinkiem.

Działa bez zarzutu, linkuje jedynie w szczegółach portu, nie ma problemu z polskimi znakami, linkuje frazy w tagach HTML typu [span], nie linkuje fraz w nagłówkach [hx]. Polecam.

Oprogramowanie do sklepu internetowego – które wybrać?

To że przyszłość zakupów jest w internecie, tego tłumaczyć nikomu raczej nie trzeba, co rusz powstają nowe sklepy internetowe… chodzą też słuchy, że w e-commerce podobno są pieniądze… skoro tak to i ja zapragnąłem spróbować.

Jakiś czas temu stanąłem zatem przed decyzją wyboru platformy e-commerce dla sklepu internetowego. Na rynku dostępnych jest wiele różnych platform lub produktów box’owych, niestety wcale nie znaczy, że jest z czego wybierać.

Poniżej garść informacji, kilka porad i linków dla osób planujących założenie własnego sklepu w necie. Mam nadzieję, że komuś się przyda. Czytaj dalej Oprogramowanie do sklepu internetowego – które wybrać?

Numerowanie listy zwracanych rekordów w MySQL

Przy różnorakich konkursach często zachodzi potrzeba wyciągnięcia pozycji konkretnego użytkownika (wpisu w bazie) na tle innych użytkowników (wpisów) w ograniczeniu czasowym.

Czyli np. mamy 10 tys wyników gry z danego dnia i chcemy poznać pozycje w rankingu, które zajmują użytkownicy o identyfikatorach 1, 2, 3.

Oczywistym rozwiązaniem jest zwykły selekt z ORDER BY po polu z punktami i następnie iterowanie w php, jednak gdy liczba danych będzie większa taki SELECT nie wykona się lub będzie bardzo niewydajny.

Rozwiązaniem dużo lepszym jest użycie podzapytania i wykonania obliczeń numerowania na samej bazie a następnie zwrócenie do php jedynie rekordów pasujących do naszych użytkowników. Czytaj dalej Numerowanie listy zwracanych rekordów w MySQL

Jak promować serwis na Facebook’u?

Tego, że w dzisiejszych czasach obecność Twojego serwisu lub marki na Facebook’u jest niezbędna, tłumaczyć chyba nie trzeba. Jeżeli Cię tam nie ma – tracisz, gdyż Twoja konkurencja już tam jest i czerpie z tego profity.

Okej, zatem mając serwis internetowy (aka marka/firma) i konto na fejsie, jak mogę czerpać tego korzyści? Czytaj dalej Jak promować serwis na Facebook’u?

Sphinx, czyli szybkie i skuteczne wyszukiwanie

Sphinx to open source’owy silnik wyszukiwarki SQL. Sphinx jest dostępny na licencji GPL 2 i jest całkowicie darmowy do zastosowań webowych.

Sphinx został stworzony przez rosyjskich programistów, aplikacja jest cały czas rozbudowywana i rozwijana, posiada płatny support.

Zasada działania Sphinxa

Sphinx jest aplikacją stand-alone, musi zostać skompilowany i zainstalowany na serwerze. Działa na zadadzie deamon-client.

Sphinx składa się z kilku narzędzi:

  • searchd – deamon wyszukiwarki. Musi być cały czas uruchomiony, na podstawie parametrów wywołania oraz konfiguracji przeszukuje indeksy i zwraca rezultaty.
  • indexer – generator indeksu. Służy do budowania indeksu, po którym następuje wyszukiwanie.
  • search – narzędzie pozwalają testować wyszukiwarkę z poziomu linii komend.
  • spelldump – narzędzie pomocne przy budowania słownika
  • indextool – narzędzie wprowadzone dopiero w wersji 0.9.9-rc2. Służy do debugowania i wyświetlania informacji o zbudowanym indeksie.

Cechy Sphinxa

  • Ekstremalnie szybkie indeksowanie (aż do 10MB/sec).
  • Ekstremalnie szybkie wyszukiwanie (około 0.1 sekundy dla 2-4 GB zbiorów danych).
  • Bardzo duża skalowalność (aż do 100 GB zbiorów danych na pojedyńczym CPU).
  • Współpracuje z wieloma typami baz danych: PostgreSQL, MySQL i MSSQL.
  • Posiada API dla języków takich jak PHP, Python, Java, Perl, Ruby i C/C++.
  • Obsługuję morfologię (przeszukiwanie pełnotekstowe (brak języka polskiego)).
  • Umożliwia sortowanie/filtrowanie wyników po zadanych atrybutach.
  • Umożliwia grupowanie rezultatów po zadanym atrybucie.
  • Wspiera stronnicowanie rezultatów.
  • Prosty, a zarazem dostarczający wiele możliwści plik konfiguracyjny.

Sphinx nie operuje bezpośrednio na bazie danych a jedynie na wcześniej przygotowanych indeksach. Dzięki temu zdejmujemy obciążenie z „wąskiego gardła”, którym w praktycznie każdej aplikacji jest baza danych i przeżucamy na serwer.

Wady Sphinxa

Jedyną znaną mi wadą Sphinxa jest to, że wcześniej stworzony indeks (po którym odbywa się wyszukiwanie) nie jest automatycznie aktualizowany o zmiany wykonane na bazie danych. Dotyczy to zarówno nowych wierszy w zaindeksowanej tabeli jak i modyfikacji zaindeksowanych już danych.

Nowe/zmienione dane pojawią się w indeksie dopiero po jego zaktualizowaniu poprzez polecenie:

indexer nazwa_indeksu --rotate

Pomimo dużej szybkości indexera, operację tą nie nie można wykonywać po każdej zmianie w bazie danych a jednie cyklicznie co kilka godzin/dni (w zależności od wielkości indeksu).

O ile brak aktualizacji indeksu o zmodyfikowane rekordy nie stanowi większego problemu, to brak nowych rekordów może stanowić dla wielu serwisów poważny problem. Często bywa tak, że użytkownicy wprowadzają jakieś dane do bazy a następnie chcą ich użyć wyszukując je. Rozwiązaniem jest mechanizm Live index updates.

W skrócie działa to następująco:

  • Na indeksowanej tabeli tworzymy 2 indexy: główny (main) i dodatkowy (delta).
  • Tworzymy tabelę sph_counter, w której będziemy zapisywać max(id) z indeksu głównego.
  • Indeks główny będzie wyszukiwał w 99,9% danych indeksowanej tabeli, indeks dodatkowy będzie wyszukiwał w 0.1% danych tabeli.
  • Dzięki rozbiciu indeksowania na 2 indeksy (duży główny, mały dodatkowy) możliwe jest dużo częstsze indeksowanie indeksu dodatkowego gdyż będzie zawierał on niewiele danych, zatem reindeksacja nie będzie obciążać serwera i bazy danych.
  • Modyfikujemy sphinx.conf wg. wytycznych z linka.
  • Ustawiamy w cronie reindeksowanie głównego indeksu „raz na dzień” oraz dodatkowego indeksu np. „co 3 minuty”.
  • Zatem, co 3 minuty będziemy uzupełniać indeks dodatkowy, natomiast raz dziennie będzie uzupełniany indeks główny.
  • Każdorazowo przy aktualizacji indeksu głównego do tabeli sph_counter zostanie zapisany wartość klucza głównego (sql_query_pre), przez co indeks pomocniczy będzie startował od zera.

Zasada działania aplikacji opartej na Sphinx’ie

  • Skonfigurowanie konfiga „sphinx.conf” i zbudowanie indeksów.
  • W aplikacji, poprzez api następuje wywołanie Sphinxa z parametrami (szukana fraza, limit, offset, order etc…).
  • Sphinx zwraca wyszukane rekordy w postaci tablicy kluczy głównych (+ zdefiniowane atrybuty).
  • Na podstawie ID’ków kluczy głównych wyciągamy dane z bazy (lub z cache’a) i prezentujemy w serwisie.

Szybki tutorial

  • Tworzymy i konfigurujemy plik konfiguracyjny sphinx.conf.
  • Bujemy indeks(y)
    indexer --all
    
  • Odpalamy demona:
    /usr/local/bin/searchd -c /usr/local/etc/sphinx.conf
    
  • Testujemy wyszukiwanie po indeksie:
    search [INDEX_NAME] SZUKANA_FRAZA
    
  • Podpinamy API
  • Testujemy wyszukiwanie poprzez API

Błąd „connection to 127.0.0.1:9312 failed (errno=111 ….”

W przypadku gdy najpierw został uruchomiony deamon Sphinxa a dopiero później stworzony indeks to aby API działało poprawnie należy zrestartować demona. To samo tyczy się przypadku gdy w indeksie sphinx.conf zostały zmienione zwracane atrybuty.

/usr/local/bin/searchd --stop
/usr/local/bin/searchd -c /usr/local/etc/sphinx.conf

Błąd „błąd „searchd error: offset out of bounds””

W przypadku gdy chcemy zwiększyć domyślną liczbę zwracanych (przetwarzanych) rekordów z 1000 na więcej należy:

  • zmienić max_matches w sekcji searchd w konfigu sphinx.conf,
  • zrestartować demona searchd,
  • ustawić 3 parametr w metodzie SetLimits().

Z Sphinx’a korzystają między innym: craigslist.org, filestube.com, netlog.com, thepiratebay.org, wikimapia.org i wiele więcej.

Niedługo, jak czas pozwoli, postaram się jeszcze napisać coś o samym pliku konfiguracyjnym Sphinxa „sphinx.conf”.

Zaosoby Amazon S3 we własnej domenie

O samym Amazon S3 nie będę się rozwodził gdyż już nie raz pisałem o tej usłudze. Generalnie jest to bardzo wydajna usługa hostingu plików w chmurze amazona.

Problem

Standardowe URLe prowadzące do zasobów zgromadzonych na serwerach europejskich wyglądają następująco:

http://UNIQUE-BUCKET-NAME.s3-external-3.amazonaws.com/katalog/plik.jpg

Gdzie UNIQUE-BUCKET-NAME to unikalna w skali globalnej nazwa nadawana przez dewelopera.

Powyższy adres jest bardzo długi i nie wygląda zbyt przyjaźnie. Jeżeli komuś zależy na pewnych walorach estetycznych to pewnie wolałby zmienić ten adres na:

http://subdomena.domena.pl/katalog/plik.jpg

Rozwiązanie

Posłużę się przykładem, gdyż taki przekaz będzie najprostszy i najlepiej rozumiany. Załóżmy, że posiadamy domenę lorem.pl, wszystkie statyczne pliki hostujemy na Amazon i chcemy je widzieć pod subdomeną static.lorem.pl.

W Amazon S3 tworzymy bucket (jeżeli już mamy utworzony to zmieniamy nazwę) o nazwie identycznej do subdomeny czyli static.lorem.pl.

Następnie tworzymy rekord CNAME dla subdomeny static.lorem.pl i ustawiamy go na „s3-external-3.amazonaws.com.” (tak, musi być kropka na końcu). Rekord CNAME można ustawić w panelu administracyjnym dostarczonym przez firmę hostingową, jeżeli nie posiadacie takiej opcji to musicie przesłać dyspozycję na support.

Koniec, zmiany nie będą widoczne od razu, należy poczekać godzinę, maksymalnie kilka godzin, pliki wgrane na Amazon powinny być widoczne z poziomu subdomeny. Należy też pamiętać, aby po wgraniu plików na Amazon ustawić plikom odpowiednie ACL (prawa dostępu). W CloudBerry: PPM na bucket->ACL->ACL Settings->Public->OK

UPDATE z ORDER BY w jednym zapytaniu na przykładzie orderingu danych

Wstęp

Mamy przykładową tabelę z userami:

DROP TABLE users;
CREATE TABLE users(
	id SERIAL PRIMARY KEY,
	name VARCHAR(50),
	created TIMESTAMP DEFAULT NOW()
);

INSERT INTO users(name) VALUES('Marcin');
INSERT INTO users(name) VALUES('Bartek');
INSERT INTO users(name) VALUES('Agnieszka');
INSERT INTO users(name) VALUES('Dominika');

Na tabeli wykonywane były wielokrotnie operacje INSERT/UPDATE/DELETE przykładowo:

UPDATE users SET name = 'Ania' WHERE name = 'Bartek';
UPDATE users SET name = 'Dalia' WHERE name = 'Marcin';
DELETE FROM users WHERE name = 'Ania';

Zatem SELECT bez orderu wyświetli nam dane posortowane względem kolejności modyfikacji:

SELECT * FROM users;

psql=> select * from users;
 id |   name    |          created
----+-----------+----------------------------
  3 | Agnieszka | 2009-12-19 10:41:49.324851
  4 | Dominika  | 2009-12-19 10:41:49.958792
  1 | Dalia     | 2009-12-19 10:41:49.297223
(3 rows)

W interface aplikacji chcemy oprogramować własną obsługę kolejności rekordów. Dodajemy zatem kolumnę, która będzie obsługiwać tą funkcjonalność.

ALTER TABLE users ADD COLUMN order_id INTEGER;

Problem

Musimy teraz uzupełnić kolumne order_id o poprawne wartości. Kolejność orderu danych, które znajdują się w bazie powinna być kolejności wprowadzania tych danych do tabeli (czyli zgodna z kolejnością pola id).

Aby zrealizować poprawne uzupełnienie orderu tworzymy pomocniczą sekwencję, która wykorzystamy do wygenerowania kolejnych liczb do orderu:

DROP SEQUENCE order_id_seq;
CREATE SEQUENCE order_id_seq;

Wykonujemy oczywiste zapytanie przypisujące rekordom z tabeli users kolejne wartości sekwencji:

UPDATE users SET order_id = NEXTVAL('order_id_seq');  

Niestety przez to, że dane nie były ustawione w dobrej kolejności ORDER ustawiony jest niepoprawnie.

psql=> SELECT * FROM users;
 id |   name    |          created           | order_id
----+-----------+----------------------------+----------
  3 | Agnieszka | 2009-12-19 10:41:49.324851 |        1
  4 | Dominika  | 2009-12-19 10:41:49.958792 |        2
  1 | Dalia     | 2009-12-19 10:41:49.297223 |        4
(3 rows)

Rozwiązanie

Aby UPDATE poprawnie ustawił kolejność orderu należy nieco zmodyfikować zapytanie i nadać danym z tabeli users odpowiednią kolejność.

W tym celu tworzymy podzapytanie sorted_ids, w którym poprzez ORDER BY ustalamy kolejność rekordów, które następnie w zapytaniu nadrzędnym zostaną zmodyfikowane właśnie w tej kolejności.

UPDATE users
	SET order_id = correct_order_id
FROM
	(
	SELECT 
		id, nextval('order_id_seq') as correct_order_id
	FROM
		users
  	ORDER BY 
		id ASC
	) as sorted_ids
WHERE
	users.id = sorted_ids.id; 

select * from users order by order_id;

Jak widać dane zostały pięknie zorderowane:

psql=> select * from users order by order_id;
 id |   name    |          created           | order_id
----+-----------+----------------------------+----------
  1 | Dalia     | 2009-12-19 10:41:49.297223 |        1
  3 | Agnieszka | 2009-12-19 10:41:49.324851 |        2
  4 | Dominika  | 2009-12-19 10:41:49.958792 |        3
(3 rows)

Przy okazji wspomnę, że w aplikacji łatwiej obsłużyć ordering odwrotny, tzn. wiersze, które posiadają największą wartość order_id są na czele a nie na końcu listy. Przy takim podejściu przy wstawianiu nowego rekordu pole order_id powinno mieć wartość MAX(order_id) + 1. Dla orderingu odwrotnego należy zmienić w podzapytaniu kolejność sortowania z id ASC na id DESC.

Należy także dbać o to, aby przy usunięciu rekordu prenumerować order_id wszystkich rekordów, które znajdują się ‚nad nim’ o -1.