Aprenda a criar soluções de pesquisa de varejo aprimoradas por IA com MongoDB e Databricks
Francesco Baldissera, Ashwin Gangadhar, Vittal Pai14 min read • Published Sep 18, 2024 • Updated Sep 18, 2024
Avalie esse Tutorial
No cenário de varejo em rápida evolução, as empresas estão constantemente buscando maneiras de otimizar as operações, melhorar a experiência do cliente e ficar à frente da concorrência. Uma das principais estratégias para conseguir isso é aproveitar as oportunidades que as experiências de busca oferecem.
Imagine o seguinte: você entra em uma loja de departamentos cheia de produtos e tem algo específico em mente. Você quer uma experiência de compra rápida e perfeita — é aqui que as exibições de produtos desempenham um papel fundamental. No mundo digital do comércio eletrônico, a funcionalidade de pesquisa do seu site deve ser uma ferramenta facilitadora para exibir com eficiência o que os usuários estão procurando.
Surpreendentemente, estatísticas revelam que apenas cerca de 50% das pesquisas em sites de varejo produzem os resultados que os clientes procuram. Pense nisso – metade do tempo, os clientes com uma forte intenção de compra ficam sem resposta para suas perguntas.
O componente de pesquisa do seu site de comércio eletrônico não é apenas um recurso; ele é a ponte entre os clientes e os produtos que eles desejam. Aprimorar a lógica do mecanismo de pesquisa com inteligência artificial é a melhor maneira de garantir que a ponte seja robusta.
Neste artigo, exploraremos como o MongoDB e o Databricks podem ser integrados para fornecer soluções robustas para o setor de varejo, com um foco específico no processadorMongoDB Apache Spark Streaming ; orquestração com fluxos de trabalho do Databricks; transformação e funcionalidade de dados com oMLFlow e as funções definidas pelo usuário do Spark; e criando um índice de catálogo de produtos, classificação, classificação e preenchimento automático com o Atlas Search.
Vamos começar!
Um sistema moderno apoiado pelo e-commerce deve ser capaz de agrupar dados de várias fontes em tempo real, bem como de carregamentos em lote, e transformar esses dados em um esquema sobre o qual um índice de pesquisa Lucene possa ser criado. Isso permite a descoberta do inventário adicionado.
A solução deve integrar dados de eventos de comportamento do cliente do site em tempo real para alimentar um “intelligence layer” que criará os critérios para exibir e solicitar os produtos mais interessantes em termos de relevância para o cliente e relevância para o negócio.
Esses recursos são bem capturados na arquitetura de e-commerce mencionada acima. Vamos dividir isso em quatro estágios ou camadas diferentes:
- Ingestão de streaming multilocatário: com a ajuda do MongoDB Kafka Connector, podemos sincronizar dados em tempo real de várias fontes para o MongoDB. Por uma questão de simplicidade, neste tutorial, não vamos nos concentrar neste estágio.
- Atlas Stream Processing: com a ajuda do MongoDB Spark Connector e dos trabalhos e blocos de anotações do Databricks, podemos consumir dados e transformá-los para criar recursos de modelo de machine learning.
- Modelagem AI/ML: Todos os fluxos de dados gerados são transformados e escritos em uma visão unificada em uma coleção do MongoDB chamada catálogo, que é usada para construir índices de pesquisa e dar suporte à query e descoberta de produtos.
- Construindo a lógica de pesquisa: com a ajuda dos recursos do Atlas Search e dos canais de agregação robustos, podemos potencializar recursos como pesquisa/descoberta, hiperpersonalização e classificação de recursos em aplicativos móveis/web.
Antes de executar o aplicativo, você precisará ter os seguintes itens instalados em seu sistema:
Neste tutorial, vamos nos concentrar em explicar como orquestrar diferentes pipelines ETL em tempo real usando o Databricks Jobs. Um trabalho do Databricks representa uma execução única e autônoma de um bloco de anotações, script ou tarefa do Databricks. Ele é usado para executar código ou análises específicas em um horário agendado ou em resposta a um evento.
Nossa solução Atlas Search destina-se a responder a eventos em tempo real que ocorrem em uma loja de e-commerce, para que a experiência do Atlas Search para um cliente possa ser personalizada e fornecer resultados que atendam a dois critérios:
- Relevante para o cliente: definiremos uma pontuação estática que compreende dados comportamentais (registros de cliques) e um status Disponível para Promise , para que os resultados do Atlas Search sejam produtos que garantimos que estão disponíveis e relevantes com base na demanda anterior.
- Relevante para a empresa: Os resultados serão pontuados com base em quais produtos são mais sensíveis ao preço, de modo que a maior elasticidade de preço significa que eles aparecem primeiro na página da lista de produtos e como resultados de pesquisa. Também calcularemos um preço sugerido ideal para o produto.
Então, vamos verificar como configurar esses processos ETL em blocos de anotações Databricks e ordená-los usando tarefas do Databricks para, em seguida, alimentar nossas coleções do MongoDB com a inteligência que usaremos para criar nossa experiência de pesquisa.
Começaremos explicando como configurar notebooks no Databricks. Os notebooks são uma ferramenta fundamental para ciência de dados e aprendizado de máquina, permitindo colaboração, coautoria em tempo real, controle de versão e visualização de dados integrada. Você também pode torná-los parte de tarefas automatizadas, chamadas de trabalhos no Databricks. Uma série de trabalhos são chamados de fluxos de trabalho. Seus blocos de anotações e fluxos de trabalho podem ser anexados a recursos de computação que você pode configurar conforme sua conveniência, ou podem ser executados por meio de autoscale.
Você pode encontrar nossos arquivos de configuração JSONda primeira tarefa em nosso GitHub. Nesses arquivos JSON, especificamos os diferentes parâmetros sobre como executar os vários trabalhos em nosso cluster Databricks. Especificamos parâmetros diferentes, como o usuário, notificações por e-mail, detalhes da tarefa, informações do cluster e configurações de notificação para cada tarefa do trabalho. Essa configuração é usada para automatizar e gerenciar tarefas de processamento e análise de dados dentro de um ambiente especificado.
Agora, sem mais delongas, vamos começar com nosso primeiro fluxo de trabalho, o “Catalog collection indexing workflow.”
O diagrama acima mostra como nossa solução executará dois trabalhos diferentes, intimamente relacionados entre si, em dois notebooks separados. Vamos descompactar esta tarefa com o código e sua explicação:
A primeira parte do script do seu bloco de anotações é onde você definirá e instalará pacotes diferentes. No código abaixo, temos todos os pacotes necessários, mas os principais —
pymongo
e tqdm
— são explicados abaixo:- O PyMongo é comumente usado em aplicativos Python que precisam armazenar, recuperar ou analisar dados armazenados no MongoDB, especialmente em aplicativos web, pipelines de dados e projetos analíticos.
- O tqdm costuma ser usado em scripts ou aplicativos Python em que há necessidade de fornecer um feedback visual aos usuários sobre o progresso de uma tarefa.
1 %pip install pymongo tqdm 2 3 4 import pandas as pd 5 import json 6 from collections import Counter 7 from tqdm import tqdm 8 from pymongo import MongoClient 9 from pyspark.sql import functions as F 10 from pyspark.sql import types as T 11 from pyspark.sql import Window 12 import pyspark 13 from pyspark import SparkContext 14 from pyspark.sql import SparkSession 15 conf = pyspark.SparkConf() 16 17 18 import copy 19 import numpy as np 20 21 22 tqdm.pandas() 23 24 MONGO_CONN = 'mongodb+srv://:@retail-demo.2wqno.mongodb.net/?retryWrites=true&w=majority'
O script lê fluxos de dados de várias coleções MongoDB utilizando o spark.readStream.format("mongodb") .
Para cada collection, são definidas configurações específicas, como o URI de conexão do MongoDB, o nome do banco de dados, o nome da collection e outras opções relacionadas a change stream e aggregation pipelines.
O trecho abaixo é a continuação do código acima. Pode ser colocado em uma célula diferente do mesmo notebook.
1 atp = spark.readStream.format("mongodb").\ option('spark.mongodb.connection.uri', MONGO_CONN).\ option('spark.mongodb.database', "search").\ option('spark.mongodb.collection', "atp_status_myn").\ option('spark.mongodb.change.stream.publish.full.document.only','true').\ option('spark.mongodb.aggregation.pipeline',[]).\ option("forceDeleteTempCheckpointLocation", "true").load()
Neste caso específico, o código está lendo da collection atp_status. Ela especifica opções para a conexão do MongoDB, incluindo o URI, e permite a captura do documento completo quando ocorrem alterações na collection do MongoDB. O pipeline de agregação vazio indica que nenhuma transformação específica é aplicada nesta etapa.
Seguindo para a próxima etapa do trabalho para a coleção atp_status, podemos dividir o trecho de código em três partes diferentes:
Depois de ler os fluxos de dados, descartamos o campo
_id
. Este é um campo especial que serve como chave primária para um documento dentro de uma coleção. Cada documento em uma collection do MongoDB deve ter um campo _id exclusivo, que o distingue de todos os outros documentos na mesma collection. Como vamos criar uma nova collection, precisamos descartar o campo _id anterior dos documentos originais e, quando o inserirmos em uma nova collection, um novo campo _id será atribuído.1 atp = atp.drop("_id")
Os fluxos de dados transformados são gravados de volta no MongoDB usando o método writeStream.format("mongodb").
Os dados são gravados na coleção catálogo_myn no banco de dados de pesquisa.
Configurações específicas são definidas para cada operação de gravação, como o URI de conexão do MongoDB, o nome do banco de dados, o nome da collection e outras opções relacionadas a upserts, pontos de verificação e modos de saída.
O trecho de código abaixo é uma continuação do notebook acima.
1 atp.writeStream.format("mongodb").\ option('spark.mongodb.connection.uri', MONGO_CONN).\ option('spark.mongodb.database', "search").\ option('spark.mongodb.collection', "catalog_myn").\ option('spark.mongodb.operationType', "update").\ option('spark.mongodb.upsertDocument', True).\ option('spark.mongodb.idFieldList', "id").\
Os locais dos pontos de verificação são especificados para cada operação de gravação. Os pontos de controle são usados para manter o estado das operações de streaming, permitindo a recuperação em caso de falhas. Os checkpoints são armazenados no diretório /tmp/ com subdiretórios específicos para cada collection.
Aqui está um exemplo de checkpointing. Ele está incluído no script logo após o código acima.
1 option("forceDeleteTempCheckpointLocation", "true").\ option("checkpointLocation", "/tmp/retail-atp-myn4/_checkpoint/").\ outputMode("append").\ start()
O trecho completo de código executa diferentes transformações de dados para as várias collection que estamos ingerindo no Databricks, mas todas seguem o mesmo padrão de ingestão, transformação e reescrita de volta ao MongoDB. Não deixe de conferir o primeiro bloco de notas de indexação completo.
Para a segunda parte do trabalho de indexação, usaremos uma função definida pelo usuário (UDF) em nosso código para incorporar nossos dados de catálogo de produtos usando um modelo de transformadores. Isso é útil para criar recursos de pesquisa vetorial.
Este é um exemplo de como definir uma função definida pelo usuário. Você pode definir suas funções no início do seu notebook para que possa reutilizá-las posteriormente para executar suas transformações de dados ou cálculos de análise. Neste caso, estamos usando-o para incorporar dados de texto de um documento.
O decorador'@F.udf()' é usado para definir uma função definida pelo usuário no PySpark usando o objeto F, que é um alias para o módulo pyspark.sql.functions. Nesse caso específico, ele está definindo uma UDF chamada 'get_vec' que recebe um único argumento text e retorna o resultado da chamada 'model.encode(text)'.
O código abaixo é uma continuação do mesmo notebook.
1 @F.udf() def get_vec(text): 2 return model.encode(text)
Nosso código de notebook continua com trechos semelhantes aos exemplos anteriores. Usaremos o MongoDB Connector para Spark para ingestão de dados da coleção de catálogo criada anteriormente.
1 catalog_status = spark.readStream.format("mongodb").\ option('spark.mongodb.connection.uri', MONGO_CONN).\ option('spark.mongodb.database', "search").\ option('spark.mongodb.collection', "catalog_myn").\ option('spark.mongodb.change.stream.publish.full.document.only','true').\ option('spark.mongodb.aggregation.pipeline',[]).\ option("forceDeleteTempCheckpointLocation", "true").load()
Em seguida, ele executa transformações de dados no DataFrame catalog_status, incluindo a adição de uma nova coluna, o atp_status que agora é um valor booleano, 1 para disponível e 0 para indisponível. Isso é útil para que possamos definir a lógica comercial dos resultados da pesquisa, mostrando apenas os produtos disponíveis.
Também calculamos o preço com desconto com base em dados de outro trabalho que explicaremos mais adiante.
O trecho abaixo é uma continuação do código do notebook acima:
1 catalog_status = catalog_status.withColumn("discountedPrice", F.col("price") * F.col("pred_price")) catalog_status = catalog_status.withColumn("atp", (F.col("atp").cast("boolean") & F.lit(1).cast("boolean")).cast("integer"))
Nós vetorizamos o título do produto e criamos um novo campo chamado “vec”. Em seguida, descartamos o campo "ID", indicando que esse campo não será atualizado na collection MongoDB de destino.
1 catalog_status.withColumn("vec", get_vec("title")) catalog_status = catalog_status.drop("_id")
Finalmente, ele configura uma operação estruturada de gravação de streaming para gravar os dados transformados em uma collection MongoDB chamada "catalog_final_myn" no banco de dados "search" enquanto gerencia o estado da consulta e o ponto de verificação.
1 catalog_status.writeStream.format("mongodb").\ option('spark.mongodb.connection.uri', MONGO_CONN).\ option('spark.mongodb.database', "search").\ option('spark.mongodb.collection', "catalog_final_myn").\ option('spark.mongodb.operationType', "update").\ option('spark.mongodb.idFieldList', "id").\ option("forceDeleteTempCheckpointLocation", "true").\ option("checkpointLocation", "/tmp/retail-atp-myn5/_checkpoint/").\ outputMode("append").\ start()
Vamos ver como configurar o segundo fluxo de trabalho para calcular uma pontuação de BI para cada produto na collection e introduzir o resultado de volta no mesmo documento para que ele possa ser reutilizado para pontuação de pesquisa.
Nesta etapa, explicaremos o script a ser executado em nosso notebook do Databricks como parte do trabalho de computação de pontuação do BI. Lembre-se de que explicaremos apenas o que torna esse trecho de código diferente do anterior, portanto, certifique-se de entender como o trecho completo funciona. Fique à vontade para clonar nosso repositório completo para ter uma visão completa em sua máquina local.
Começamos definindo a configuração do Apache Spark usando o objeto SparkConf e especificando a dependência de pacote necessária para nosso Spark Connector.
1 conf = pyspark.SparkConf() conf.set("spark.jars.packages", "org.mongodb.spark:mongo-spark-connector_2.12:10.1.0")
Em seguida, inicializamos uma sessão do Spark para nosso aplicativo Spark chamada "test1" em execução no modo local. Ele também configura o Spark com a dependência do pacote do Spark Connector, que é configurada no objeto conf definido anteriormente. Essa sessão do Spark pode ser usada para executar várias tarefas de processamento e análise de dados usando o Apache Spark.
O código abaixo é uma continuação do trecho de notebook explicado acima:
1 spark = SparkSession.builder \ 2 .master("local") \ 3 .appName("test1") \ 4 .config(conf = conf) \ 5 .getOrCreate()
Usaremos os pipelines de agregação do MongoDB em nosso trecho de código para obter um conjunto de documentos, cada um representando um "product_id" exclusivo junto com as contagens correspondentes de visualizações totais, compras e eventos de carro. Usaremos os dados resultantes transformados para alimentar um algoritmo deEmpirical Webes e calcular um valor com base na função de distribuição cumulativa (CDF) de uma distribuição beta.
Dessa forma, podemos calcular a relevância de um produto com base nos dados comportamentais descritos anteriormente. Também usaremos funções de janela para calcular diferentes estatísticas sobre cada um dos produtos - como a média de compras e o beta de compra (a diferença entre o total médio de cliques e o total médio de compras) - para usar como entrada para criar uma relevância de BI pontuação. Isso é o que é mostrado no código abaixo:
1 @F.udf(T.FloatType()) 2 3 def beta_fn(pct,a,b): 4 return float(100*beta.cdf(pct, a,b)) w = Window().partitionBy() df = 5 df.withColumn("purchase_alpha", F.avg('purchase').over(w)) df = df.withColumn("cart_alpha", F.avg('cart').over(w)) df = df.withColumn("total_views_mean", F.avg('total_views').over(w)) df = df.withColumn("purchase_beta", F.expr('total_views_mean - purchase_alpha')) 6 7 df = df.withColumn("cart_beta", F.expr('total_views_mean - cart_alpha')) df = df.withColumn("purchase_pct", F.expr('(purchase+purchase_alpha)/(total_views+purchase_alpha+purchase_beta)')) 8 df = df.withColumn("cart_pct", F.expr('(purchase+cart_alpha)/(total_views+cart_alpha+cart_beta)'))
Depois de calcular a pontuação de BI do nosso produto, queremos usar um algoritmo de aprendizado de máquina para calcular a elasticidade-preço da demanda pelo produto e o preço ideal.
Para calcular o preço recomendado ideal, primeiro precisamos descobrir um pipeline que moldará os dados de acordo com o que precisamos. Obtenha a definição do pipeline em nosso repositório.
Primeiro, pegaremos os dados da collection de logs de cliques (clog) do MongoDB Atlas que estão sendo ingeridos no banco de dados em tempo real e criaremos um DataFrame que será usado como entrada para um modelo de aprendizado de máquina regressor Random Forest. Aproveitaremos a biblioteca MLFlow para poder executar estágios de MLOps, executar testes e registrar o modelo de melhor desempenho que será usado no segundo trabalho para calcular a elasticidade de preço da demanda, o desconto sugerido e o preço ideal para cada produto. Vamos ver como ficou o código!
1 model_name = "retail_competitive_pricing_model_1" 2 with mlflow.start_run(run_name=model_name): 3 # Create and fit a linear regression model 4 model = RandomForestRegressor(n_estimators=50, max_depth=3) 5 model.fit(X_train, y_train) 6 wrappedModel = CompPriceModelWrapper(model) 7 8 # Log model parameters and metrics 9 mlflow.log_params(model.get_params()) 10 mlflow.log_metric("mse", np.mean((model.predict(X_test) - y_test) ** 2)) 11 12 # Log the model with a signature that defines the schema of the model's inputs and outputs. 13 # When the model is deployed, this signature will be used to validate inputs. 14 signature = infer_signature(X_train, wrappedModel.predict(None,X_train)) 15 16 # MLflow contains utilities to create a conda environment used to serve models. 17 # The necessary dependencies are added to a conda.yaml file which is logged along with the model. 18 conda_env = _mlflow_conda_env( 19 additional_conda_deps=None, 20 additional_pip_deps=["scikit-learn=={}".format(sklearn.__version__)], 21 additional_conda_channels=None, 22 ) 23 mlflow.pyfunc.log_model(model_name, python_model=wrappedModel, conda_env=conda_env, signature=signature)
Depois de fazer a divisão de teste e treinamento necessária para ajustar o modelo, aproveitamos o pacote de modelos do mlFlow para poder registrar parâmetros, métricas e dependências do modelo.
Para o próximo estágio, aplicamos o modelo formado anteriormente e registrado aos dados de vendas:
1 model_name = "retail_competitive_pricing_model_1" 2 apply_model_udf = mlflow.pyfunc.spark_udf(spark, f"models:/{model_name}/staging") 3 4 # Apply the model to the new data 5 columns = ['old_sales','total_sales','min_price','max_price','avg_price','old_avg_price'] 6 udf_inputs = struct(*columns) 7 udf_inputs
Em seguida, basta criar o DataFrame de vendas com os dados resultantes. Mas, primeiro, usamos a função .fillna para garantir que todos os nossos valores nulos sejam convertidos em flutuantes 0.0. Precisamos fazer isso para que nosso modelo tenha os dados adequados e porque a maioria dos modelos de machine learning retorna um erro se você passar valores nulos.
Agora, podemos calcular novas colunas para adicionar ao DataFrame de vendas: o preço ideal previsto, a elasticidade de preço da demanda por produto e uma coluna de desconto que será arredondada para o próximo inteiro mais próximo. O código abaixo é uma continuação do código acima — ambos residem no mesmo notebook:
1 sales = sales.fillna(0.0) 2 sales = sales.withColumn("pred_price",apply_model_udf(udf_inputs)) 3 4 sales = sales.withColumn("price_elasticity", F.expr("((old_sales - total_sales)/(old_sales + total_sales))/(((old_avg_price - avg_price)+1)/(old_avg_price + avg_price))")) 5 6 sales = sales.withColumn("discount", F.ceil((F.lit(1) - F.col("pred_price"))*F.lit(100)))
Em seguida, enviamos os dados de volta usando o MongoDB Connector para Spark para a coleção MongoDB adequada. Eles serão usados junto com o restante como a linha de base sobre a qual criaremos a lógica comercial de pesquisa do nosso aplicativo.
1 sales.select("id", "pred_price", "price_elasticity").write.format("mongodb").\ option('spark.mongodb.connection.uri', MONGO_CONN).\ option('spark.mongodb.database', "search").\ option('spark.mongodb.collection', "price_myn").\ option('spark.mongodb.idFieldList', 'id').\ mode('overwrite').\ save()
Depois que esses fluxos de trabalho forem configurados, você poderá ver as novas coleções e os documentos atualizados para seus produtos.
Para construir a lógica de pesquisa, primeiro você precisará criar um index. É assim que garantiremos que nosso aplicativo funcione perfeitamente como uma consulta de pesquisa, em vez de ter que examinar todos os documentos da coleção. Limitaremos a verificação definindo os critérios para essas verificações.
Para entender mais sobre indexação no MongoDB, você pode conferir o artigo na documentação. Mas, para fins deste tutorial, vamos mergulhar nos dois parâmetros principais que você precisará definir para criar nossa solução:
Mapeamentos: essa chave determina como os campos no índice devem ser armazenados e como devem ser tratados quando as consultas forem feitas em relação a eles.
Campos: Os campos descrevem os atributos ou colunas do índice. Cada campo pode ter tipos de dados específicos e configurações associadas. Implementamos a funcionalidade de número classificável para os campos 'pred_price', 'price_elasticity' e 'score'. Dessa forma, nossos resultados de pesquisa são organizados por relevância.
As últimas etapas da construção da solução tratam da definição do mapeamento de índice para o aplicativo. Você pode encontrar o snippet de mapeamento completo em nosso repositório GitHub.
Para configurar o índice, você pode inserir o trecho no MongoDB Atlas navegando na página inicial do cluster e clicando na aba “Search:
Em seguida, você pode clicar em "Create Index. " Certifique-se de selecionar "JSON Editor ":
Cole o trecho JSON acima — certifique-se de selecionar o banco de dados e a collection corretos! No nosso caso, o nome da collection é
catalog_final_myn
.Para definir índices de preenchimento automático, você pode seguir as mesmas instruções de navegação do estágioConstruindo a lógica de pesquisa, mas no editor JSON, seu snippet de código pode variar. Siga nosso tutorial para aprender como configurar totalmente o preenchimento automático no Atlas Search.
Para nossa solução de pesquisa, confira o código abaixo. Definimos como os dados devem ser tratados e indexados para os recursos de autocompletar.
1 { 2 "mappings": { 3 "dynamic": false, 4 "fields": { 5 "query": [ 6 { 7 "foldDiacritics": false, 8 "maxGrams": 7, 9 "minGrams": 3, 10 "tokenization": "edgeGram", 11 "type": "autocomplete" 12 } 13 ] 14 } 15 } 16 }
Vamos detalhar cada um dos parâmetros:
foldDiacritics: Definir essa opção como false significa que os sinais diacríticos nos caracteres (como os acentos nas letras) são tratados de forma distinta. Por exemplo, "résumé" e "resume" seriam tratados como palavras diferentes.
minGrams e maxGrams: especificam os comprimentos mínimo e máximo dos n-grams de borda. Nesse caso, ele indexaria substrings (edgeGrams) com comprimentos que variam de 3 a 7.
Tokenização: O valor edgeGram significa que o texto é tokenizado em substrings a partir do início da string. Por exemplo, para a palavra "example", com minGrams definido como 3, os tokens seriam "exa", "exam", "examp", etc. Isso é comumente usado em cenários de autocompletar para corresponder a palavras parciais.
Depois de tudo isso, você deve ter uma funcionalidade de pesquisa aprimorada por IA para sua vitrine de e-commerce!
Em resumo, abordamos como integrar o MongoDB Atlas e o Databricks para criar um recurso de pesquisa de alto desempenho e inteligência para um aplicativo de e-commerce.
Ao usar o MongoDB Connector para Spark e Databricks, juntamente com o MLFlow para MLOps, criamos pipelines em tempo real para AI. Além disso, configuramos índices do MongoDB Atlas Search, utilizando recursos como o preenchimento automático, para criar um mecanismo de pesquisa de ponta.
Compreender as complexidades dos modelos de negócios de comércio eletrônico já é complicado o suficiente sem precisar lidar com integrações complicadas e sobrecargas operacionais! Contar com as ferramentas certas para o trabalho faz com que você avance vários meses à frente da concorrência.