Blog

Elasticsearch BBQ vs. OpenSearch FAISS: Leistungsvergleich der Vektorsuche

Ein Leistungsvergleich zwischen Elasticsearch BBQ und OpenSearch FAISS.

Vektorsuche mit binärer Quantisierung: Elasticsearch mit BBQ ist 5x schneller als OpenSearch mit FAISS. Elastic hat von unserer Community Anfragen erhalten, die Leistungsunterschiede zwischen Elasticsearch und OpenSearch zu klären, insbesondere im Bereich der semantischen Suche/Vektorsuche. Daher haben wir diese Leistungstests durchgeführt, um klare, datenbasierte Vergleiche zu ermöglichen.

Elasticsearch BBQ vs. OpenSearch FAISS – Vergleich von Geschwindigkeit, Durchsatz und Trefferquote

Showdown der binären Quantisierung

Das Speichern hochdimensionaler Vektoren in ihrer ursprünglichen Form kann speicherintensiv sein. Quantisierungstechniken komprimieren diese Vektoren in eine kompakte Darstellung und reduzieren so den Speicherbedarf drastisch. Die Suche erfolgt dann im komprimierten Raum, was den Rechenaufwand reduziert und die Suche insbesondere bei großen Datensätzen beschleunigt.

Elastic hat sich zum Ziel gesetzt, Lucene zu einer leistungsstarken Vektor-Engine zu machen. Wir haben Better Binary Quantization (BBQ) in Elasticsearch 8.16 auf Basis von Lucene eingeführt und in den Versionen 8.18 und 9.0 weiterentwickelt. BBQ basiert auf einem neuen Ansatz der Skalarquantisierung , der die float32-Dimensionen auf Bits reduziert und so eine Speicherreduzierung von ca. 95 % bei gleichzeitig hoher Ranking-Qualität ermöglicht.

OpenSearch hingegen verwendet mehrere Vektor-Engines: nmslib (jetzt veraltet), Lucene und FAISS. In einem früheren Blog haben wir Elasticsearch und OpenSearch für die Vektorsuche verglichen. Wir haben drei verschiedene Datensätze verwendet und unterschiedliche Kombinationen von Engines und Konfigurationen auf beiden Produkten getestet.

In diesem Blog geht es um die derzeit in beiden Produkten verfügbaren binären Quantisierungsalgorithmen. Wir haben Elasticsearch mit BBQ und OpenSearch mit der binären Quantisierung von FAISS unter Verwendung des openai_vector Rally-Tracks getestet.

Das Hauptziel bestand darin, die Leistung beider Lösungen bei gleichem Erinnerungsniveau zu bewerten. Was bedeutet Rückruf ? Die Rückrufquote ist eine Kennzahl, die misst, wie viele relevante Ergebnisse erfolgreich von einem Suchsystem abgerufen werden.

Bei dieser Auswertung ist insbesondere der Recall@k wichtig, wobei k die Anzahl der berücksichtigten Top-Ergebnisse darstellt. Recall@10, Recall@50 und Recall@100 messen daher, wie viele der wirklich relevanten Ergebnisse in den ersten 10, 50 bzw. 100 abgerufenen Elementen erscheinen. Die Rückrufquote wird auf einer Skala von 0 bis 1 (oder 0 % bis 100 % Präzision) ausgedrückt. Und das ist wichtig, weil wir über ungefähres KNN (ANN) und nicht über exaktes KNN sprechen, bei dem die Rückrufrate immer 1 (100 %) beträgt.

Für jeden Wert von k haben wir auch n angegeben, also die Anzahl der Kandidaten, die vor der endgültigen Rangfolge berücksichtigt wurden. Dies bedeutet, dass das System für Recall@10, Recall@50 und Recall@100 zunächst n Kandidaten mithilfe des binären Quantisierungsalgorithmus abruft und sie dann in eine Rangfolge bringt, um zu bestimmen, ob die obersten k Ergebnisse die erwarteten relevanten Elemente enthalten.

Durch die Steuerung von n können wir den Kompromiss zwischen Effizienz und Genauigkeit analysieren. Ein höherer n-Wert erhöht normalerweise die Rückrufquote, da mehr Kandidaten für die Rangfolge zur Verfügung stehen, erhöht aber auch die Latenz und verringert den Durchsatz. Umgekehrt beschleunigt ein niedrigerer n- Wert zwar die Abfrage, kann aber die Trefferquote verringern, wenn zu wenige relevante Kandidaten im anfänglichen Satz enthalten sind.

