Verwenden der Elasticsearch Java-API (Aggregation)

Dieser Artikel ist eine Fortsetzung von Verwenden der Elasticsearch-Java-API (BulkRequest-Registrierung). Siehe oben für Anforderungen, Datenstruktur usw.

Übrigens möchte ich mit verschiedenen in Bulk registrierten Daten umgehen.

Bedarf

――Ich möchte die Top 10 der täglichen Verkaufsrankings anzeigen. ――Ich möchte in der Lage sein, den Datumsbereich nach Tag, Woche, Monat usw. umzuschalten. ――Ich möchte die Anzahl der Verkaufsrankings ändern können. ――Ich möchte es nach General und Genre getrennt sehen können.

Erfassungsmethode

Kibana

image.png

Sie können jetzt die TOP 5 eines bestimmten Tages anzeigen.

Abfrage

Die obige Konfiguration kann wie folgt als Abfrage ausgedrückt werden.

GET sample-ranking-qiita-*/_search
{
  "aggs": {
    "range": {
      "date_range": {
        "field": "execDate",
        "ranges": [
          {
            "from": "2018/01/15T00:00:00+09:00",
            "to": "2018/01/15T23:59:59+09:00"
          }
        ]
      },
      "aggs": {
        "title_rate": {
          "terms": {
            "field": "title.keyword",
            "size": 5,
            "order": {
              "sum_rate": "desc"
            }
          },
          "aggs": {
            "sum_rate": {
              "sum": {
                "field": "rate"
              }
            }
          }
        }
      }
    }
  }
}

Ist es nicht ein bisschen schwer, die Verschachtelung von Aggs zu sehen? Die folgenden drei Aggs sind definiert.

  1. Summiere die rates mit dem Namen sum_rate
  2. Zeigen Sie sum_rate by title in absteigender Reihenfolge mit dem Namen title_rate an
  3. Der Aggregationsbereich von "title_rate" liegt zwischen "von" und "bis" von "execDate".

SQL

Als Referenz ist dies das Bild von SQL. (Ich habe es nicht ausprobiert, es tut mir leid, wenn ich einen Fehler mache.)

ranking.sql


SELECT
	ranking
	, title
	, sum_rate
FROM (
	select
		ROW_NUMBER() OVER (ORDER BY sum_rate DESC) AS ranking
		, tb1.title
		, tb1.sum_rate 
	FROM (
		SELECT
			title
			, SUM(rate) as sum_rate 
		FROM
			tbl_ranking
		WHERE
			execDate >= '2018/01/15T00:00:00+09:00'
			AND execDate <= '2018/01/15T23:59:59+09:00'
		GROUP BY
			title
	) tb1
)
WHERE
	ranking <= 5

Akquisitionsergebnis

Wenn ich das Ergebnis der Abfrage erhalte, ist das Ergebnis von aggs im Tag "aggregations" verschachtelt. Die Liste im Tag "Buckets" ist die Ergebnisliste für jede Aggs. Die gleichen Titel, die in Kibana angezeigt werden, sind in einer Reihe. Der Gesamtratenwert stimmt ebenfalls überein.

{
  "took": 426,
  "timed_out": false,
  "_shards": {
    "total": 15,
    "successful": 15,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 24800,
    "max_score": 1,
    "hits": [
(Weggelassen)
    ]
  },
  "aggregations": {
    "range": {
      "buckets": [
        {
          // 3. title_Aggregierter Datumsbereich der Rate------------------------------
          "key": "2018/01/14T15:00:00+0000-2018/01/15T14:59:59+0000",
          "from": 1515942000000,
          "from_as_string": "2018/01/14T15:00:00+0000",
          "to": 1516028399000,
          "to_as_string": "2018/01/15T14:59:59+0000",
          "doc_count": 400,
          // 2.Titel eine andere Summe_Rate absteigende Reihenfolge------------------------------
          "title_rate": {
            "doc_count_error_upper_bound": -1,
            "sum_other_doc_count": 380,
            "buckets": [
              {
                "key": "Neue Eröffnung",
                "doc_count": 4,
                "sum_rate": {
                  // 1.Rate insgesamt------------
                  "value": 355
                }
              },
              {
                "key": "Schüler",
                "doc_count": 4,
                "sum_rate": {
                  "value": 337
                }
              },
              {
                "key": "Zwischentest",
                "doc_count": 4,
                "sum_rate": {
                  "value": 333
                }
              },
              {
                "key": "Die Wahrheit des Gastgebers",
                "doc_count": 4,
                "sum_rate": {
                  "value": 292
                }
              },
              {
                "key": "Ein Moment des Spiels",
                "doc_count": 4,
                "sum_rate": {
                  "value": 292
                }
              }
            ]
          }
        }
      ]
    }
  }
}

