2 votes

Django DRF elastic search dsl, Appliquer un boosting fonctionnel basé sur la valeur numérique d'un autre champ

J'essaie d'ajuster la pertinence du résultat de la recherche d'un champ en fonction de la valeur numérique d'un autre champ (le champ se trouve dans un autre modèle). En regardant le Documentation ES il semble que stimulations fonctionnelles est ce dont j'ai besoin pour mon cas d'utilisation.

Notez que j'utilise le django-elasticsearch-dsl ; paquet.

Mon cas d'utilisation :

  1. J'ai un champ de recherche dans lequel les utilisateurs peuvent rechercher des entreprises sur la base du nom de l'entreprise.
  2. Je veux renvoyer les noms de sociétés qui correspondent à la requête où le tri (pertinence) dépend de l'actif net de la société, où plus le nombre est élevé, plus il est pertinent (champ dans un autre modèle).

Définition du modèle :

class Company(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=255, blank=False, null=False)

    def __str__(self):
        return self.name

class CompanyNetAsset(models.Model):
    id = models.AutoField(primary_key=True)
    assets = models.IntegerField
    company_id = models.ForeignKey('Company', on_delete=models.PROTECT, blank=True, null=True)

    def __str__(self):
        return self.name

mon document es :

...
custom_stop_words = token_filter(
    'custom_stopwords',
    type='stop',
    ignore_case=True,
    stopwords=['the', 'and']

)

html_strip = analyzer(
    'html_strip',
    tokenizer="standard",
    filter=["lowercase", "asciifolding", custom_stop_words],
    char_filter=["html_strip"],
)

@INDEX.doc_type
class CompanyDocument(Document):
    id = fields.IntegerField(attr='id')

    name = fields.TextField(
        analyzer=html_strip,
        fields={
            'raw': fields.TextField(analyzer='keyword'),
        }
    )

    class Django:
        model = Company

et voici le DocumentViewSet :

class CompanyDocumentViewSet(DocumentViewSet):
    """The Company Document view."""
    serializer_class = CompanyDocumentSerializer
    lookup_field = 'id'
    document = CompanyDocument
    filter_backends = [
        FilteringFilterBackend,
        SearchFilterBackend,
    ]
    search_fields = (
        'name'
    )

    filter_fields = {
        'id': None,
        'name': 'name.raw',

    }

Une idée de la façon dont je peux y parvenir en utilisant le paquet drf ES ?

UPDATE

Voici un exemple de requête :

 /api/v1/employers/companies/?search=name:foundation%20center

 "results": [
        {
            "id": 469329,
            "name": "THE FOUNDATION CENTER",
            "city": "NEW YORK",
            "state": "NY"
        },
        {
            "id": 323012,
            "name": "OVERTURE CENTER FOUNDATION",
            "city": "MADISON",
            "state": "WI"
        },
        {
            "id": 367286,
            "name": "PEACE CENTER FOUNDATION",
            "city": "GREENVILLE",
            "state": "SC"
        },
       ...

Et voici une sortie fils du document :

{'settings': {'number_of_shards': 1,
  'number_of_replicas': 1,
  'analysis': {'analyzer': {'html_strip': {'tokenizer': 'standard',
     'filter': ['lowercase', 'asciifolding', 'custom_stopwords'],
     'char_filter': ['html_strip'],
     'type': 'custom'}},
   'filter': {'custom_stopwords': {'ignore_case': True,
     'stopwords': ['the', 'and'],
     'type': 'stop'}}}},
 'mappings': {'properties': {'id': {'type': 'integer'},
   'city': {'type': 'text'},
   'state': {'type': 'text'},
   'name': {'analyzer': 'html_strip',
    'fields': {'raw': {'analyzer': 'keyword', 'type': 'text'}},
    'type': 'text'}}}}

2voto

jzzfs Points 18

ES ne prend pas en charge les jointures au moment de la requête, vous devrez donc répliquer le fichier CompanyNetAsset dans les attributs de l Company pour pouvoir laisser le assets influencer votre tri.

Concrètement, il s'agit de régler le Company comme ceci :

class Company(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=255, blank=False, null=False)
    assets = models.IntegerField

    def __str__(self):
        return self.name

Ajustez ensuite le Document :

@INDEX.doc_type
class CompanyDocument(Document):
    id = fields.IntegerField(attr='id')

    name = fields.TextField(
        analyzer=html_strip,
        fields={
            'raw': fields.TextField(analyzer='keyword'),
        }
    )

    assets = fields.IntegerField(attr='assets')

    class Django:
        model = Company

Enfin, réindexez vos documents et définissez le tri :

class CompanyDocumentViewSet(DocumentViewSet):
    """The Company Document view."""
    serializer_class = CompanyDocumentSerializer
    lookup_field = 'id'
    document = CompanyDocument
    filter_backends = [
        FilteringFilterBackend,
        SearchFilterBackend,
    ]
    search_fields = (
        'name'
    )

    filter_fields = {
        'id': None,
        'name': 'name.raw',

    }

    # Define ordering fields        # <--
    ordering_fields = {
        'assets': None
    }

    # Specify default ordering      
    ordering = ('assets')

Pour faire respecter la ordre de tri via l'URI , cours :

GET /api/v1/employers/companies/?search=name:foundation%20center&ordering=-assets

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X