Memodelkan jenis data kompleks di Azure AI Search
Himpunan data eksternal yang digunakan untuk mengisi indeks Pencarian Azure AI dapat berada dalam banyak bentuk. Terkadang data-data tersebut termasuk substruktur hierarkis atau berlapis. Contohnya mungkin mencakup beberapa alamat untuk satu pelanggan, beberapa warna dan ukuran untuk satu produk, beberapa penulis satu buku, dan sebagainya. Dalam istilah pemodelan, Anda mungkin melihat struktur ini disebut sebagai jenis data kompleks, majemuk, komposit atau agregat. Istilah yang digunakan Azure AI Search untuk konsep ini adalah jenis kompleks. Di Azure AI Search, jenis kompleks dimodelkan menggunakan bidang kompleks. Bidang kompleks adalah bidang yang berisi anak-anak (subbidang) yang bisa dari jenis data apa pun, termasuk jenis kompleks lainnya. Ini berfungsi dengan cara yang sama seperti jenis data terstruktur dalam bahasa pemrogram.
Bidang kompleks mewakili satu objek dalam dokumen, atau array objek, bergantung pada jenis data. Bidang jenis Edm.ComplexType
mewakili satu objek, sedangkan bidang jenis Collection(Edm.ComplexType)
mewakili array objek.
Azure AI Search secara asli mendukung jenis dan koleksi yang kompleks. Jenis ini memungkinkan Anda memodelkan hampir semua struktur JSON dalam indeks Pencarian Azure AI. Di versi Azure AI Search API sebelumnya, hanya set baris yang diratakan yang dapat diimpor. Pada versi terbaru, indeks Anda sekarang dapat lebih terkait erat dengan data sumber. Dengan kata lain, jika data sumber Anda memiliki jenis kompleks, indeks Anda juga dapat memiliki jenis kompleks.
Untuk memulai, kami sarankan Himpunan data Hotel, yang bisa Anda muat di wizard Impor data portal Microsoft Azure. Wizard mendeteksi jenis kompleks dalam sumber dan menyarankan skema indeks berdasarkan struktur yang terdeteksi.
Catatan
Dukungan untuk jenis kompleks menjadi tersedia secara umum mulai dari api-version=2019-05-06
.
Jika solusi pencarian Anda dibangun berdasarkan penyelesaian sebelumnya himpunan data yang diratakan dalam koleksi, Anda harus mengubah indeks untuk menyertakan jenis kompleks seperti yang didukung dalam versi API terbaru. Untuk informasi selengkapnya tentang peningkatan versi API, lihat Meningkatkan ke versi REST API terbaru atau Meningkatkan ke versi .NET SDK terbaru.
Contoh struktur kompleks
Dokumen JSON berikut terdiri dari bidang sederhana dan bidang kompleks. Bidang kompleks, seperti Address
dan Rooms
, memiliki subbidang. Address
memiliki satu set nilai untuk subbidang tersebut, karena merupakan objek tunggal dalam dokumen. Sebaliknya, Rooms
memiliki beberapa set nilai untuk subbidangnya, satu untuk setiap objek dalam koleksi.
{
"HotelId": "1",
"HotelName": "Stay-Kay City Hotel",
"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,
}
. . .
]
}
Membuat bidang kompleks
Seperti definisi indeks apa pun, Anda dapat menggunakan portal, REST API, atau .NET SDK untuk membuat skema yang menyertakan jenis kompleks.
Azure SDK lainnya menyediakan sampel di Python, Java, dan JavaScript.
Masuk ke portal Azure.
Pada halaman Gambaran Umum layanan pencarian, pilih tab Indeks.
Buka indeks yang sudah ada atau buat indeks baru.
Pilih tab Bidang , lalu pilih Tambahkan bidang. Bidang kosong ditambahkan. Jika Anda bekerja dengan kumpulan bidang yang sudah ada, gulir ke bawah untuk menyiapkan bidang.
Beri nama bidang dan atur jenisnya ke atau
Edm.ComplexType
Collection(Edm.ComplexType)
.Pilih elipsis di ujung kanan, lalu pilih Tambahkan bidang atau Tambahkan subbidang, lalu tetapkan atribut.
Batas koleksi kompleks
Selama pengindeksan, Anda dapat memiliki maksimum 3.000 elemen di semua koleksi kompleks dalam satu dokumen. Elemen koleksi kompleks adalah anggota koleksi tersebut. Untuk Kamar (satu-satunya koleksi kompleks dalam contoh Hotel), setiap kamar adalah elemen. Dalam contoh di atas, jika "Stay-Kay City Hotel" memiliki 500 kamar, dokumen hotel akan memiliki 500 elemen kamar. Untuk koleksi kompleks berlapis, setiap elemen berlapis juga dihitung, selain elemen luar (induk).
Batas ini hanya berlaku untuk koleksi kompleks, dan bukan jenis kompleks (seperti Alamat) atau koleksi untai (karakter) (seperti Tag).
Memperbarui bidang kompleks
Semua aturan pengindeksan ulang yang berlaku terhadap bidang secara umum masih berlaku untuk bidang kompleks. Menambahkan bidang baru ke jenis kompleks tidak memerlukan pembangunan ulang indeks, tetapi sebagian besar modifikasi lainnya memerlukan pembangunan ulang.
Pembaruan struktural untuk definisi
Anda dapat menambahkan subbidang baru ke bidang kompleks kapan saja tanpa perlu membangun kembali indeks. Misalnya, menambahkan "ZipCode" ke Address
atau "Amenities" ke Rooms
diizinkan, sama seperti penambahan bidang tingkat atas ke indeks. Dokumen yang sudah ada memiliki nilai null untuk bidang baru hingga Anda secara eksplisit mengisi bidang tersebut dengan memperbarui data Anda.
Perhatikan bahwa dalam jenis kompleks, setiap subbidang memiliki jenis dan dapat memiliki atribut, seperti halnya bidang tingkat atas
Pembaruan data
Memperbarui dokumen yang ada dalam indeks dengan tindakan bekerja dengan upload
cara yang sama untuk bidang kompleks dan sederhana: semua bidang diganti. Namun, merge
(atau mergeOrUpload
saat diterapkan ke dokumen yang sudah ada) tidak berfungsi sama di semua bidang. Secara khusus, merge
tidak mendukung penggabungan elemen dalam koleksi. Batasan ini ada untuk koleksi jenis primitif dan koleksi kompleks. Untuk memperbarui koleksi, Anda perlu mengambil nilai koleksi lengkap, membuat perubahan, lalu menyertakan koleksi baru dalam permintaan API Indeks.
Mencari bidang kompleks dalam kueri teks
Ekspresi pencarian bentuk bebas berfungsi seperti yang diharapkan dengan jenis kompleks. Jika ada bidang atau subbidang yang dapat dicari di mana saja dalam dokumen cocok, maka dokumen itu sendiri cocok.
Kueri menjadi lebih bernuansa ketika Anda memiliki beberapa istilah dan operator, serta beberapa istilah memiliki nama bidang yang ditentukan, seperti yang dimungkinkan dengan sintaks Lucene. Misalnya, kueri ini mencoba mencocokkan dua istilah, "Portland" dan "OR", terhadap dua subbidang bidang Alamat:
search=Address/City:Portland AND Address/State:OR
Kueri seperti ini tidak berkorelasi untuk pencarian teks lengkap, tidak seperti filter. Dalam filter, kueri di atas subbidang koleksi kompleks berkorelasi menggunakan variabel rentang di any
atau all
. Kueri Lucene di atas menampilkan dokumen yang berisi keduanya, baik "Portland, Maine" dan "Portland, Oregon", bersama dengan kota-kota lain di Oregon. Ini terjadi karena setiap klausa berlaku untuk semua nilai bidangnya di seluruh dokumen, sehingga tidak ada konsep "subdokumen saat ini". Untuk informasi selengkapnya tentang ini, lihat Memahami filter koleksi OData di Azure AI Search.
Mencari bidang kompleks dalam kueri RAG
Pola RAG meneruskan hasil pencarian ke model obrolan untuk AI generatif dan pencarian percakapan. Secara default, hasil pencarian yang diteruskan ke LLM adalah set baris yang diratakan. Namun, jika indeks Anda memiliki jenis kompleks, kueri Anda dapat menyediakan bidang tersebut jika Anda terlebih dahulu mengonversi hasil pencarian ke JSON, lalu meneruskan JSON ke LLM.
Contoh parsial mengilustrasikan teknik:
- Menunjukkan bidang yang Anda inginkan dalam perintah atau dalam kueri
- Pastikan bidang dapat dicari dan diambil dalam indeks
- Pilih bidang untuk hasil pencarian
- Memformat hasilnya sebagai JSON
- Mengirim permintaan untuk penyelesaian obrolan ke penyedia model
import json
# Query is the question being asked. It's sent to the search engine and the LLM.
query="Can you recommend a few hotels that offer complimentary breakfast? Tell me their description, address, tags, and the rate for one room they have which sleep 4 people."
# Set up the search results and the chat thread.
# Retrieve the selected fields from the search index related to the question.
selected_fields = ["HotelName","Description","Address","Rooms","Tags"]
search_results = search_client.search(
search_text=query,
top=5,
select=selected_fields,
query_type="semantic"
)
sources_filtered = [{field: result[field] for field in selected_fields} for result in search_results]
sources_formatted = "\n".join([json.dumps(source) for source in sources_filtered])
response = openai_client.chat.completions.create(
messages=[
{
"role": "user",
"content": GROUNDED_PROMPT.format(query=query, sources=sources_formatted)
}
],
model=AZURE_DEPLOYMENT_MODEL
)
print(response.choices[0].message.content)
Untuk contoh end-to-end, lihat Mulai Cepat: Pencarian generatif (RAG) dengan data grounding dari Azure AI Search.
Pilih bidang kompleks
Parameter $select
digunakan untuk memilih bidang mana yang ditampilkan dalam hasil pencarian. Untuk menggunakan parameter ini untuk memilih subbidang tertentu dari bidang kompleks, sertakan bidang induk dan subbidang yang dipisahkan oleh garis miring (/
).
$select=HotelName, Address/City, Rooms/BaseRate
Bidang harus ditandai sebagai Dapat Diambil dalam indeks jika Anda memerlukannya di hasil pencarian. Hanya bidang yang ditandai sebagai Dapat Diambil yang dapat digunakan dalam pernyataan $select
.
Memfilter, memfaset, dan mengurutkan bidang kompleks
Sintaks jalur OData sama yang digunakan untuk pemfilteran dan pencarian bidang juga dapat digunakan untuk pemfasetani, pengurutan, dan pemilihan bidang dalam permintaan pencarian. Untuk jenis kompleks, aturan berlaku yang mengatur sub-bidang mana yang dapat ditandai sebagai dapat diurutkan atau dapat difaset. Untuk informasi selengkapnya tentang aturan ini, lihat Referensi membuat API Indeks.
Faset subbidang
Setiap subbidang dapat ditandai sebagai faset kecuali jenisnya Edm.GeographyPoint
atau Collection(Edm.GeographyPoint)
.
Jumlah dokumen yang dikembalikan dalam hasil faset dihitung untuk dokumen induk (hotel), bukan subdokumen dalam koleksi kompleks (kamar). Misalnya, anggap hotel memiliki 20 kamar jenis "suite". Mengingat parameter facet=Rooms/Type
faset ini, jumlah faset adalah satu untuk hotel, bukan 20 untuk kamar.
Mengurutkan bidang kompleks
Operasi pengurutan berlaku untuk dokumen (Hotel) dan bukan subdokumen (Kamar). Ketika Anda memiliki koleksi jenis kompleks, seperti Rooms, penting untuk menyadari bahwa Anda tidak dapat mengurutkan Rooms sama sekali. Bahkan, Anda tidak dapat mengurutkan koleksi apa pun.
Operasi pengurutan berfungsi ketika bidang memiliki satu nilai per dokumen, apakah bidang tersebut adalah bidang sederhana, atau subbidang dalam tipe kompleks. Misalnya, Address/City
diizinkan untuk diurutkan karena hanya ada satu alamat per hotel, jadi $orderby=Address/City
mengurutkan hotel menurut kota.
Pemfilteran pada bidang kompleks
Anda dapat merujuk ke subbidang bidang kompleks dalam ekspresi filter. Cukup gunakan sintaks jalur OData yang sama yang digunakan untuk pemfasetan, pengurutan, dan pemilihan bidang. Misalnya, filter berikut mengembalikan semua hotel di Kanada:
$filter=Address/Country eq 'Canada'
Untuk memfilter bidang koleksi kompleks, Anda bisa menggunakan ekspresi lambda dengan any
dan all
operator. Dalam hal ini, variabel rentang ekspresi lambda adalah objek dengan subbidang. Anda dapat merujuk ke subbidang tersebut dengan sintaks jalur OData standar. Misalnya, filter berikut mengembalikan semua hotel dengan setidaknya satu kamar deluxe dan semua kamar bebas rokok:
$filter=Rooms/any(room: room/Type eq 'Deluxe Room') and Rooms/all(room: not room/SmokingAllowed)
Seperti halnya bidang sederhana tingkat atas, subbidang sederhana bidang kompleks hanya dapat disertakan dalam filter jika memiliki atribut yang dapat difilter yang diatur ke true
dalam definisi indeks. Untuk informasi selengkapnya, lihat Referensi membuat API Indeks.
Penanganan masalah untuk batas pengumpulan kompleks
Ingat bahwa Azure AI Search membatasi objek kompleks dalam koleksi hingga 3.000 objek per dokumen. Melebihi batas ini menghasilkan pesan berikut:
A collection in your document exceeds the maximum elements across all complex collections limit.
The document with key '1052' has '4303' objects in collections (JSON arrays).
At most '3000' objects are allowed to be in collections across the entire document.
Remove objects from collections and try indexing the document again."
Jika Anda membutuhkan lebih dari 3.000 item, Anda dapat menyalurkan (|
) atau menggunakan segala bentuk pemisah untuk memisahkan nilai, menggabungkannya, dan menyimpannya sebagai string yang dibatasi. Tidak ada batasan jumlah string yang disimpan dalam array. Menyimpan nilai kompleks saat string melewati batasan koleksi yang kompleks.
Untuk mengilustrasikan, asumsikan Anda memiliki "searchScope
array " dengan lebih dari 3.000 elemen:
"searchScope": [
{
"countryCode": "FRA",
"productCode": 1234,
"categoryCode": "C100"
},
{
"countryCode": "USA",
"productCode": 1235,
"categoryCode": "C200"
}
. . .
]
Solusi untuk menyimpan nilai sebagai string yang dibatasi mungkin terlihat seperti ini:
"searchScope": [
"|FRA|1234|C100|",
"|FRA|*|*|",
"|*|1234|*|",
"|*|*|C100|",
"|FRA|*|C100|",
"|*|1234|C100|"
]
Menyimpan semua varian pencarian dalam string yang dibatasi sangat membantu dalam skenario pencarian di mana Anda ingin mencari item yang hanya memiliki "FRA" atau "1234" atau kombinasi lain dalam array.
Berikut adalah cuplikan pemformatan filter di C# yang mengonversi input menjadi string yang dapat dicari:
foreach (var filterItem in filterCombinations)
{
var formattedCondition = $"searchScope/any(s: s eq '{filterItem}')";
combFilter.Append(combFilter.Length > 0 ? " or (" + formattedCondition + ")" : "(" + formattedCondition + ")");
}
Daftar berikut ini menyediakan input dan string pencarian (output) berdampingan:
Untuk kode kabupaten "FRA" dan kode produk "1234", output yang diformat adalah
|FRA|1234|*|
.Untuk kode produk "1234", output yang diformat adalah
|*|1234|*|
.Untuk kode kategori "C100", output yang diformat adalah
|*|*|C100|
.
Hanya berikan kartubebas (*
) jika Anda menerapkan solusi array string. Jika tidak, jika Anda menggunakan jenis kompleks, filter Anda mungkin terlihat seperti contoh ini:
var countryFilter = $"searchScope/any(ss: search.in(countryCode ,'FRA'))";
var catgFilter = $"searchScope/any(ss: search.in(categoryCode ,'C100'))";
var combinedCountryCategoryFilter = "(" + countryFilter + " and " + catgFilter + ")";
Jika Anda menerapkan solusinya, pastikan untuk menguji secara ekstensif.
Langkah berikutnya
Coba Himpunan data Hotel di wizard Impor data. Anda memerlukan informasi koneksi Azure Cosmos DB yang disediakan dalam readme untuk mengakses data.
Dengan informasi tersebut di tangan, langkah pertama Anda dalam wizard ini adalah membuat sumber data Azure Cosmos DB baru. Selanjutnya di wizard, saat Anda masuk ke halaman indeks target, Anda akan melihat indeks dengan jenis kompleks. Buat dan muat indeks ini, lalu jalankan kueri untuk memahami struktur baru.