Java-API

Lassen Sie uns nun die obige Abfrage mit der Java-API abrufen.

AggsSampleViewer.java


public class AggsSampleViewer {

	private static Logger logger = LoggerFactory.getLogger(AggsSampleViewer.class);

	//Indexformat für Jahr / Monat(yyyyMM)
	private static DateTimeFormatter YM_INDEX_FORMATTER;
	//Ausführungsdatum Format(yyyy/MM/dd'T'HH:mm:ss+09:00)
	private static DateTimeFormatter DT_INDEX_FORMATTER;

	//Verschiedene Einstellungsinformationen
	QiitaSettingBean setting;


	/**
	 *Konstrukteur
	 * @Informationen zur Einstellung der Parameter
	 */
	public AggsSampleViewer(QiitaSettingBean setting) {
		super();
		this.setting = setting;

		DT_INDEX_FORMATTER = DateTimeFormatter.ofPattern(setting.getElasticearch().getExecDateFormat());
		YM_INDEX_FORMATTER = DateTimeFormatter.ofPattern(setting.getElasticearch().getIndexYmFormat());
	}

	/**
	 *Ausführung des Akquisitionsprozesses
	 *
	 * @param subIndex Subindexname(Null zum Zeitpunkt des vollständigen Erwerbs)
	 * @throws Exception
	 */
	public void execute(String subIndex) throws Exception {

        //Erstellen Sie einen Client für ELS in Java
        // setting.getElasticearch().getAddress():IP Adresse
        // setting.getElasticearch().getPort():Port-Nummer(Normalerweise 9300)
		TransportClient client = new PreBuiltTransportClient(Settings.EMPTY).addTransportAddress(
				new InetSocketTransportAddress(InetAddress.getByName(setting.getElasticearch().getAddress()),
						setting.getElasticearch().getPort()));

		//Indexgenerierung für die Suche(Wenn ein Subindex angegeben wird, ist auch ein Subindex enthalten)
		String searchIndex = setting.getElsImport().getIndex();
		if (StringUtils.isNotEmpty(subIndex)) {
			searchIndex = searchIndex + "-" + subIndex;
		}
		searchIndex += "*";

		logger.debug("■■■ Suchzielindex: " + searchIndex);

		// sum_Aggs for rate: Berechnen Sie den Gesamtwert der Rate
		AggregationBuilder sumRateAggs = AggregationBuilders.sum("sum_rate").field("rate");

		// title_Aggs for rate: Absteigende Reihenfolge der Rate nach Titel
		AggregationBuilder titleRateAggs = AggregationBuilders.terms("title_rate")
				//Nach Titel aggregieren
				.field("title.keyword")
				// setting.getRankingLimit():Ranking Nummer
				.size(setting.getRankingLimit())
				//Die Anzeigereihenfolge ist Summe_absteigende Reihenfolge der Rate
				.order(Order.aggregation("sum_rate", false))
				//zu subaggs, summe_Aggs für Rate hinzugefügt
				.subAggregation(sumRateAggs)
				;

		//Aggs für Datumsbereich
		AggregationBuilder dateRangeAggs = AggregationBuilders.dateRange("range")
				//Das Ziel des Datumsbereichs ist das Ausführungsdatum der Verarbeitung
				.field("execDate")
				//Stellen Sie den Datumsbereich vom Tag vor 0:00 am Verarbeitungszieltag bis 0:00 am Tag ein
				//Beispiel) setting.getNow(): 2018/01/16 13:20:35
				//		from:2018/01/15 00:00:00
				//		to  :2018/01/16 00:00:00
				.addRange(
						setting.getNow().minusDays(1).truncatedTo(ChronoUnit.DAYS).format(DT_INDEX_FORMATTER)
						, setting.getNow().truncatedTo(ChronoUnit.DAYS).format(DT_INDEX_FORMATTER)
				)
				//subaggs, Titel_Aggs für Rate hinzugefügt
				.subAggregation(titleRateAggs)
				;

		//Ganze Abfrage
		SearchRequestBuilder requestBuilder = client.prepareSearch(searchIndex)
				.addAggregation(dateRangeAggs);


		//Suchergebnisse abrufen
		SearchResponse res = requestBuilder.get();

		//Holen Sie sich das Ergebnis von Aggs der gesamten Abfrage
		for (Aggregation aggs : res.getAggregations()) {
			logger.debug("aggs name: "+ aggs.getName());
		}

		//[Aggs für Datumsbereich](Art:date_range) ----------------
		InternalDateRange resDateRangeAggs = res.getAggregations().get("range");
		//Überprüfen Sie den Eimer in Aggs auf Datumsbereich
		for (InternalDateRange.Bucket dateRangeBucket : resDateRangeAggs.getBuckets()) {
			//Schlüssel und Dokument_Die Anzahl kann mit jedem Eimer ermittelt werden
			logger.debug("* resDateRangeAggs bucket key: "+ dateRangeBucket.getKey());
			logger.debug("* resDateRangeAggs bucket doc_count: "+ dateRangeBucket.getDocCount());
			//Von und Bis kann erhalten werden, da es sich für den Datumsbereich auf den Bucket bezieht.
			logger.debug("* resDateRangeAggs bucket from: "+ dateRangeBucket.getFromAsString());
			logger.debug("* resDateRangeAggs bucket to: "+ dateRangeBucket.getToAsString());

			//Überprüfen Sie die Aggs-Ergebnisse in Aggs auf den Datumsbereich
			for (Aggregation aggs : dateRangeBucket.getAggregations()) {
				logger.debug("* resDateRangeAggs bucket aggs: "+ aggs.getName());
			}

			// 【title_Aggs für Rate](Art:terms) ----------------
			Terms resTitleRateAggs = dateRangeBucket.getAggregations().get("title_rate");

			// title_Überprüfen Sie den Eimer in Aggs auf Rate
			for (Terms.Bucket termBucket : resTitleRateAggs.getBuckets()) {
				logger.debug("** resTitleRateAggs bucket key: "+ termBucket.getKey());
				logger.debug("** resTitleRateAggs bucket doc_count: "+ termBucket.getDocCount());

				// title_Aggs Ergebnisbestätigung für Rate
				for (Aggregation aggs : termBucket.getAggregations()) {
					logger.debug("** resTitleRateAggs bucket aggs: "+ aggs.getName());
				}

				// 【sum_Aggs für Rate](Art:sum) ----------------
				Sum resSumRateAggs = termBucket.getAggregations().get("sum_rate");

				//Es gibt keine Aggs in Sum und Sie können Summenergebnisse erhalten
				logger.debug("*** resSumRateAggs sum name: "+ resSumRateAggs.getName());
				logger.debug("*** resSumRateAggs sum value: "+ resSumRateAggs.getValueAsString());
			}
		}
	}
}

