2021年7月18日 星期日

Vespa weighted set

Vespa 有個 weightedset 的型態,我個人其實覺得 Vespa 的文件在針對 weightedset 的描述有點難懂,所以這邊會用範例來整理一下 weightedset 的實際效果。

weightedset 的格式

weightedset 從名字可以看出它是個 Set,並且是有權重的,所以可以蠻容易想像它的格式會長得像這樣:

{
    "key1": 1,
    "key2": 10
}

這個是型態為 weightedset<string> 的狀況,我覺得應該也是大多數使用情境的狀況吧。weightedset 的型態宣告是在宣告它的 key 要使用什麼型態,不過除了 string 以外還能使用什麼型態,好像沒有在文件上看到。而 weightedset 的權重的部份,依據文件描述是只能使用 32-bit 數字,也就是說是不能有浮點數的。

weightedset 的用途

目前就我所知,weightedset 主要就是用在 match 和 rank 兩種地方。match 指的是找出具有特定 key 的 document;rank 則指的是依據權重讓 Vespa 調整找到的 document 的順序。接下來會分別針對 match 和 rank 有一些範例來說明 weightedset 產生的效果為何。

match 的範例

假設我有兩個 document,document 裡面有個欄位叫做 weights,型態是 weightedset<string>。schema 定義如下:

field weights type weightedset<string> {
    indexing: summary | attribute
}

實際的資料假設長這樣:

doc #1:
"weights": {
    "attribute1": 10,
    "attribute2": 4
}

doc #2:
"weights": {
    "attribute1": 130
}

若我們以 match attribute2 來搜尋的話,結果會是只能搜尋到 doc #1:

select * from test where weightedset(weights, {"attribute2": 1});

這個結果應該是蠻容易想像的,不管 weight 是給多少,因為都只有 doc #2 才有 attribute2 這個 key,所以只會找到 doc #2 而已。從效能考量來說,Vespa 團隊也建議若可以的話,用 weightedset 取代 OR 的操作是比較好的選擇。舉例來說:

select * from test where weightedset(weights, {"attribute1": 1, "attribute2": 1});

上述的 YQL 可以同時搜尋到 doc #1 和 doc #2,也就是說它實際的搜尋效果跟下面用 OR 的 query 是差不多的。

select * from test where weights contains "attribute1" OR weights contains "attribute2";
rank 的範例

rank 其實是 weightedset 比較奇妙的地方。而且不知道是不是我漏看了或者我看不懂,我沒有看到 Vespa 文件裡對於 weightedset 有太詳細的描述…。

首先,我們還是跟上面一樣使用相同的測試資料:

doc #1:
"weights": {
    "attribute1": 10,
    "attribute2": 4
}

doc #2:
"weights": {
    "attribute1": 130
}

假設我們執行的 query 是指定 attribute1 的權重為 1,那麼結果會是如何呢?

select * from test where weightedset(weights, {"attribute1": 1});

本來我自己以為結果應該會是 doc #2 在前面、doc #1 在後面,因為 doc #2 欄位裡的 attribute1 權重比較高,算出來的分數應該會比較多才對!但結果我錯了 。

{
    "id": "id:test:test::doc1",
    "relevance": 0.0017429193899782135,
    ....
},
{
    "id": "id:test:test::doc2",
    "relevance": 0.0017429193899782135,
    ....
}

結果顯示,兩個 document 得到的分數是一樣的!

接著如果改成在 query 裡將 attribute 1 的權重從 1 提昇到 100 的話呢….

YQL:
select * from test where weightedset(weights, {"attribute1": 100});

results:
{
    "id": "id:test:test::doc1",
    "relevance": 0.17429193899782136
    ....
},
{
    "id": "id:test:test::doc2",
    "relevance": 0.17429193899782136,
    ....
}

跟前一個結果比較起來,其實可以發現,兩個 document 的分數也同時增加了 100 倍!

如果把 query 改為找 attribute 2,權重一樣是 100 的話呢?

YQL:
select * from test where weightedset(weights, {"attribute2": 100});

results:
{
    "id": "id:test:test::doc1",
    "relevance": 0.17429193899782136,
    ....
}

可以看到結果是只會搜尋到 doc #1,但是它得到的分數跟上一個測試一樣,都是 0.17429193899782136。

到這裡其實就可以歸納出 weightedset 的規則了。weightedset 在預設的 native rank 裡,是完全不參考 field 裡的權重,單純只有參考 query 的權重而已。所以在第一和第二個測試中,attribute1 的權重給 1 的時候,doc #1 和 doc #2 會得到相同的分數,因為它們都有 attribute1 這個 key,但由於只計算 query 的權重,也沒有其他因子,因此它們會獲得完全一樣的分數。而第三個測試則是因為 doc #2 沒有 attribute2,所以根本不會被搜尋到。同時,在沒有其他因子的情況下,query 的權重提高 N 倍,結果的分數也會跟著提高 N 倍。

參考資料
  1. Vespa Schema Reference: type weightedset

沒有留言: