Nesta postagem do blog, daremos uma olhada na API de visualização para pesquisa de texto completo em Couchbase 4.5. Observe que essa API, lançada na versão mais recente do Java SDK (2.2.4), ainda é @Experimental.
Abordaremos o assunto:
- Pesquisa de texto completo no Couchbase?
- A API Java
- Vários tipos de consultas
- Obtendo explicações sobre os acertos
- Conclusão
Essa API experimental pode ser usada com o Couchbase Server 4.5 Developer Preview, desde que você use a API 2.2.4 cliente Java SDK, que você pode obter por meio do Maven. Adicione a seguinte dependência ao seu pom.xml:
|
1 2 3 4 5 |
com.couchbase.client java-client 2.2.4 |
Pesquisa de texto completo no Couchbase?
Sim! O próximo 4.5 (codinome Watson) incluirá um indexador de texto completo (FTS, também conhecido como CBFT) baseado no software livre Bleve projeto. O Bleve trata de pesquisa de texto completo e indexação em Go (grito para o nosso próprio Marty Schoch por ter iniciado esse projeto).
A ideia é aproveitar o Bleve para fornecer uma pesquisa de texto completo pronta para uso no Couchbase Server, sem a necessidade de usar conectores para software externo (que é executado em seu próprio cluster). Se essa solução pronta para uso não atender totalmente às suas necessidades, é claro que você ainda poderá usar estes conectoresmas, para necessidades mais simples, você pode optar por uma única solução.
O FTS oferece uma série de recursos que são fornecidos pelo Bleve: Analisadores de texto, Tokenizers e filtros de token de pós-processamento que estão além do escopo desta postagem, bem como os vários tipos de consultas que você pode executar nos índices resultantes. Vamos ver quais são esses tipos e como você pode esperar usá-los no contexto do Java SDK.
No restante desta postagem do blog, usaremos três índices que você poderá criar por meio do console administrativo da Web no próximo 4.5 Developer Preview:


Aqui está a lista de índices na interface do usuário:

