As consultas SQL++ podem acessar os dados armazenados no cluster do Couchbase de várias maneiras. Há situações em que ter a lógica de negócios como parte de suas consultas de dados também pode ser benéfico. O SQL++ oferece suporte a isso com as Funções Definidas pelo Usuário (UDFs) que estão disponíveis desde Couchbase 7.0.
Nesta postagem do blog, criamos uma UDF em JavaScript que consulta pontos a partir da localização de um usuário, dinamicamente, usando SQL++. Além disso, realizamos os mesmos cálculos de distância com uma UDF em Python dentro do Serviço de análise.
Nosso caso de uso de consulta geoespacial
Nosso aplicativo gerará pontos de interesse geográfico a partir do nosso banco de dados que estão próximos à localização GPS do usuário, semelhante a serviços como o Google Maps, mostrado na captura de tela abaixo. Para este exemplo, usaremos o conjunto de dados de amostra de viagens que está disponível em um bucket de amostra fornecido pelo Couchbase.
Em particular, estamos interessados em ver o marcos históricos e aeroportos em torno da localização atual do usuário. Isso não pode ser feito usando uma consulta SQL++ diretamente, pois o cálculo da distância é baseado na localização geográfica em tempo real do usuário. O SQL++ suporta a definição de UDFs em JavaScript para executar a lógica personalizada nas consultas.

