Variations de produit - Problème lors de nombreuses variantes

a marqué ce sujet comme résolu.

Bonsoir ! :)

Je suis en train de développer une petite application où l’utilisateur peut créer des produits. Chaque produit peut avoir des variations. Il peut varier en couleur, en taille, etc. En fait, c’est l’utilisateur qui décide des variations. Il peut donc en ajouter pas mal.

Chaque variations à des variantes. Par exemple, la variation "Couleur" peut posséder les variantes "Bleu", "Jaune", "Vert", etc…

Là encore, c’est l’utilisateur qui les définis.

Dans mon application, l’utilisateur créer des variations, puis peut créer des variantes pour chaque variations.

A chaque fois qu’il ajoute une variante, j’utilise une fonction "cartesian" pour générer toutes les combinaisons possibles.

Ca fonctionne très bien… à condition de se limiter à deux ou trois variations et le même nombre de variantes par variation.

Dès l’instant où l’on dépasse un peu, l’application rame de plus en plus au fur et à mesure de l’ajout des variantes.

En plus de ça, une fois que l’utilisateur à créer les variantes, et qu’il sauvegarde le produit, j’ai une erreur 500 "Request too long". Ce qui est normal, puisque j’appel mon API avec trouze million de variantes.

Avez-vous une solution pour régler ce problème ?

Sachant que mes variantes peuvent avoir des données de sauvegardée, donc je ne peux pas vraiment générer les variantes quand j’en ai besoin dans l’application. D’autant plus que ça ferais ramer l’application partout où les variantes seraient utilisées (partout).

Je suis un peu perdu… :(

Merci ! :)

+0 -0

Dans mon application, l’utilisateur créer des variations, puis peut créer des variantes pour chaque variations.

A chaque fois qu’il ajoute une variante, j’utilise une fonction "cartesian" pour générer toutes les combinaisons possibles.

FougereBle

Pourquoi tu fais ça ? Il y a un intérêt à générer toutes les combinaisons possibles ?

C’est certainement ça qui fait ramé la vue.

+0 -0

C’est quoi qui rame au juste ?

Ta base de données ? Le serveur ? L’API ? Le front ?

Tu définis tes variantes et variations comment ?

viki53

C’est le front. A la création des variantes.

Voici le code actuel (pas optimisé, mais je pense que j’aurais quand même des problèmes) :

Ici c’ess les données que je me suis servis pour développer le truc. Mais bien sûr, dans le front, les tableaux sont vide, et je fait simplement un "push" quand je créer une variation.

A noter que j’ai développer le truc dans NodeJS à la base, d’où les require.

const { v4: uuidv4 } = require('uuid');

const blueUUID = uuidv4()

const variations = [
  { // Ceci est une variation
    id: 1,
    name: 'Couleur',
    values: [
      { // Ceci est une variante de la variation
        id: blueUUID,
        value: 'Bleu'
      },
      {
        id: 2,
        value: 'Rouge'
      },
    ]
  },
  {
    id: 2,
    name: 'Taille',
    values: [
      {
        id: 1,
        value: '12'
      },
      {
        id: 2,
        value: '14'
      },
    ]
  },
]

const variants = [ // Toutes les combinaisons
  {
    id: 0,
    variations: [
      { id: blueUUID, value: 'Bleu', variationId: 1 },
      { id: 1, value: '12', variationId: 2 }
    ],
    data: {
      price: 200
    }
  }
]

module.exports = {
  variations,
  variants
}

Et le code qui génère :

const cartesian = require('cartesian')
const variationsData = require('./variations')

// Le code qui suit est dans une fonction dans l'app réel. Cette fonction
// est appelée à chaque fois que je fait un .push dans les variants des variations.

const combination = cartesian(variationsData.variations.map((variation) => variation.values.map((value) => ({
  ...value,
  variationId: variation.id
}))))

let variants = combination.map((item, itemIndex) => ({
  id: itemIndex,
  variations: item,
  data: {
    unitPrice: 0,
  }
}))