In diesem Vergleich zeigte Elasticsearch bei identischen Setups eine geringere Latenz und einen höheren Durchsatz als OpenSearch.

Methodik

Die vollständige Konfiguration sowie Terraform-Skripte, Kubernetes-Manifeste und der spezifische Rally-Track sind in diesem Repository unter openai_vector_bq verfügbar.

Wie bei vorherigen Benchmarks haben wir einen Kubernetes-Cluster verwendet, der aus Folgendem besteht:

  • 1 Knotenpool für Elasticsearch 9.0 mit 3 e2-standard-32 Maschinen (128 GB RAM und 32 CPUs)

  • 1 Knotenpool für OpenSearch 2.19 mit 3 e2-standard-32 Maschinen (128 GB RAM und 32 CPUs)

  • 1 Knotenpool für Rally mit 2 e2-standard-4 Maschinen (16 GB RAM und 4 CPUs)

Elasticsearch BBQ vs Opensearch FAISS Methodik-Setup

Wir haben einen Elasticsearch-Cluster Version 9.0 und einen OpenSearch-Cluster Version 2.19 eingerichtet.

Sowohl Elasticsearch als auch OpenSearch wurden mit genau demselben Setup getestet: Wir haben openai_vector Rally Track mit einigen Modifikationen verwendet, das 2,5 Millionen Dokumente aus dem NQ-Datensatz verwendet, angereichert mit Einbettungen, die mit dem Modell text-embedding-ada-002 von OpenAI generiert wurden.

{
  "source-file": "open_ai_corpus-initial-indexing.json.bz2",
  "document-count": 2580961,
  "compressed-bytes": 32076749416,
  "uncompressed-bytes": 90263571686
}

Die Ergebnisse berichten über die gemessene Latenz und den Durchsatz bei verschiedenen Rückrufstufen (Recall@10, Recall@50 und Recall@100) unter Verwendung von 8 gleichzeitigen Clients zur Durchführung von Suchvorgängen. Wir haben einen einzelnen Shard und keine Replikate verwendet.

Wir haben die folgenden Kombinationen von kn-rescore ausgeführt, zB 10-2000-2000 oder k:10, n:2000 und rescore:2000 würden die besten k (10) von n Kandidaten (2000) abrufen, indem ein Rescore auf 2000 Ergebnisse angewendet wird (was einem „Oversample-Faktor“ von 1 entspricht). Jede Suche wurde 10.000 Mal ausgeführt, mit 1.000 Suchvorgängen als Aufwärmphase:

Rückruf@10

  • 10-40-40

  • 10-50-50

  • 10-100-100

  • 10-200-200

  • 10-500-500

  • 10-750-750

  • 10-1000-1000

  • 10-1500-1500

  • 10-2000-2000

Rückruf@50

  • 50-150-150

  • 50-200-200

  • 50-250-250

  • 50-500-500

  • 50-750-750

  • 50-1000-1000

  • 50-1200-1200

  • 50-1500-1500

  • 50-2000-2000

Rückruf@100

  • 100-200-200

  • 100-250-250

  • 100-300-300

  • 100-500-500

  • 100-750-750

  • 100-1000-1000

  • 100-1200-1200

  • 100-1500-1500

  • 100-2000-2000

Um den Benchmark zu replizieren, sind in den Kubernetes-Manifesten für Rally-Elasticsearch und Rally-Opensearch alle relevanten Variablen in einer ConfigMap externalisiert, die hier (ES) und hier (OS) verfügbar ist. Der Parameter search_ops kann angepasst werden, um jede Kombination aus k, n und Rescore zu testen.

OpenSearch Rally-Konfiguration

/k8s/rally-openai_vector-os-bq.yml

apiVersion: v1
kind: ConfigMap
metadata:
  name: rally-params-os
  labels:
    app: rally-opensearch
