Explore o novo chatbot do Developer Center! O MongoDB AI chatbot pode ser acessado na parte superior da sua navegação para responder a todas as suas perguntas sobre o MongoDB .

Junte-se a nós no Amazon Web Services re:Invent 2024! Saiba como usar o MongoDB para casos de uso de AI .
Desenvolvedor do MongoDB
Central de desenvolvedor do MongoDBchevron-right
Produtoschevron-right
Atlaschevron-right

Desbloqueando a pesquisa semântica: crie um mecanismo de pesquisa de filmes baseado em Java com o Atlas Vector Search e o Spring Boot

Tim Kelly10 min read • Published Sep 18, 2024 • Updated Sep 18, 2024
Pesquisa vetorialJavaAtlas
Ícone do FacebookÍcone do Twitterícone do linkedin
Avalie esse Tutorial
star-empty
star-empty
star-empty
star-empty
star-empty
No mundo da tecnologia em rápida evolução, a busca por resultados de pesquisa mais relevantes, personalizados e intuitivos levou ao aumento da popularidade da pesquisa semântica.
O Vector Search do MongoDB permite que você pesquise seus dados relacionados semanticamente, tornando possível pesquisar seus dados por significado, não apenas por correspondência de palavras-chave.
Neste tutorial, analisaremos como podemos criar um aplicativo Spring Boot que possa executar uma pesquisa semântica em uma collection de filmes por suas descrições de trama.

O que precisaremos

Antes de começar, você precisará de algumas coisas.
  • Java 11 ou superior
  • Maven ou Gradle, mas este tutorial fará referência ao Maven
  • Uma contaOpenAI, para gerar nossas incorporações

Configure seu cluster do MongoDB

Visite o dashboard doMongoDB Atlas e configure seu cluster. Para aproveitar o operador$vectorSearch em um pipeline de agregação, você precisa executar o MongoDB Atlas 6.0.11 ou superior.
A seleção de sua versão do MongoDB Atlas está disponível na parte inferior da tela ao configurar seu cluster em "Configurações adicionais".
Tela de criação de cluster, configurações adicionais indicando que o número da versão é MongoDB 7.0.
Ao configurar sua implantação, você será solicitado a configurar um usuário de banco de dados e regras para sua conexão de rede.
Se precisar de mais ajuda para começar a usar o MongoDB Atlas, confira nosso tutorial.
Para este projeto, usaremos os dados de amostra fornecidos pelo MongoDB. Quando você fizer login pela primeira vez no painel, verá uma opção para carregar dados de exemplo em seu banco de dados.
Painel do MongoDB Atlas para um cluster vazio, solicitando o carregamento de dados.
Se você procurar no banco de dadossample_mflix, verá uma collection chamada embedded_movies. Esses documentos contêm um campo chamado plot_embeddings que contém uma matriz de números de ponto flutuante (nossos vetores). Esses números são sem sentido para nós quando vistos diretamente com o olho humano, mas nos permitirão realizar nossa busca semântica por filmes com base em seu enredo. Se você quiser incorporar seus próprios dados, poderá usar os métodos deste tutorial para fazer isso ou pode conferir o tutorial que mostra como configurar um trigger em seu banco de dados para incorporar automaticamente seus dados.

Crie um índice de pesquisa vetorial

Para usar o operador$vectorSearch em nossos dados, precisamos configurar um índice de Atlas Search apropriado. Selecione a aba "Atlas Search" no seu cluster e clique em "Criar Índice do Atlas Search".
Aba de pesquisa Cluster com botão para Criar Índice de Pesquisa.
Queremos escolher a "Opção JSON Editor" e clicar em "Avançar".
Criar tela de configuração de índice do Atlas Search, exibindo as opções Editor Visual ou Editor JSON .
Nesta página, vamos selecionar nosso banco de dados de destino, sample_mflix, e embedded_moviescollection, para este tutorial.
O nome não é muito importante, mas nomeie o índice - PlotVectorSearch, por exemplo - e copie o seguinte JSON.
1{
2 "fields": [{
3 "type": "vector",
4 "path": "plot_embedding",
5 "numDimensions": 1536,
6 "similarity": "euclidean"
7 }]
8}
insira a descrição da imagem aqui
Os campos especificam o nome do campo de incorporação em nossos documentos,plot_embedding, as dimensões do modelo usado para incorporar, 1536, e a função de similaridade a ser usada para encontrar os K-vizinhos mais próximos, dotProduct. É muito importante que as dimensões no índice correspondam às do modelo usado para incorporação. Esses dados foram incorporados usando o mesmo modelo que usaremos, mas outros modelos estão disponíveis e podem usar dimensões diferentes.
Confira nossa documentação do Vector Atlas Search para obter mais informações sobre essas definições de configuração.

