Modelowanie złożonych typów danych w usłudze Azure AI Search

Zewnętrzne zestawy danych używane do wypełniania indeksu usługi Azure AI Search mogą znajdować się w wielu kształtach. Czasami obejmują hierarchiczną lub zagnieżdżonych podstruktury. Przykłady mogą obejmować wiele adresów dla jednego klienta, wiele kolorów i rozmiarów pojedynczej jednostki SKU, wielu autorów pojedynczej książki itd. W kategoriach modelowania można zobaczyć te struktury nazywane złożonymi, złożonymi, złożonymi lub agregowanymitypami danych. Termin Azure AI Search używa dla tej koncepcji jest typem złożonym. W usłudze Azure AI Search złożone typy są modelowane przy użyciu złożonych pól. Pole złożone to pole zawierające pola podrzędne (podpola), które mogą być dowolnym typem danych, w tym innymi innymi typami złożonymi. Działa to podobnie jak typy danych strukturalnych w języku programowania.

Pola złożone reprezentują pojedynczy obiekt w dokumencie lub tablicę obiektów w zależności od typu danych. Pola typu Edm.ComplexType reprezentują pojedyncze obiekty, a pola typu Collection(Edm.ComplexType) reprezentują tablice obiektów.

Usługa Azure AI Search natywnie obsługuje złożone typy i kolekcje. Te typy umożliwiają modelowanie niemal dowolnej struktury JSON w indeksie usługi Azure AI Search. W poprzednich wersjach interfejsów API usługi Azure AI Search można zaimportować tylko spłaszczone zestawy wierszy. W najnowszej wersji indeks może teraz ściślej odpowiadać danym źródłowym. Innymi słowy, jeśli dane źródłowe mają złożone typy, indeks może mieć również złożone typy.

Aby rozpocząć, zalecamy zestaw danych Hotele, który można załadować w kreatorze importu danych w witrynie Azure Portal. Kreator wykrywa złożone typy w źródle i sugeruje schemat indeksu na podstawie wykrytych struktur.

Uwaga

Obsługa typów złożonych stała się ogólnie dostępna, począwszy od .api-version=2019-05-06

Jeśli rozwiązanie wyszukiwania jest oparte na wcześniejszych obejściach spłaszczonego zestawu danych w kolekcji, należy zmienić indeks tak, aby uwzględniał złożone typy tak, jak są obsługiwane w najnowszej wersji interfejsu API. Aby uzyskać więcej informacji na temat uaktualniania wersji interfejsu API, zobacz Uaktualnianie do najnowszej wersji interfejsu API REST lub Uaktualnianie do najnowszej wersji zestawu .NET SDK.

Przykład złożonej struktury

Poniższy dokument JSON składa się z prostych pól i pól złożonych. Pola złożone, takie jak Address i Rooms, mają pola podrzędne. Address ma jeden zestaw wartości dla tych pól podrzędnych, ponieważ jest to pojedynczy obiekt w dokumencie. Rooms Natomiast ma wiele zestawów wartości dla swoich pól podrzędnych, po jednym dla każdego obiektu w kolekcji.

{
  "HotelId": "1",
  "HotelName": "Secret Point Motel",
  "Description": "Ideally located on the main commercial artery of the city in the heart of New York.",
  "Tags": ["Free wifi", "on-site parking", "indoor pool", "continental breakfast"],
  "Address": {
    "StreetAddress": "677 5th Ave",
    "City": "New York",
    "StateProvince": "NY"
  },
  "Rooms": [
    {
      "Description": "Budget Room, 1 Queen Bed (Cityside)",
      "RoomNumber": 1105,
      "BaseRate": 96.99,
    },
    {
      "Description": "Deluxe Room, 2 Double Beds (City View)",
      "Type": "Deluxe Room",
      "BaseRate": 150.99,
    }
    . . .
  ]
}

Indeksowanie typów złożonych