Cálculo de distâncias a partir de coordenadas de GPS
Há muitas maneiras de calcular a distância entre dois conjuntos de coordenadas de GPS. Neste exemplo, calcularemos a distância usando a função Fórmula Haversine. Ele fornece a distância aproximada entre duas coordenadas de GPS, considerando o caminho como uma esfera em vez de uma distância em linha reta.
O código JavaScript para calcular distâncias geográficas é mostrado neste exemplo:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
função degreesToRadians(graus) { retorno graus * Matemática.PI / 180; } função distânciaEmKmEntreCoordenadasTerrestres(lat1, lon1, lat2, lon2) { var EarthRadiusKm = 6371; var dLat = degreesToRadians(lat2-lat1); var dLon = degreesToRadians(lon2-lon1); lat1 = degreesToRadians(lat1); lat2 = degreesToRadians(lat2); var a = Matemática.pecado(dLat/2) * Matemática.pecado(dLat/2) + Matemática.pecado(dLon/2) * Matemática.pecado(dLon/2) * Matemática.cos(lat1) * Matemática.cos(lat2); var c = 2 * Matemática.atan2(Matemática.quadrado(a), Matemática.quadrado(1-a)); retorno EarthRadiusKm * c; } |
Definimos duas funções JavaScript - uma que realiza a conversão entre graus e radianos e outra que calcula a distância em quilômetros entre as coordenadas de GPS da outra função.
Importação de UDFs para o Couchbase
Essas funções JavaScript agora podem ser importadas para o Couchbase usando o API RESTcomo mostrado abaixo, com o enrolar comando:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
enrolar -v -X POST http://localhost:8093/avaliador/v1/bibliotecas/matemática -u <usuário>:<senha> -d 'função degreesToRadians(graus) { retorno graus * Matemática.PI / 180; } função distânciaEmKmEntreCoordenadasTerrestres(lat1, lon1, lat2, lon2) { var EarthRadiusKm = 6371; var dLat = degreesToRadians(lat2-lat1); var dLon = degreesToRadians(lon2-lon1); lat1 = degreesToRadians(lat1); lat2 = degreesToRadians(lat2); var a = Matemática.sin(dLat/2) * Matemática.sin(dLat/2) + Matemática.sin(dLon/2) * Matemática.sin(dLon/2) * Matemática.cos(lat1) * Matemática.cos(lat2); var c = 2 * Matemática.atan2(Matemática.sqrt(a), Matemática.sqrt(1-a)); retorno EarthRadiusKm * c; }' |
Após essa etapa, o UDF pode ser definido na seção Editor de consultas:
|
1 2 3 |
CRIAR FUNÇÃO degreesToRadians(a) LÍNGUA JAVASCRIPT AS "degreesToRadians" AT "matemática" CRIAR FUNÇÃO distânciaEmKmEntreCoordenadasTerrestres(lat1, lon1, lat2, lon2) LÍNGUA JAVASCRIPT AS "distânciaEmKmEntreCoordenadasTerrestres" AT "matemática" |
Aqui, o matemática referência é para o JavaScript biblioteca que criamos para avaliar o UDF.
O UDF agora pode ser testado com coordenadas de GPS de amostra no SQL++ usando o Executar Função conforme mostrado abaixo
|
1 |
EXECUTAR FUNÇÃO distânciaEmKmEntreCoordenadasTerrestres(51.5, 0, 38.8, -77.1) |
Podemos observar que a função funciona como pretendido quando fornecemos as coordenadas de GPS manualmente para a função.
Conectando o UDF aos dados do Couchbase
No amostra de viagem conjunto de dados, temos as coordenadas de GPS do marcos históricos e aeroportos juntamente com outros locais de interesse, como hotéis.
Podemos integrá-los em nossas consultas, como a que está abaixo:
|
1 2 3 4 5 6 |
SELECIONAR distânciaEmKmEntreCoordenadasTerrestres(a.geo.lat, a.geo.solitário, 51.509865, -0.118092) AS distância, a.nome do aeroporto, a.cidade DE `viagens-amostra`.inventário.aeroporto a ORDEM BY distância ASC LIMITE 10; |
Essa consulta retorna uma lista dos dez aeroportos mais próximos da localização do usuário (51.509865, -0.118092). Fornecemos a latitude (a.geo.lat) e longitude (a.geo.lon) que estão incorporados nos documentos usando o poder do SQL++.
|
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 51 52 |
[ { "nome do aeroporto": "Todos os aeroportos", "cidade": "Londres", "distância": 0.6998675034052988 }, { "nome do aeroporto": "Waterloo International", "cidade": "Londres", "distância": 0.7880158040048914 }, { "nome do aeroporto": "Londres St Pancras", "cidade": "Londres", "distância": 2.289359875405007 }, { "nome do aeroporto": "Estação de Euston", "cidade": "Londres", "distância": 2.30782110865356 }, { "nome do aeroporto": "Estação de trem de St Pancras", "cidade": "Londres", "distância": 2.582290289682563 }, { "nome do aeroporto": "Estação de Paddington", "cidade": "Londres", "distância": 4.069442660124984 }, { "nome do aeroporto": "Heliporto de Londres", "cidade": "Londres", "distância": 6.062824964656381 }, { "nome do aeroporto": "Elstree", "cidade": "Elstree", "distância": 8.735152174563803 }, { "nome do aeroporto": "Cidade", "cidade": "Londres", "distância": 12.009592036043564 }, { "nome do aeroporto": "Londres - Kings Cross", "cidade": "Londres", "distância": 16.891716659500467 } ] |
Da mesma forma, podemos calcular os pontos de interesse ao redor do usuário usando a função marco histórico em uma distância cada vez maior do usuário:
|
1 2 3 4 5 6 7 |
SELECIONAR distânciaEmKmEntreCoordenadasTerrestres(l.geo.lat, l.geo.solitário, 51.509865, -0.118092) AS distância, l.atividade, l.cidade, l.conteúdo DE `viagens-amostra`.inventário.marco histórico l ORDEM BY distância ASC LIMITE 10; |
Resultados da consulta mostrando pontos de referência próximos:
|
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 51 52 53 54 55 56 57 58 59 60 61 62 |
[ { "atividade": "ver", "cidade": "Londres", "content" (conteúdo): "A Somerset House abriga três galerias de arte: O excepcional '''Courtauld Institute''' exibe uma coleção de arte dos séculos XIX e XX, incluindo obras famosas de Degas, Matisse e Kandinsky; a The Gilbert Collection apresenta uma coleção de arte decorativa; e a The Hermitage Rooms, a mais recente adição à Somerset House, abriga exposições temporárias de obras emprestadas pelo Museu Hermitage de São Petersburgo. O pátio central é repleto de fontes no verão, mas no inverno é instalada uma pista de patinação no gelo, que é muito popular, portanto, os visitantes devem reservar com antecedência ou esperar muito tempo para patinar.", "distância": 0.10940067520415872 }, { "atividade": "ver", "cidade": "Londres", "content" (conteúdo): "A Agulha de Cleópatra teve origem na antiga cidade [[Egito]]iana do [[Cairo/Heliópolis|Heliópolis]], no Templo de Atum, mas os romanos a transferiram para [[Alexandria]] em 12 a.C. Em 1819, o vice-rei Mehemet Ali presenteou os britânicos com a Agulha de Cleópatra, comemorando as vitórias militares no Egito, mas ela permaneceu em Alexandria até 1877, quando foi providenciado um transporte para levá-la a Londres. Durante a viagem, o navio virou em uma tempestade, matando seis tripulantes. Pensava-se que a Agulha de Cleópatra estava perdida, mas arrastões espanhóis a encontraram flutuando alguns dias depois e, após alguns reparos, ela chegou a Londres em 21 de janeiro de 1878. O obelisco é ladeado por duas falsas esfinges, que mostram os efeitos dos bombardeios em Londres durante a Segunda Guerra Mundial. Hoje, a Agulha de Cleópatra apresenta algum desgaste devido à exposição ao clima úmido de Londres.", "distância": 0.2153782246329736 }, { "atividade": "comprar", "cidade": "Londres", "content" (conteúdo): "Venda diária de livros de segunda mão perto da margem do Tâmisa. Um lugar agradável para simplesmente procurar livros (clássicos e modernos), mapas e gravuras.", "distância": 0.329776385402355 }, { "atividade": "ver", "cidade": "Londres", "content" (conteúdo): "[[Londres]] (em [[Londres/Covent Garden|Covent Garden]])", "distância": 0.34889537479151833 }, { "atividade": "ver", "cidade": "Londres", "content" (conteúdo): "A área de skate mais famosa e popular de Londres, situada parcialmente embaixo do Queen Elizabeth Hall, ao longo da Queen's Walk e do Tâmisa. Também é popular entre grafiteiros, praticantes de BMX e assim por diante.", "distância": 0.36487940944981834 }, { "atividade": "ver", "cidade": "Londres", "content" (conteúdo): "Escondido sob a ponte Waterloo, o BFI Southbank, anteriormente conhecido como National Film Theatre, se apresenta como a casa do cinema e tem três telas que exibem filmes clássicos (inclusive mudos), de língua estrangeira e menos populares. Recentemente reformado, ele agora tem uma nova entrada na Theatre Alley, uma loja dedicada a filmes, um espaço de exposição interativo e um excelente bar/restaurante na parte de trás. Os visitantes também podem acessar a Mediatheque - percorrer gratuitamente o extenso arquivo de filmes e TV do BFI. Bar e restaurante bacanas. Os ingressos geralmente estão disponíveis na hora.", "distância": 0.378692262237853 }, { "atividade": "ver", "cidade": "Londres", "content" (conteúdo): "Local de música com apresentações diárias. | image=Queen Elizabeth Hall.jpg", "distância": 0.3859430181613397 }, { "atividade": "bebida", "cidade": "Londres", "content" (conteúdo): "O antídoto para os bares gays: uma atmosfera de pub e ótima música. | image=The Retro Bar.jpg", "distância": 0.39732030942983415 }, { "atividade": "ver", "cidade": "Londres", "content" (conteúdo): "Três grandes auditórios, o Olivier, o Lyttelton e o Cottesloe. O teatro Olivier é o maior, com um palco aberto e um auditório em forma de leque ao redor. Isso garante que todos os assentos tenham uma boa visão. A maioria das produções mais "populares" é apresentada nesse teatro, pois o espaço oferecido é muito maior do que o da maioria dos teatros. O teatro Lyttelton é mais tradicional, com um arco de procênio com boas vistas da maioria dos assentos. O Cottesloe é um pequeno teatro estúdio, com cerca de 400 lugares. Alguns ingressos estarão disponíveis no dia, seja para assentos diurnos (chegue antes das 09:30 e faça fila) ou em espera (chegue antes das 18:00 e faça fila), ou você pode comprar on-line. Os espetáculos mais populares, especialmente os que acontecem perto do Natal no Olivier, esgotam com meses de antecedência. Os ingressos para o National Theatre são geralmente mais baratos do que os da maioria dos outros teatros. Há também a temporada "£10 Travelex" no verão, que oferece um grande número de ingressos (mais de 100.000 assentos por ano) a £10. É necessário fazer reserva com antecedência para esses espetáculos. Há também um espaço para exposições no saguão do teatro Lyttelton, que frequentemente abriga exposições fotográficas populares. O jazz gratuito é tocado com frequência à noite no foyer do piso térreo. Durante o verão, há um festival diário gratuito ao ar livre de apresentações, música, comédia e cabaré, conhecido como Watch This Space. São fornecidas espreguiçadeiras (e grama artificial) para assistir. Exposições gratuitas. Passeios pelos bastidores £5. (http://www.nationaltheatrelondon.com/tickets/)", "distância": 0.42625112040817054 }, { "atividade": "bebida", "cidade": "Londres", "content" (conteúdo): "Eventos musicais gratuitos todas as noites. O melhor lugar para experimentar música eletrônica underground, indie, dub-step e muito mais.", "distância": 0.4323026974543284 } ] |
Se executarmos essas consultas para vários usuários ao mesmo tempo, poderemos ter problemas de desempenho, pois estamos usando os recursos de computação que fazem parte do cluster do Couchbase.
Nesses cenários, o Couchbase Analytics pode reduzir o impacto em seu cluster. O Couchbase Analytics foi projetado para executar com eficiência consultas complexas em muitos registros. As consultas complexas podem incluir grandes operações ad hoc de junção, conjunto, agregação e agrupamento - qualquer uma delas pode resultar em consultas de longa duração, alto uso da CPU, alto consumo de memória ou latência excessiva da rede devido à busca de dados e à coordenação entre nós.
Funções definidas pelo usuário com o Couchbase Analytics
O Couchbase Analytics nos permite definir e usar Funções definidas pelo usuário em Python mas, no momento da redação deste documento, exige que você ativar o Developer Preview no Couchbase Server. Isso pode ser feito com o seguinte comando:
|
1 2 |
$ /optar/couchbase/caixa/couchbase-cli ativar-desenvolvedor-visualização \ --ativar -c localhost:8091 -u <nome de usuário> -p <senha> |
A próxima etapa é criar um pacote Python, localmente, no ambiente de desenvolvimento com o Python UDF. Nesse caso, o UDF é um método para calcular a distância entre duas coordenadas de GPS.
|
1 2 3 4 5 6 7 |
# distance.py de geopia importação distância classe cálculo de distância: def calculate_distance(autônomo, lat1, lon1, lat2, lon2) -> flutuante: """Calcular a distância usando a distância geodésica""" retorno distância.distância((lat1, lon1), (lat2, lon2)).km |
Aqui, calculamos o distância geodésica (a distância mais curta entre pontos ao longo de um caminho curvo) entre as duas coordenadas de GPS com a ajuda de uma biblioteca, geopia.
Empacotamento do UDF
Para empacotar a biblioteca, usamos um pacote shiv que pode empacotar o código junto com seus requisitos para qualquer plataforma. Aqui, estamos usando o Linux, pois o Couchbase Server está sendo executado em um ambiente Linux dentro do Docker.
|
1 |
shiv -o distância.pyz --local-pacotes . --plataforma manylinux2010_x86_64 --python-versão 39 --somente-binário=:todos: geopia |
Para fazer upload desse pacote binário com o UDF para o Couchbase, precisamos usar o comando API REST para o serviço do Analytics.
|
1 |
enrolar -X POST -u <nome de usuário>:<senha> -F "type=python" -F "data=@./distance.pyz" localhost:8095/análises/biblioteca/Padrão/pylib |
Isso faz o upload do UDF empacotado para o pylib na biblioteca padrão escopo (anteriormente universo de dados) do ambiente do Analytics. Agora podemos definir o UDF na seção Workbench de análise.
Essa declaração de definição indica que estamos definindo um UDF chamado distância_em_km que pode ser chamado a partir da função Python calculate_distance definido na classe cálculo de distância dentro do módulo Python distância.
|
1 2 |
CRIAR ANALÍTICA FUNÇÃO distância_em_km(lat1, lon1, lat2, lon2) AS "distância", "cálculo de distância.calculate_distance" AT pylib; |
Agora podemos usar o UDF em nossas consultas do Analytics da mesma forma que usamos o UDF em nossas consultas do SQL++.
|
1 2 3 4 5 6 7 8 |
SELECIONAR distância_em_km(51.5, 0, 38.8, -77.1) AS "distância" RESULTADOS: [ { "distância": 5933.5299530300545 } ] |
Mapeamento de dados do Data Service no Analytics Service
Para consultar os dados no Data Service a partir do Analytics Service, precisamos mapa o amostra de viagem coleções de dados no Analytics que cria uma cópia sombra em tempo real dos dados no Data Service no Analytics. Para este exemplo, precisamos mapear as coleções com dados geográficos, ou seja, o ponto de referência, aeroporto e hotel coleções do escopo do inventário no amostra de viagem balde.
Execução do UDF do Analytics nos dados do Couchbase
Agora, podemos executar as mesmas consultas que estávamos executando antes no SQL++, mas com o Analytics Service. Apenas o nome do UDF foi alterado. O restante da interface é semelhante ao que tínhamos com as consultas do SQL++. Os resultados também serão semelhantes aos resultados anteriores.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
SELECIONAR distância_em_km(a.geo.lat, a.geo.solitário, 51.509865, -0.118092) AS distância, a.nome do aeroporto, a.cidade DE `viagens-amostra`.inventário.aeroporto a ORDEM BY distância ASC LIMITE 10; SELECIONAR distância_em_km(l.geo.lat, l.geo.solitário, 51.509865, -0.118092) AS distância, l.atividade, l.cidade, l.conteúdo DE `viagens-amostra`.inventário.marco histórico l ORDEM BY distância ASC LIMITE 10; |
Essa abordagem é adequada quando queremos executar essas consultas sem afetar o Serviço de dados que é comumente usado para nossos dados transacionais. Os dados são sincronizados entre o Dados e o Serviço de análise internamente em tempo real no Couchbase.
Resumo
Nesta postagem do blog, você aprendeu a criar uma função definida pelo usuário (UDF) em JavaScript que calcula as distâncias entre duas coordenadas de GPS. Você viu como importar a UDF para o Couchbase e, em seguida, integrá-la a uma consulta SQL++ para alimentar aplicativos que poderiam fornecer pontos de interesse em torno de um usuário. Também mostramos como você pode executar o mesmo cálculo de distância em um UDF baseado em Python usando o serviço Analytics para reduzir o impacto no cluster transacional do Couchbase.
Para leitura e referência adicionais, consulte os seguintes recursos: