Data set difference between fields on different indexes

The composite aggregation does not return all results at once. It allows you to page through the buckets. The after_key that's returned is something you can use to get the next page of buckets, using the after parameter in a subsequent request:

GET logs_server*/_search
{
  "size": 0,
  "aggs": {
    "all_ips": {
      "composite": {
        "sources": [
          {
            "dstIP": {
              "terms": {
                "field": "geoip.city_name.keyword"
              }
            }
          }
        ],
        "after": { "dstIP" : "5.189.xx.xx" }
      },
      "aggs": {
        "indexes_per_ip": {
          "terms": {
            "field": "_index",
            "size": 2
          }
        },
        "index_count_bucket_filter": {
          "bucket_selector": {
            "buckets_path": {
              "index_count": "indexes_per_ip._bucket_count"
            },
            "script": "params.index_count == 1"
          }
        }
      }
    }
  }
}

To make paging more efficient, you could set the size parameter in the composite aggregation to for example 1000.