Podczas indeksowania można mieć maksymalnie 3000 elementów we wszystkich złożonych kolekcjach w ramach jednego dokumentu. Elementem złożonej kolekcji jest elementem członkowskim tej kolekcji, więc w przypadku pokoi (jedyna złożona kolekcja w przykładzie hotelowym) każdy pokój jest elementem. W powyższym przykładzie, jeśli "Secret Point Motel" miał 500 pokoi, dokument hotelowy będzie miał 500 elementów pokoju. W przypadku zagnieżdżonych kolekcji każdy zagnieżdżony element jest również zliczany, oprócz elementu zewnętrznego (nadrzędnego).

Ten limit dotyczy tylko złożonych kolekcji, a nie złożonych typów (takich jak Adres) lub kolekcji ciągów (takich jak Tagi).

Tworzenie pól złożonych

Podobnie jak w przypadku dowolnej definicji indeksu, możesz użyć portalu, interfejsu API REST lub zestawu .NET SDK , aby utworzyć schemat zawierający złożone typy.

Inne zestawy SDK platformy Azure udostępniają przykłady w językach Python, Java i JavaScript.

  1. Zaloguj się w witrynie Azure Portal.

  2. Na stronie Przegląd usługi wyszukiwania wybierz kartę Indeksy.

  3. Otwórz istniejący indeks lub utwórz nowy indeks.

  4. Wybierz kartę Pola , a następnie wybierz pozycję Dodaj pole. Zostanie dodane puste pole. Jeśli pracujesz z istniejącą kolekcją pól, przewiń w dół, aby skonfigurować pole.

  5. Nadaj polu nazwę i ustaw typ na Edm.ComplexType wartość lub Collection(Edm.ComplexType).

  6. Wybierz wielokropek po prawej stronie, a następnie wybierz pozycję Dodaj pole lub Dodaj pole podrzędne, a następnie przypisz atrybuty.

Aktualizowanie pól złożonych

Wszystkie reguły ponownego indeksowania, które mają zastosowanie do pól w ogóle, nadal mają zastosowanie do pól złożonych. W tym miejscu dodano kilka głównych reguł, dodanie pola do typu złożonego nie wymaga ponownego kompilowania indeksu, ale większość modyfikacji.

Aktualizacje strukturalne definicji

Nowe pola podrzędne można dodawać do złożonego pola w dowolnym momencie bez konieczności ponownego kompilowania indeksu. Na przykład dodanie wartości "ZipCode" do Address lub "Udogodnienia" Rooms jest dozwolone, podobnie jak dodanie pola najwyższego poziomu do indeksu. Istniejące dokumenty mają wartość null dla nowych pól do momentu jawnego wypełnienia tych pól przez zaktualizowanie danych.

Zwróć uwagę, że w obrębie typu złożonego każde pole podrzędne ma typ i może mieć atrybuty, podobnie jak pola najwyższego poziomu

Aktualizacje danych

Aktualizowanie istniejących dokumentów w indeksie za pomocą upload akcji działa tak samo w przypadku złożonych i prostych pól: wszystkie pola są zastępowane. merge Jednak (lub mergeOrUpload w przypadku zastosowania do istniejącego dokumentu) nie działa tak samo we wszystkich polach. W szczególności merge nie obsługuje scalania elementów w kolekcji. To ograniczenie istnieje w przypadku kolekcji typów pierwotnych i kolekcji złożonych. Aby zaktualizować kolekcję, musisz pobrać pełną wartość kolekcji, wprowadzić zmiany, a następnie uwzględnić nową kolekcję w żądaniu interfejsu API indeksowania.

Wyszukiwanie pól złożonych

Wyrażenia wyszukiwania w dowolnej formie działają zgodnie z oczekiwaniami w przypadku typów złożonych. Jeśli dowolne pole z możliwością wyszukiwania lub pole podrzędne w dowolnym miejscu w dokumencie jest zgodne, sam dokument jest zgodny.

