Saviez-vous qu'en réalité, les icebergs sont situés horizontalement dans l'eau , et non verticalement, comme dans la plupart des images de stock?
Mais même si vous oubliez le groupe traditionnel de gadgets d'entreprise tels que l'analyse, la prise en charge de la compatibilité descendante et les tests A / B et que vous vous concentrez uniquement sur le code directement lié à la fonctionnalité implémentée, vous pouvez voir que sa complexité devient souvent incontrôlable.
Dans cet article, je vais vous parler de plusieurs fonctionnalités que mes collègues et moi avons implémentées dans Joom à différents moments, de l'énoncé du problème aux détails de mise en œuvre, et montrerai à quel point des choses apparemment simples se transforment en un enchevêtrement de logique complexe qui nécessite de nombreux développements. itérations.
Recherche par utilisateurs
L'une des grandes sections de l'application Joom est le réseau social interne, où les acheteurs peuvent rédiger des critiques de produits, les aimer et en discuter, et s'abonner les uns aux autres. Et quel réseau social sans recherche d'utilisateurs!
Bien sûr, la recherche n'est pas si simple (du moins après mon article précédent ). Mais j'avais déjà toutes les connaissances nécessaires, et nous avions également un composant prêt à l'emploi dans notre entreprise
joom-mongo-connector
qui était capable de transférer des données d'une collection vers MongoDB dans un index Elasticsearch, si nécessaire en ajustant des données supplémentaires et en effectuant d'autres post-traitements. La tâche semblait assez simple.
Une tâche... Créez un backend pour la recherche par les utilisateurs des réseaux sociaux. Aucun filtre nécessaire, le tri par nombre d'abonnés suffira pour commencer.
D'accord, cela semble vraiment simple.
socialUsers
Nous configurons le débordement de la collection vers Elasticsearch en écrivant une configuration en YAML. Sur le backend, nous ajoutons un nouveau point de terminaison avec une API similaire à l'API de recherche de produits, mais jusqu'à présent sans prise en charge des filtres et des tris (seuls le texte de la requête et la pagination restent, c'est tout). Dans le handler, on fait une simple requête au cluster Elasticsearch (l'essentiel est de ne pas se tromper avec le cluster!), Du résultat on obtient les identifiants des documents trouvés - ce sont des identifiants utilisateurs - selon les utilisateurs eux-mêmes, puis nous nous convertissons au client JSON, cachant les informations privées des regards indiscrets, et prêts. Ou pas?
Le premier problème que nous avons rencontré était la translittération. Les noms d'utilisateur ont été tirés des réseaux sociaux, où les utilisateurs de Russie (et ils étaient majoritaires à l'époque) les écrivaient souvent en latin. Vous essayez de trouver Mads, et il est sur Facebook de Mads, et c'est tout - il n'est pas dans les résultats. De même, Ivan ne pourra pas trouver Ivan, mais j'aimerais beaucoup.
C'est la première complication - lors de l'indexation, nous avons commencé à accéder à l'API Microsoft Translator pour la translittération et à enregistrer deux versions du prénom et du nom, et le composant d'indexation général a commencé à dépendre du client transliterator (et dépend toujours).
Eh bien, le deuxième problème, qui est facile à prévoir si votre langue maternelle est le russe, mais existe également dans d'autres langues européennes - des formes minuscules et des abréviations de noms. Si Ivan a décidé de se nommer Vanya sur Facebook, la demande d'Ivan ne le trouvera plus, quel que soit le degré de translittération.
Donc, la complication suivante a été que nous avons trouvé un index de noms diminutifs sur Gramota.ru (du dictionnaire unique de Nikandr Aleksandrovich Petrovsky de noms russes), l'avons ajouté à la base de code sous forme de plaque codée en dur (environ deux mille lignes) et est devenu un index non seulement le nom et sa translittération, mais aussi tous ont trouvé des formes minuscules (fait amusant: en anglais il y a un terme hypocorismes pour eux). Nous avons pris chaque mot dans le nom d'utilisateur et avons fait une recherche dans notre humble feuille de calcul.
Une capture d'écran notariée de la base de code Joom. Vers 2018.
Mais ensuite, afin de ne pas offenser l'autre moitié de nos utilisateurs, répartis dans une couche inégale à travers le monde non russophone, nous avons lancé un cri aux responsables pays de Joom et leur avons demandé de nous trouver des ouvrages de référence des abréviations de national. noms dans leurs pays. Si ce n'est pas académique, du moins certains. Et il s'est avéré que dans certaines langues, en plus de la tradition d'avoir un nom composé (Juan Carlos, Maria Aurora), il y avait aussi des réductions de deux, trois ou même quatre mots en un (María de las Nieves → Marinieves).
Cette nouvelle circonstance nous a privé de la possibilité d'effectuer une recherche un mot à la fois. Maintenant, nous devons diviser la séquence de mots en fragments de longueur arbitraire, et de plus, différentes partitions peuvent conduire à des abréviations différentes! Nous ne voulions pas plonger dans les profondeurs de la linguistique et écrire une intelligence artificielle qui abrégeait un nom espagnol comme un Espagnol vivant l'abrégerait, alors nous avons esquissé, pardonnez à Knut, l'excès combinatoire.
Et, comme c'est toujours le cas avec les recherches combinatoires, il a éclaté sur l'un des utilisateurs et il a fallu de toute urgence lui couper une limite sur le nombre maximum d'orthographes générées. Cela a encore compliqué le code, ce qui était si inattendu pour cette tâche.
Traduction automatique de marchandises
Tâche . Il est nécessaire de traduire les noms et descriptions des produits fournis par les vendeurs en anglais dans la langue de l'utilisateur.
Tout le monde a probablement vu des mèmes sur la traduction tordue des noms de produits chinois. Nous les avons également vus, mais le délai de commercialisation souhaité ne nous a pas permis de proposer quelque chose de mieux que d'utiliser une API existante pour la traduction.
Il est facile d'écrire un client HTTP, de créer un compte et lorsque les marchandises sont délivrées à l'utilisateur, il est facile de les traduire dans la langue de l'appareil. Mais les traductions ne sont pas bon marché et il serait inutile de traduire le même produit populaire en russe pour chacune des dizaines de milliers de vues. Par conséquent, nous avons activé la mise en cache: pour chaque produit, nous sauvegardions les traductions dans la base de données et, s'il y avait des traductions là-bas, nous n'allions plus chez le traducteur.
Mais le potentiel d'économies était toujours là. Nous avons décidé qu'un compromis raisonnable entre la qualité et le prix de la traduction serait de battre les descriptions des phrases et de les mettre en cache - après tout, les mêmes modèles de phrases se retrouvent souvent dans les produits et il est inutile de les traduire à chaque fois. C'est ainsi qu'une autre couche d'abstraction est apparue dans notre traducteur - une couche entre le client HTTP et le cache qui stocke des marchandises entières dans différentes langues, qui est engagée dans la division du texte en fragments.
Après le lancement, la qualité des traductions, bien sûr, nous a hantés, et nous nous sommes dit: et si nous utilisions un traducteur plus cher? Mais est-ce que ce sera bon pour nos paroles spécifiques? Vous ne pouvez pas les comparer à l'œil nu, vous devez faire un test A / B. Ainsi, dans notre cache de traduction, en plus de l'ID du produit, l'ID du traducteur est apparu, et nous avons commencé à demander une traduction de l'ID du traducteur, en fonction du groupe de test A / B dans lequel se trouvait l'utilisateur.
Le cher traducteur a bien fonctionné, mais il était encore trop coûteux de l'exécuter sur tous les produits. Mais nous sommes allés dans des pays dont notre traducteur principal a si mal résisté aux langues nationales que nous étions prêts à débourser pour un lancement réussi; la logique du choix d'un traducteur est donc devenue plus compliquée.
Ensuite, nous avons décidé que certains magasins de la plate-forme sont si bons et que la plate-forme est tellement enracinée dans leur succès qu'elle est toujours prête à traduire leurs produits avec un traducteur plus cher. La logique du choix d'un traducteur a donc commencé à dépendre de l'utilisateur, du pays et de l'identifiant du magasin.
Et finalement, nous avons décidé qu'au cours des plusieurs années d'existence de Joom, notre traducteur principal pourrait s'améliorer, et peut-être qu'il est logique de mettre à jour le cache de traduction à certains intervalles. Mais qu'en est-il sans un test A / B? Le champ de fraîcheur est donc apparu dans notre cache, et les choses se sont à nouveau compliquées. En conséquence, notre composant de traduction est incroyablement complexe, et ce malgré le fait que nous n'y ayons même pas encore intégré de linguistique informatique maison. Pour l'instant.
Conversion de tailles de vêtements
L'un des problèmes les plus douloureux lors de l'achat de vêtements et de chaussures en ligne est peut-être de choisir la bonne taille. Et si, lorsqu'ils sont livrés depuis des entrepôts locaux, des joueurs comme Lamoda peuvent simplement ramener plusieurs tailles à la fois et reprendre avec la même facilité l'inadapté, cela ne fonctionnera pas dans un transfrontalier. Les colis prennent beaucoup de temps, le coût de chaque kilogramme supplémentaire est élevé et leurs expéditeurs ne s'attendent pas à un flux important de courrier entrant.
En outre, le problème est aggravé par le fait que les vendeurs de différents pays peuvent avoir des idées complètement différentes sur les tailles. Le M chinois pourrait facilement se révéler être le XS russe, et le terrifiant 9XL n'est peut-être pas si différent du XXL. Les utilisateurs cousus doivent se fier aux mesures, mais même celles-ci ne sont pas toujours correctes: par exemple, l'utilisateur s'attend à ce que la circonférence de la poitrine d'une personne soit indiquée et le vendeur indique les mesures des vêtements lui-même - elles diffèrent de cinq à dix pour cent . Nous ne voulons pas que l'utilisateur se soucie autant de faire ses achats sur Joom!
Tâche . Au lieu des tailles fournies par les vendeurs, montrez aux utilisateurs les tailles que nous avons calculées à partir d'un tableau unique basé sur les circonférences.
D'accord. Nous prenons un tableau de tailles, que nous analysons à partir de la description du produit (cela est fait par un vaisseau spatial séparé pour 5k lignes) et est stocké dans un champ séparé, et nous remplaçons les tailles qu'il contient par celles calculées. Codez en dur la table pour convertir la circonférence en taille, trouvée sur Internet, et profitez de la vie.
Mais s'il n'y a pas de table ou s'il n'y a pas assez de lignes, cela ne fonctionne pas. La fonctionnalité est désactivée implicitement sur le produit plusieurs fois.
Hmm, dans le tableau les circonférences du corps humain, et la plupart des vendeurs les indiquent en mesurant sur les choses elles-mêmes. Coudre le coefficient de différence. Le chef de produit Rodion, l'heureux propriétaire du M-ki parfait, se rend au centre commercial, mesure un tas de choses différentes sur lui-même et est livré avec des coefficients - ils sont similaires, mais diffèrent considérablement pour différentes catégories de produits. Pour un col roulé enveloppant, la différence est de près de 0%, et pour un pull, tous de 10%. En outre, les vêtements d'extérieur varient en termes de coupe: coupe slim, coupe normale, coupe ample, ce qui donne un swing de ± 5%. Maintenant, notre coefficient (immortalisé par moi dans le code comme le coefficient de Rodion ) se compose de deux facteurs.
Pour déterminer l'atterrissage, nous créons un autre analyseur qui essaie de l'extraire du nom ou de la description du produit. Si le produit n'entre pas dans l'une des catégories vérifiées par Rodion, la fonction est implicitement désactivée numéro deux.
La touche finale: de nombreux produits listent le buste de l'aisselle à l'aisselle, soit seulement la moitié de la circonférence, ce qui se traduit par des tailles ridiculement petites. Nous ajoutons la logique que si la circonférence est inférieure à X, eh bien, cela ne peut pas être, c'est clairement la moitié de la circonférence, et nous la multiplions par deux. Il est bon que les adultes ne diffèrent généralement pas les uns des autres de deux fois la circonférence de la poitrine.
Maintenant, tout est si compliqué que lors du test d'une fonctionnalité par type de produit dans le panneau d'administration, il est impossible de comprendre pourquoi elle ne s'est pas activée ou n'a pas fonctionné d'une manière ou d'une autre. Nous ajoutons une grande couche de logique au code, en enregistrant en détail les raisons de la désactivation de la conversion. Afin de pouvoir retracer complètement la cause de l'arrêt sur un produit spécifique, vous devez transmettre plusieurs fois des messages d'erreur vers le haut, enrichis de détails. Le code devient terrifiant.
Et tout fonctionne différemment selon le groupe du test A / B, bien sûr.
Conclusion
Méfiez-vous des