Beiträge von Torsten Lunze

13Feb

Im Mitteldeutschen Barcamp in Jena fand eine Session zum Thema SEO (Search Engine Optimization) statt. In dieser wurden Prinzipien diskutiert um ein gutes Ranking für eine Webseite insbesondere unter google zu erzielen. Ein paar Ideen möchte ich an dieser Stelle zusammenfassen.

1. Jede Seite, die durch eine Suche auffindbar sein soll, sollte nur einmal vorhanden sein. Wenn mehrere Einstieg URLs vorhanden sind, so sollten diese, am besten per Refer, auf die Hauptseite verweisen.

2. Jede Seite sollte unter einem permanenten Link erreichbar sein. Der Link sollte sich für die Webseite auch in Zukunft nicht mehr ändern sowie unabhängig seinvon irgendwelchen Sessionvariablen oder vorhergehenden Klickpfaden.

3. HTML Konformität ist für google nicht zwingend erforderlich. Jedoch hat die Beachtungein dieser den Vorteil, dass google die Seite vollständig parsen kann. Treten Fehler beim Parsen der Seite auf, so kann das dazu führen, dass nicht alle Links betrachtet werden, oder die Seite nicht in einem Suchergebnis erscheint, da der Content nicht analysiert wurden konnte.

4. Die Reihenfolge von Content innerhalb einer HTML Seite spielt eine untergeordnete Rolle. Scheitert das Parsen der HTML Seite in der Mitte oder Ende so können nur die Links und Texte, die bis dahin gescannt wurden, verarbeitet werden. Beinhaltet der Anfang eine für die Suche unwichtige Subnavigation, so ist dies eine unnötige Fehlerquelle. Es kann daher sinnvoller sein, den Content immer an den Anfang an einer Seite zu platzieren und stattdessen Navigation o. ä. ans Ende.

4. Das meta Tag description kann verwendet werden, um das Preview in dem Suchergebnis zu definieren. Beim nicht Verwenden des Tags versucht die Searchengine sich eine Description herauszusuchen. Die Vorhersagbarkeit des Ergebnisses stößt dabei gern an ihre Grenzen.

5. Für unwichtige Webseiten, die nicht in einem Suchergebnis erscheinen sollen, kann einem Link ein no-follow mitgegeben werden. Siehe dazu auch hier http://googleblog.blogspot.com/2005/01/preventing-comment-spam.html

6. Google abonniert RSS Feeds. D. h. erscheint eine neue Seite in einem RSS Feed, so scannt Google diese Seite nach neuen Suchbegriffen schneller als wenn es beim Scannen des Webs über diese neue Seite stolpern würde.

7. Während der Session tauchte die Frage auf, welche Bedeutung Headline Tags (h1, h2 usw.) haben, und welche Relevanz diese für die Suche haben. Die überraschende Antwort war, dass h2 oder h3 unter Umständen mehr Bedeutung haben als h1. Dies ist sicherlich abhängig von dem Aufbau der Gesamtseite. Der Hintergrund - sinnfrei wiedergeben - ist, dass h1 mitunter als Gesamtüberschrift angesehen wird, die für den konkreten Content der Seite nur eine untergeordnete Bedeutung hat.

8. Wo platziert man einen Link auf seine Seite, um in einem Suchranking von google weiter oben zu erscheinen? Die Antwort ist an sich ganz banal, man sucht sich die Seite(n) heraus, die für die Zielsuchbegriffe am höchsten gerankt sind, und platziert dort, irgendwie, einen Link. Das “irgendwie” ist dabei der Kreativität des Einzelnen überlassen.

Alle Angaben sind ohne Gewähr. :-)

Technorati Tags: , , , , ,

18Jan

Für gezielte Datenbankexporte oder -views möchte man oft eine Zeile für einen Eintrag in der SQL Abfrage erhalten. Beim Verknüpfen von mehreren Tabellen führt dies bei 1:n oder n:m Beziehungen zu dem Nachteil, dass für die Basistabelle mehrere Zeilen entstehen. Zur Verdeutlichung sei folgendes Beispiel (in PostgreSQL) gegeben:

create table account (
ID BIGINT not null,
EMAIL CHARACTER VARYING(1024) not null unique,
primary key (ID)
);


create table history (
ID BIGINT not null,
ACCOUNT_FK BIGINT not null,
ENTRY CHARACTER VARYING(1024) not null unique,
primary key (ID)
);

Ziel soll es sein, ein Abfrage zu erstellen, die alle Accounts und die dazu zugehörigen Historyeinträge zurückliefert. Allerdings soll pro Account nur eine Zeile verwendet werden.

Folgende Abfrage führt nicht zum Ziel:

select account.email, history.entry
from account, history
where account.id = history.account_fk;