Configurar um projeto do Spring Boot

Para configurar nosso projeto, vamos usar o Spring Initializr. Isso gerará o arquivopom.xml, que conterá as dependências do nosso projeto.
Para este projeto, você deseja selecionar as opções na captura de tela abaixo e criar um JAR:
Tela de configuração do Spring Initializr.
  • Projeto: Maven
  • Linguagem: Java
  • Dependências: Web reativa do Spring e MongoDB reativo dos dados do Spring
  • Gerar: JAR
Abra o projeto Maven no IDE de sua escolha e vamos escrever código!

Modelo do MongoDB

Antes de fazer qualquer coisa, abra seu arquivo pom.xml. Na seção de propriedades, você precisará adicionar o seguinte:
1<mongodb.version>4.11.0</mongodb.version>
Isso forçará sua API do Spring Boot a usar o 4.11. Versão 0 dos drivers do MongoDB Java. Sinta-se livre para usar uma versão mais atualizada para usar alguns dos recursos mais atualizados, como o métodovectorSearch(). Você também notará que, em todo este aplicativo, usamos os Reactive Streams do MongoDB Java. Isso ocorre porque estamos criando uma API assíncrona. Operações de AI, como gerar incorporações, podem ser intensivas em computação e consumir muito tempo. Uma API assíncrona permite que essas tarefas sejam processadas em segundo plano, liberando o sistema para lidar com outras solicitações ou operações simultaneamente. Agora, vamos codificar!
Para representar nosso documento em Java, usaremos objetos Java antigos simples(POJOs). Os dados que vamos manipular são os documentos dos dados de exemplo que você acabou de carregar em seu cluster. Para cada documento e subdocumento, precisamos de um POJO. Os documentos do MongoDB já têm muitas semelhanças com POJOs e são simples de configurar usando o driver MongoDB.
No documento principal, temos três subdocumentos: Imdb, Tomatoese Viewer. Assim, precisaremos de quatro POJOs para nosso documentoMovie.
Primeiro precisamos criar um pacote chamado com.example.mdbvectorsearch.model e adicionar nossa classe Movie.java.
Usamos o @BsonProperty("_id") para atribuir nosso campo_id no JSON para ser mapeado para o nosso campoId no Java, de modo a não violar as convenções de nomenclatura do Java.
1public class Movie {
2
3 @BsonProperty("_id")
4 private ObjectId Id;
5 private String title;
6 private int year;
7 private int runtime;
8 private Date released;
9 private String poster;
10 private String plot;
11 private String fullplot;
12 private String lastupdated;
13 private String type;
14 private List<String> directors;
15 private Imdb imdb;
16 private List<String> cast;
17 private List<String> countries;
18 private List<String> genres;
19 private Tomatoes tomatoes;
20 private int num_mflix_comments;
21 private String plot_embeddings;
22
23 // Getters and setters for Movie fields
24
25}
Adicione outra classe chamada Imdb.
1public static class Imdb {
2
3 private double rating;
4 private int votes;
5 private int id;
6
7 // Getters and setters for Imdb fields
8
9}
Ainda outro chamado Tomatoes.
1public static class Tomatoes {
2
3 private Viewer viewer;
4 private Date lastUpdated;
5
6 // Getters and setters for Tomatoes fields
7
8}
E, finalmente, Viewer.
1public static class Viewer {
2
3 private double rating;
4 private int numReviews;
5
6 // Getters and setters for Viewer fields
7
8}
Dica: Para criar os getters e setters, muitos IDEs têm atalhos.

Conecte-se ao seu banco de dados