Wir werden Aggs mit derselben verschachtelten Struktur wie die Abfrage definieren. Es ist ein wenig schwierig herauszufinden, welche Aggs verwendet werden sollen und welcher Eimer zurückgegeben wird ^^; Am besten überprüfen Sie die Formel in diesem Bereich.

Wie die Formel definiert wird, wird geschrieben, aber welche Art von Informationen als Ergebnis zurückgegeben werden, wird nicht geschrieben ... Wird dies berücksichtigt, indem die automatische Vervollständigung usw. vollständig genutzt wird?

https://www.elastic.co/guide/en/elasticsearch/client/java-api/current/java-aggs.html

Die Ausführung erfolgt wie folgt.

main.java


AggsSampleViewer viewer = new AggsSampleViewer(setting);

//Generierung von Informationen zum Gesamtranking
logger.debug("■ Gesamtrangliste----------------------------------------");
viewer.execute(null);

//Shonen Ranking Informationsgenerierung
logger.debug("■ Jungenrangliste----------------------------------------");
viewer.execute("boy");

Ausführungsergebnis

■ Gesamtrangliste---------------------------------------- 
■■■ Suchzielindex: sample-ranking-qiita* 
aggs name: range 
* resDateRangeAggs bucket key: 2018/01/14T15:00:00+0000-2018/01/15T15:00:00+0000 
* resDateRangeAggs bucket doc_count: 400 
* resDateRangeAggs bucket from: 2018/01/14T15:00:00+0000 
* resDateRangeAggs bucket to: 2018/01/15T15:00:00+0000 
* resDateRangeAggs bucket aggs: title_rate 
** resTitleRateAggs bucket key:Neue Eröffnung
** resTitleRateAggs bucket doc_count: 4 
** resTitleRateAggs bucket aggs: sum_rate 
*** resSumRateAggs sum name: sum_rate 
*** resSumRateAggs sum value: 355.0 
** resTitleRateAggs bucket key:Schüler
** resTitleRateAggs bucket doc_count: 4 
** resTitleRateAggs bucket aggs: sum_rate 
*** resSumRateAggs sum name: sum_rate 
*** resSumRateAggs sum value: 337.0 
** resTitleRateAggs bucket key:Zwischentest
** resTitleRateAggs bucket doc_count: 4 
** resTitleRateAggs bucket aggs: sum_rate 
*** resSumRateAggs sum name: sum_rate 
*** resSumRateAggs sum value: 333.0 
** resTitleRateAggs bucket key:Die Wahrheit des Gastgebers
** resTitleRateAggs bucket doc_count: 4 
** resTitleRateAggs bucket aggs: sum_rate 
*** resSumRateAggs sum name: sum_rate 
*** resSumRateAggs sum value: 292.0 
** resTitleRateAggs bucket key:Ein Moment des Spiels
** resTitleRateAggs bucket doc_count: 4 
** resTitleRateAggs bucket aggs: sum_rate 
*** resSumRateAggs sum name: sum_rate 
*** resSumRateAggs sum value: 292.0 
■ Jungenrangliste---------------------------------------- 
■■■ Suchzielindex: sample-ranking-qiita-boy* 
aggs name: range 
* resDateRangeAggs bucket key: 2018/01/14T15:00:00+0000-2018/01/15T15:00:00+0000 
* resDateRangeAggs bucket doc_count: 141 
* resDateRangeAggs bucket from: 2018/01/14T15:00:00+0000 
* resDateRangeAggs bucket to: 2018/01/15T15:00:00+0000 
* resDateRangeAggs bucket aggs: title_rate 
** resTitleRateAggs bucket key:Schüler
** resTitleRateAggs bucket doc_count: 4 
** resTitleRateAggs bucket aggs: sum_rate 
*** resSumRateAggs sum name: sum_rate 
*** resSumRateAggs sum value: 337.0 
** resTitleRateAggs bucket key:Knockout und Folge 2
** resTitleRateAggs bucket doc_count: 4 
** resTitleRateAggs bucket aggs: sum_rate 
*** resSumRateAggs sum name: sum_rate 
*** resSumRateAggs sum value: 287.0 
** resTitleRateAggs bucket key:Ergebnisse vorwärts und Aussehen
** resTitleRateAggs bucket doc_count: 4 
** resTitleRateAggs bucket aggs: sum_rate 
*** resSumRateAggs sum name: sum_rate 
*** resSumRateAggs sum value: 278.0 
** resTitleRateAggs bucket key:Herz
** resTitleRateAggs bucket doc_count: 4 
** resTitleRateAggs bucket aggs: sum_rate 
*** resSumRateAggs sum name: sum_rate 
*** resSumRateAggs sum value: 267.0 
** resTitleRateAggs bucket key:Meisterschaft
** resTitleRateAggs bucket doc_count: 3 
** resTitleRateAggs bucket aggs: sum_rate 
*** resSumRateAggs sum name: sum_rate 
*** resSumRateAggs sum value: 260.0 

