私有數(shù)據(jù)
私有數(shù)據(jù)的應(yīng)用
在同一通道內(nèi)某一組織持有私有數(shù)據(jù),只要被認證的組織才可以訪問雀鹃。如果為了保持數(shù)據(jù)的隱私性而建立不同的通道,可能會增加通道以及相關(guān)鏈碼的維護成本。
私有數(shù)據(jù)集
一個私有數(shù)據(jù)集由兩個元素組成:
- 真實的私有數(shù)據(jù)
只有在被認證的組織中的peer節(jié)點才能查看通過gossip協(xié)議通訊的私有數(shù)據(jù)屈呕。這些數(shù)據(jù)被保存在peer節(jié)點的私有數(shù)據(jù)庫(SideDB)中。共識節(jié)點中不包含私有數(shù)據(jù)棺亭。 - 數(shù)據(jù)的哈希值
數(shù)據(jù)的哈希值被確認虎眨、排序公示、以及記錄在通道中每個peer節(jié)點賬本中镶摘。這些哈希數(shù)據(jù)作為交易的證據(jù)嗽桩,用于狀態(tài)的檢驗,以及審計
案例
考慮一個由5個組織組成的聯(lián)盟凄敢,在一個通道內(nèi)進行貿(mào)易交易:
- 農(nóng)場主
- 分銷商
- 貨運商
- 批發(fā)商
- 零售商
a. 分銷商想與農(nóng)場主以及貨運商建立一條私有的機密的交易線路碌冶,交易數(shù)據(jù)不讓批發(fā)商以及零售商知道(不暴露交易價格)
b. 分銷商想與批發(fā)商建立一條私有的交易關(guān)系,應(yīng)為他向他們收取的費用低于零售商
b. 批發(fā)商想與零售商以及貨運商建立一條私有的交易關(guān)系
這樣在5個組織之間存在3條各自隱私的私有數(shù)據(jù)集合(private data collections PDC)
- PDC1:分銷商涝缝、農(nóng)場主和貨運商
- PDC2:分銷商和批發(fā)商
- PDC3:批發(fā)商扑庞、零售商和貨運商
[圖片上傳失敗...(image-b10218-1532614924062)]
私有數(shù)據(jù)在fabric中的應(yīng)用
私有數(shù)據(jù)集合定義配置文件
私有數(shù)據(jù)集合的定義,使用控制臺命令 peer chaincode 鏈碼部署時俊卤,使用配置項 --collections-config 指定配置文件嫩挤,如果使用sdk部署鏈碼請參考相關(guān)文檔
集合定義配置文件由5個屬性組成:
- name:集合名稱
- policy:定義組織peer節(jié)點,允許使用簽名策略語法來持久化收集數(shù)據(jù)消恍,每個成員被包含在簽名策略列表中岂昭。
- requiredPeerCount:在peer節(jié)點簽名背書并返回提案響應(yīng)給客戶端,成功傳送私有數(shù)據(jù)的最小認可節(jié)點狠怨。
- maxPeerCount:為了數(shù)據(jù)冗余保存的目的约啊,需要同步保存私有數(shù)據(jù)的被認可的最大peer節(jié)點數(shù)量。如果一個可信節(jié)點宕機后佣赖,其他的持有私有冗余數(shù)據(jù)的節(jié)點依然可以提供數(shù)據(jù)請求訪問服務(wù)恰矩。
- blockToLive:私有數(shù)據(jù)在sideDB保存時長。如果要一致保存憎蛤,則配置為0.
樣例如下:
[
{
"name": "collectionMarbles",
"policy": "OR('Org1MSP.member', 'Org2MSP.member')",
"requiredPeerCount": 0,
"maxPeerCount": 3,
"blockToLive":1000000
},
{
"name": "collectionMarblePrivateDetails",
"policy": "OR('Org1MSP.member')",
"requiredPeerCount": 0,
"maxPeerCount": 3,
"blockToLive":3
}
]
提交私有數(shù)據(jù)
授權(quán)peer節(jié)點之間私有數(shù)據(jù)的同步需要通過core.yaml配置文件中g(shù)ossip模塊下面的pvtData子模塊的配置項
- pullRetryThreshold 限定將嘗試從peer節(jié)點中提取對應(yīng)于給定塊的私有數(shù)據(jù)的最大持續(xù)時間外傅,直到在沒有私有數(shù)據(jù)的情況下提交塊為止纪吮。
- 如果請求節(jié)點在pullRetryThreshold限定時間內(nèi)檢索完私有數(shù)據(jù),節(jié)點將向賬本提交交易(包含私有數(shù)據(jù)的哈希值)萎胰,并在狀態(tài)數(shù)據(jù)庫保存私有數(shù)據(jù)碾盟,從邏輯上區(qū)分其他通道的狀態(tài)數(shù)據(jù)
- 如果請求節(jié)點沒有在pullRetryThreshold限定時間內(nèi)檢索完私有數(shù)據(jù),節(jié)點將向他的區(qū)塊鏈(*不明白)提交交易(包含私有數(shù)據(jù)的哈希值)技竟,不包括私有數(shù)據(jù)冰肴。
- 如果一個有權(quán)擁有私有數(shù)據(jù)的節(jié)點丟失了私有數(shù)據(jù),他將沒有能力為將來涉及到該私有數(shù)據(jù)的交易背書榔组。
- transientstoreMaxBlockRetention 從當前賬本高度開始計算至設(shè)定值以及設(shè)定值倍數(shù)的提交區(qū)塊熙尉,刪除臨時保存的私有數(shù)據(jù)。
- pushAckTimeout 等待每個peer節(jié)點推送背書私有數(shù)據(jù)確認信息的最長時間搓扯。
- btlPullMargin 阻止實時拉取邊界检痰,用作緩沖區(qū)以防止peer節(jié)點嘗試從即將在下N個塊中清除的peer節(jié)點處獲取私有數(shù)據(jù)。這有助于新加入的peer節(jié)點更快的同步當前區(qū)塊鏈數(shù)據(jù)擅编。
背書
背書節(jié)點在向其他被認可的節(jié)點散播私有數(shù)據(jù)中扮演重要的角色攀细,他確保在通道中私有數(shù)據(jù)的有效性。為了協(xié)助散播爱态,在集合定義中的參數(shù)maxPeerCount 和requiredPeerCount 控制了傳播行為谭贪。
當背書節(jié)點不能成功的向小于requiredPeerCount配置的節(jié)點傳播私有數(shù)據(jù),他將向客戶端返回一個錯誤锦担。背書節(jié)點將嘗試向其他組織的節(jié)點散播私有數(shù)據(jù)俭识,以確保每個授權(quán)組織都有一份私有數(shù)據(jù)的拷貝。由于交易不是在鏈碼執(zhí)行時提交洞渔,因此背書節(jié)點和接收節(jié)點將私有數(shù)據(jù)的副本與區(qū)塊鏈一起存儲在本地臨時存儲中套媚,直到交易被提交。
在鏈碼中操作私有數(shù)據(jù)集合
鏈碼的shim APIs接口提供常用私有數(shù)據(jù)操作方法:
- PutPrivateData(collection,key,value)
- GetPrivateData(collection,key)
- GetPrivateDataByRange(collection, startKey, endKey string)
- GetPrivateDataByPartialCompositeKey(collection, objectType string, keys []string)
- GetPrivateDataQueryResult(collection, query string) (*只有使用couchdb才能使用富查詢語句)
在鏈碼提案中傳遞私有數(shù)據(jù)
由于鏈碼提案保存在區(qū)塊鏈中磁椒,因此不要在鏈碼提案的主要部分中包含私有數(shù)據(jù)也很重要堤瘤。在鏈碼提案中有一塊特殊的區(qū)域叫做臨時區(qū)域,被用于從客戶端傳遞私有數(shù)據(jù)到peer節(jié)點調(diào)用鏈碼浆熔。鏈碼可以使用GetTransient()接口從臨時區(qū)域獲取數(shù)據(jù)本辐。
注意事項
- 調(diào)用執(zhí)行范圍查詢或者富查詢鏈碼的客戶端應(yīng)該知道,他們可能會受到結(jié)構(gòu)集的子集医增,具體取決于私有數(shù)據(jù)的傳播慎皱。客戶端可以查詢多個peer節(jié)點叶骨,并比較結(jié)果以確定peer節(jié)點是否可能缺少某些結(jié)果集茫多。
- 不支持在單個交易中執(zhí)行查詢(范圍查詢或者富查詢)并且更新數(shù)據(jù),由于查詢結(jié)果無法在無法訪問私有數(shù)據(jù)或者丟失私有數(shù)據(jù)的節(jié)點進行驗證忽刽。如果鏈碼同時調(diào)用查詢天揖、更新私有數(shù)據(jù)夺欲,提案請求將會返回一個錯誤。如果你的應(yīng)用程序可以容仍鏈碼執(zhí)行和驗證/提交時間之間的結(jié)果集更改宝剖,那么你可以調(diào)用一個鏈碼函數(shù)來執(zhí)行查詢洁闰,然后調(diào)用第二個鏈碼函數(shù)來進行更新。注意万细,在同一交易中,調(diào)用GetPrivateData()檢索單個鍵纸泄,并且調(diào)用PutPrivateData()更新數(shù)據(jù)赖钞,因為所有的peer節(jié)點可以一句散列鍵版本來驗證鍵讀取。
- 注意聘裁,私有數(shù)據(jù)集合僅僅定義了哪些組織的節(jié)點被授權(quán)接收以及存儲私有數(shù)據(jù)雪营,意味著哪些peer節(jié)點可以被用于查詢私有數(shù)據(jù),私有數(shù)據(jù)集合本身不限制鏈碼中的訪問控制衡便。例如献起,如果非授權(quán)客戶端能夠在有權(quán)訪問私有數(shù)據(jù)的peer節(jié)點調(diào)用鏈碼,則鏈碼在邏輯上仍然像往常一樣強制執(zhí)行訪問控制镣陕,例如通過GetCreator()鏈碼API或使用客戶端身份鏈碼庫谴餐。
為私有數(shù)據(jù)集合建立索引
在使用couchdb作為狀態(tài)數(shù)據(jù)庫的時候,可以使用json格式的文件配置數(shù)據(jù)庫的索引呆抑,并且索引文件需要放在指定目錄下岂嗓,比如:
索引文件路徑:META-INF/statedb/couchdb/indexes
索引文件內(nèi)容:
{"index":{"fields":["docType","owner"]},"ddoc":"indexOwnerDoc", "name":"indexOwner","type":"json"}
私有數(shù)據(jù)清理
通過上面提到的配置項blockToLive,來執(zhí)行私有數(shù)據(jù)的定期清理。
回想一下鹊碍,在提交之前厌殉,peer節(jié)點私有數(shù)據(jù)保存在本地臨時數(shù)據(jù)庫。
升級集合定義
如果鏈碼引用了集合侈咕,則鏈碼將使用先前的集合定義,除非在升級時指定了新的集合定義公罕。如果在升級期間指定了集合配置,則必須包含每個現(xiàn)有集合的定義耀销,并且可以添加新的集合定義楼眷。
當peer節(jié)點提交包含鏈碼升級的交易時,集合更新將生效树姨,請注意摩桶,無法刪除集合帽揪,因為在通道的區(qū)塊鏈中可能存在無法刪除的先前私有數(shù)據(jù)哈希
實際操作
1. 創(chuàng)建一個集合定義的json格式文件硝清,配置項參數(shù)說明見上面部分。
// collections_config.json
[
{
"name": "collectionMarbles",
"policy": "OR('Org1MSP.member', 'Org2MSP.member')",
"requiredPeerCount": 0,
"maxPeerCount": 3,
"blockToLive":1000000
},
{
"name": "collectionMarblePrivateDetails",
"policy": "OR('Org1MSP.member')",
"requiredPeerCount": 0,
"maxPeerCount": 3,
"blockToLive":3
}
]
在鏈碼初始化的時候使用命令加載該配置文件
peer chaincode instantiate ... --collections-config
2. 使用鏈碼API對私有數(shù)據(jù)進行讀寫操作
在鏈碼中定義私有數(shù)據(jù)
// Org1 與 Org2 組織中的peer節(jié)點可以從sidedb中訪問私有數(shù)據(jù)
type marble struct {
ObjectType string `json:"docType"`
Name string `json:"name"`
Color string `json:"color"`
Size int `json:"size"`
Owner string `json:"owner"`
}
// Org1組織中的Peer節(jié)點可以從sidedb中訪問私有數(shù)據(jù)
type marblePrivateDetails struct {
ObjectType string `json:"docType"`
Name string `json:"name"`
Price int `json:"price"`
}type marble struct {
ObjectType string `json:"docType"`
Name string `json:"name"`
Color string `json:"color"`
Size int `json:"size"`
Owner string `json:"owner"`
}
type marblePrivateDetails struct {
ObjectType string `json:"docType"`
Name string `json:"name"`
Price int `json:"price"`
}
定義的規(guī)則如下:
- "name,color,size,and owner" 可以被Org1和Org2組織下的所有成員訪問
- "price" 僅僅被Org1組織下的成員訪問
在彈珠游戲私人數(shù)據(jù)樣例中定義了兩組不同的私有數(shù)據(jù)转晰。此數(shù)據(jù)到限制其訪問的集合策略的映射由鏈碼API控制芦拿。具體來說士飒,使用集合定義讀寫私人數(shù)據(jù)是通過接口GetPrivateData()和PutPrivateData()來執(zhí)行的,具體說明蔗崎。
下圖說明了彈珠游戲私有數(shù)據(jù)樣本使用的私有數(shù)據(jù)模型酵幕。
[圖片上傳失敗...(image-b56201-1532614924062)]
[圖片上傳失敗...(image-944cf3-1532614924062)]
-
讀集合數(shù)據(jù)
使用鏈碼API GetPrivateData()從數(shù)據(jù)庫中查詢私有數(shù)據(jù)。GetPrivateData有兩個參數(shù)缓苛,集合名稱與數(shù)據(jù)鍵芳撒。回想一下集合collectionMarbles 允許Org1和Org2中的成員訪問sideDB中的私有數(shù)據(jù)未桥,集合collectionMarblePrivateDetails允許Org1中的成員訪問sideDB中的私有數(shù)據(jù)笔刹。相關(guān)實現(xiàn)細節(jié),請參閱一下兩個彈珠游戲私有數(shù)據(jù)函數(shù):- readMarble 查詢"name, color, size, owner"屬性的值
- readMarblePrivateDetails 查詢"price"屬性的值
-
寫集合數(shù)據(jù)
使用鏈碼API PutPrivateData() 往私有數(shù)據(jù)庫中存儲私有數(shù)據(jù)冬耿。- 使用集合名稱collectionMarbles舌菜,寫私有數(shù)據(jù)"name, color, size, owner"
- 使用集合名稱collectionMarblePrivateDetails,寫私有數(shù)據(jù)"price"
如下的樣例中亦镶,在initMarble函數(shù)中日月,調(diào)用PutPrivateData() 兩次,分別設(shè)置不同的私有數(shù)據(jù)缤骨。
// ==== Create marble object and marshal to JSON ====
objectType := "marble"
marble := &marble{objectType, marbleName, color, size, owner}
marbleJSONasBytes, err := json.Marshal(marble)
if err != nil {
return shim.Error(err.Error())
}
//Alternatively, build the marble json string manually if you don't want to use struct marshalling
//marbleJSONasString := `{"docType":"Marble", "name": "` + marbleName + `", "color": "` + color + `", "size": ` + strconv.Itoa(size) + `, "owner": "` + owner + `"}`
//marbleJSONasBytes := []byte(str)
// === Save marble to state ===
err = stub.PutPrivateData("collectionMarbles", marbleName, marbleJSONasBytes)
if err != nil {
return shim.Error(err.Error())
}
// ==== Save marble private details ====
objectType = "marblePrivateDetails"
marblePrivateDetails := &marblePrivateDetails{objectType, marbleName, price}
marblePrivateDetailsBytes, err := json.Marshal(marblePrivateDetails)
if err != nil {
return shim.Error(err.Error())
}
err = stub.PutPrivateData("collectionMarblePrivateDetails", marbleName, marblePrivateDetailsBytes)
if err != nil {
return shim.Error(err.Error())
}
作為額外的數(shù)據(jù)隱私優(yōu)勢爱咬,由于正在使用集合,因此z還有私有數(shù)據(jù)哈希值傳遞給orderer節(jié)點荷憋,而不是私有數(shù)據(jù)本身台颠,從而使私有數(shù)據(jù)對orderer節(jié)點保密
3. 使用集合安裝并初始化鏈碼
創(chuàng)建一個solo共識的fabric網(wǎng)絡(luò)(1個orderer,兩個org組織,分別包含兩個peer節(jié)點)勒庄,包含以下節(jié)點:
- peer0.org1.example.com
- peer1.org1.example.com
- peer0.org2.example.com
- peer1.org2.example.com
- orderer.example.com
a. 安裝鏈碼
使用如下命令分別在4個peer節(jié)點安裝彈珠游戲鏈碼
peer chaincode install -n marblesp -v 1.0 -0 github.com/chaincode/marbles02_private/go/
安裝成功后串前,peer節(jié)點日志會看到如下信息:
install -> INFO 003 Installed remotely response:<status:200 payload:"OK">
b. 初始化鏈碼
使用管理員MSP在peer節(jié)點初始化鏈碼
peer chaincode instantiate -o orderer.example.com:7050 -C mychannel -n marblesp -v 1.0 -c '{"Args":["init"]}' -P "OR('Org1MSP.member','Org2MSP.member')" --collections-config $GOPATH/src/github.com/chaincode/marbles02_private/collections_config.json
上面的參數(shù)根據(jù)實際情況進行改寫修改
當鏈碼初始化成功以后,可以看到如下日志信息:
[chaincodeCmd] checkChaincodeCmdParams -> INFO 001 Using default escc
[chaincodeCmd] checkChaincodeCmdParams -> INFO 002 Using default vscc
4. 存儲私有數(shù)據(jù)
使用如下命令初始化一個彈珠:
peer chaincode invoke -o orderer.example.com:7050 -C mychannel -n marblesp -c '{"Args":["initMarble","marble1","blue","35","tom","99"]}'
初始化成功后实蔽,可以看到如下日志:
[chaincodeCmd] chaincodeInvokeOrQuery->INFO 001 Chaincode invoke successful. result: status:200
5. 在授權(quán)peer節(jié)點上查詢私有數(shù)據(jù)
如下的鏈碼例子荡碾,使用readMarble與readMarblereadMarblePrivateDetails方法分別查詢collectionMarble與collectionMarblePrivateDetails集合中的私有數(shù)據(jù):
// ===============================================
// readMarble - read a marble from chaincode state
// ===============================================
func (t *SimpleChaincode) readMarble(stub shim.ChaincodeStubInterface, args []string) pb.Response {
var name, jsonResp string
var err error
if len(args) != 1 {
return shim.Error("Incorrect number of arguments. Expecting name of the marble to query")
}
name = args[0]
valAsbytes, err := stub.GetPrivateData("collectionMarbles", name) //get the marble from chaincode state
if err != nil {
jsonResp = "{\"Error\":\"Failed to get state for " + name + "\"}"
return shim.Error(jsonResp)
} else if valAsbytes == nil {
jsonResp = "{\"Error\":\"Marble does not exist: " + name + "\"}"
return shim.Error(jsonResp)
}
return shim.Success(valAsbytes)
}
// ===============================================
// readMarblereadMarblePrivateDetails - read a marble private details from chaincode state
// ===============================================
func (t *SimpleChaincode) readMarblePrivateDetails(stub shim.ChaincodeStubInterface, args []string) pb.Response {
var name, jsonResp string
var err error
if len(args) != 1 {
return shim.Error("Incorrect number of arguments. Expecting name of the marble to query")
}
name = args[0]
valAsbytes, err := stub.GetPrivateData("collectionMarblePrivateDetails", name) //get the marble private details from chaincode state
if err != nil {
jsonResp = "{\"Error\":\"Failed to get private details for " + name + ": " + err.Error() + "\"}"
return shim.Error(jsonResp)
} else if valAsbytes == nil {
jsonResp = "{\"Error\":\"Marble private details does not exist: " + name + "\"}"
return shim.Error(jsonResp)
}
return shim.Success(valAsbytes)
}
在Org1的成員節(jié)點下查詢marble1的私有數(shù)據(jù)"name,color,size,owner":
peer chaincode query -C mychannel -n marblesp -c '{"Args":["readMarble","marble1"]}'
查詢結(jié)果如下:
{"color":"blue","docType":"marble","name":"marble1","owner":"tom","size":35}
在Org1的成員節(jié)點下查詢marble1的私有數(shù)據(jù)"price":
peer chaincode query -C mychannel -n marblesp -c '{"Args":["readMarblePrivateDetails","marble1"]}'
查詢結(jié)果如下:
{"docType":"marblePrivateDetails","name":"marble1","price":99}
6. 在未授權(quán)peer節(jié)點上查詢私有數(shù)據(jù)
從上面定義的私有數(shù)據(jù)訪問集合中,Org2定義了"name,color,size,owner"私有數(shù)據(jù)的訪問權(quán)限局装,但是沒有定義"price"私有數(shù)據(jù)的訪問權(quán)限坛吁。
- 查詢被授權(quán)的私有數(shù)據(jù)
peer chaincode query -C mychannel -n marblesp -c '{"Args":["readMarble","marble1"]}'
查詢結(jié)果如下:
{"docType":"marble","name":"marble1","color":"blue","size":35,"owner":"tom"}
- 查詢未被授權(quán)的私有數(shù)據(jù)
peer chaincode query -C mychannel -n marblesp -c '{"Args":["readMarblePrivateDetails","marble1"]}'
查詢結(jié)果如下:
{"Error":"Failed to get private details for marble1: GET_STATE failed:
transaction ID: b04adebbf165ddc90b4ab897171e1daa7d360079ac18e65fa15d84ddfebfae90:
Private data matching public hash version is not available. Public hash
version = &version.Height{BlockNum:0x6, TxNum:0x0}, Private data version =
(*version.Height)(nil)"}"
7. 清理私有數(shù)據(jù)
在私有數(shù)據(jù)集合中配置的屬性blockToLive,可以設(shè)置在多少個區(qū)塊后清理私有數(shù)據(jù)铐尚,只留下數(shù)據(jù)的哈希值拨脉,作為交易的不可改變的證據(jù)。
比如我們在創(chuàng)建上面的樣例鏈碼的時候宣增,設(shè)置私有數(shù)據(jù)集合屬性blockToLive=4玫膀。
在上面的樣例中,我們已經(jīng)初始化了一個marble1爹脾,其中price=99帖旨,使用如下命令查詢:
peer chaincode query -C mychannel -n marblesp -c '{"Args":["readMarblePrivateDetails","marble1"]}'
查詢結(jié)果如下:
peer chaincode invoke -o orderer.example.com:7050 --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n marblesp -c '{"Args":["transferMarble","marble2","tom"]}'
然后再創(chuàng)建一個新的marble2資產(chǎn):
peer chaincode invoke -o orderer.example.com:7050 --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n marblesp -c '{"Args":["initMarble","marble2","blue","35","tom","99"]}'
此時區(qū)塊增加1攒钳。
此時查詢marble1資產(chǎn)中的price值:
peer chaincode query -C mychannel -n marblesp -c '{"Args":["readMarblePrivateDetails","marble1"]}'
查詢結(jié)果如下:
{"docType":"marblePrivateDetails","name":"marble1","price":99}
在使用如下命令分別把marble2資產(chǎn)轉(zhuǎn)移給其他角色:
peer chaincode invoke -o orderer.example.com:7050 -C mychannel -n marblesp -c '{"Args":["transferMarble","marble2","tom"]}'
在把資產(chǎn)轉(zhuǎn)移3次以后缠诅,區(qū)塊再次增加3次财饥。此時再查詢marble1資產(chǎn)中的price值:
peer chaincode invoke -o orderer.example.com:7050 --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n marblesp -c '{"Args":["transferMarble","marble2","tom"]}'
查詢結(jié)果如下:
Error: endorsement failure during query. response: status:500
message:"{\"Error\":\"Marble private details does not exist: marble1\"}"
表明私有數(shù)據(jù)已經(jīng)被清除闸氮。
8. 使用帶有索引的私有數(shù)據(jù)
索引也可用于私有數(shù)據(jù)集合,通過打包與鏈碼同級目錄下META-INF/statedb/couchdb/collections/<collection_name>/indexes的索引文件货抄。
為了將鏈碼部署到生產(chǎn)環(huán)境述召,建議在鏈碼同級目錄下定義索引,以便鏈碼和相關(guān)所以作為一個單元一起自動部署蟹地,一旦鏈碼被安裝在節(jié)點并被初始化到通道桨武。當指定--collections-config 標志指向集合json文件的位置時,關(guān)聯(lián)索引將在通道上的鏈碼實例化時自動部署锈津。