variants = variants.map((v) => {
  console.log('Check for variant', v)

  // Cette partie un peu compliqué sert à garder les data des précédentes
  // variantes dans le cas où une nouvelle variation est ajoutée.
  // Par exemple, si on à "Couleur" et "Taille", et que plus tard on
  // ajoute "Poid", il ne faut pas perdre les anciennes data des variantes
  // qui n'avaient pas encore la propriété "Poid".
  // Pour ce faire, on regarde uniquement la correspondance des propriétés
  // qui existaient déjà
  const oldVariant = variationsData.variants.find((ov) => {
    let match = true

    console.log('Check for old variant', ov)

    ov.variations.forEach((ovv) => {
      const vv = v.variations.find((vv) => vv.id === ovv.id && vv.variationId === ovv.variationId)

      console.log('Check for old variation variant', ovv)

      if (!vv) {
        console.log('not found')
          match = false
      } else {
        console.log('found')
      }
    })

    return match
  })

  if (oldVariant) {
    v.data = {
      ...oldVariant.data,
      ...v.data
    }
  }

  console.log('=================')
  console.log('=================')

  return v
})

// Juste pour les tests, n'est pas là dans le front réel
variants.forEach((variant) => {
  console.log(variant)
})

// Normalement, je fait un :
// this.variants = variants

Dans mon application, l’utilisateur créer des variations, puis peut créer des variantes pour chaque variations.

A chaque fois qu’il ajoute une variante, j’utilise une fonction "cartesian" pour générer toutes les combinaisons possibles.

FougereBle

Pourquoi tu fais ça ? Il y a un intérêt à générer toutes les combinaisons possibles ?

C’est certainement ça qui fait ramé la vue.

ache

Je suis obligé. Après, l’utilisateur doit pouvoir activer/désactiver une variante (là, je parle d’une variante générée, pas les variantes d’une variation particulière), y ajouter des données (des prix customs par exemple), et l’utilisateur final doit pouvoir acheter ces produits en sélectionnant une variation.

Pour la partie commande, je pourrais changer ça en demandant de choisir une variante pour chaque variation via un select. Ca marcherais bien et ça m’éviterais de générer toutes les variantes.

Par contre, je ne pourrais pas activer/désactiver une variante particulière ni enregistrer des données dedans.

Edit : En fait, le problème est bien plus profond que ça. Dans l’idée où j’arrivais effectivement à générer toutes les variantes possibles, il y à un moment où je les affichent dans un tableau pour demander à l’utilisateur d’entrer des prix. Il va donc se retrouver avec un tableau immense de plusieurs centaines de lignes, il va devoir entrer les prix de plusieurs centaines de lignes (et là on parle que d’un seul article sur des dizaines, mais ça doit pouvoir s’adapter à des centaines d’articles), dire si il veut tel ou tel article de dispo… il va mettre 3 ans à tout entrer.

Donc le problème viens bien avant la génération des variantes : C’est un problème de conception du logiciel.