Eine Möglichkeit ist es, die Einträge in der History zu verknüpfen, z. B. über ein concat Funktion. Wenn diese als Aggregatfunktion zur Verfügung steht, kann diese in einem group by verwendet werden.

In PostgreSQL ist dies wie folgt möglich:

CREATE AGGREGATE textcat_all(
basetype = text,
sfunc = textcat,
stype = text,
initcond = ''
);

Mit folgender Abfrage kommt man dann zu dem Ziel nur noch eine Zeile pro Account zu erhalten:


select account.email, textcat_all(history.entry || ',')
from account, history
where account.id = history.account_fk;
group by account.email

(Siehe dazu auch hier http://archives.postgresql.org/pgsql-novice/2003-09/msg00177.php)

In mysql ist dies noch einfacher, denn hier existiert bereits eine solche Aggregationsfunktion: http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html#function_group-concat


10Jan

Standardmäßig generiert die Hibernate Andromda Cartridge die Mappingdateien so, dass eine Sequenz für alle Tabellen genutzt wird. Eine Umstellung auf eine Sequenz für jede Tabelle ist dabei sehr einfach möglich. In der andromda.xml muss die Hibernate Cartridge einfach wie folgt angepaßt werden:

<namespace name="hibernate">
<properties>
...
<property name="defaultHibernateGeneratorClass">sequence</property>
<property name="sequenceIdentifierSuffix">_seq</property>
...
</properties>
</namespace>

Der finale Sequzenzname ergibt sich dabei aus dem Tabellennamen plus den Wert, der der Property sequenceIdentifierSuffix zugeordnet ist.


15Nov

Bei der Verwendung von Spring stellt sich im Buildmanagement die Frage wie mit Properties umgegangen wird. So hat jeder Test-, Live- und Entwicklungsserver seine eigene Datenbank oder andere unterschiedliche Einstellungen. Eine oft gewählte Variante ist die Ersetzung der Properties durch den Buildprozess. Dies hat allerdings den Nachteil, dass für eine Änderung der Properties der Buildprozess neu ausgeführt werden muss.

Mit Spring bietet sich dabei eine Alternative: Hier kann ein Propertyfile welches z. B. im Classpath liegt eingebunden werden:

<bean id="propertyPlaceholder" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:hibernate.properties" />
</bean>

Die Properties, die dann in dem File ‘hibernate.properties’ definiert sind, können innerhalb der Springkonfiguration genutzt werden:

...
<property name="hibernateProperties">
<props>
<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
...
</props>
</property>
...

Das Property File selbst ist dabei einfach wie erwartet:

hibernate.show_sql=true
hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
hibernate.hbm2ddl.auto=update


03Jul

Um in einer Webanwendung sicherzustellen, dass die Nutzer keinen Viren oder andere Schädlinge durch Uploads einschleusen, gibt es verschieden Möglichkeiten. Eine Möglichkeit ist es, in der Webanwendung selbst eine Virenüberprüfung anzustossen. Eine Alternative ist es, die Virenprüfung in einem vorgeschaltenem Apache vorzunehmen.

Diese Funktionalität kann mit dem Modul ModSecurity für den Apache erreicht werden. Dieses erkennt bei einem Request des Clients mitgesendete Dateien. Durch bestimmte Filterregeln kann ModSecurity so konfiguiert werden, dass ein Script auf dem Server aufgerufen wird:

SecRule FILES_TMPNAMES "@inspectFile /usr/local/share/clamav/modsecurity-clamav.pl" t:none,redirect:'%{REQUEST_FILENAME}?error=virus'

Diese Regel bewirkt, dass jede mitgesendete Datei auf der Festplatte zwischengespeichert wird, und das Script ausgeführt wird. Das Script selbst wiederrum ruft den Virenscanner auf, und parst das Ergebnis. Je nach dem ob ein Virus erkannt wird, wird ‘0′ oder ‘1′ zurückgegeben. Wenn kein Virus gefunden wurde, wird der Request wie gewohnt fortgesetzt. Im Fehlerfall wird eine definierte Aktion ausgeführt, wie z. B. die Weiterleitung zu einer Fehlerseite, die dem Nutzer eine Fehlermeldung ausgibt. In dem Beispiel wird ein Redirect mit der Request URL und einem Parameter error=virus zum Client gesendet.

Als Virenscanner empfiehlt sich der OpenSource Virenscanner ClamAV der für verschiedenste Systeme zur Verfügung steht. Das bereits oben beschrieben Script modsecurity-clamav.pl übernimmt den Aufruf und das Parsen des Scanneoutputs. Ein Beispiel Script findet sich hier.

Wenn noch keine Apache vorhanden ist, muss dieser als Reverse Proxy konfiguriert werden. Anfragen an den Apache müssen dabei an die eigentliche Webanwendung weitergeleitet werden. Zusätzlich muss ein URL Rewriting der Webanwendungsresponse stattfinden. Dies ist z. B. hier beschrieben.