Em seu arquivo principal, configure um pacote com.example.mdbvectorsearch.config e adicione uma classe, MongodbConfig.java. É aqui que nos conectaremos ao nosso banco de dados e criaremos e configuraremos nosso cliente. Se você está usado para usar o Spring Data MongoDB, muito disso geralmente é ofuscado. Estamos fazendo isso dessa forma para aproveitar alguns dos recursos mais recentes do driver Java do MongoDB para suportar vetores.
A partir da interface do MongoDB Atlas, obteremos nossa connection string e a adicionaremos ao nosso arquivoapplication.properties. Também especificaremos o nome do nosso banco de dados aqui.
1mongodb.uri=mongodb+srv://<username>:<password>@<cluster>.mongodb.net/
2mongodb.database=sample_mflix
Agora, em sua classeMongodbConfig, importe esses valores e denote isso como uma classe de configuração com a anotação @Configuration.
1@Configuration
2public class MongodbConfig {
3
4 @Value("${mongodb.uri}")
5 private String MONGODB_URI;
6
7 @Value("${mongodb.database}")
8 private String MONGODB_DATABASE;
Em seguida, precisamos criar um cliente e configurá-lo para lidar com a tradução de e para BSON para nossos POJOs. Aqui, configuramos um CodecRegistry para lidar com essas conversões e usamos um codec padrão, pois eles são capazes de lidar com os principais tipos de dados Java. Em seguida, envolvemos estes em um MongoClientSettings e criamos nosso MongoClient.
1 @Bean
2 public MongoClient mongoClient() {
3 CodecRegistry pojoCodecRegistry = CodecRegistries.fromRegistries(
4 MongoClientSettings.getDefaultCodecRegistry(),
5 CodecRegistries.fromProviders(
6 PojoCodecProvider.builder().automatic(true).build()
7 )
8 );
9
10 MongoClientSettings settings = MongoClientSettings.builder()
11 .applyConnectionString(new ConnectionString(MONGODB_URI))
12 .codecRegistry(pojoCodecRegistry)
13 .build();
14
15 return MongoClients.create(settings);
16 }
Nossa última etapa será então obter nosso banco de dados, e terminamos esta classe.
1 @Bean
2 public MongoDatabase mongoDatabase(MongoClient mongoClient) {
3 return mongoClient.getDatabase(MONGODB_DATABASE);
4 }
5}

Incorpore seus dados com a API OpenAI

Vamos enviar a solicitação fornecida do usuário para a API OpenAI para ser incorporada. Uma incorporação é uma série (vetor) de números de ponto flutuante. A distância entre dois vetores mede seu relacionamento. Pequenas distâncias sugerem alto parentesco e grandes distâncias sugerem baixo parentesco.
Isso transformará nosso prompt de linguagem natural, como "Toys that come to life when no one is looking", em uma grande array de números de ponto flutuante que se parecerão com esta [-0.012670076, -0.008900887, ..., 0.0060262447, -0.031987168].
Para fazer isso, precisamos criar alguns arquivos. Todo o nosso código para interagir com o OpenAI estará contido em nossa classeOpenAIService.java e Go para com.example.mdbvectorsearch.service. O @Service no topo de nossa classe determina ao Spring Boot que ela pertence a essa camada de serviço e contém lógica de negócios.
1@Service
2public class OpenAIService {
3
4 private static final String OPENAI_API_URL = "https://api.openai.com";
5
6 @Value("${openai.api.key}")
7
8 private String OPENAI_API_KEY;
9
10 private WebClient webClient;
11
12 @PostConstruct
13 void init() {
14 this.webClient = WebClient.builder()
15 .clientConnector(new ReactorClientHttpConnector())
16 .baseUrl(OPENAI_API_URL)
17 .defaultHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE)
18 .defaultHeader("Authorization", "Bearer " + OPENAI_API_KEY)
19 .build();
20 }
21
22
23 public Mono<List<Double>> createEmbedding(String text) {
24 Map<String, Object> body = Map.of(
25 "model", "text-embedding-ada-002",
26 "input", text
27 );
28
29 return webClient.post()
30 .uri("/v1/embeddings")
31 .bodyValue(body)
32 .retrieve()
33 .bodyToMono(EmbeddingResponse.class)
34 .map(EmbeddingResponse::getEmbedding);
35 }
36}
Usamos o Spring WebClient para fazer as chamadas para a API OpenAI. Em seguida, criamos as incorporações. Para fazer isso, passamos em nosso texto e especificamos nosso modelo de incorporação (por exemplo, text-embedding-ada-002). Você pode ler mais sobre as opções de parâmetro da API OpenAI em seus Docs.
Para passar e receber os dados da API Open AI, precisamos especificar nossos modelos para os dados que estão sendo recebidos. Vamos adicionar dois modelos ao nosso pacotecom.example.mdbvectorsearch.model , EmbeddingData.java e EmbeddingResponse.java.
1public class EmbeddingData {
2 private List<Double> embedding;
3
4 public List<Double> getEmbedding() {
5 return embedding;
6 }
7
8 public void setEmbedding(List<Double> embedding) {
9 this.embedding = embedding;
10 }
11}
1public class EmbeddingResponse {
2 private List<EmbeddingData> data;
3
4 public List<Double> getEmbedding() {
5 return data.get(0).getEmbedding();
6 }
7
8 public List<EmbeddingData> getData() {
9 return data;
10 }
11
12 public void setData(List<EmbeddingData> data) {
13 this.data = data;
14 }
15}