Zapytania są bardziej zniuansowane, gdy masz wiele terminów i operatorów, a niektóre terminy mają określone nazwy pól, jak to możliwe w składni Lucene. Na przykład to zapytanie próbuje dopasować dwa terminy: "Portland" i "OR" względem dwóch pól podrzędnych pola adresu:

search=Address/City:Portland AND Address/State:OR

Zapytania takie jak te są niekorzystne w przypadku wyszukiwania pełnotekstowego, w przeciwieństwie do filtrów. W filtrach zapytania dotyczące podpole złożonej kolekcji są skorelowane przy użyciu zmiennych zakresu w elem any lub all. Powyższe zapytanie Lucene zwraca dokumenty zawierające zarówno "Portland, Maine" i "Portland, Oregon" wraz z innymi miastami w Oregonie. Dzieje się tak, ponieważ każda klauzula ma zastosowanie do wszystkich wartości pola w całym dokumencie, więc nie ma pojęcia "bieżącego dokumentu podrzędnego". Aby uzyskać więcej informacji na ten temat, zobacz Understanding OData collection filters in Azure AI Search (Omówienie filtrów kolekcji OData w usłudze Azure AI Search).

Wybieranie pól złożonych

Parametr służy do wybierania $select pól zwracanych w wynikach wyszukiwania. Aby użyć tego parametru, aby wybrać określone pola podrzędne pola złożonego, uwzględnij pole nadrzędne i pole podrzędne oddzielone ukośnikiem (/).

$select=HotelName, Address/City, Rooms/BaseRate

Pola muszą być oznaczone jako Możliwe do pobrania w indeksie, jeśli mają być wyświetlane w wynikach wyszukiwania. W instrukcji $select można używać tylko pól oznaczonych jako Możliwe do pobrania.

Filtrowanie, tworzenie aspektów i sortowanie złożonych pól

Ta sama składnia ścieżki OData używana do filtrowania i wyszukiwania pól może być również używana do tworzenia aspektów, sortowania i wybierania pól w żądaniu wyszukiwania. W przypadku typów złożonych reguły określają, które pola podrzędne mogą być oznaczone jako sortowalne lub aspektowe. Aby uzyskać więcej informacji na temat tych reguł, zobacz Dokumentację interfejsu API tworzenia indeksu.

Pola podrzędne tworzenia aspektów

Każde pole podrzędne można oznaczyć jako element aspektowy, chyba że ma Edm.GeographyPoint typ lub Collection(Edm.GeographyPoint).

Liczby dokumentów zwracanych w wynikach aspektu są obliczane dla dokumentu nadrzędnego (hotel), a nie dokumentów podrzędnych w złożonej kolekcji (pokoje). Załóżmy na przykład, że hotel ma 20 pokoi typu "suite". Biorąc pod uwagę ten parametr facet=Rooms/Typeaspektu , liczba aspektów jest jedną dla hotelu, a nie 20 dla pokoi.

Sortowanie złożonych pól

Operacje sortowania dotyczą dokumentów (hoteli), a nie dokumentów podrzędnych (Pokoje). Jeśli masz złożoną kolekcję typów, taką jak Pokoje, ważne jest, aby pamiętać, że w ogóle nie można sortować w pokojach. W rzeczywistości nie można sortować w żadnej kolekcji.

Operacje sortowania działają, gdy pola mają pojedynczą wartość na dokument, niezależnie od tego, czy pole jest prostym polem, czy polem podrzędnym w typie złożonym. Na przykład może być sortowane, Address/City ponieważ istnieje tylko jeden adres na hotel, więc $orderby=Address/City sortuje hotele według miasta.

Filtrowanie w polach złożonych

W wyrażeniu filtru można odwoływać się do pól podrzędnych pola złożonego pola. Wystarczy użyć tej samej składni ścieżki OData, która jest używana do tworzenia aspektów, sortowania i wybierania pól. Na przykład następujący filtr zwraca wszystkie hotele w Kanadzie:

$filter=Address/Country eq 'Canada'

