Jak w WordPress usunąć rel=”next” i rel=”prev”?

Według wytycznych Google atrybuty znacznika LINK rel=”next” i rel=”prev” znajdujące się w sekcji HEAD służą do wskazywania zależności pomiędzy stronami na liście stron… czyli opisują pozycje w pagerze. Jest to istotna podpowiedź na temat duplikacji oraz zależności treści w serwisie i wpływa pozytywnie na SEO.

Duże było moje zdziwienie gdy niedawno odkryłem, że WordPress standardowo wstawia te atrybuty na stronach ze szczegółami wpisu wstawiając w nich linki do następnego i poprzedniego (w kontekście daty publikacji) wpisu. Każdy post jest unikalny, posiada unikalny adres url i zazwyczaj nie jest semantycznie powiązany z następnym/poprzednim.

Wpisy z rel=”next” i rel=”prev” powinny być na stronie z listą wpisów w kategorii gdzie mamy stronicowanie, natomiast na stronach ze szczegółami wpisu znaczniki te są kompletnie niepotrzebne i mogą powodować jedynie zamieszanie.. Czytaj dalej „Jak w WordPress usunąć rel=”next” i rel=”prev”?”

Instalacja Apache 2.4 + PHP 5.4 pod Windows 7

Z pewnością w necie znajduje się już wiele poradników w temacie instalacji i konfiguracji PHP + Apache, jednak nie zawsze zawierają wszelkie niezbędne informacje. Wczoraj sam przebrnąłem przez proces aktualizacji z Apache 2.2 i PHP 5.2 do Apache 2.4 + PHP 5.4 co defakto oznaczało ponowną instalacją i konfiguracją tych dwóch softów. Czytaj dalej „Instalacja Apache 2.4 + PHP 5.4 pod Windows 7”

Sprawdzanie permutacji ciągu znaków w PostgreSQL

Permutacja w języku matematycznym to „wzajemnie jednoznaczne przekształcenie pewnego zbioru na siebie”. Używając języka mniej technicznego permutacje, na przykładzie liter w słowie, to wszystkie możliwe ustawienia literek w słowie, zatem dla słowa „marcin” permutacjami będą: „amrcin”, „mracin”, „mrcain” etc… w słowie 6 znakowym będzie ich łącznie 6!.

Problem

W bazie danych mamy pole tekstowe z zapisanymi pewnymi ciągami znaków, chcemy sprawdzić te dane pod kątem występowania w nich permutacji pewnego stringu. Czyli mając w danych wejściowych np. „mrcain” chcemy sprawdzić czy w bazie nie ma „marcin” lub innej permutacji. Czytaj dalej „Sprawdzanie permutacji ciągu znaków w PostgreSQL”

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.

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.

Relecyjna baza ‘ala MySQL’ w chmurze Amazon AWS

Amazon w ramach AWS (Amazon Web Services) uruchomił publiczną wersję beta usługi relacyjnaj bazy danych 'ala MySQL’ (Amazon RDS) w swojej chmurze.

Amazon RDS ma cechować się:

  • prostotą konfiguracji i wdrożenia do aplikacji,
  • pełną kompatybilnością z bazą danych MySQL,
  • prostotą w zarządzaniu bazami, backupami i dostępnymi narzędziami,
  • pełną automatyką w backup’owaniu i czynnościach serwisowych,
  • skalowalnością w zależności od potrzeb uzytkownika
  • niezawodnością
  • bardzo dostępną ceną, tak jak w przypadku innych usług developerzy kasowani będą jedynie za zaosby, które rzeczywiście zużywają ich aplikacje.

Czy rzeczywiście tak będzie? Mam nadzieje. Przeczytaj więcej o Amazon RDS.