Seu pipeline de agregação vetorial do Atlas Search no Spring Boot

Nós temos nosso banco de dados. Podemos incorporar nossos dados. Estamos prontos para enviar e receber nossos documentos de cinema. Como realmente realizamos nossa semântica Atlas Search?
A camada de acesso a dados da implementação de nossa API ocorre no repositório. Crie um pacote com.example.mdbvectorsearch.repository e adicione a interface MovieRepository.java.
1public interface MovieRepository {
2 Flux<Movie> findMoviesByVector(List<Double> embedding);
3}
Agora, implementamos a lógica do nosso métodofindMoviesByVector na implementação desta interface. Adicione uma classe MovieRepositoryImpl.java ao pacote. Esse método implementa a lógica de dados de nosso aplicativo e faz a incorporação do texto inserido pelo usuário, incorporado usando a API OpenAI, em seguida, usa o estágio de agregação $vectorSearchem relação à nossa coleçãoembedded_movies, usando o índice que configuramos anteriormente.
1@Repository
2public class MovieRepositoryImpl implements MovieRepository {
3
4 private final MongoDatabase mongoDatabase;
5
6 public MovieRepositoryImpl(MongoDatabase mongoDatabase) {
7 this.mongoDatabase = mongoDatabase;
8 }
9
10 private MongoCollection<Movie> getMovieCollection() {
11 return mongoDatabase.getCollection("embedded_movies", Movie.class);
12 }
13
14 @Override
15 public Flux<Movie> findMoviesByVector(List<Double> embedding) {
16 String indexName = "PlotVectorSearch";
17 int numCandidates = 100;
18 int limit = 5;
19
20 List<Bson> pipeline = asList(
21 vectorSearch(
22 fieldPath("plot_embedding"),
23 embedding,
24 indexName,
25 numCandidates,
26 limit));
27
28 return Flux.from(getMovieCollection().aggregate(pipeline, Movie.class));
29 }
30}
Para a lógica de negócios do nosso aplicativo, precisamos criar uma classe de serviço. Crie uma classe chamada MovieService.java em nosso pacoteservice .
1@Service
2public class MovieService {
3
4 private final MovieRepository movieRepository;
5 private final OpenAIService embedder;
6
7 @Autowired
8 public MovieService(MovieRepository movieRepository, OpenAIService embedder) {
9 this.movieRepository = movieRepository;
10 this.embedder = embedder;
11 }
12
13 public Mono<List<Movie>> getMoviesSemanticSearch(String plotDescription) {
14 return embedder.createEmbedding(plotDescription)
15 .flatMapMany(movieRepository::findMoviesByVector)
16 .collectList();
17 }
18}
O métodogetMoviesSemanticSearch pegará a descrição do gráfico em linguagem natural do usuário, incorporará-a usando a API OpenAI, realizará uma pesquisa vetorial em nossa collectionembedded_movies e retornará os cinco resultados mais semelhantes.
Esse serviço pegará o texto inserido pelo usuário, incorporará-o usando a API OpenAI e, em seguida, usará o estágio de agregação $vectorSearchem relação à nossa coleçãoembedded_movies, usando o índice que definimos anteriormente.
Isso retorna um Mono envolvendo nossa lista de Movie objetos. Tudo o que resta agora é passar alguns dados e chamar nossa função.
Temos a lógica em nosso aplicativo. Agora, vamos transformá-lo em uma API! Primeiro, precisamos configurar nosso controlador. Isso nos permitirá receber a entrada do usuário para o nosso aplicativo. Vamos configurar um endpoint para receber a descrição do enredo do usuário e retornar os resultados da pesquisa semântica. Crie um pacote com.example.mdbvectorsearch.servicee adicione a classeMovieController.java.
1@RestController
2public class MovieController {
3
4 private final MovieService movieService;
5
6 @Autowired
7 public MovieController(MovieService movieService) {
8 this.movieService = movieService;
9 }
10
11 @GetMapping("/movies/semantic-search")
12 public Mono<List<Movie>> performSemanticSearch(@RequestParam("plotDescription") String plotDescription) {
13 return movieService.getMoviesSemanticSearch(plotDescription);
14 }
15}
Definimos um endpoint /movies/semantic-search que lida com solicitações de obtenção, captura plotDescription como um parâmetro de query e delega a operação Atlas Search para MovieService.
Você pode usar sua ferramenta favorita para testar os pontos de conexão da API, mas só vamos enviar um comando cURL.
1curl -X GET "http://localhost:8080/movies/semantic-search?plotDescription=A%20cop%20from%20china%20and%20cop%20from%20america%20save%20kidnapped%20girl"
Observação: usamos %20 para indicar espaços em nossa URL.
Aqui chamamos nossa API com a consulta "A cop from China and a cop from America save a kidnapped girl". Não há título, mas acho que é uma boa descrição de um determinado filme de ação/comédia estrelado por Jackie Chan e Chris Tucker. Aqui está uma versão ligeiramente abreviada do meu resultado. Vamos verificar nossos resultados!
1Movie title: Rush Hour
2Plot: Two cops team up to get back a kidnapped daughter.
3
4Movie title: Police Story 3: Supercop
5Plot: A Hong Kong detective teams up with his female Red Chinese counterpart to stop a Chinese drug czar.
6
7Movie title: Fuk sing go jiu
8Plot: Two Hong-Kong cops are sent to Tokyo to catch an ex-cop who stole a large amount of money in diamonds. After one is captured by the Ninja-gang protecting the rogue cop, the other one gets ...
9
10Movie title: Motorway
11Plot: A rookie cop takes on a veteran escape driver in a death defying final showdown on the streets of Hong Kong.
12
13Movie title: The Corruptor
14Plot: With the aid from a NYC policeman, a top immigrant cop tries to stop drug-trafficking and corruption by immigrant Chinese Triads, but things complicate when the Triads try to bribe the policeman.
Consideramos ahora do pico nossa melhor correspondência. Exatamente o que eu tinha em mente! Se a premissa ressoa com você, há alguns outros filmes de que você pode assistir.
Você mesmo pode testar isso alterando o plotDescription que temos no comando cURL.

