Effacer des doublons avec une requête

Bonjour à tous,

j'ai besoin d'aide en ce qui concerne le _delete_by_query. Voilà mon problème.
J'utilise Logstash pour me remonter des adresses IP de GLPI et de IPAM.
Je stock ensuite ces IP dans une même variable (histoire d'obtenir un doublon si l'adresse IP et sortie de GLPI et d'IPAM).
Mon fichier de configuration Logstash ressemble à ça :

# Fichier pour comparaison d'adresse IP
input {
	jdbc {
		jdbc_driver_library => "/usr/share/java/mysql-connector-java-8.0.15.jar"
		jdbc_driver_class => "com.mysql.jdbc.Driver"
		jdbc_connection_string => "jdbc:mysql://my_GLPI_server:3306/my_base?useSSL=true&verifyServerCertificate=false&requireSSL=true"
		jdbc_user => "myUser"
		jdbc_password_filepath => "/path/to/password"
		jdbc_validate_connection => "true"
		tracking_column_type => "timestamp"
		tracking_column => "date_mod"
		tags => ["ip-glpi"]
#		schedule => "* * * * *"
		statement => "SELECT DISTINCT name as ip_commune FROM glpi_ipaddresses where name not in ('','::','::1','::1/128','0.0.0.0','127.0.0.1')"
		clean_run => "true"
	}
	jdbc {
		jdbc_driver_library => "/usr/share/java/mysql-connector-java-8.0.15.jar"
		jdbc_driver_class => "com.mysql.jdbc.Driver"
		jdbc_connection_string => "jdbc:mysql://my_IPAM_server:3306/my_base?useSSL=true&verifyServerCertificate=false&requireSSL=true"
		jdbc_user => "myUser"
		jdbc_password_filepath => "/path/to/password"
		jdbc_validate_connection => "true"
#		schedule => "* * * * *"
		tags => ["ip-ipam"]
		statement => "SELECT DISTINCT ip_addr as ip_commune FROM devices"
		clean_run => "true"
	}	
}

output {
    if "ip-glpi" in [tags] or "ip-ipam" in [tags] {
	elasticsearch {
		hosts => ["127.0.0.1:9000"]
		index => "index-ip"
	}
    }
}

Ensuite mon but et de supprimer tous les doublons de mon index-ip (effacement des deux valeurs) afin de purger l'index afin d'obtenir un index d'IPs unique.

Le but et de pouvoir après afficher dans une visualisation uniquement les adresses IPs tagué ["ip-ipam"] Et j'obtiendrais alors une liste d'IP qui ne sont pas dans la base de données GLPI.

J'avais donc pensé à utilisé l'API avec une requête du genre :

GET /index-ip/_query
{
  "aggs": {
      "dup": {
         "terms": {
            "size" : 200,
            "field": "ip_commune.keyword",
            "min_doc_count": 2
     }
    }
  }
}

Qui me renvoi les Doublons:

{
  "took" : 3,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 754,
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "index-ip",
        "_type" : "doc",
        "_id" : "V64TV2kB0AORAV2GIdMZ",
        "_score" : 1.0,
        "_source" : {
          "ip_commune" : "98.183.164.36",
          "@version" : "1",
          "tags" : [
            "ip-ipam"
          ],
          "@timestamp" : "2019-03-07T07:34:24.874Z"

   ... ( Code couper car trop long )

  "aggregations" : {
    "dup" : {
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 0,
      "buckets" : [
        {
          "key" : "98.183.164.124",
          "doc_count" : 2
        },
        {
          "key" : "98.183.164.142",
          "doc_count" : 2
        },
        {
          "key" : "98.183.164.166",
          "doc_count" : 2
        },
        {
          "key" : "98.183.164.172",
          "doc_count" : 2
        },
        {
          "key" : "98.183.164.232",
          "doc_count" : 2
        },
        {
          "key" : "98.183.164.25",
          "doc_count" : 2
        },
        {
          "key" : "98.183.164.27",
          "doc_count" : 2
        },
        {
          "key" : "98.183.164.28",
          "doc_count" : 2
        },
        {
          "key" : "98.183.164.29",
          "doc_count" : 2
        },
        {
          "key" : "98.183.164.35",
          "doc_count" : 2
        },
        {
          "key" : "98.183.164.36",
          "doc_count" : 2
        },
        {
          "key" : "98.183.164.37",
          "doc_count" : 2
        },
        {
          "key" : "98.183.164.38",
          "doc_count" : 2
        },
        {
          "key" : "98.183.164.39",
          "doc_count" : 2
        },
        {
          "key" : "98.183.164.58",
          "doc_count" : 2
        },
        {
          "key" : "98.183.27.21",
          "doc_count" : 2
        },
        {
          "key" : "98.183.27.22",
          "doc_count" : 2
        },
        {
          "key" : "98.183.27.23",
          "doc_count" : 2
        },
        {
          "key" : "98.183.27.24",
          "doc_count" : 2
        },
        {
          "key" : "98.183.27.25",
          "doc_count" : 2
        },
        {
          "key" : "98.183.27.27",
          "doc_count" : 2
        },
        {
          "key" : "98.183.27.28",
          "doc_count" : 2
        }
      ]
    }
  }
}

Je veux essayer de les effacer ensuite avec une requête du style _delete_by_query :

POST /index-ip/_delete_by_query
{
 "query":{
   "must": [
    {"terms":{"field": "ip_commune.keyword", "min_doc_count": 2}}
    ]
 } 
}

Mais rien ne fonctionne je n'arrive en rien à passer ou a retranscrire le résultat de ma requête _query et de la transformer en _delete_by_query.

J'ai lu les documentations et fais pas mal de recherche mais, je n’aboutis à rien depuis plusieurs semaines :confused: Quelqu'un aurais une idée ou bien je vais dans la mauvaise direction et il existe une méthode beaucoup plus simple ?

Comme la possibilité de comparée les documents d'un index et d'en reconstruire un nouveau, ou bien tous simplement une bonne feinte avec les metrics des visualisations afin d'arriver au même résultat ...

En tous cas si quelqu'un a une idée elle est la bienvenue :smile:

Tu ne peux pas utiliser une agrégation comme étant une "query".

Tu pourrais éventuellement ajouter un top_hits avec size:1 pour récupérer un des _id en doublon puis faire un BULK pour effacer "à la main" tous les documents ainsi identifiés.

Pas de moyen simple...

Une autre solution serait de gérer le problème lors de l'ingestion.
Générer un _id en hashant l'adresse IP et tout autre champ commun. Ainsi un document "identique" aurait le même _id et serait purement et simplement remplacé par sa dernière version.

Merci dadoonet,
Je dois utilisé le top_hits size:1 dans ma requête _query pour récupérer un seul _id parmi la requête précédente ? et après utiliser BULK pour effacer le document grâce à sont ID ? un peut comme ça ? :

POST _bulk
{ "delete" : 
      {"_dindex":"index-ip*",
        "_type":"_doc",
        "_id":"Document_IP_id"
      }
}

Après l'idée de traiter la donnée pendant l'ingestion me semblais être une bonne idée également, mais, le problème c'est que si j'écrase la dernière valeurs avec un _id identique c'est que je vais conserver la nouvelle valeur dans mon index alors qu'il faudrait que je puisse effacer les deux ! c'est juste au final pour comparer deux liste quelque part ... :slight_smile:

Oh? Tu ne veux pas supprimer que le doublon mais les 2 enregistrements ?

Dans ce cas, un delete by query avec une terms query est suffisant? https://www.elastic.co/guide/en/elasticsearch/reference/6.6/query-dsl-terms-query.html

Genre tu récupères d'abord comme tu l'as fait toutes les IP puis ensuite:

POST /index-ip/_delete_by_query
{
 "query":{
    "terms":{ "ip_commune.keyword" : ["98.183.164.124", "98.183.164.142","..."]}
 } 
}

Un truc du genre... Attention à tester avec un _search avant pour éviter de faire des co....!

Ah oui,
vraiment pas mal ça ! je vais essayer ça tout de suite !
Après, je me dis que là, je fait ça que avec quelques IPs mais, plus tard il va y en avoir entre 15xx et 18xx, donc je devrais automatisé le process, mais si ça marche comme ça, je pense qu'un petit script python devrais pouvoir faire l'affaire =)

Dadoonet,
tu es un génie :smile:
Ça marche très bien et je ne possèdent plus du tout une seul des deux valeurs, ce qui est parfait !
Maintenant je dois juste voir quel et le langage le plus pratique pour faire les requêtes à l'API et automatisé tous ça :blush:
Un grand merci pour ton aide en tous cas !

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.