Nós temos:
- a
índice de cervejaque indexa todo o conteúdo de cada documento nobalde.amostra de cerveja - a
travelIndexque indexa todo o conteúdo de cada documento nobalde.amostra de viagem - um índice de alias,
commonIndexque é uma união dos dois índices acima.
A API Java
O ponto de entrada do recurso de pesquisa de texto completo no Java SDK está no Balde, usando o consulta(SearchQuery ftq) método. Isso é consistente com os métodos de consulta existentes já presentes na API para executar um Consulta ou um N1qlQuery.
A API para pesquisa de texto completo segue o padrão construtor padrão. Identifique o tipo de consulta que você deseja e use o construtor correspondente para construí-la, obtenha o Pesquisa usando construir() e executá-lo usando bucket.query(searchQuery).
Vamos dar um exemplo (muito simples) e ver como ele pode ser consumido:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
//we'll use that Cluster and Bucket for the remainder of the examples Cluster cluster = CouchbaseCluster.create("127.0.0.1"); Bucket bucket = cluster.openBucket("beer-sample"); //we use a simple form of query: SearchQuery ftq = MatchQuery.on("beerIndex").match("national").limit(3).build(); //we fire the query and look at results SearchQueryResult result = bucket.query(ftq); System.out.println("totalHits: " + result.totalHits()); for (SearchQueryRow row : result) { System.out.println(row); } |
Se analisarmos cada seção individualmente, veremos o que aconteceu:
- Criamos um
MatchQueryem um único período. - Ele é executado na amostra de cerveja (
.on(beerIndex), procura por ocorrências textuais da palavra "national" (.query("national")) ou termos próximos. - Uma configuração adicional é feita para limitar o número de resultados a 3 (
limite(3)) e a consulta real é criada nesse ponto (.build()). - A consulta é executada (
bucket.query(ftq)) e retorna umResultado da pesquisa. - Emitimos o resultado
totalHits()e linhas individuais (também acessíveis como uma lista por meio dehits()).
A execução desse código gera resultados:
|
1 2 3 4 |
totalHits: 31 SearchQueryHit{id='dc_brau', score=0.09068310490562362, fragments={}} SearchQueryHit{id='brouwerij_nacional_balashi', score=0.12085760187148556, fragments={}} SearchQueryHit{id='cervecera_nacional', score=0.09863195902067363, fragments={}} |
Vemos que o total de acessos nos dá o número real de acessos antes de o limite ser aplicado. O hits() O método retorna 3 SearchQueryRow objetos, conforme solicitado.
Cada acerto contém a chave do documento associado no Couchbase (id()), bem como mais informações sobre a correspondência, por exemplo, uma pontuação para a correspondência (pontuação())... Se quiser, você pode recuperar o documento associado usando bucket.get(row.id()):
|
1 2 3 4 5 6 |
result = bucket.query(ftq); System.out.println("totalHits: " + result.totalHits()); for (SearchQueryRow row : result) { System.out.println(row); System.out.println(bucket.get(row.id()).content()); } |
Isso nos dá, para o primeiro acerto:
|
1 2 3 |
SearchQueryHit{id='dc_brau', score=0.09068310490562362, fragments={}} {"country":"United States","website":"https://www.dcbrau.com/","code":"20018","address":["3178-B Bladensburg Rd. NE"],"city":"Washington","phone":"","name":"DC Brau", "description":"The first brewery to open in the nation's capital since Prohibition.","state":"DC","type":"brewery","updated":"2011-08-08 19:02:40"} |
Se observarmos atentamente o JSON do documento, perceberemos onde o documento provavelmente correspondeu. Na seção "descrição" do documento, há esta frase:
A primeira cervejaria a ser aberta na naçãodesde a Lei Seca.
Observe também que a consulta de texto procurou a palavra solicitada e palavras derivadas que têm a mesma raiz. Na verdade, ela aplicou uma imprecisão de 2 (consulte a próxima seção).
Esse padrão também pode ser aplicado a outros tipos de consultas, portanto, vamos dar uma olhada em mais algumas e ver que tipo de pesquisa pode ser realizada.
Vários tipos de consultas
Consulta Fuzzy
As consultas difusas podem ser realizadas com o MatchQuery, especificando um Distância de Levenshtein como o máximo fuzziness() para permitir o termo:
|
1 2 3 4 5 6 7 8 9 10 11 |
result = bucket.query(MatchQuery.on("beerIndex") .match("sammar") .field("name") .fuzziness(2) //actually the default .build()); System.out.println("nFuzzy Match Query"); System.out.println("totalHits (fuzziness = 2): " + result.totalHits()); for (SearchQueryRow row : result) { System.out.println(bucket.get(row.id()).content().get("name")); } |
Em uma imprecisão de 2Isso corresponde a palavras como "hammer" (martelo), "mamma" (mamãe) ou "summer" (verão):
|
1 2 3 4 5 |
Fuzzy Match Query totalHits (fuzziness = 2): 45 Mamma Mia! Pizza Beer Redhook Long Hammer IPA Summer Wheat |
Em uma imprecisão de 1não foi encontrada nenhuma correspondência:
|
1 2 |
Fuzzy Match Query totalHits (fuzziness = 1): 0 |
Um tipo de consulta dedicada à imprecisão e que não aplica nenhum analisador também é fornecido no FuzzyQuery.
Vários termos: MatchPhrase
Como vimos, MatchQuery é uma consulta baseada em termos que permite especificar opcionalmente a imprecisão e também aplica o mesmo filtro ao termo pesquisado que pode ter sido aplicado ao campo (por exemplo, stemming, etc.):
|
1 2 3 4 |
MatchQuery.on("beerIndex") .match("sesonal") .fuzziness(2) .field("description").build(); |
Você pode pesquisar vários termos em uma única consulta usando um Combinar frase consulta. Os termos são analisados e a imprecisão pode ser ativada opcionalmente:
|
1 |
MatchPhraseQuery.on("beerIndex").matchPhrase("summer seasonal").field("description"); |
Consulta Regexp
A RegexpQuery não faz apenas a correspondência literal, mas permite a correspondência usando uma expressão regular. Veja este exemplo:
|
1 2 3 4 5 6 7 8 9 10 |
result = bucket.query(RegexpQuery.on("beerIndex") .regexp("[tp]ale") .field("name") .build()); System.out.println("nRegexp Query"); System.out.println("totalHits: " + result.totalHits()); for (SearchQueryRow row : result) { System.out.println(bucket.get(row.id()).content().get("name")); } |
Observe que essa consulta visa um campo específico no json (campo("nome")). Queremos todos os nomes que contenham "tale" ou "pale". Aqui estão alguns nomes que correspondem a essa consulta:
|
1 2 3 4 5 |
Regexp Query totalHits: 408 Tall Tale Pale Ale Bard's Tale Beer Company Pale Ale |
Consulta de prefixo
A PrefixQuery procura ocorrências de palavras que começam com a cadeia de caracteres fornecida:
|
1 2 3 4 5 6 7 8 9 10 |
result = bucket.query(PrefixQuery.on("beerIndex") .prefix("weiss") .field("name") .build()); System.out.println("nPrefix Query"); System.out.println("totalHits: " + result.totalHits()); for (SearchQueryRow row : result) { System.out.println(bucket.get(row.id()).content().get("name")); } |
Mais uma vez, olhamos apenas para o interior do nome desta vez para palavras que começam com "weiss":
|
1 2 3 4 5 6 |
Prefix Query totalHits: 74 Bavarian-Weissbier Hefeweisse / Weisser Hirsch Münchner Kindl Weissbier / Münchner Weisse Franziskaner Hefe-Weissbier Hell / Franziskaner Club-Weiss Weissenheimer Wheat |
Consultas de intervalo e data
FTS também é bom com dados não textuais. Por exemplo, o NumericRangeQuery permite que você procure valores numéricos dentro de um intervalo fornecido:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
result = bucket.query(NumericRangeQuery.on("beerIndex") .min(3) .max(4) .field("abv") .fields("name", "abv") .build()); System.out.println("nNumeric Range Query"); System.out.println("totalHits: " + result.totalHits()); for (SearchQueryRow row : result) { JsonDocument doc = bucket.get(row.id()); System.out.println(""" + doc.content().get("name") + "", abv: " + doc.content().get("abv")); } |
Quais são as saídas:
|
1 2 3 4 5 |
Numeric Range Query totalHits: 62 "Stud Service Stout", abv: 3.1 "Blonde", abv: 3.0 "Locke Mountain Light", abv: 3.7 |
As datas também são cobertas com o DateRangeQuery:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
Calendar calendar = Calendar.getInstance(); calendar.set(2011, Calendar.MARCH, 1); Date start = calendar.getTime(); calendar.set(2011, Calendar.APRIL, 1); Date end = calendar.getTime(); result = bucket.query(DateRangeQuery.on("beerIndex") .start(start) .end(end) .field("updated") .fields("name", "updated") .build()); System.out.println("nDate Range Query"); System.out.println("totalHits: " + result.totalHits()); for (SearchQueryRow row : result) { JsonDocument doc = bucket.get(row.id()); System.out.println(""" + doc.content().get("name") + "", updated: " + doc.content().get("updated")); } |
Quais são as saídas:
|
1 2 3 4 5 6 |
Date Range Query totalHits: 4 "Dank", updated: 2011-03-16 09:06:54 "Oso", updated: 2011-03-16 09:05:15 "Summer Teeth", updated: 2011-03-08 12:22:14 "Columbus Brewing Company", updated: 2011-03-08 12:19:07 |
Consulta genérica
FTS também oferecem uma forma mais genérica de consulta que combina frases, termos e muito mais usando o Sintaxe de consulta de cadeia de caracteres. Isso pode ser acessado na API por meio do Consulta de cadeia de caracteres.
Combinação
Além disso, você pode combinar critérios simples como MatchQuery usando consultas combinadas. Usando essas duas consultas de termos simples:
|
1 2 |
MatchQuery bitterQuery = MatchQuery.on("beerIndex").match("bitter").field("description").build(); MatchQuery maltyQuery = MatchQuery.on("beerIndex").match("malty").field("description").build(); |
Você pode combiná-los de diferentes maneiras:
- a
conjunçãoprocura por todos os termos
|
1 |
ConjunctionQuery.on("beerIndex").conjuncts(bitterQuery, maltyQuery) |
- a
disjunçãoprocura pelo menos um termo
|
1 |
DisjunctionQuery.on("beerIndex").disjuncts(bitterQuery, maltyQuery) |
- a
consulta booleanapermite que você combine as duas abordagens
|
1 |
BooleanQuery.on("beerIndex").must(bitterQuery).mustNot(maltyQuery) |
Obtendo explicações sobre os acertos
Se você quiser obter insights sobre a pontuação e a correspondência de uma determinada SearchQueryRowvocê pode criar sua consulta usando o .explain(true) e obter detalhes do índice no parâmetro explicação() campo:
|
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 |
{"message":"sum of:","children":[{"message":"product of:","children":[{"message":"sum of:","children":[{"message":"product of:","children":[{"message":"sum of:","children":[ { "message": "weight(_all:national^1.000000 in penn_brewery-penn_marzen), product of:", "children": [ { "message": "queryWeight(_all:national^1.000000), product of:", "children": [ { "message": "boost", "value": 1 }, { "message": "idf(docFreq=17, maxDocs=7303)", "value": 7.005668743723945 }, { "message": "queryNorm", "value": 0.1427415478209491 } ], "value": 0.9999999999999999 }, { "message": "fieldWeight(_all:national in penn_brewery-penn_marzen), product of:", "children": [ { "message": "tf(termFreq(_all:national)=1", "value": 1 }, { "message": "fieldNorm(field=_all, doc=penn_brewery-penn_marzen)", "value": 0.10000000149011612 }, { "message": "idf(docFreq=17, maxDocs=7303)", "value": 7.005668743723945 } ], "value": 0.7005668848116544 } ], "value": 0.7005668848116543 } ],"value":0.7005668848116543},{"message":"coord(1/1)","value":1}],"value":0.7005668848116543}],"value":0.7005668848116543},{"message":"coord(1/1)","value":1}],"value":0.7005668848116543}],"value":0.7005668848116543} |
Conclusão
Esperamos que essa prévia da API tenha despertado seu interesse!
Vá em frente e faça o download do primeiro Visualização do Couchbase 4.5 para desenvolvedores com o serviço de pesquisa de texto completo incorporado. Esperamos que você possa começar a pesquisar rapidamente usando o serviço associado API do Java SDK.
E até lá... Boa codificação!
- A equipe do Java SDK