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 SKU, beberapa pembuat buku tunggal, 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": "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,
    }
    . . .
  ]
}

Pengindeksan jenis kompleks

Selama pengindeksan, Anda dapat memiliki maksimal 3000 elemen di semua koleksi kompleks dalam satu dokumen. Elemen koleksi kompleks adalah anggota koleksi tersebut, jadi dalam kasus Rooms (satu-satunya koleksi kompleks dalam contoh Hotel), setiap kamar adalah elemen. Dalam contoh di atas, jika "Secret Point Motel" 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).

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.

  1. Masuk ke portal Azure.

  2. Pada halaman Gambaran Umum layanan pencarian, pilih tab Indeks.

  3. Buka indeks yang sudah ada atau buat indeks baru.

  4. 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.

  5. Beri nama bidang dan atur jenisnya ke atau Edm.ComplexTypeCollection(Edm.ComplexType).

  6. Pilih elipsis di ujung kanan, lalu pilih Tambahkan bidang atau Tambahkan subbidang, lalu tetapkan atribut.

Memperbarui bidang kompleks

Semua aturan pengindeksan ulang yang berlaku terhadap bidang secara umum masih berlaku untuk bidang kompleks. Pengulangan beberapa aturan utama di sini, penambahan bidang ke jenis kompleks tidak memerlukan pembangunan ulang indeks, tetapi sebagian besar modifikasi dilakukan.

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

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.

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/Typefaset 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.

Azure Search memiliki batasan bahwa objek kompleks dalam koleksi di seluruh satu dokumen tidak boleh melebihi 3000.

Pengguna akan mengalami kesalahan di bawah ini selama pengindeksan ketika koleksi kompleks melebihi batas 3000.

"Koleksi dalam dokumen Anda melebihi elemen maksimum di semua batas koleksi kompleks. Dokumen dengan kunci '1052' memiliki objek '4303' dalam koleksi (array JSON). Paling banyak objek '3000' diizinkan untuk berada dalam koleksi di seluruh dokumen. Hapus objek dari koleksi dan coba indeks dokumen lagi."

Dalam beberapa kasus penggunaan, kita mungkin perlu menambahkan lebih dari 3000 item ke koleksi. Dalam kasus penggunaan tersebut, kita 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 di Azure Search. Menyimpan nilai kompleks ini karena string menghindari batasan. Pelanggan perlu memvalidasi apakah solusi ini memenuhi persyaratan skenario mereka.

Misalnya, tidak mungkin menggunakan jenis kompleks jika array "searchScope" di bawah ini memiliki lebih dari 3000 elemen.


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

Menyimpan nilai kompleks ini sebagai string dengan pemisah menghindari batasan

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

Daripada menyimpannya dengan kartubebas, kita juga dapat menggunakan penganalisis kustom yang membagi kata menjadi | untuk mengurangi ukuran penyimpanan.

Alasan kami telah menyimpan nilai dengan kartubebas alih-alih hanya menyimpannya seperti di bawah ini

|FRA|1234|C100|

adalah untuk memenuhi skenario pencarian di mana pelanggan mungkin ingin mencari item yang memiliki negara Prancis, terlepas dari produk dan kategori. Demikian pula, pelanggan mungkin perlu mencari untuk melihat apakah item memiliki produk 1234, terlepas dari negara atau kategori.

Jika kita hanya menyimpan satu entri

|FRA|1234|C100|

tanpa kartubebas, jika pengguna hanya ingin memfilter di Prancis, kami tidak dapat mengonversi input pengguna agar sesuai dengan array "searchScope" karena kami tidak tahu kombinasi Prancis apa yang ada dalam array "searchScope" kami

Jika pengguna hanya ingin memfilter berdasarkan negara, katakanlah Prancis. Kami akan mengambil input pengguna dan membangunnya sebagai string seperti di bawah ini:

|FRA|*|*|

yang kemudian dapat kita gunakan untuk memfilter pencarian azure saat kita mencari dalam array nilai item

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

Demikian pula, jika pengguna mencari Prancis dan kode produk 1234, kami akan mengambil input pengguna, membangunnya sebagai string yang dibatasi seperti di bawah ini, dan mencocokkannya dengan array pencarian kami.

|FRA|1234|*|

Jika pengguna mencari 1234 kode produk, kami akan mengambil input pengguna, membangunnya sebagai string yang dibatasi seperti di bawah ini, dan mencocokkannya dengan array pencarian kami.

|*|1234|*|

Jika pengguna mencari kode kategori C100, kami akan mengambil input pengguna, membangunnya sebagai string yang dibatasi seperti di bawah ini, dan mencocokkannya dengan array pencarian kami.

|*|*|C100|

Jika pengguna mencari Prancis dan kode produk 1234 dan kode kategori C100, kami akan mengambil input pengguna, membangunnya sebagai string yang dibatasi seperti di bawah ini, dan mencocokkannya dengan array pencarian kami.

|FRA|1234|C100|

Jika pengguna mencoba mencari negara yang tidak ada dalam daftar kami, itu tidak akan cocok dengan array yang dibatasi "searchScope" yang disimpan dalam indeks pencarian, dan tidak ada hasil yang akan dikembalikan. Misalnya, pengguna mencari Kanada dan kode produk 1234. Pencarian pengguna akan dikonversi ke

|CAN|1234|*|

Ini tidak akan cocok dengan salah satu entri dalam array yang dibatasi dalam indeks pencarian kami.

Hanya pilihan desain di atas yang memerlukan entri kartubebas ini; jika telah disimpan sebagai objek kompleks, kita bisa saja melakukan pencarian eksplisit seperti yang ditunjukkan di bawah ini.

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

Dengan demikian, kita dapat memenuhi persyaratan di mana kita perlu mencari kombinasi nilai dengan menyimpannya sebagai string yang dibatasi alih-alih koleksi kompleks jika koleksi kompleks kita melebihi batas Azure Search. Ini adalah salah satu solusinya, dan pelanggan perlu memvalidasi apakah ini akan memenuhi persyaratan skenario mereka.

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.