<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-404420132869830190</id><updated>2012-02-16T15:40:24.652-03:00</updated><category term='Impuestos'/><category term='Lucene'/><category term='Ruby'/><category term='Reflexiones'/><category term='Ferret'/><title type='text'>['my', 'blog'] * " "</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://blog.jeandroguett.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/404420132869830190/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://blog.jeandroguett.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Jean Droguett</name><uri>http://www.blogger.com/profile/10779729787374006352</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>5</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-404420132869830190.post-6357736736077197045</id><published>2010-05-29T19:48:00.022-04:00</published><updated>2010-05-29T21:22:51.773-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='Lucene'/><category scheme='http://www.blogger.com/atom/ns#' term='Ferret'/><title type='text'>SpellChecker en Ruby</title><content type='html'>Estoy utilizando Ferret para las búsquedas (Full text search) escrita en Ruby y C inspirada en Apache Lucene que está escrita en Java.&lt;br /&gt;No encontré por ninguna parte la implementación de SpellChecker para Ferret(si realmente existe entonces me ayudó para entender mejor Lucene  :-) ), así es que tomé el código de Lucene SpellChecker en java (versión 2.4.1) buscando también inspiración :-)&lt;br /&gt;&lt;br /&gt;No está al 100%, ya que por ahora no considera la frecuencia, sólo considera la &lt;a href="http://es.wikipedia.org/wiki/Distancia_de_Levenshtein"&gt;distancia de Levenshtein&lt;/a&gt;, pero para una primera versión del proyecto en el que la uso es suficiente (&lt;a href="http://www.buskauto.com"&gt;www.buskauto.com&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;La implementación es la misma que en Lucene:&lt;br /&gt;* se crean &lt;a href="http://es.wikipedia.org/wiki/N-grama"&gt;ngramas&lt;/a&gt; de las palabras para el diccionario.&lt;br /&gt;* luego para sugerir una o más palabras se crean los ngramas de la palabra con error&lt;br /&gt;* se buscan en el diccionario las palabras que coincidan con algún ngrama.&lt;br /&gt;* y se prioriza por la distancia de Levenshtein con respecto a la palabra con error.&lt;br /&gt;&lt;br /&gt;&lt;span class="fullpost"&gt;&lt;br /&gt;&lt;br /&gt;Se utiliza así:&lt;br /&gt;&lt;pre class="prettyprint lang-ruby"&gt;dir = Store::RAMDirectory.new()&lt;br /&gt;@spell = SpellChecker::SpellChecker.new(dir)&lt;br /&gt;@spell.indexDictionary(reader, :texto)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="prettyprint lang-ruby"&gt;@spell.suggestSimilar(word, 3, indexReader, field, false)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;A continuación el código:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint lang-ruby"&gt;require 'ferret'&lt;br /&gt;require 'text'&lt;br /&gt;&lt;br /&gt;class String&lt;br /&gt;  attr_accessor :___distance&lt;br /&gt;  attr_accessor :___freq&lt;br /&gt;end  &lt;br /&gt;&lt;br /&gt;module SpellChecker&lt;br /&gt;  include Ferret&lt;br /&gt;  class SpellChecker&lt;br /&gt;    &lt;br /&gt;    def initialize(spellIndex)&lt;br /&gt;      @spellIndex = spellIndex&lt;br /&gt;      writer = Index::IndexWriter.new(:dir =&gt; @spellIndex)&lt;br /&gt;      writer.close&lt;br /&gt;      @searcher = Search::Searcher.new(@spellIndex)&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    def indexDictionary(reader, term)&lt;br /&gt;      writer = Index::IndexWriter.new(:dir =&gt; @spellIndex, :analyzer =&gt;  Analysis::WhiteSpaceAnalyzer.new)&lt;br /&gt;      writer.field_infos.add_field(:word, :store =&gt; :yes)&lt;br /&gt;      &lt;br /&gt;      reader.terms(term).each{|word,b|&lt;br /&gt;        if word.length &gt;= 3 and !exist(word)&lt;br /&gt;          doc = createDocument(word)&lt;br /&gt;          writer &lt;&lt; doc&lt;br /&gt;        end&lt;br /&gt;      }&lt;br /&gt;      writer.optimize&lt;br /&gt;      writer.close&lt;br /&gt;      @searcher.close&lt;br /&gt;      @searcher = Search::Searcher.new(@spellIndex)&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    def suggestSimilar(word, numSug, ir, field, more_popular)&lt;br /&gt;      freq = 0&lt;br /&gt;      if ir and field&lt;br /&gt;        freq = field.respond_to?("collect") ? field.collect{|f| ir.doc_freq(f, word)}.max : ir.doc_freq(field, word)&lt;br /&gt;      end&lt;br /&gt;      #freq = (ir and field) ? ir.doc_freq(field, word) : 0&lt;br /&gt;      goal_freq = (more_popular and ir and field) ? freq : 0 &lt;br /&gt;      &lt;br /&gt;      query = Search::BooleanQuery.new&lt;br /&gt;      hash = createDocument(word)&lt;br /&gt;      hash.each do |key, gram|&lt;br /&gt;        add(query, key, gram)&lt;br /&gt;      end&lt;br /&gt;      &lt;br /&gt;      min = 0.5&lt;br /&gt;      pq = Utils::PriorityQueue.new(numSug) {|a, b| a.___distance &gt; b.___distance}&lt;br /&gt;      top_docs = @searcher.search(query)&lt;br /&gt;      top_docs.hits.each do |hit|&lt;br /&gt;        sug_word = @searcher[hit.doc][:word]&lt;br /&gt;        next if sug_word.eql?(word)&lt;br /&gt;        sug_word.___distance = 1.0 - (Text::Levenshtein.distance(sug_word, word) / [word.size, sug_word.size].max.to_f)&lt;br /&gt;        next if sug_word.___distance &lt; min&lt;br /&gt;        &lt;br /&gt;        if ir and field&lt;br /&gt;          sug_word.___freq = field.respond_to?("collect") ? field.collect{|f| ir.doc_freq(f, sug_word)}.max : ir.doc_freq(field, sug_word)&lt;br /&gt;          next if (more_popular and goal_freq &gt; sug_word.___freq) or sug_word.___freq &lt; 1&lt;br /&gt;        end&lt;br /&gt;        pq.insert(sug_word)&lt;br /&gt;        min = pq.top.___distance if pq.size == numSug&lt;br /&gt;      end&lt;br /&gt;      &lt;br /&gt;      a = Array.new(pq.size)&lt;br /&gt;      a.collect{|x| pq.pop }&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    def add(bq, name, value)&lt;br /&gt;      if value.respond_to?("each")&lt;br /&gt;        value.each{|v|&lt;br /&gt;          tq = Search::TermQuery.new(name, v)&lt;br /&gt;          bq.add_query(tq, :should)&lt;br /&gt;        }&lt;br /&gt;      else&lt;br /&gt;        tq = Search::TermQuery.new(name, value)&lt;br /&gt;        bq.add_query(tq, :should)&lt;br /&gt;      end&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    def createDocument(word)&lt;br /&gt;      doc = {:word =&gt; word}&lt;br /&gt;      doc = addGram(word, doc)&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    def addGram(word, doc)&lt;br /&gt;      len = word.length&lt;br /&gt;      ng1 = getMin(len)&lt;br /&gt;      ng2 = getMax(len)&lt;br /&gt;      &lt;br /&gt;      ng1.upto(ng2) do |ng|&lt;br /&gt;        key = "gram" + ng.to_s&lt;br /&gt;        doc[key] = Array.new&lt;br /&gt;        gend = nil&lt;br /&gt;        0.upto(len - ng) do |i|&lt;br /&gt;          gram = word[i..i+ng-1]&lt;br /&gt;          doc[key] &lt;&lt; gram&lt;br /&gt;          doc["start" + ng.to_s] = gram if i == 0&lt;br /&gt;          gend = gram;&lt;br /&gt;        end&lt;br /&gt;        doc["end" + ng.to_s] = gend if gend&lt;br /&gt;      end&lt;br /&gt;      doc&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    def getMin(l)&lt;br /&gt;      if l &gt; 5&lt;br /&gt;        3&lt;br /&gt;      elsif l == 5&lt;br /&gt;        2&lt;br /&gt;      else&lt;br /&gt;        1&lt;br /&gt;      end&lt;br /&gt;    end&lt;br /&gt;    def getMax(l)&lt;br /&gt;      if l &gt; 5&lt;br /&gt;        4&lt;br /&gt;      elsif l == 5&lt;br /&gt;        3&lt;br /&gt;      else&lt;br /&gt;        2&lt;br /&gt;      end&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    def exist(word)&lt;br /&gt;      @searcher.doc_freq(:word, word) &gt; 0&lt;br /&gt;    end&lt;br /&gt;end  &lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/404420132869830190-6357736736077197045?l=blog.jeandroguett.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.jeandroguett.com/feeds/6357736736077197045/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=404420132869830190&amp;postID=6357736736077197045' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/404420132869830190/posts/default/6357736736077197045'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/404420132869830190/posts/default/6357736736077197045'/><link rel='alternate' type='text/html' href='http://blog.jeandroguett.com/2010/05/spellchecker-en-ruby.html' title='SpellChecker en Ruby'/><author><name>Jean Droguett</name><uri>http://www.blogger.com/profile/10779729787374006352</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-404420132869830190.post-8140276574699648508</id><published>2009-05-06T00:03:00.019-04:00</published><updated>2009-05-06T01:19:33.960-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Lucene'/><title type='text'>Lucene crear índice y buscar</title><content type='html'>Ya que he estado trabajando con Lucene, lo mejor será anotar lo aprendido.&lt;br /&gt;Este es un ejemplo básico de Lucene.&lt;br /&gt;&lt;br /&gt;* Creación de un índice, en este caso se guarda en el índice la descripción del producto y su Id.&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint lang-java"&gt;&lt;br /&gt;public void crearIndice(File indexDir, Analyzer analyzer, List&lt;producto&gt; productos) throws CorruptIndexException, LockObtainFailedException, IOException {&lt;br /&gt;   IndexWriter writer = new IndexWriter(indexDir, analyzer, true, IndexWriter.MaxFieldLength.UNLIMITED);&lt;br /&gt;   writer.setUseCompoundFile(false);&lt;br /&gt;&lt;br /&gt;   for (Producto producto : productos) {&lt;br /&gt;      Document doc = new Document();&lt;br /&gt;      doc.add(new Field("descripcion", producto.getDescripcion(), Field.Store.YES, Field.Index.ANALYZED));&lt;br /&gt;      doc.add(new Field("id", producto.getId() + "", Field.Store.YES, Field.Index.NO));&lt;br /&gt;      writer.addDocument(doc);&lt;br /&gt;   }&lt;br /&gt;   writer.optimize();&lt;br /&gt;   writer.commit();&lt;br /&gt;   writer.close();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;indexDir&lt;/span&gt;: directorio donde se crearán los archivos de Lucene. &lt;span class="fullpost"&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Document&lt;/span&gt;: el índice esta compuesto por documentos, en este ejemplo, cada documento representa un producto (id, descripción).&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;analyzer&lt;/span&gt;: analizador, en este ejemplo se usa StandarAnalyzer que transforma el texto a minúsculas, elimina los caracteres no alfanuméricos y separa las palabras, ver más abajo ejemplo de StandarAnalyzer.&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Field.Store.YES&lt;/span&gt;: Se almacena el texto.&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Field.Index.ANALYZED&lt;/span&gt;: Se analiza el texto, lo que permitirá buscar por una o más palabras del texto.&lt;br /&gt;&lt;br /&gt;Ejemplo de StandarAnalyzer:&lt;br /&gt;&lt;pre class="prettyprint lang-java"&gt;&lt;br /&gt;public class TestStandardAnalyzer {&lt;br /&gt;   public static void main(String[] args) throws IOException {&lt;br /&gt;      Analyzer analyzer = new StandardAnalyzer();&lt;br /&gt;      TokenStream resultado = analyzer.tokenStream("", new StringReader("Éste es un-texto + analizado // por StandarAnalyzer 123"));&lt;br /&gt;      Token token = new Token();&lt;br /&gt;      while ((token = resultado.next(token)) != null) {&lt;br /&gt;         System.out.println("analyzer: " + token.term());&lt;br /&gt;      }&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;El resultado es:&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;analyzer: éste&lt;br /&gt;analyzer: es&lt;br /&gt;analyzer: un&lt;br /&gt;analyzer: texto&lt;br /&gt;analyzer: analizado&lt;br /&gt;analyzer: por&lt;br /&gt;analyzer: standaranalyzer&lt;br /&gt;analyzer: 123&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;* Búsqueda en el índice:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint lang-java"&gt;&lt;br /&gt;   public List&lt;Producto&gt; buscar(File indexDir, Analyzer analyzer, String texto) throws CorruptIndexException, IOException, ParseException {&lt;br /&gt;      List&lt;Producto&gt; productos = new ArrayList&lt;Producto&gt;();&lt;br /&gt;      IndexSearcher is = new IndexSearcher(FSDirectory.getDirectory(indexDir));&lt;br /&gt;      QueryParser qp = new QueryParser("descripcion", analyzer);&lt;br /&gt;      qp.setDefaultOperator(QueryParser.AND_OPERATOR);&lt;br /&gt;      Query query = qp.parse(texto);&lt;br /&gt;      TopDocCollector collector = new TopDocCollector(100);&lt;br /&gt;      is.search(query, collector);&lt;br /&gt;      ScoreDoc[] hits = collector.topDocs().scoreDocs;&lt;br /&gt;&lt;br /&gt;      for (int i = 0; i &lt; hits.length; i++) {&lt;br /&gt;         int docId = hits[i].doc;&lt;br /&gt;         Document doc = is.doc(docId);&lt;br /&gt;         Producto p = new Producto();&lt;br /&gt;         p.setId(Integer.parseInt(doc.get("id")));&lt;br /&gt;         p.setDescripcion(doc.get("descripcion"));&lt;br /&gt;         productos.add(p);&lt;br /&gt;      }&lt;br /&gt;      is.close();&lt;br /&gt;      return productos;&lt;br /&gt;   }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;QueryParser("descripcion", analyzer)&lt;/span&gt;: indica por cual campo realizaremos la búsqueda, en este ejemplo sólo podemos buscar por "descripción".&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;qp.setDefaultOperator(QueryParser.AND_OPERATOR)&lt;/span&gt;: se debe aplicar el operador lógico AND, es decir, si la búsqueda es por dos o más palabras, sólo debe entregar los documentos donde estén todas las palabras.&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Query query = qp.parse(texto)&lt;/span&gt;: Transforma el texto para su búsqueda. Ver más abajo ejemplo de QueryParser.&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;is.search(query, collector)&lt;/span&gt;: ejecuta la búsqueda y el resultado queda en collector.&lt;br /&gt;&lt;br /&gt;Ejemplo de QueryParser:&lt;br /&gt;&lt;pre class="prettyprint lang-java"&gt;&lt;br /&gt;public class TestQueryParser {&lt;br /&gt;   public static void main(String[] args) throws ParseException {&lt;br /&gt;      QueryParser qp = new QueryParser("descripcion", new StandardAnalyzer());&lt;br /&gt;      qp.setDefaultOperator(QueryParser.AND_OPERATOR);&lt;br /&gt;      Query query = qp.parse("iPod nano");&lt;br /&gt;      System.out.println("Con AND_OPERATOR: " + query);&lt;br /&gt;      qp.setDefaultOperator(QueryParser.OR_OPERATOR);&lt;br /&gt;      query = qp.parse("iPod nano");&lt;br /&gt;      System.out.println("Con OR_OPERATOR: " + query);&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;El resultado es:&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;Con AND_OPERATOR: +descripcion:ipod +descripcion:nano&lt;br /&gt;Con OR_OPERATOR: descripcion:ipod descripcion:nano&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Ejemplo completo:&lt;br /&gt;&lt;pre class="prettyprint lang-java"&gt;&lt;br /&gt;public class Indice {&lt;br /&gt;   public static void main(String[] args) throws CorruptIndexException, LockObtainFailedException, IOException, ParseException {&lt;br /&gt;      File indexDir = new File("/tmp/productos");&lt;br /&gt;      Analyzer analyzer = new StandardAnalyzer();&lt;br /&gt;      List&lt;Producto&gt; productos = new ArrayList&lt;Producto&gt;();&lt;br /&gt;      Producto p = new Producto(1, "iPod mini");&lt;br /&gt;      productos.add(p);&lt;br /&gt;      p = new Producto(2, "iPod shuffle");&lt;br /&gt;      productos.add(p);&lt;br /&gt;      p = new Producto(3, "iPod touch");&lt;br /&gt;      productos.add(p);&lt;br /&gt;&lt;br /&gt;      Indice indice = new Indice();&lt;br /&gt;      indice.crearIndice(indexDir, analyzer, productos);&lt;br /&gt;      System.out.println("Indice creado");&lt;br /&gt;&lt;br /&gt;      List&lt;Producto&gt; resultado = indice.buscar(indexDir, analyzer, "ipod mini");&lt;br /&gt;      System.out.println("Resultado: " + resultado.size() + " producto(s)");&lt;br /&gt;      for (Producto producto : resultado) {&lt;br /&gt;         System.out.println("id: " + producto.getId() + " producto: " + producto.getDescripcion());&lt;br /&gt;      }&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public void crearIndice(File indexDir, Analyzer analyzer, List&lt;Producto&gt; productos) throws CorruptIndexException, LockObtainFailedException, IOException {&lt;br /&gt;      IndexWriter writer = new IndexWriter(indexDir, analyzer, true, IndexWriter.MaxFieldLength.UNLIMITED);&lt;br /&gt;      writer.setUseCompoundFile(false);&lt;br /&gt;&lt;br /&gt;      for (Producto producto : productos) {&lt;br /&gt;         Document doc = new Document();&lt;br /&gt;         doc.add(new Field("descripcion", producto.getDescripcion(), Field.Store.YES, Field.Index.ANALYZED));&lt;br /&gt;         doc.add(new Field("id", producto.getId() + "", Field.Store.YES, Field.Index.NO));&lt;br /&gt;         writer.addDocument(doc);&lt;br /&gt;      }&lt;br /&gt;      writer.optimize();&lt;br /&gt;      writer.commit();&lt;br /&gt;      writer.close();&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public List&lt;Producto&gt; buscar(File indexDir, Analyzer analyzer, String texto) throws CorruptIndexException, IOException, ParseException {&lt;br /&gt;      List&lt;Producto&gt; productos = new ArrayList&lt;Producto&gt;();&lt;br /&gt;      IndexSearcher is = new IndexSearcher(FSDirectory.getDirectory(indexDir));&lt;br /&gt;      QueryParser qp = new QueryParser("descripcion", analyzer);&lt;br /&gt;      qp.setDefaultOperator(QueryParser.AND_OPERATOR);&lt;br /&gt;      Query query = qp.parse(texto);&lt;br /&gt;      TopDocCollector collector = new TopDocCollector(100);&lt;br /&gt;      is.search(query, collector);&lt;br /&gt;      ScoreDoc[] hits = collector.topDocs().scoreDocs;&lt;br /&gt;&lt;br /&gt;      for (int i = 0; i &lt; hits.length; i++) {&lt;br /&gt;         int docId = hits[i].doc;&lt;br /&gt;         Document doc = is.doc(docId);&lt;br /&gt;         Producto p = new Producto();&lt;br /&gt;         p.setId(Integer.parseInt(doc.get("id")));&lt;br /&gt;         p.setDescripcion(doc.get("descripcion"));&lt;br /&gt;         productos.add(p);&lt;br /&gt;      }&lt;br /&gt;      is.close();&lt;br /&gt;      return productos;&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/404420132869830190-8140276574699648508?l=blog.jeandroguett.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.jeandroguett.com/feeds/8140276574699648508/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=404420132869830190&amp;postID=8140276574699648508' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/404420132869830190/posts/default/8140276574699648508'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/404420132869830190/posts/default/8140276574699648508'/><link rel='alternate' type='text/html' href='http://blog.jeandroguett.com/2009/05/lucene-crear-indice-y-buscar.html' title='Lucene crear índice y buscar'/><author><name>Jean Droguett</name><uri>http://www.blogger.com/profile/10779729787374006352</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-404420132869830190.post-8967049771301880849</id><published>2008-04-15T20:48:00.004-04:00</published><updated>2008-04-16T14:53:00.077-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Reflexiones'/><title type='text'>Mi AFP me dice que seré pobre.</title><content type='html'>Estoy exagerando con el título, cuando me jubile no estaré en la calle pero disminuirán considerablemente mis ingresos, veamos que me dice:&lt;br /&gt;&lt;br /&gt;Mi AFP tiene en su página una sección llamada "proyección de pensión".&lt;br /&gt;Esta proyección me dice que &lt;span class="fullpost"&gt;obtendré una pensión igual al 53% de mi sueldo bruto actual. El supuesto es una rentabilidad anual del 5% de mi fondo. &lt;br /&gt;&lt;br /&gt;Estoy en el fondo A que, según la www.safp.cl, mi AFP logró una rentabilidad en Marzo de -4,03%, en Febrero de 4,63% y en Enero de -9,42%, por lo tanto el acumulado del 2008 es de -9,05%, espero que una rentabilidad anual del 5% lo consideren como el caso menos optimista :-) . Aunque en el 2007 la rentabilidad fue de 10%. ¿Quién me puede decir que ocurrirá en los próximos 30 o 40 años? :-)&lt;br /&gt;&lt;br /&gt;El resultado de esta proyección de mi pensión está expresada en el valor actual de la moneda, por lo tanto, considerando la inflación, baja aún más mi pensión. &lt;br /&gt;El IPC desde el 2000 al 2007 a aumentado un 22,67%, es decir, mis 100 pesos del año 2000 hoy valen menos de 78 pesos. (Valores del IPC en www.bcentral.cl). &lt;br /&gt;Está bien, mi sueldo debería subir por lo menos un porcentaje similar al del IPC.&lt;br /&gt;&lt;br /&gt;La jubilación se considera como una renta más, por lo tanto, está afecta a impuesto, es decir, baja aún más mi pensión.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Bien, esta es mi situación.&lt;br /&gt;Puedo conformarme, "cuando me jubile mis gastos serán menores". Veamos:&lt;br /&gt;* Contribuciones: Se me duplican, ya que habrá finalizado el plazo que indica el DFL2.&lt;br /&gt;* Servicios básicos: siguen.&lt;br /&gt;* Vacaciones: Ya que tendré tanto tiempo libre, mmm, me tendré que quedar en la casa.&lt;br /&gt;* Salud: mmm, ¿debo seguir?. Acá me quedo con cero.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;¿Quién tiene la culpa? ¿La AFP? ¿El gobierno? No, sólo yo :-)&lt;br /&gt;&lt;br /&gt;Ahora sólo debo ver como cambiar esta situación, he escuchado alguna palabras que debo estudiar como:&lt;br /&gt;&lt;br /&gt;* Cuenta de Ahorro (siempre que la tasa de interés sea mayor que la inflación ¿Existen? )&lt;br /&gt;* APV&lt;br /&gt;* Fondos Mutuos&lt;br /&gt;* Depósitos a Plazo&lt;br /&gt;* Acciones&lt;br /&gt;* Inversiones&lt;br /&gt;* Gastar menos :-), esto lo necesito para los puntos anteriores (ahorrar).&lt;br /&gt;* Buscar otro trabajo&lt;br /&gt;* Segundo trabajo&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;NOTA: Este porcentaje (pensión / sueldo) puede cambiar para otra persona que por ejemplo empezó a una edad más temprana a imponer.&lt;br /&gt;También se debe considerar el tope imponible (60UF), ya que si tu sueldo es menor a este tope se impone por el 10% del sueldo, pero si es mayor al tope se impone por el 10% de 60UF. Visto de otra forma: una persona cuyo sueldo es mayor a 60UF va obtener una pensión (sin APV) igual a una persona cuyo sueldo es de 60UF.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/404420132869830190-8967049771301880849?l=blog.jeandroguett.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.jeandroguett.com/feeds/8967049771301880849/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=404420132869830190&amp;postID=8967049771301880849' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/404420132869830190/posts/default/8967049771301880849'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/404420132869830190/posts/default/8967049771301880849'/><link rel='alternate' type='text/html' href='http://blog.jeandroguett.com/2008/04/mi-afp-me-dice-que-ser-pobre.html' title='Mi AFP me dice que seré pobre.'/><author><name>Jean Droguett</name><uri>http://www.blogger.com/profile/10779729787374006352</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-404420132869830190.post-6219731468738132988</id><published>2008-04-07T15:08:00.018-04:00</published><updated>2009-04-27T22:57:36.530-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Ruby'/><title type='text'>Java2Ruby</title><content type='html'>A veces descubro que aún sigo escribiendo en Java, así que espero que con estos apuntes  pueda escribir en Ruby. :-)&lt;br /&gt;&lt;span class="fullpost"&gt;&lt;br /&gt;&lt;br /&gt;Java:&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;for( int i = 0; i &amp;lt; 6; i++){&lt;br /&gt;   System.out.println(i);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Ruby:&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;3.times{|i| puts i}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Java:&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;String a = null;&lt;br /&gt;String b = "hola";&lt;br /&gt;if( a == null){&lt;br /&gt;    a = b;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Ruby:&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;b = "hola"&lt;br /&gt;a = a || b&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Java:&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;public static String join(Collection coleccion, String separador) {&lt;br /&gt;  StringBuffer buffer = new StringBuffer();&lt;br /&gt;  Iterator iterador = coleccion.iterator();&lt;br /&gt;  while (iterador.hasNext()) {&lt;br /&gt;    buffer.append(iterador.next());&lt;br /&gt;    if (iterador.hasNext()) {&lt;br /&gt;        buffer.append(separador);&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;  return buffer.toString();&lt;br /&gt;}&lt;br /&gt;String b = join(a, ",");&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Ruby:&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;a = [1,2,3,4,5]&lt;br /&gt;b = a * ","&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;¿Superaré las dos líneas en Ruby?   :-)&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/404420132869830190-6219731468738132988?l=blog.jeandroguett.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.jeandroguett.com/feeds/6219731468738132988/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=404420132869830190&amp;postID=6219731468738132988' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/404420132869830190/posts/default/6219731468738132988'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/404420132869830190/posts/default/6219731468738132988'/><link rel='alternate' type='text/html' href='http://blog.jeandroguett.com/2008/04/java2ruby.html' title='Java2Ruby'/><author><name>Jean Droguett</name><uri>http://www.blogger.com/profile/10779729787374006352</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-404420132869830190.post-4953727925658893407</id><published>2008-03-20T19:36:00.001-03:00</published><updated>2008-04-09T09:48:06.829-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Impuestos'/><title type='text'>¿ Dónde está mi timbre  y mi estampilla ?</title><content type='html'>¿ Timbre y estampilla ?&lt;br /&gt;&lt;br /&gt;No,  es  otro IVA   :-)&lt;br /&gt;&lt;br /&gt;Donde no está el IVA está este impuesto y a veces se ponen de acuerdo para estar los dos.&lt;br /&gt;&lt;br /&gt;No pagas IVA, por ejemplo, al obtener un crédito en un banco, entonces pagas este impuesto. Pero cuando gastas este crédito pagas el IVA.&lt;br /&gt;&lt;br /&gt;Buscando cómo se cálcula este impuesto, en particular para los créditos de consumo encontré esto en www.sii.cl:&lt;br /&gt;&lt;br /&gt;&lt;span class="fullpost"&gt;&lt;br /&gt;&lt;br /&gt;"&lt;span style=";font-family:Arial;font-size:10;"  &gt;El                 Impuesto de Timbres y Estampillas se aplica a los documentos o                 actos que involucran una operación de crédito de dinero, por                 ejemplo letras de cambio o pagarés. La base imponible es el                 monto del capital especificado en cada documento. La tasa del                 impuesto es variable dependiendo del período que medie entre la                 emisión del documento y el plazo de vencimiento de éste, la                 cual es de 0,134% por el valor del documento por cada mes o                 fracción del plazo, con un tope máximo de 1,608%. Los cheques                 y los protestos de pagarés están gravados con una cantidad                 fija por concepto de Impuesto de Timbres y Estampillas.&lt;/span&gt;"&lt;br /&gt;&lt;br /&gt;Las tasas cambiaron para los años 2007, 2008 y 2009 en adelante.&lt;br /&gt;2007 mínimo 0,125% y máximo 1,5%&lt;br /&gt;2008 mínimo 0,1125% y máximo 1,35%&lt;br /&gt;2009 en adelante mínimo 0,1% y máximo 0,2%&lt;br /&gt;&lt;br /&gt;Si entiendo bien, para el caso de un crédito de consumo de 6 meses, para este año, la tasa será:&lt;br /&gt;&lt;br /&gt;6 * 0,1125% = 0,675%  sobre el total del crédito&lt;br /&gt;&lt;br /&gt;y para uno de 12 meses:  12 * 0.1125% = 1,35% sobre el total del crédito.&lt;br /&gt;&lt;br /&gt;En 12 cuotas llegamos al máximo, entonces de 12 o más cuotas la tasa será la misma (1,35%).&lt;br /&gt;&lt;br /&gt;Por ejemplo para un crédito de 7.000.000 tendremos que gravar un impuesto de 94.500.&lt;br /&gt;&lt;br /&gt;Pero no es así.&lt;br /&gt;&lt;br /&gt;Revisando los simuladores de algunos bancos veo que antes de calcular este impuesto se agregan otros cargos, por ejemplo:&lt;br /&gt;&lt;br /&gt;(C)  7.000.000 = crédito&lt;br /&gt;(N)         2.000 = notario&lt;br /&gt;(S)     120.000 = seguro de desgravamen, depende del banco, es alredor de 2.500 por mes, en este caso la simulación fue en 48 cuotas.&lt;br /&gt;&lt;br /&gt;Bien, cómo el trato con el banco es un crédito con cuotas iguales, no vamos a pagar el impuesto ni el notario al tomar el crédito, si no que también estos montos se van al total del crédito. Uff :-)&lt;br /&gt;Ah, pero agreguemos también el seguro de desgravamen. :-)&lt;br /&gt;&lt;br /&gt;Llamemos (I) al  impuesto.&lt;br /&gt;Tenemos entonces que el total del crédito es:  (1)  TC = C + N + S + I&lt;br /&gt;Por lo tanto aplicamos la tasa a TC:   (2)  I = TC * p,&lt;br /&gt;con p = 1,35/100 = 0,0135&lt;br /&gt;&lt;br /&gt;Si unimos (1) y (2) obtenemos: (3) TC = C + N + S + (TC * p)&lt;br /&gt;&lt;br /&gt;Moviendo para un lado:&lt;br /&gt;(4) TC  -  TC*p = C + N + S&lt;br /&gt;(5) TC (1 - p) = C + N + S&lt;br /&gt;(6) TC  = (C + N + S) / (1-p)&lt;br /&gt;&lt;br /&gt;Con (6) obtenemos el total del crédito TC = 7.219.463&lt;br /&gt;&lt;br /&gt;y con (2) obtienes tu timbre y estampilla   I = 97.463   :-)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Uff, cuando ha aumentado nuestro crédito y aún nos faltan los intereses.  :-(&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Nota: hay simuladores que aún están usando la tasa del 2007.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/404420132869830190-4953727925658893407?l=blog.jeandroguett.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.jeandroguett.com/feeds/4953727925658893407/comments/default' title='Enviar comentarios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=404420132869830190&amp;postID=4953727925658893407' title='3 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/404420132869830190/posts/default/4953727925658893407'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/404420132869830190/posts/default/4953727925658893407'/><link rel='alternate' type='text/html' href='http://blog.jeandroguett.com/2008/03/dnde-est-mi-timbre-y-mi-estampilla.html' title='¿ Dónde está mi timbre  y mi estampilla ?'/><author><name>Jean Droguett</name><uri>http://www.blogger.com/profile/10779729787374006352</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry></feed>