data:
  user-tags.json: |
    {
      "product": "OpenSearch",
      "product-version": "OpenSearch-2.19.0",
      "product-label": "OpenSearch-2.19-faiss",
      "benchmark-run": "19-feb-recall@100"
    }
  track-params.json: |
    {
      "mapping_type": "vectors-only-mapping-with-docid",
      "standalone_search_clients": 8,
      "standalone_search_iterations": 5000,
      "ann_threshold": 0,
      "vector_mode": "on_disk",
      "compression_level": "32x",
      "vector_method_name": "hnsw",
      "vector_method_engine": "faiss",
      "search_ops": [
        [100, 200, 200],
        [100, 250, 250],
        [100, 300, 300],
        [100, 500, 500],
        [100, 750, 750],
        [100, 1000, 1000],
        [100, 1200, 1200],
        [100, 1500, 1500],
        [100, 2000, 2000]
      ]
    }

Opensearch-Indexkonfiguration

Die Variablen aus der ConfigMap werden dann auf die Indexkonfiguration angewendet, einige Parameter bleiben unverändert. Die 1-Bit-Quantisierung in OpenSearch wird durch Einstellen der Komprimierungsstufe auf „32x“ konfiguriert.

index-vectors-only-mapping-with-docid-mapping.json

{
  "settings": {
    {% if preload_pagecache %}
    "index.store.preload": [
      "vec", "vex", "vem", "veq", "veqm", "veb", "vebm"
    ],
    {% endif %}
    "index.number_of_shards": {{ number_of_shards | default(1) }},
    "index.number_of_replicas": {{ number_of_replicas | default(0) }},
    "index.knn": true,
    "index.knn.advanced.approximate_threshold": {{ ann_threshold | default(15000) }}
  },
  "mappings": {
    "dynamic": false,
    "properties": {
      "docid": {
        "type": "keyword"
      },
      "emb": {
        "type": "knn_vector",
        "dimension": 1536,
        "space_type": "innerproduct",
        "data_type": "float",
        "mode": {{ vector_mode | default("in_memory") | tojson }},
        "compression_level": {{ compression_level | default("32x") | tojson }},
        "method": {
          "name": {{ vector_method_name | default("hnsw") | tojson }},
          "engine": {{ vector_method_engine | default("faiss") | tojson }},
          "parameters": {
            "ef_construction": 100,
            "m": 16
          }
        }
      }
    }
  }
}

Elasticsearch Rally-Konfiguration

/k8s/rally-openai_vector-es-bq.yml

apiVersion: v1
kind: ConfigMap
metadata:
  name: rally-params-es
  labels:
    app: rally-elasticsearch
data:
  user-tags.json: |
    {
      "product": "Elasticsearch",
      "product-version": "Elasticsearch-9.0.0-ade01164",
      "product-label": "Elasticsearch-9.0-BBQ",
      "benchmark-run": "19-feb-recall@100"
    }
  track-params.json: |
    {
      "mapping_type": "vectors-only-mapping-with-docid",
      "standalone_search_clients": 8,
      "standalone_search_iterations": 5000,
      "vector_index_type": "bbq_hnsw",
      "search_ops": [
        [100, 200, 200],
        [100, 250, 250],
        [100, 300, 300],
        [100, 500, 500],
        [100, 750, 750],
        [100, 1000, 1000],
        [100, 1200, 1200],
        [100, 1500, 1500],
        [100, 2000, 2000]
      ]
    }

Elasticsearch-Indexkonfiguration

index-vectors-only-mapping-with-docid-mapping.json