Comment pourrais-je penser le logiciel pour que :

  • Même avec des centaines de produits et des centaines de combinaisons possible, ce soit rapide pour l’utilisateur
  • Pour ne pas avoir à enregistrer toutes les combinaisons possible
  • Pour permettre à l’utilisateur d’entrer des données pour chaque variantes (par exemple, un "Pull - Bleu - 8 ans" peut avoir un prix différent de "Pull - Jaune - 8 ans".
  • Pour permettre à l’utilisateur d’activer/désactiver une variante particulière (par exemple, activer "Pull - Bleu - 8 ans", "Robe - Jaune - 8 ans" mais désactiver "Pull - Jaune - 8 ans").
+0 -0

Tu pourrais commencer par regrouper les boucles, je vois deux .map pour les variantes par exemple.

Ensuite évite de toucher aux variable d’un composant Vue tant qu’il n’est pas totalement construit : toute modification intermédiaire va demander un refresh inutile du template sinon.

Pour moi il ne devrait pas y avoir de gros problème avec si peu de boucles, ça devrait relativement bien tourner.

N’hésite pas à faire une analyse de performances avec les DevTools aussi pour voir ce qui prend le plus de temps/ressources.

Donc le problème viens bien avant la génération des variantes : C’est un problème de conception du logiciel.

FougereBle

Non. Conceptuellement tu as pensé que c’était nécessaire. Alors que l’utiliser pose tous plein de problèmes.

Y a-t-il un sens à générer tous les variants ?

Un pull - 8ans et un pull - 64 ans existent dans exactement les mêmes couleurs ?

Ne peux-tu pas définir autrement un variant ?

8ans => 8€. (Prix de base)
+20ans => 25€.

Bleu => *1.2 (Variant multiplicatif)
Rouge => *1.5

Manche longue => +10€(Variant additif)

Avec éventuellement des combinaisons un peu plus spécifiques défini au cas pas cas.

Aussi, tu sais que tu as des outils de développement qui te permettent de savoir où bloque l’interface ?

+0 -0

Tu pourrais commencer par regrouper les boucles, je vois deux .map pour les variantes par exemple.

Je ne vois pas comment je peux les regrouper. Les deux map sont bien distinctes.

Ensuite évite de toucher aux variable d’un composant Vue tant qu’il n’est pas totalement construit : toute modification intermédiaire va demander un refresh inutile du template sinon.

A moins d’avoir fait une erreur, je ne crois pas avoir modifié une variable. A laquelle tu pense ?

Pour moi il ne devrait pas y avoir de gros problème avec si peu de boucles, ça devrait relativement bien tourner.

N’hésite pas à faire une analyse de performances avec les DevTools aussi pour voir ce qui prend le plus de temps/ressources.

viki53

Je ne savais pas qu’on pouvais faire une analyse de performance avec ça. Je vais regarder de plus près.

Par contre, même si j’arrive à faire optimise tout ça (mais plus il y aura de variations et de variants, plus le nombre de boucle va augmenter de manière exponentielle), je rencontre toujours le problème que mon JSON finale est BEAUCOUP trop grand, et j’ai l’erreur "Request too long" (ou quelque chose comme ça).

@ache : Alors j’ai effectivement pensé à faire comme ça… mais au vu de l’application, je ne pense pas que ce soit possible, car les prix varient beaucoup au moment de la commande. En plus, il faut pouvoir importer un fichier CSV qui contient les prix dans l’application.

En plus de ça, cela rend impossible l’enregistrement de données dans chacune des variantes, ce qui est absolument nécessaire.

+0 -0

Pour moi la première chose à faire dans ce genre de situation, c’est de définir un nombre raisonnable de variants – parce que comme tu l’as bien détecté, l’explosion combinatoire de ta demande fonctionnelle fait que tu peux très vite te retrouver avec un nombre de variants qui n’a plus aucun sens.

Ensuite, tu te crées des cas où tu approches de cette limite (au moins : peu de critères avec de nombreuses possibilités chacun, beaucoup de critères avec peu de possibilités chacun, et un cas équilibré), et tu regardes si dans ce cas où tu considères que c’est la limite ton application fonctionne encore correctement.

Si oui, bravo, ton problème c’était juste une question de produit cartésien qui explose, et ça sera de toutes façons ingérable et n’a plus aucun sens : il faudra limiter les possibilités de création pour ne pas se retrouver dans ce cas.

Si non, il faut commencer à étudier le code (en commençant par le tracer, ce que je ne sais pas faire avec tes technos) pour trouver où ça coince.

Enfin, tu peux avoir réellement besoin de gérer des cas où la combinatoire totale n’a pas de sens. Mais dans ce cas, il te faut trouver une bonne approche pour ne pas devoir gérer tous les cas. Par exemple, ne permettre la création que de produits qui existent, plutôt que de chercher à gérer tous les produits qui peuvent exister.

Enfin, tu peux avoir réellement besoin de gérer des cas où la combinatoire totale n’a pas de sens. Mais dans ce cas, il te faut trouver une bonne approche pour ne pas devoir gérer tous les cas. Par exemple, ne permettre la création que de produits qui existent, plutôt que de chercher à gérer tous les produits qui peuvent exister.

Faire en sorte que l’utilisateur doivent ajoute chacun des cas lui-même. 😈

+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte