Búsquedas con Elastic Search
Vamos a añadir a nuestra página capacidades de buscas contenido en los tweets de los usuarios. Podríamos hacerlo mediante una consulta en la base de datos. Esto funcionaría bien con pocos tweets. Pero cuando el número de tweets creciera no tendríamos un buen rendimiento, tenemos que indexar el contenido de los tweets para poder hacer búsquedas mucho mas eficientes.
Para añadir esta funcionalidad vamos a utilizar el módulo Elastic Search creado por @_felipera.
Lee la documentación del módulo pasar saber cómo instalarlo
http://www.playframework.org/modules/elasticsearch-0.2/home
Cuando tengamos el módulo instalado. Lo único que debemos hacer para habilitar las búsquedas es anotar nuestra clase.
apps/models/Tweet.java
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@ElasticSearchable
@Entity
public class Tweet extends Model {
<span class="nd">@Required</span>
<span class="nd">@MaxLength</span><span class="o">(</span><span class="mi">140</span><span class="o">)</span>
<span class="kd">public</span> <span class="n">String</span> <span class="n">msg</span><span class="o">;</span>
<span class="nd">@ElasticSearchEmbedded</span><span class="o">(</span><span class="n">fields</span><span class="o">={</span><span class="s">"username"</span><span class="o">})</span>
<span class="nd">@ManyToOne</span>
<span class="kd">public</span> <span class="n">User</span> <span class="n">author</span><span class="o">;</span>
<span class="kd">public</span> <span class="n">Date</span> <span class="n">date</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="n">Tweet</span> <span class="nf">create</span><span class="o">(</span><span class="n">String</span> <span class="n">msg</span><span class="o">,</span> <span class="n">User</span> <span class="n">author</span><span class="o">){</span>
<span class="n">Tweet</span> <span class="n">t</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Tweet</span><span class="o">();</span>
<span class="n">t</span><span class="o">.</span><span class="na">msg</span> <span class="o">=</span> <span class="n">msg</span><span class="o">;</span>
<span class="n">t</span><span class="o">.</span><span class="na">author</span> <span class="o">=</span> <span class="n">author</span><span class="o">;</span>
<span class="n">t</span><span class="o">.</span><span class="na">date</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Date</span><span class="o">();</span>
<span class="k">return</span> <span class="n">t</span><span class="o">;</span>
<span class="o">}</span>
Creamos un método en el controlador que se encargue de hacer la búsqueda.
app/controllers/Timeline.java
1
2
3
4
5
6
7
public static void search(String query){
if(query != null && !query.isEmpty()){
SearchResults<Tweet> tweets = ElasticSearch.search(QueryBuilders.fieldQuery(“msg”, query), Tweet.class);
render(tweets, query);
}
render();
}
Creamos la vista
app/views/Timeline/search.html
<h3>Encontrados ${tweets.totalCount} tweets para la consulta ‘${query}’</h3>
<table>
<thead>
<tr>
<th>id</th>
<th>Tweet</th>
</tr>
</thead>
<tbody>
#{list items:tweets.objects, as:‘tweet’}
<tr>
<td>${tweet.id}</td>
<td>${tweet.msg}</td> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#{form @Timeline.search()}
<input type=“text” name=“query” value=“${query}” class=“xxlarge”/>
<input type=“submit” class=“btn primary” value=“Buscar” />
{/form}
{if tweets}
</tr>
#{/list}
</tbody>
</table>{/if}
Ejercicio
Implementar la búsqueda en tiempo real.
- Crear un nuevo método en el API que permita realizar la búsqueda
- A medida que el usuario va escribiendo en la caja de texto, realiza una nueva consulta
Solución
app/controllers/Api.java
}
1
2
3
4
5
6
7
8
9
10
11
12
13
public static void search(String query){
ApiResponse resp = new ApiResponse();
if(query != null && !query.isEmpty()){
resp.status = “OK”;
SearchResults<Tweet> tweets = ElasticSearch.search(QueryBuilders.prefixQuery(“msg”, query), Tweet.class);
resp.result = tweets.objects;
}else{
resp.status = “ERROR”;
resp.message = “Empty query”;
}
<span class="n">renderJSON</span><span class="o">(</span><span class="k">new</span> <span class="n">JSONSerializer</span><span class="o">().</span><span class="na">include</span><span class="o">(</span><span class="s">"status"</span><span class="o">,</span> <span class="s">"message"</span><span class="o">,</span> <span class="s">"result.msg"</span><span class="o">,</span> <span class="s">"result.id"</span><span class="o">).</span><span class="na">exclude</span><span class="o">(</span><span class="s">"*"</span><span class="o">).</span><span class="na">serialize</span><span class="o">(</span><span class="n">resp</span><span class="o">));</span>
app/views/search.html
<table>
<thead>
<tr>
<th>id</th>
<th>Tweet</th>
</tr>
</thead>
<tbody data-bind=“template:{name : ‘tweet-row’, foreach:tweets}”> </table> <script type=“text/html” id=“tweet-row”>
<tr>
<td></td>
<td></td> <script>
viewModel = {
query : ko.observable(‘${query}’),
tweets : ko.observableArray([])
} </script>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#{form @Timeline.search()}
<input type=“text” name=“query” value=“${query}” class=“xxlarge” placeholder=“Busqueda” data-bind=“value:query, valueUpdate:‘afterkeydown’”/>
{/form}
<span class="nt"></tbody></span>
</tr>
</script>{set ‘endScript’}
<span class="kd">function</span> <span class="nx">Tweet</span><span class="p">(</span><span class="nx">id</span><span class="p">,</span> <span class="nx">msg</span><span class="p">){</span>
<span class="k">this</span><span class="p">.</span><span class="nx">id</span> <span class="o">=</span> <span class="nx">id</span><span class="p">;</span>
<span class="k">this</span><span class="p">.</span><span class="nx">msg</span> <span class="o">=</span> <span class="nx">msg</span><span class="p">;</span>
<span class="p">}</span>
<span class="nx">ko</span><span class="p">.</span><span class="nx">dependentObservable</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">lastQueryRequest</span><span class="p">)</span> <span class="k">this</span><span class="p">.</span><span class="nx">lastQueryRequest</span><span class="p">.</span><span class="nx">abort</span><span class="p">();</span>
<span class="kd">var</span> <span class="nx">action</span> <span class="o">=</span> <span class="err">#</span><span class="p">{</span><span class="nx">jsAction</span> <span class="err">@</span><span class="nx">Api</span><span class="p">.</span><span class="nx">search</span><span class="p">(</span><span class="s1">':query'</span><span class="p">)</span><span class="o">/</span><span class="p">}</span>
<span class="k">this</span><span class="p">.</span><span class="nx">lastQueryRequest</span> <span class="o">=</span> <span class="nx">$</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="nx">action</span><span class="p">({</span><span class="na">query</span> <span class="p">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">query</span><span class="p">()}),</span> <span class="kd">function</span><span class="p">(</span><span class="nx">data</span><span class="p">){</span>
<span class="k">if</span><span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">status</span> <span class="o">===</span> <span class="s1">'OK'</span><span class="p">){</span>
<span class="kd">var</span> <span class="nx">tweets</span> <span class="o">=</span> <span class="nx">$</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">result</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">item</span><span class="p">){</span><span class="k">return</span> <span class="k">new</span> <span class="nx">Tweet</span><span class="p">(</span><span class="nx">item</span><span class="p">.</span><span class="nx">id</span><span class="p">,</span> <span class="nx">item</span><span class="p">.</span><span class="nx">msg</span><span class="p">);});</span>
<span class="nx">viewModel</span><span class="p">.</span><span class="nx">tweets</span><span class="p">(</span><span class="nx">tweets</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">});</span>
<span class="p">},</span> <span class="nx">viewModel</span><span class="p">);</span>
<span class="nx">ko</span><span class="p">.</span><span class="nx">applyBindings</span><span class="p">(</span><span class="nx">viewModel</span><span class="p">);</span>
{/set}