{
  "settings": {
    {# non-serverless-index-settings-marker-start #}
    {%- if build_flavor != "serverless" or serverless_operator == true -%}
    {% if preload_pagecache %}
    "index.store.preload": [ "vec", "vex", "vem", "veq", "veqm", "veb", "vebm" ],
    {% endif %}
    "index.number_of_shards": {{ number_of_shards | default(1) }},
    "index.number_of_replicas": {{ number_of_replicas | default(0) }}
    {%- endif -%}
    {# non-serverless-index-settings-marker-end #}
  },
  "mappings": {
    "dynamic": false,
    "properties": {
      "docid": {
        "type": "keyword"
      },
      "emb": {
        "type": "dense_vector",
        "element_type": "float",
        "dims": 1536,
        "index": true,
        "similarity": "dot_product",
        "index_options": {
          "type": {{ vector_index_type | default("bbq_hnsw") | tojson }},
          "ef_construction": 100,
          "m": 16
        }
      }
    }
  }
}

Ergebnisse

Es gibt mehrere Möglichkeiten, die Ergebnisse zu interpretieren. Sowohl für die Latenz als auch für den Durchsatz haben wir auf jeder Rückrufebene ein vereinfachtes und ein detailliertes Diagramm erstellt. Es ist leicht, Unterschiede zu erkennen, wenn wir bei jeder Metrik das Prinzip „höher ist besser“ berücksichtigen. Allerdings ist die Latenz ein negativer Faktor (je niedriger, desto besser), während der Durchsatz ein positiver Faktor ist. Für die vereinfachten Diagramme haben wir (Rückruf / Latenz) * 10000 (einfach „Geschwindigkeit“ genannt) und Rückruf * Durchsatz verwendet. Beide Messwerte bedeuten also, dass mehr Geschwindigkeit und mehr Durchsatz besser sind. Lasst uns loslegen.

Rückruf @ 10 - vereinfacht

Bei dieser Rückrufebene ist Elasticsearch BBQ bis zu 5-mal schneller (durchschnittlich 3,9-mal schneller) und hat im Durchschnitt einen 3,2-mal höheren Durchsatz als OpenSearch FAISS.

Elasticsearch BBQ ist bis zu 5-mal schneller (durchschnittlich 3,9-mal schneller) und bietet im Durchschnitt einen 3,2-mal höheren Durchsatz als OpenSearch FAISS (Recall@10).
Elasticsearch BBQ ist bis zu 5-mal schneller (durchschnittlich 3,9-mal schneller) und bietet im Durchschnitt einen 3,2-mal höheren Durchsatz als OpenSearch FAISS.

Rückruf @ 10 - Detailliert

Detaillierter Vergleich der Latenzzeiten von `recall@10`: Elasticsearch BBQ vs. Opensearch FAISS
Detaillierter Vergleich des Durchsatzes bei recall@10: Elasticsearch BBQ vs. Opensearch FAISS.

Aufgabe

Latenz.Mittelwert

Durchsatz.Mittelwert

Durchschnittliche Rückrufzahl

Elasticsearch-9.0-BBQ

10-100-100

11,70

513,58

0,89

Elasticsearch-9.0-BBQ

10-1000-100

27,33

250,55

0,95

Elasticsearch-9.0-BBQ

10-1500-1500

35,93

197,26

0,95

Elasticsearch-9.0-BBQ

10-200-200

13.33

456,16

0,92

Elasticsearch-9.0-BBQ

10-2000-2000

44,27

161,40

0,95

Elasticsearch-9.0-BBQ

10-40-40

10,97

539,94

0,84

Elasticsearch-9.0-BBQ

10-50-50

11.00

535,73

0,85

Elasticsearch-9.0-BBQ

10-500-500

19,52

341,45

0,93

Elasticsearch-9.0-BBQ

10-750-750

22,94

295,19

0,94

OpenSearch-2.19-faiss

10-100-100

35,59

200,61

0,94

OpenSearch-2.19-faiss

10-1000-1000

156,81

58,30

0,96

OpenSearch-2.19-faiss

10-1500-1500

181,79

42,97

0,96

OpenSearch-2.19-faiss

10-200-200

47,91

155,16

0,95

OpenSearch-2.19-faiss

10-2000-2000

232,14

31,84

0,96

OpenSearch-2.19-faiss

10-40-40

27,55

249,25

0,92

OpenSearch-2.19-faiss

10-50-50

28,78

245,14

0,92

OpenSearch-2.19-faiss

10-500-500

79,44

97,06

0,96

OpenSearch-2.19-faiss

10-750-750

104,19

75,49

0,96

Rückruf @ 50 - vereinfacht

Bei diesem Rückrufniveau ist Elasticsearch BBQ bis zu 5x schneller (durchschnittlich 4,2x schneller) und hat durchschnittlich 3,9x mehr Durchsatz als OpenSearch FAISS.

Recal @50 Vektorleistungsvergleich Elasticsearch BBQ vs Opensearch FAISS
Elasticsearch BBQ ist bis zu 5-mal schneller (durchschnittlich 4,2-mal schneller) und bietet im Durchschnitt einen 3,9-mal höheren Durchsatz als OpenSearch FAISS.

Detaillierte Ergebnisse – Rückruf @ 50

Recall@50 Elasticsearch BBQ und Opensearch FAISS Latenzergebnisse
Recall@50 Elasticsearch BBQ und Opensearch FAISS Durchsatzergebnisse

Aufgabe

Latenzmittelwert

Durchsatzmittelwert

Durchschnittliche Rückrufrate

Elasticsearch-9.0-BBQ

50-1000-1000

25,71

246,44

0,95

Elasticsearch-9.0-BBQ

50-1200-1200

28,81

227,85

0,95

Elasticsearch-9.0-BBQ

50-150-150

13.43

362,90

0,90

Elasticsearch-9.0-BBQ

50-1500-1500

33,38

202,37

0,95

Elasticsearch-9.0-BBQ

50-200-200

12,99

406,30

0,91

Elasticsearch-9.0-BBQ

50-2000-2000

42,63

163,68

0,95

Elasticsearch-9.0-BBQ

50-250-250

14.41

373,21

0,92

Elasticsearch-9.0-BBQ

50-500-500

17.15

341,04

0,93

Elasticsearch-9.0-BBQ

50-750-750

31,25

248,60

0,94

OpenSearch-2.19-faiss

50-1000-1000

125,35

62,53

0,96

OpenSearch-2.19-faiss

50-1200-1200

143,87

54,75

0,96

OpenSearch-2.19-faiss

50-150-150

43,64

130,01

0,89

OpenSearch-2.19-faiss

50-1500-1500

169,45

46,35

0,96

OpenSearch-2.19-faiss

50-200-200

48,05

156,07

0,91

OpenSearch-2.19-faiss

50-2000-2000

216,73

36,38

0,96

OpenSearch-2.19-faiss

50-250-250

53,52

142,44

0,93

OpenSearch-2.19-faiss

50-500-500

78,98

97,82

0,95

OpenSearch-2.19-faiss

50-750-750

103,20

75,86

0,96

Rückruf @ 100

Bei dieser Rückrufebene ist Elasticsearch BBQ bis zu 5-mal schneller (durchschnittlich 4,6-mal schneller) und hat im Durchschnitt einen 3,9-mal höheren Durchsatz als OpenSearch FAISS.

Recall @100 Elasticsearch BBQ V Opensearch FAISS results
Vergleich der Latenz- und Durchsatzleistung von Elasticsearch BBQ und Opensearch FAISS

Detaillierte Ergebnisse – Rückruf @ 100

Detaillierte Latenzergebnisse – Recall @ 100 Elasticsearch BBQ vs Opensearch FAISS
Detaillierte Durchsatzergebnisse - Recall @ 100 Elasticsearch BBQ vs Opensearch FAISS.

Aufgabe

Latenz.Mittelwert

Durchsatz.Mittelwert

Durchschnittliche Rückrufzahl

Elasticsearch-9.0-BBQ

100-1000-1000

27,82

243,22

0,95

Elasticsearch-9.0-BBQ

100-1200-1200

31.14

224,04

0,95

Elasticsearch-9.0-BBQ

100-1500-1500

35,98

193,99

0,95

Elasticsearch-9.0-BBQ

100-200-200

14.18

403,86

0,88

Elasticsearch-9.0-BBQ

100-2000-2000

45,36

159,88

0,95

Elasticsearch-9.0-BBQ

100-250-250

14,77

433,06

0,90

Elasticsearch-9.0-BBQ

100-300-300

14,61

375,54

0,91

Elasticsearch-9.0-BBQ

100-500-500

18,88

340,37

0,93

Elasticsearch-9.0-BBQ

100-750-750

23,59

285,79

0,94

OpenSearch-2.19-faiss

100-1000-1000

142,90

58,48

0,95

OpenSearch-2.19-faiss

100-1200-1200

153,03

51,04

0,95

OpenSearch-2.19-faiss

100-1500-1500

181,79

43,20

0,96

OpenSearch-2.19-faiss

100-200-200

50,94

131,62

0,83

OpenSearch-2.19-faiss

100-2000-2000

232,53

33,67

0,96

OpenSearch-2.19-faiss

100-250-250

57,08

131,23

0,87

OpenSearch-2.19-faiss

100-300-300

62,76

120,10

0,89

OpenSearch-2.19-faiss

100-500-500

84,36

91,54

0,93

OpenSearch-2.19-faiss

100-750-750

111,33

69,95

0,94

Verbesserungen beim Grillen

BBQ hat seit seiner Erstveröffentlichung eine lange Entwicklung durchgemacht. Zu Vergleichszwecken haben wir bei Elasticsearch 8.16 neben dem aktuellen einen Benchmark-Lauf von 8.16 eingefügt und können sehen, wie sich Rückruf und Latenz seitdem verbessert haben.

Verbesserungen der Latenz und des Recall-Werts von Elasticsearch 9.0 BBQ im Vergleich zu Elasticsearch 8.16 BBQ

In Elasticsearch 8.18 und 9.0 haben wir den Kernalgorithmus zur Quantisierung der Vektoren neu geschrieben. BBQ war in 8.16 zwar gut, aber die neuesten Versionen sind sogar noch besser. Sie können hier und hier darüber lesen. Kurz gesagt, jeder Vektor wird einzeln durch optimierte skalare Quantile quantisiert. Dadurch profitieren Benutzer von einer höheren Genauigkeit bei der Vektorsuche ohne Leistungseinbußen, wodurch die Vektorabfrage von Elasticsearch noch leistungsfähiger wird.

Fazit

In diesem Leistungsvergleich zwischen Elasticsearch BBQ und OpenSearch FAISS übertrifft Elasticsearch OpenSearch bei der Vektorsuche deutlich und erreicht über verschiedene Rückrufebenen hinweg durchschnittlich bis zu 5-mal schnellere Abfragegeschwindigkeiten und einen 3,9-mal höheren Durchsatz.

Zu den wichtigsten Ergebnissen gehören:

  • Recall@10: Elasticsearch BBQ ist bis zu 5x schneller (durchschnittlich 3,9x schneller) und hat im Durchschnitt einen 3,2x höheren Durchsatz als OpenSearch FAISS.

  • Recall@50: Elasticsearch BBQ ist bis zu 5x schneller (durchschnittlich 4,2x schneller) und hat im Durchschnitt einen 3,9x höheren Durchsatz als OpenSearch FAISS.

  • Recall@100: Elasticsearch BBQ ist bis zu 5x schneller (durchschnittlich 4,6x schneller) und hat im Durchschnitt einen 3,9x höheren Durchsatz als OpenSearch FAISS.

Diese Ergebnisse unterstreichen die Effizienz- und Leistungsvorteile von Elasticsearch BBQ, insbesondere in hochdimensionalen Vektorsuchszenarien. Die in Elasticsearch 8.16 eingeführte Better Binary Quantization (BBQ)-Technik bietet eine erhebliche Speicherreduzierung (~95 %) bei gleichzeitiger Beibehaltung einer hohen Ranking-Qualität und ist daher eine hervorragende Wahl für groß angelegte Vektorsuchanwendungen.

Bei Elastic arbeiten wir unermüdlich an Innovationen, um Apache Lucene und Elasticsearch zu verbessern und die beste Vektordatenbank für Such- und Abrufanwendungsfälle bereitzustellen, einschließlich RAG (Retrieval Augmented Generation). Unsere jüngsten Fortschritte haben die Leistung erheblich gesteigert und die Vektorsuche schneller und platzsparender gemacht als zuvor, aufbauend auf den Vorteilen von Lucene 10. Dieser Blog ist ein weiteres Beispiel für diese Innovation.

Zugehörige Inhalte

LINQ to Elasticsearch ES|QL: C# schreiben, Elasticsearch abfragen

Florian Bernd

Erweiterung der Chatbot-Funktionen mit NLP und Vektorsuche in Elasticsearch

Priscilla Parodi

Elasticsearch vs. OpenSearch: Vergleich der Leistung bei der Vektorsuche

Ugo Sangiorgi

KI-Plagiat: Plagiatserkennung mit Elasticsearch

Priscilla Parodi

Fortgeschrittene RAG-Techniken Teil 2: Abfragen und Testen

Han Xiang Choong

Sind Sie bereit, hochmoderne Sucherlebnisse zu schaffen?

Eine ausreichend fortgeschrittene Suche kann nicht durch die Bemühungen einer einzelnen Person erreicht werden. Elasticsearch wird von Datenwissenschaftlern, ML-Ops-Experten, Ingenieuren und vielen anderen unterstützt, die genauso leidenschaftlich an der Suche interessiert sind wie Sie. Lasst uns in Kontakt treten und zusammenarbeiten, um das magische Sucherlebnis zu schaffen, das Ihnen die gewünschten Ergebnisse liefert.

Probieren Sie es selbst aus