Conclusão

Este tutorial percorreu as etapas abrangentes de criação de um aplicativo de pesquisa semântica usando MongoDB Atlas, OpenAI e Spring Boot.
A pesquisa semântica oferece uma pluralidade de aplicativos, que vão desde consultas sofisticadas de produtos em sites de e-commerce até recomendações de filmes personalizados. Este guia foi elaborado para equipá-lo com o essencial, abrindo caminho para seu próximo projeto.
Está pensando em integrar a pesquisa vetorial em seu próximo projeto? Confira este artigo — Como modelar seus documentos para pesquisa vetorial — para aprender como projetar seus documentos para pesquisa vetorial.
Principais comentários nos fóruns
Ainda não há comentários sobre este artigo.
Iniciar a conversa

Ícone do FacebookÍcone do Twitterícone do linkedin
Avalie esse Tutorial
star-empty
star-empty
star-empty
star-empty
star-empty
Relacionado
Artigo

5 Maneiras de reduzir custos com o MongoDB Atlas


Sep 11, 2024 | 3 min read
Tutorial

Using the Confluent Cloud With Atlas Stream Processing


Nov 19, 2024 | 5 min read
Início rápido

Como conectar o MongoDB Atlas ao Vercel usando a nova integração


Aug 05, 2024 | 4 min read
Tutorial

Sincronize seu aplicativo móvel com o MongoDB Atlas e o Google Cloud MySQL


Feb 08, 2024 | 6 min read
Sumário