Ich konnte die gleichen Ergebnisse wie Kibana und die Abfrage erzielen! Ah, es ist natürlich als aggregierte Abfrage, aber mit "aggs" können nur die für die Aggregation verwendeten Felder erfasst werden, und andere Felder (in diesem Fall Autoren usw. in diesem Fall) können nicht erfasst werden. Es wäre schön, wenn ich das Ergebnis der Verknüpfung mit dem Feld in "aggs" auf einmal mit "query" erhalten könnte, aber es scheint nicht so. Wenn Sie die "Größe" der "Abfrage" erhöhen, erhöht sich die Wahrscheinlichkeit, dass sie abgerufen wird, aber ich denke, dass die Obergrenze der "Größe" der "Abfrage" "10000" beträgt, sodass Sie sie definitiv in diesen Bereich bringen können. Wenn Sie sich nicht sicher sind, ist es besser, es ruhig mit einer anderen Abfrage zu bekommen.

Das Buchgenre wird als Subindex definiert, um die Einschränkung des Suchbereichs zu vereinfachen. Ich fragte mich, ob eine Verengung als Index die Belastung bei der Suche nach einem langfristigen Bereich so weit wie möglich verringern würde. Natürlich kann es in die Abfragebedingung aufgenommen werden.

AggsSampleViewer2.java


		//Abfrage zur Genrefilterung
		QueryBuilder queryBuilder = QueryBuilders.termQuery("index", subIndex);


		//Ganze Abfrage
		SearchRequestBuilder requestBuilder = client.prepareSearch(searchIndex)
				.addAggregation(dateRangeAggs);

		//Abfrage hinzufügen, wenn Subindex angegeben ist
		if (StringUtils.isNotEmpty(subIndex)) {
			requestBuilder.setQuery(queryBuilder);
		}