Aby filtrować według złożonego pola kolekcji, można użyć wyrażenia lambda z operatoramianyi .all W takim przypadku zmienna zakresu wyrażenia lambda jest obiektem z polami podrzędnymi. Można odwoływać się do tych pól podrzędnych przy użyciu standardowej składni ścieżki OData. Na przykład następujący filtr zwraca wszystkie hotele z co najmniej jednym pokojem typu deluxe i wszystkimi pokojami niesmokingowymi:

$filter=Rooms/any(room: room/Type eq 'Deluxe Room') and Rooms/all(room: not room/SmokingAllowed)

Podobnie jak w przypadku prostych pól najwyższego poziomu, proste pola podrzędne złożonych pól mogą być uwzględniane tylko w filtrach, jeśli mają atrybut filtrowalny ustawiony na true wartość w definicji indeksu. Aby uzyskać więcej informacji, zobacz Dokumentację interfejsu API tworzenia indeksu.

Usługa Azure Search ma ograniczenie, że złożone obiekty w kolekcjach w jednym dokumencie nie mogą przekraczać 3000.

Podczas indeksowania użytkownicy napotkają następujący błąd, gdy złożone kolekcje przekraczają limit 3000.

"Kolekcja w dokumencie przekracza maksymalny limit wszystkich złożonych kolekcji. Dokument z kluczem "1052" zawiera obiekty "4303" w kolekcjach (tablice JSON). Co najwyżej 3000 obiektów może znajdować się w kolekcjach w całym dokumencie. Usuń obiekty z kolekcji i spróbuj ponownie zaindeksować dokument".

W niektórych przypadkach użycia może być konieczne dodanie ponad 3000 elementów do kolekcji. W tych przypadkach użycia możemy przesłać potokiem (|) lub użyć dowolnej formy ogranicznika, aby rozdzielić wartości, połączyć je i zapisać jako ciąg rozdzielany. Nie ma ograniczeń dotyczących liczby ciągów przechowywanych w tablicy w usłudze Azure Search. Przechowywanie tych złożonych wartości jako ciągów pozwala uniknąć ograniczenia. Klient musi sprawdzić, czy to obejście spełnia wymagania dotyczące scenariusza.

Na przykład nie byłoby możliwe użycie typów złożonych, jeśli poniższa tablica "searchScope" zawierała więcej niż 3000 elementów.


"searchScope": [
  {
     "countryCode": "FRA",
     "productCode": 1234,
     "categoryCode": "C100" 
  },
  {
     "countryCode": "USA",
     "productCode": 1235,
     "categoryCode": "C200" 
  }
]

Przechowywanie tych złożonych wartości jako ciągów z ogranicznikiem pozwala uniknąć ograniczenia

"searchScope": [
        "|FRA|1234|C100|",
        "|FRA|*|*|",
        "|*|1234|*|",
        "|*|*|C100|",
        "|FRA|*|C100|",
        "|*|1234|C100|"
]

Zamiast przechowywać je z symbolami wieloznacznymi, możemy również użyć analizatora niestandardowego dzielącego wyraz na | w celu zmniejszenia rozmiaru magazynu.

Powód, dla którego przechowywaliśmy wartości z symbolami wieloznacznymi zamiast przechowywać je tak jak poniżej

|FRA|1234|C100|

Jest przeznaczony do wyszukiwania scenariuszy, w których klient może chcieć wyszukać elementy, które mają kraj Francja, niezależnie od produktów i kategorii. Podobnie klient może potrzebować wyszukiwania, aby sprawdzić, czy produkt ma produkt 1234, niezależnie od kraju lub kategorii.

Jeśli przechowywaliśmy tylko jeden wpis

|FRA|1234|C100|

bez symboli wieloznacznych, jeśli użytkownik chce filtrować tylko we Francji, nie możemy przekonwertować danych wejściowych użytkownika w celu dopasowania do tablicy "searchScope", ponieważ nie wiemy, jaka kombinacja Francji jest obecna w naszej tablicy "searchScope"

