<?xml version="1.0" encoding="UTF-8"?> <rss
version="2.0"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:wfw="http://wellformedweb.org/CommentAPI/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
> <channel><title>empty pages &#187; java</title> <atom:link href="http://look-in.net/category/java/feed/" rel="self" type="application/rss+xml" /><link>http://look-in.net</link> <description></description> <lastBuildDate>Mon, 05 Dec 2011 06:07:14 +0000</lastBuildDate> <generator>http://wordpress.org/?v=2.9.2</generator> <language>en</language> <sy:updatePeriod>hourly</sy:updatePeriod> <sy:updateFrequency>1</sy:updateFrequency> <item><title>&#171;Flexible&#187; protocol buffer implementation.</title><link>http://look-in.net/2011/06/25/flexible-protocol-buffer/</link> <comments>http://look-in.net/2011/06/25/flexible-protocol-buffer/#comments</comments> <pubDate>Sat, 25 Jun 2011 10:08:45 +0000</pubDate> <dc:creator>slookin</dc:creator> <category><![CDATA[java]]></category> <category><![CDATA[projects]]></category> <category><![CDATA[google]]></category> <category><![CDATA[protocolbuf]]></category> <guid
isPermaLink="false">http://look-in.net/?p=463</guid> <description><![CDATA[Every body knows about protobuf from Google team. It is very useful and effective protocol for data exchanges.
&#171;Protocol Buffers are a way of encoding structured data in an efficient yet extensible format. Google uses Protocol Buffers for almost all of  its internal RPC protocols and file formats.&#187;
For my project  I need to implement specific [...]]]></description> <content:encoded><![CDATA[<p>Every body knows about <a
href="http://code.google.com/p/protobuf/" target="_blank">protobuf</a> from Google team. It is very useful and effective protocol for data exchanges.</p><p
style="text-align: left;"><em>&laquo;Protocol Buffers are a way of encoding structured data in an efficient yet extensible format. Google uses Protocol Buffers for almost all of  its internal RPC protocols and file formats.&raquo;</em></p><p>For my project<em> </em> I need to implement specific router for messages from different clients. So my router (receiver) should be able to read (parse) body of message in order to get information about for routing. But I couldn’t predict all protobuf message format on design phase, as result I should analyze message format on runtime.</p><p><span
id="more-463"></span></p><p>Google team suggest to use special technique &laquo;<a
href="http://code.google.com/apis/protocolbuffers/docs/techniques.html#self-description" target="_blank">Self-describing Messages</a>&raquo; : insert additional field to message with full protocol description. As result all messages will have information about their structue. It is good trick, but the message size will extremely big. (main advantage of protobuf in my project is small message size). So I decide to have full message format catalogue on my router and update this catalogue in runtime.</p><p>The plain-text .proto file should be prepared as message using descriptor.proto and protoc utility.</p><p>The code below shows how router will read protocol format from file system and parse incoming message.</p><p>We will use <strong>addressbook.proto</strong> from examples:</p><pre>package tutorial;
option java_package = "com.example.tutorial";
option
java_outer_classname = "AddressBookProtos";
message Person {
  required string name = 1;
  required int32 id = 2;        // Unique ID number for this person.
  optional string email = 3;
  enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
}
  message PhoneNumber {
    required string number = 1;
    optional PhoneType type = 2 [default = HOME];
  }
  repeated PhoneNumber phone = 4;
}
// Our address book file is just one of these.
message AddressBook {
  repeated Person person = 1;
}</pre><p>Clients (sender) can implement classical way for producing message (generate java classes via protoc):</p><p><strong>protoc &#8211;java_out=. addressbook.proto </strong></p><p>And following example of message generate:</p><div><p><span
style="font-family: Courier New; font-size: 10pt;"> </span></p><pre>
<div><span style="font-family: Courier New; font-size: 10pt;">   Person.Builder person = Person.<em>newBuilder</em>();</span></div>
<div><span style="font-family: Courier New; font-size: 10pt;">   person.setId(Integer.<em>valueOf</em>(42));</span></div>
<div><span style="font-family: Courier New; font-size: 10pt;">   person.setEmail(</span><span style="font-family: Courier New; color: #2a00ff; font-size: 10pt;">"test_email@gmail.com");</span></div>
<div><span style="font-family: Courier New; font-size: 10pt;">   person.setName(</span><span style="font-family: Courier New; color: #2a00ff; font-size: 10pt;">"Viktor Villari");</span></div>
<div><span style="font-family: Courier New; font-size: 10pt;">   Person p = person.build();</span></div>
<div><span style="font-family: Courier New; font-size: 10pt;">   FileOutputStream fstream = </span><span style="font-family: Courier New; color: #7f0055; font-size: 10pt;"><strong>new</strong></span><span style="font-family: Courier New; font-size: 10pt;"> FileOutputStream(</span><span style="font-family: Courier New; color: #0000c0; font-size: 10pt;"><em>messagePath);</em></span></div>
<div><span style="font-family: Courier New; font-size: 10pt;">   CodedOutputStream outSream = CodedOutputStream.<em>newInstance</em>(fstream);</span></div>
<div><span style="font-family: Courier New; font-size: 10pt;">   p.writeTo(outSream);</span></div>
<div><span style="font-family: Courier New; font-size: 10pt;">   outSream.flush();</span></div>
</pre><pre>as result we will have file with message (<span style="font-family: Courier New; color: #0000c0; font-size: 10pt;"><em>messagePath</em></span>)</pre><pre>After it we should generate descriptor for our addressbook.proto:</pre><pre><strong>protoc --descriptor_set_out=address.proto.descriptor  addressbook.proto </strong></pre><pre>I transfer address.proto.descriptor to receiver format catalogue and following code allow me to parse message:</pre><pre>
<div><span style="font-family: Courier New; color: #3f7f5f; font-size: 10pt;">   // read protocol Descriptor</span></div>
<div><span style="font-family: Courier New; font-size: 10pt;">   FileInputStream input = </span><span style="font-family: Courier New; color: #7f0055; font-size: 10pt;"><strong>new</strong></span><span style="font-family: Courier New; font-size: 10pt;"> FileInputStream(</span><span style="font-family: Courier New; color: #0000c0; font-size: 10pt;"><em>protoDescripter);</em></span></div>
<div><span style="font-family: Courier New; font-size: 10pt;">   DescriptorProtos.FileDescriptorSet fdsProto = DescriptorProtos.FileDescriptorSet.<em>parseFrom</em>(input);</span></div>
<div><span style="font-family: Courier New; color: #3f7f5f; font-size: 10pt;">// point to specific file in FileDescriptorSet</span></div>
<div><span style="font-family: Courier New; font-size: 10pt;">   System.</span><span style="font-family: Courier New; color: #0000c0; font-size: 10pt;"><em>out</em></span><span style="font-family: Courier New; font-size: 10pt;">.println(</span><span style="font-family: Courier New; color: #2a00ff; font-size: 10pt;">"File name = "</span><span style="font-family: Courier New; font-size: 10pt;"> + fdsProto.getFile(0).getName());</span></div>
<div><span style="font-family: Courier New; font-size: 10pt;">   FileDescriptor fileDescr = FileDescriptor.<em>buildFrom</em>(fdsProto.getFile(0), </span><span style="font-family: Courier New; color: #7f0055; font-size: 10pt;"><strong>new</strong></span><span style="font-family: Courier New; font-size: 10pt;"> FileDescriptor[0]);</span></div>
<div><span style="font-family: Courier New; color: #3f7f5f; font-size: 10pt;">// point to specific message type in FileDescriptor</span></div>
<div><span style="font-family: Courier New; font-size: 10pt;">   System.</span><span style="font-family: Courier New; color: #0000c0; font-size: 10pt;"><em>out</em></span><span style="font-family: Courier New; font-size: 10pt;">.println(</span><span style="font-family: Courier New; color: #2a00ff; font-size: 10pt;">"Message type = "</span><span style="font-family: Courier New; font-size: 10pt;"> + fileDescr.getMessageTypes().get(0).getName());</span></div>
<div><span style="font-family: Courier New; font-size: 10pt;">   Descriptor messageType=fileDescr.getMessageTypes().get(0);</span></div>
<div><span style="font-family: Courier New; color: #3f7f5f; font-size: 10pt;">// read and parse incomming message</span></div>
<div><span style="font-family: Courier New; font-size: 10pt;">   input = </span><span style="font-family: Courier New; color: #7f0055; font-size: 10pt;"><strong>new</strong></span><span style="font-family: Courier New; font-size: 10pt;"> FileInputStream(</span><span style="font-family: Courier New; color: #0000c0; font-size: 10pt;"><em>messagePath);</em></span></div>
<div><span style="font-family: Courier New; font-size: 10pt;">   DynamicMessage dm = DynamicMessage.<em>parseFrom</em>(messageType, input);</span></div>
<div><span style="font-family: Courier New; color: #3f7f5f; font-size: 10pt;">// output fields from message</span></div>
<div><span style="font-family: Courier New; font-size: 10pt;">   Iterator&lt;FieldDescriptor&gt; i = messageType.getFields().iterator();</span></div>
<div><span style="font-family: Courier New; color: #7f0055; font-size: 10pt;"><strong>while</strong></span><span style="font-family: Courier New; font-size: 10pt;"> (i.hasNext()) {</span></div>
<div><span style="font-family: Courier New; font-size: 10pt;">    FieldDescriptor field = i.next();</span></div>
<div><span style="font-family: Courier New; font-size: 10pt;">    System.</span><span style="font-family: Courier New; color: #0000c0; font-size: 10pt;"><em>out</em></span><span style="font-family: Courier New; font-size: 10pt;">.println(messageType.getName() + </span><span style="font-family: Courier New; color: #2a00ff; font-size: 10pt;">"."</span><span style="font-family: Courier New; font-size: 10pt;"> + field.getName() + </span><span style="font-family: Courier New; color: #2a00ff; font-size: 10pt;">" value="</span></div>
<div><span style="font-family: Courier New; font-size: 10pt;">      + dm.getField(field) + </span><span style="font-family: Courier New; color: #2a00ff; font-size: 10pt;">" type="</span><span style="font-family: Courier New; font-size: 10pt;"> + field.getJavaType());</span></div>
<div><span style="font-family: Courier New; font-size: 10pt;">   }</span></div>
<div><span style="font-family: Courier New; font-size: 10pt;">So, output of code:</span></div>
File name = addressbook.proto
Message type = Person
Person.name value=Viktor Villari type=STRING
Person.id value=42 type=INT
Person.email value=test_email@gmail.com type=STRING
Person.phone value= type=MESSAGE
<span style="font-family: Courier New; font-size: 10pt;"></span></pre><pre><span style="font-family: Courier New; font-size: 10pt;"><strong>Resume</strong>: using this technique you able to build dynamic message parsing without increase size of each message.</span></pre></div> ]]></content:encoded> <wfw:commentRss>http://look-in.net/2011/06/25/flexible-protocol-buffer/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>Statistics from RequisitePro via RPX</title><link>http://look-in.net/2010/11/08/statistics-from-requisitepro-via-rpx/</link> <comments>http://look-in.net/2010/11/08/statistics-from-requisitepro-via-rpx/#comments</comments> <pubDate>Sun, 07 Nov 2010 21:34:15 +0000</pubDate> <dc:creator>slookin</dc:creator> <category><![CDATA[java]]></category> <category><![CDATA[projects]]></category> <category><![CDATA[requistepro]]></category> <category><![CDATA[rpx]]></category> <guid
isPermaLink="false">http://look-in.net/?p=382</guid> <description><![CDATA[
I&#8217;m spent this long weekend for study RXP protocol which IBM recommend to communicate with IBM Rational RequisitePro.
As I know java, I decide to write small utility on Java RPX. I was my mistake, because IBM Java RPX is worst API which I sow in last years. Methods like OpenProject(arg0, arg1, arg2, arg3, arg4, [...]]]></description> <content:encoded><![CDATA[<p
style="text-align: center;"><a
href="http://look-in.net/wp-content/uploads/2010/11/screen.png" class="thickbox no_icon" rel="gallery-382" title="screen"><img
class="size-full wp-image-383 aligncenter" title="screen" src="http://look-in.net/wp-content/uploads/2010/11/screen.png" alt="" width="404" height="247" /></a></p><p>I&#8217;m spent this long weekend for study RXP protocol which IBM recommend to communicate with<a
href="http://www-01.ibm.com/software/awdtools/reqpro/" target="_blank"> IBM Rational RequisitePro</a>. <span
id="more-382"></span></p><p>As I know java, I decide to write small utility on Java <a
href="http://www.ibm.com/developerworks/rational/library/445.html#N10045" target="_blank">RPX</a>. I was my mistake, because IBM Java RPX is worst API which I sow in last years. Methods like OpenProject(arg0, arg1, arg2, arg3, arg4, arg5) make me crazy.</p><p>As result I use VBA RPX API as reference and Java RPX API just as implementation.</p><p>In any case, result of my homework &#8211; I&#8217;m able to build statistics based on RequisirePro repository about how often I change requirements in project. It helps me to analyse time when my requirements set is stable and ready for implementation. Also this simple chart show me which external actions affect requirements (architecture overview stage, review requirements by development team or test team).</p><p>In my next step I going to implement RSS stream for all requirements changes in project scope, it should help my team follow in any changes in product in frame iterative process.</p> ]]></content:encoded> <wfw:commentRss>http://look-in.net/2010/11/08/statistics-from-requisitepro-via-rpx/feed/</wfw:commentRss> <slash:comments>4</slash:comments> </item> <item><title>Перевод статьи &#171;Введение в Lucene&#187;</title><link>http://look-in.net/2005/02/16/lucene-introduction-article/</link> <comments>http://look-in.net/2005/02/16/lucene-introduction-article/#comments</comments> <pubDate>Wed, 16 Feb 2005 14:00:49 +0000</pubDate> <dc:creator>slookin</dc:creator> <category><![CDATA[java]]></category> <category><![CDATA[translation]]></category> <category><![CDATA[java translation lucene searchengine]]></category> <guid
isPermaLink="false">http://localhost/wordpress/index.php/2007/11/27/lucene/</guid> <description><![CDATA[ Перевел статью по индексированию и поиску с использование Lucene API, работающие примеры на Java.
автор Erik Hatcher (01/27/2005) источник
перевод Сергей Лукин
По мере усложнения восприятия мира люди изобретали различные виды классификации, каталоголизации по различным иерархическим схемам (как то по видам и родам животных, по направлениям музыки в каталоге дисков). Развитие интернета и электронных хранилищ позволили сделать [...]]]></description> <content:encoded><![CDATA[<p><a
href="http://look-in.net/wp-content/uploads/2007/11/lucene.gif" title="Lucene logo" class="thickbox no_icon" rel="gallery-33"><img
src="http://look-in.net/wp-content/uploads/2007/11/lucene.gif" alt="Lucene logo" align="left" hspace="20" /></a><small> </small>Перевел статью по индексированию и поиску с использование Lucene API, работающие примеры на Java.</p><p><small>автор <a
href="http://today.java.net/pub/au/15" target="_blank">Erik Hatcher</a> (01/27/2005) <a
href="http://today.java.net/pub/a/today/2003/07/30/LuceneIntro.html" target="_blank">источник</a><br
/> перевод <a
href="http://look-in.net/" target="_blank">Сергей Лукин</a><br
/> </small><span
id="more-33"></span></p><p>По мере усложнения восприятия мира люди изобретали различные виды классификации, каталоголизации по различным иерархическим схемам (как то по видам и родам животных, по направлениям музыки в каталоге дисков). Развитие интернета и электронных хранилищ позволили сделать огромный шаг в построение сложных каталогов для быстрого поиска информации. Как пример, Yahoo содержит огромный классификатор сайтов интернета. Но поиск по каталогу долог и требует от человека хорошего знания предметной области, поэтому требовался &laquo;свободный&raquo; поиск, сквозной поиск по всему каталогу, с произвольно составленным запросом. Лучший пример этого демонстрирует Google.</p><p>Если же от вашего приложения пользователи требуют такой  функциональности, то Lucene это лучший способ сделать это быстро, просто и эффективно.</p><p>Lucene это высокопроизводительный, масштабируемый, поисковый движок. В нем одновременно реализованы и функции индексирования и функции поиска, доступ к этим функциям предоставляется через API Lucene. В первой части этой статья вы увидите пример использования Lucene для индексирования файлов в каталоге и подкаталогах. Далее небольшое отступление описывающее формат индексного каталога Lucene. Потом о краткое исследования методов анализа текста и, наконец реализацию поиска.</p><h2>Индексирование</h2><p>В начале создадим класс Indexer который будем использовать для индексирования всех текстовых файлов в указанной директории. Этот утилитарный класс с единственным внешним (public) методом index(), который принимает два аргумента. Первый indexDir &#8211; объект класса File который ссылается на каталог в котором будет создаватся индексная база. Второй аргумент это другой объект класса File который в свою очередь ссылается на каталог который будет индексироваться.</p><pre lang="java">
public static void index(File indexDir, File dataDir) throws IOException {
  if (!dataDir.exists() || !dataDir.isDirectory()) {
     throw new IOException(dataDir + " does not exist or is not a directory");
  }
  IndexWriter writer = new IndexWriter(indexDir, new StandardAnalyzer(), true);
  indexDirectory(writer, dataDir);
  writer.close();
}</pre><p>После проверки что dataDir существует и это каталог, мы инстанцируем объект класса IndexWriter который будет использоваться для создания индекса. Конструктор IndexWriter принимает в качестве первого параметра каталог в котором индексная база будет создана последний аргумент указывает что мы будем пересоздавать индекс, а не дополнять существующий (если таковой есть в каталоге). Средний параметр &#8211; это анализатор, который используется для анализа полей. Анализ полей будет описан ниже, но сейчас мы можем быть уверенны что все важные слова в файлах будут проиндексированы благодаря анализатору StandardAnalyzer.</p><p>Метод indexDirectory() ходит по дереву каталогов, сканирует его на наличие файлов с расширением .txt. Любой .txt файл будет проиндексирован методом indexFile(), а любой каталог будет обработан методом indexDirectory(), все остальные файлы будут проигнорированы. Ниже приведен код метода indexDirectory</p><pre lang="java">private static void indexDirectory(IndexWriter writer, File dir)  throws IOException
{
      File[] files = dir.listFiles();
      for (int i = 0; i &lt; files.length; i++) {
            File f = files[i];
            if (f.isDirectory()) {
                indexDirectory(writer, f);  // recurse
            } else if (f.getName().endsWith(".txt")) {
                indexFile(writer, f);
            }
      }
}</pre><p>Метод indexDirectory() живет полностью независимо от Lucene. Этот пример использования Lucene основной &#8211;  крайне редко при использовании Lucene приходиться много программировать с Lucene API, надо просто использовать готовое. В завершени класса Indexer, мы реализуем самое главное &#8211; индексирование отдельного текстового файла:</p><pre lang="java">
private static void indexFile(IndexWriter writer, File f) throws IOException {
  System.out.println("Indexing " + f.getName());
  Document doc = new Document();
  doc.add(Field.Text("contents", new FileReader(f)));
  doc.add(Field.Keyword("filename", f.getCanonicalPath()));
  writer.addDocument(doc);
}</pre><p>И&#8230; поверите вы или нет, но мы сделали это!<br
/> Мы проиндексировали все текстовые файлы в дереве каталогов.<br
/> Это действительно так просто. Подведем итог, перечислим шаги которые мы предприняли:</p><li>Создали IndexWriter.</li><li>Определили каждый файл который необходимо проиндексировать. (прошлись по дереву каталогов, и выбрали<br
/> все файлы с расширением .txt)</li><li>Для каждого файла создали объект класса Document с желаемыми полями.</li><li>Добавили этот документ в инстанс IndexWriter.Теперь соберем все эти методы в класс Indexer и добавим импорт необходимых пакетов.Индексировать файлы можно вызвав метод Indexer.index( indexDir, dataDir). Мы также добавим метод main() класса Indexer, который при запуске из командной строки принимает два аргумента &#8211; каталоги для индексной базы и индексируемый каталог.</li><pre lang="java">
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;import java.io.*;
public class Indexer {
 public static void index(File indexDir, File dataDir) throws IOException {
 	if (!dataDir.exists() || !dataDir.isDirectory()) {
 		throw new IOException(dataDir
 				+ " does not exist or is not a directory");
 	}
 	IndexWriter writer = new IndexWriter(indexDir, new StandardAnalyzer(),
 			true);
 	indexDirectory(writer, dataDir);
 	writer.close();
 }
private static void indexDirectory(IndexWriter writer, File dir)
 throws IOException {
 	File[] files = dir.listFiles();
 	for (int i = 0; i &lt; files.length; i++) {
 		File f = files[i];
 		if (f.isDirectory()) {
 			indexDirectory(writer, f); // recurse
 		} else if (f.getName().endsWith(".txt")) {
 			indexFile(writer, f);
 		}
 	}
 }
private static void indexFile(IndexWriter writer, File f)
 		throws IOException {
 	System.out.println("Indexing " + f.getName());
 	Document doc = new Document();
 	doc.add(Field.Text("contents", new FileReader(f)));
 	doc.add(Field.Keyword("filename", f.getCanonicalPath()));
 	writer.addDocument(doc);
 }
public static void main(String[] args) throws Exception {
 	if (args.length != 2) {
 		throw new Exception(
 		"Usage: " + Indexer.class.getName() + " &lt; index &gt; &lt; data &gt;");
 	}
File indexDir = new File(args[0]);
 	File dataDir = new File(args[1]);
 	index(indexDir, dataDir);
 }
}</pre><p>В этом примере в каждый документ входит два поля: contents &#8211; содержание текстового файла и его полное имя (с путем) &#8211; filename. Поле с содержанием специальным образом обрабатывается с помощью StandardAnalyzer, это будет описано позже. Поле filename индексируется как есть. Статические методы класса Filed: Text и Keywords будут подробно объяснены после краткого взгляда внутрь Lucene.</p><h2>Внутри Lucene индекса</h2><p>Формат индекса Lucene &#8211; это каталог с различными файлами. Вы можете успешно использовать Lucene и без понимания структуры каталога. Можно свободно пропустить этот раздел и рассматривать каталог как черный ящик без заботы о том что внутри. Если же вы готовы рассматривать глубже, то вы обнаружите что файлы созданные в прошлом разделе содержат статистику и другие данные облегчающие быстрый поиск и ранжирование. Индекс содержит последовательность документов. В нашем примере, каждый документ представляет информацию о текстовом файле.</p><h3>Документ (<code>Document</code>)</h3><p>Документ это основный объект, которым оперирует Lucene. Документы содержат последовательность полей Поля имеют имена (&laquo;contents&raquo; и &laquo;filename&raquo; в нашем примере).Значения полей это последовательность элементов (terms).</p><h3>Элементы (<code>Terms</code>)</h3><p>Элементы это мельчайшие части конкретного поля. Поля имеют три атрибута.</p><li>Stored (Сохранен) &#8212; Текст будет доступен в документе полученном в результате поиска.</li><li>Indexed (Индексированный) &#8212; Помещает поля как пригодные для поиска.</li><li>Tokenized (Размеченный) &#8212; Текст добавляется через запущенный анализатор и разделяется на релевантные части (это делается только для индексированных полей).Сохраненные поля удобны для немедленного получения исходного текста сразу после поиска, такие как первичный ключ базы данных или имя файла. Сохраненные поля сильно увеличивают размер индекса, поэтому использовать их нужно мудро.<br
/> Информация размеченных полей хранится очень эффективно, так как одни и те же элементы в тех же полях для множества документов сохраняются только единожды, с указанием на документ что содержит их.Класса Field имеет несколько статических методов для создания полей с различной комбинацией этих атрибутов. Вот они:</li><li><code>Field.Keyword</code> &#8212; Индексирован и сохранен, но не размеченный. Ключевые поля<br
/> обычно используются для таких данных как имена файлов, номера партий, первичные ключи,<br
/> и другой текст который необходимо оставить незатронутым.</li><li><code>Field.Text</code> &#8212; Индексирован и размечен. Этот текст также<br
/> сохранен есть он добавлен как класс <code>String</code>, и не будет сохранен если добавлен как класс Reader.</li><li><code>Field.UnIndexed</code> &#8212; Только сохраняется. По таким поля нельзя искать.</li><li><code>Field.UnStored</code> &#8212; Индексирован и размечен, но не сохранен. Такие поля идеальны, если вы хотите искать по этому полю, но хранить его отдельно от индексной базы и нет необходимости получать оригинальный текст сразу при выполнении поискового запроса.Глядя на выше сказанное, Lucene кажется относительно простым. Но он просто внешне, внутренняя реализация сложна, и основная сложность заключена в методах анализа текста, и в том как хранить элементы из размеченных полей.</li><h2>Анализ</h2><p>Анализ происходит в полях что помечены как размечаемые(<code>tokenized</code>). В нашем примере, мы индексируем поле contents &#8211; содержание текстовых файлов. Наша цель сделать все слова в текстовом файле пригодными для поиска,<br
/> но на практике цель немного отличается нам не нужно делать индекс чувствительным к каждому полю. Такие слова как &laquo;a&raquo;, &laquo;and&raquo; и &laquo;the&raquo; обычно обдуманно не подходят для поиска и исключением их из индекса можно оптимизировать<br
/> индекс, такие слова называется стоп-слова.</p><p>Какую сущность мы ищем? Что является словом, как отделить одно слово от другого. Акронимы, электронные адреса, интернет ссылки и другие такие конструкции, оставлять незатронутым и делать доступными для поиска? Если<br
/> слово в единственном числе проиндексировано, делать ли доступным для поиска его множественное число?<br
/> Это все интересные и сложные вопросы, ответы на которые позволяют выбрать какой из анализаторов использовать, или создавать свой.</p><p>В нашем примере, мы используем в встроенный в Lucene анализатор <code>StandardAnalyzer</code>, но существуют и другие встроенные анализаторы, а также и некоторые дополнительные. Приведем не большой кусочек кода который позволяет рассмотреть работу различных анализаторов на двух различных строках.</p><pre lang="java">import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.WhitespaceAnalyzer;
import org.apache.lucene.analysis.StopAnalyzer;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.Token;
import org.apache.lucene.analysis.SimpleAnalyzer;
import org.apache.lucene.analysis.snowball.SnowballAnalyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import java.io.StringReader;
import java.io.IOException;public class AnalysisDemo {
    private static final String[] strings = {
        "The quick brown fox jumped over the lazy dogs",
        "XY&amp;Z Corporation - xyz@example.com"    };
private static final Analyzer[] analyzers = new Analyzer[]{
        new WhitespaceAnalyzer(),
        new SimpleAnalyzer(),
        new StopAnalyzer(),
        new StandardAnalyzer(),
        new SnowballAnalyzer("English", StopAnalyzer.ENGLISH_STOP_WORDS)
    };
public static void main(String[] args) throws IOException {
        for (int i = 0; i &lt; strings.length; i++) {
            analyze(strings[i]);
        }
    }
private static void analyze(String text) throws IOException {
        System.out.println("Analzying \"" + text + "\"");
        for (int i = 0; i &lt; analyzers.length; i++) {
            Analyzer analyzer = analyzers[i];
            System.out.println("\t" + analyzer.getClass().getName() + ":");
            System.out.print("\t\t");
            TokenStream stream = analyzer.tokenStream("contents",
                  new StringReader(text));
                  new StringReader(text));
            while (true) {
                Token token = stream.next();
                if (token == null) break;
System.out.print("[" + token.termText() + "] ");
            }
            System.out.println("\n");
        }
    }
}</pre><p>Метод analyze использует исследовательскую форму Lucene API, для обычного индексирования эти функции не нужны, но удобны для рассмотрения как различные анализаторы размечают текст на элементы (термы). Результат выполнения следующий:</p><pre>Analzying "The quick brown fox jumped over the lazy dogs"org.apache.lucene.analysis.WhitespaceAnalyzer:[The] [quick] [brown] [fox] [jumped] [over] [the] [lazy] [dogs]
org.apache.lucene.analysis.SimpleAnalyzer:
[the] [quick] [brown] [fox] [jumped] [over] [the] [lazy] [dogs]
org.apache.lucene.analysis.StopAnalyzer:
[quick] [brown] [fox] [jumped] [over] [lazy] [dogs]
org.apache.lucene.analysis.standard.StandardAnalyzer:
[quick] [brown] [fox] [jumped] [over] [lazy] [dogs]
org.apache.lucene.analysis.snowball.SnowballAnalyzer:
[quick] [brown] [fox] [jump] [over] [lazi] [dog]
Analzying "XY&amp;Z Corporation - xyz@example.com"
org.apache.lucene.analysis.WhitespaceAnalyzer:
[XY&amp;Z] [Corporation] [-] [xyz@example.com]
org.apache.lucene.analysis.SimpleAnalyzer:
[xy] [z] [corporation] [xyz] [example] [com]
org.apache.lucene.analysis.StopAnalyzer:
[xy] [z] [corporation] [xyz] [example] [com]
org.apache.lucene.analysis.standard.StandardAnalyzer:
[xy&amp;z] [corporation] [xyz@example] [com]
org.apache.lucene.analysis.snowball.SnowballAnalyzer:
[xy&amp;z] [corpor] [xyz@exampl] [com]</pre><p>Анализатор <code>WhitespaceAnalyzer</code> самый простой, он просто разбивает на элементы основываясь на пробелах.<br
/> Он даже не меняет регистр букв. Поиск регистрочувствительный поэтому обычной практикой приведение к нижнему регистру текста в процессе фазы анализа. Остальные анализаторы приводят к нижнему регистру в процессе анализа. <code>SimpleAnalyzer</code> разбивает текс основываясь на несимвольных разделителях, таких как специальные символы (&#8216;&amp;&#8217;, &#8216;@&#8217;, и &#8216;.&#8217;). <code>StopAnalyzer</code> использует  фенкциональность <code>SimpleAnalyzer</code> и так же удаляет основные английские стоп-слова.</p><p>Наиболее сложный анализатор встроенный в ядро Lucene это <code>StandardAnalyzer</code>. Под ним скрывается основанный на JavaCC парсер с правилами для электронных адресов, акронимов, веб-адресов, дробных чисел, а так же он приводит к нижнему регистру и удаляет слова входящие в список стоп-слов. Анализатор построен по архитектуре цепочки фильтров, таким образом несколько разноцелевых правил комбинируются.</p><p>Анализатор <code>SnowballAnalyzer</code> демонстрирует не встроенный на данных момент в Lucene функциональность. Эта часть кода доступна в jakarta-lucene-sandbox CVS хранилище. Он показывает наиболее специфичные результаты. Алгоритм его основан на языке текста, и использует стемминг (stemming). Алгоритмы стемминга пытаются привести слово к его основной корневой форме. Это мы видим в примере с  &laquo;lazy&raquo; который был приведен к &laquo;lazi&raquo;. Слово &laquo;laziness&raquo; так же<br
/> будет приведено к &laquo;lazi&raquo;, тем самым при поиск мы найдем оба документа сразу. Другой интересный пример работы<br
/> <code>SnowballAnalzyer</code> с текстом &laquo;corporate corporation corporations corpse&raquo;, который приведет к следующим результатам</p><pre><code>[corpor] [corpor] [corpor] [corps]</code></pre><p>На этом мы остановим исследование текстовых анализаторов. Так как за этой темой стоит множество диссертаций и патентов, и конечно множество исследований. Теперь, зная как разбивается текст при индексировании, построим класс для поиска.</p><h2>Поиск</h2><p>В соответствии с нашим примером индексирования, создадим класс поиска Seacher который показывает результаты для того же индекса. Основные части этого класса:</p><pre lang="java">
import org.apache.lucene.document.Document;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Hits;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.store.Directory;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import java.io.File;public class Searcher {
    public static void main(String[] args) throws Exception {
        if (args.length != 2) {
            throw new Exception(
            "Usage: " + Searcher.class.getName() + " &lt; index &gt; &lt;query&gt;");
        }
File indexDir = new File(args[0]);
        String q = args[1];
if (!indexDir.exists() || !indexDir.isDirectory()) {
            throw new Exception(indexDir + " is does not exist or is not a directory.");
        }
search(indexDir, q);
    }
public static void search(File indexDir, String q)  throws Exception{
        Directory fsDir = FSDirectory.getDirectory(indexDir, false);
        IndexSearcher is = new IndexSearcher(fsDir);
Query query = QueryParser.parse(q, "contents", new StandardAnalyzer());
        Hits hits = is.search(query);
        System.out.println("Found "
        hits.length() + " document(s) that matched query '" + q + "':");
        for (int i = 0; i &lt; hits.length(); i++) {
            Document doc = hits.doc(i);
            System.out.println(doc.get("filename"));
        }
    }
}</pre><p>Объект Query из API Lucene создается в IndexSearcher.search методе. Объект Query может быть создан через<br
/> API используя встроенные подклассы Query:</p><li>TermQuery</li><li>BooleanQuery</li><li>PrefixQuery</li><li>WildcardQuery</li><li>RangeQuery</li><p>и несколько других. В нашем случае мы используем метод parse класса QueryParser для разбора введенного пользователем запроса. QueryParser это сложный основанный на JavaCC парсер который разбирает из запроса, похожего на Google запрос, в представление Lucene API Query.<br
/> Синтаксис запросов Lucene документирован на сайте Lucene, выражения могут содержать логические операции, указание на поля в которых искать, группировку, ранжирование запросов и многое другое. Как пример, выражение &laquo;+java -microsoft&raquo;, которое вернет совпадения для документов содержащих слово &laquo;java&raquo;, но не содержащих слово &laquo;microsoft.&raquo; QueryParser.parse необходимо указать поле по умолчанию для поиска, и в нашем случае мы укажем &laquo;contents&raquo; поле. Это будет эквивалентно запросу &laquo;+contents:java &#8211; contents:microsoft&raquo;, но будет намного удобнее в использовании пользователям.<br
/> Так же разработчик должен указать какой анализатор использовать для разметки запроса. В нашем случае мы используем StandardAnalyzer, который тот же самый что и при индексировании. Обычно один и тот же анализатор используется и для индексирования и для разбора строки запроса. Если мы использовали SnowballAnalyzer то как было показано в примере исследований анализатора, то запрос со словом &laquo;laziness&raquo; вернет все документы с элементом &laquo;lazi&raquo;.После поиска, возвращается набор ссылок на результаты &#8211; Hints Collection.<br
/> Ссылки возвращаются в порядке определяемый &laquo;очками&raquo; &#8211; релевантностью документа. Обсуждение алгоритма начисления очков не входит в рамки этой статьи, но можно быть уверенным что алгоритм по умолчанию подойдет к большинству приложений, и существует возможность настройки его в тех редких случаях когда этого алгоритма недостаточно.<br
/> Набор результатов сам по себе не является набором документов которые были найдены. Так сделано в частности и для повышения эффективности. Но ссылка (hint) представляет простой метод для получения документа. В нашем примере<br
/> мы выводим на экран поле с именем &laquo;filename&raquo; для каждого документа что были получены в результатах поиска.</p><h2>Подводя итоги</h2><p>Lucene логичный и красиво построенный продукт с изумительной функциональностью, это требует от разработчика искусного подхода к построению приложения вокруг него. Мы кратко обсудили проблему выбора анализатора, кроме этого<br
/> перед разработчиком стоят следующие вопросы:</p><li>Что является моим &laquo;документом&raquo;? (строки из базы, параграфы текстового файла)</li><li>Какие поля и как мне индексировать?</li><li>Как пользователи будут использовать поиск?Эта статья служит введением в возможности Lucene,<br
/> демонстрируя как наиболее просто использовать Lucene.[от переводчика] В этой статье не рассмотрены такие интересные вопросы как:</li><li> Индексирование русскоязычных документов.</li><li> &laquo;Подсветка&raquo; найденного слова в результатах поиска.</li><li> Оптимизация индексирования, сведение двух индексов в один.</li><h2>Источники</h2><p>Для дополнительной информации о Lucene, посетите сайт <a
href="http://jakarta.apache.org/lucene" traget="_blank">Lucene</a>.<br
/> Там вы сможете найти информацию о синтаксисе запросов и формате индексных файлов.</p><p>Erik Hatcher соавтор книги об Ant&#8217;е,<br
/> &laquo;Java Development with Ant&raquo; (опубликовано издательством Manning),<br
/> и так же соавтор книги &laquo;Lucene in Action&raquo;.</p> ]]></content:encoded> <wfw:commentRss>http://look-in.net/2005/02/16/lucene-introduction-article/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> </channel> </rss>