AggsSampleViewer3.java


		//
		AggregationBuilder dateRangeAggs = AggregationBuilders.dateRange("range")
				//
				.field("execDate")
				//Ändern des Datumsbereichs Wenn Sie den Datumsbereich ändern möchten, z. B. wöchentlich oder monatlich, wechseln Sie innerhalb von "addRange" zu "dateRangeAggs". Ich habe auch Informationen zur Bereichsdefinition erstellt, weil ich sie in den Einstellungen frei ändern möchte. Aggs für Datumsbereich Das Ziel des Datumsbereichs legt den Datumsbereich für das Ausführungsdatum der Verarbeitung von Von bis Bis des Suchbereichs fest.
				//Beispiel)Von 10 Tagen vor dem Ausführungstermin bis Mitternacht am Ausführungstag
				//"searchRange": {
				//		"from": "-10",
				//		"fromChrono": "days",
				//		"to": "0",
				//		"toChrono": "days"
				//}
				.addRange(
						DT_INDEX_FORMATTER.format(
								//Ab der Suchbereichseinstellung am Ausführungsdatum wird in Einheiten der angegebenen Einheiten hinzugefügt.
								setting.getNow()
									.plus(setting.getSearchRange().getFrom()
											, setting.getSearchRange().findFromChronoUnit())
								//Nach dem Tag abschneiden (genau um 0 Uhr machen)
								.truncatedTo(ChronoUnit.DAYS))
						, DT_INDEX_FORMATTER.format(
								//Fügen Sie das To der Suchbereichseinstellung am Ausführungsdatum in der angegebenen Einheit hinzu
								setting.getNow()
									.plus(setting.getSearchRange().getTo()
											, setting.getSearchRange().findToChronoUnit())
								//Nach dem Tag abschneiden (genau um 0 Uhr machen)
								.truncatedTo(ChronoUnit.DAYS))
				)
				//subaggs, Titel_Aggs für Rate hinzugefügt
				.subAggregation(titleRateAggs)
				;

RangeBean.java


public class RangeBean {
	private int from;
	private String fromChrono;
	private int to;
	private String toChrono;

	public ChronoUnit findFromChronoUnit() {
		return findChronoUnit(fromChrono);
	}

	public ChronoUnit findToChronoUnit() {
		return findChronoUnit(toChrono);
	}

	/**
	 *Ändern Sie die Chrono-Zeichenfolge in Einheit
	 *
	 * @param chrono
	 * @return
	 */
	public ChronoUnit findChronoUnit(String chrono) {
		if (StringUtils.equals(chrono, "days")) {
			return ChronoUnit.DAYS;
		}
		if (StringUtils.equals(chrono, "weeks")) {
			return ChronoUnit.WEEKS;
		}
		if (StringUtils.equals(chrono, "months")) {
			return ChronoUnit.MONTHS;
		}
		if (StringUtils.equals(chrono, "years")) {
			return ChronoUnit.YEARS;
		}

		return null;
	}

	//(Weggelassen)
}

Jetzt können Sie den Bereich nach Belieben festlegen, entweder monatlich oder drei Jahre.

Das nächste Mal möchte ich schreiben, wie eine Abfrage mit Java-API angegeben und abgerufen wird.

Recommended Posts

Verwenden der Elasticsearch Java-API (Aggregation)
Verwenden Sie die Elasticsearch Java-API (BulkRequest-Registrierung) [Zusätzliche Hinweise]
Ich habe versucht, die Elasticsearch-API in Java zu verwenden
Statusüberwachung von Java-Apps mit Elasticsearch