Jeśli użytkownik chce filtrować tylko według kraju, załóżmy, że Francja. Wprowadzimy dane wejściowe użytkownika i skonstruujemy je jako ciąg, jak pokazano poniżej:

|FRA|*|*|

którego następnie możemy użyć do filtrowania w usłudze Azure Search w miarę wyszukiwania w tablicy wartości elementów

foreach (var filterItem in filterCombinations)
        {
            var formattedCondition = $"searchScope/any(s: s eq '{filterItem}')";
            combFilter.Append(combFilter.Length > 0 ? " or (" + formattedCondition + ")" : "(" + formattedCondition + ")");
        }

Podobnie, jeśli użytkownik wyszukuje kod produktu France i 1234, użyjemy danych wejściowych użytkownika, skonstruujemy go jako ciąg rozdzielany, jak pokazano poniżej, i dopasujemy go do naszej tablicy wyszukiwania.

|FRA|1234|*|

Jeśli użytkownik wyszukuje kod produktu 1234, użyjemy danych wejściowych użytkownika, skonstruujemy go jako ciąg rozdzielany, jak pokazano poniżej, i dopasujemy go do naszej tablicy wyszukiwania.

|*|1234|*|

Jeśli użytkownik wyszukuje kod kategorii C100, użyjemy danych wejściowych użytkownika, skonstruujemy go jako ciąg rozdzielany, jak pokazano poniżej, i dopasujemy go do naszej tablicy wyszukiwania.

|*|*|C100|

Jeśli użytkownik wyszukuje kod produktu France i kod kategorii 1234 i C100, użyjemy danych wejściowych użytkownika, skonstruujemy go jako ciąg rozdzielany, jak pokazano poniżej, i dopasujemy go do naszej tablicy wyszukiwania.

|FRA|1234|C100|

Jeśli użytkownik spróbuje wyszukać kraje, które nie znajdują się na naszej liście, nie będzie pasował do rozdzielanej tablicy "searchScope" przechowywanej w indeksie wyszukiwania i nie zostaną zwrócone żadne wyniki. Na przykład użytkownik wyszukuje kod Kanady i produktu 1234. Wyszukiwanie użytkownika zostanie przekonwertowane na

|CAN|1234|*|

Nie będzie to zgodne z żadnymi wpisami w rozdzielanej tablicy w naszym indeksie wyszukiwania.

Tylko powyższy wybór projektu wymaga wprowadzenia tej symboli wieloznacznego; Jeśli został on zapisany jako obiekt złożony, moglibyśmy po prostu wykonać jawne wyszukiwanie, jak pokazano poniżej.

           var countryFilter = $"searchScope/any(ss: search.in(countryCode ,'FRA'))";
            var catgFilter = $"searchScope/any(ss: search.in(categoryCode ,'C100'))";
            var combinedCountryCategoryFilter = "(" + countryFilter + " and " + catgFilter + ")";

W związku z tym możemy spełnić wymagania, w których musimy wyszukać kombinację wartości, przechowując ją jako ciąg rozdzielany zamiast złożonej kolekcji, jeśli nasze złożone kolekcje przekraczają limit usługi Azure Search. Jest to jedno z obejść, a klient musi sprawdzić, czy spełnia to wymagania dotyczące scenariusza.

Następne kroki

Wypróbuj zestaw danych Hotels w kreatorze Importuj dane. Aby uzyskać dostęp do danych, potrzebne są informacje o połączeniu usługi Azure Cosmos DB podane w pliku readme.

Dzięki tym informacjom pierwszym krokiem kreatora jest utworzenie nowego źródła danych usługi Azure Cosmos DB. Dalej w kreatorze, gdy dotrzesz do docelowej strony indeksu, zobaczysz indeks z typami złożonymi. Utwórz i załaduj ten indeks, a następnie wykonaj zapytania, aby zrozumieć nową strukturę.