Hyperledger Fabric 实战(八):couchdb 丰富查询 selector 语法

原创
2019/01/14 10:40
阅读数 3.3K

Couchdb 查询 Selector 选择器语法

组合字符列表:

  • "$and" - 数组参数
  • "$or" - 数组参数
  • "$not" - 单一参数
  • "$nor" - 数组参数
  • "$all" - 数组参数(数组值的特殊运算符)
  • "$elemMatch" - 单一参数(数组值的特殊运算符)

条件参数列表:
平等运算符

  • "$lt" - 任意 JSON
  • "$lte" - 任意 JSON
  • "$eq" - 任意 JSON
  • "$ne" - 任意 JSON
  • "$gte" - 任意 JSON
  • "$gt" - 任意 JSON

对象相关运算符

  • "$exists" - 布尔值,检查字段是否存在,无论其值如何
  • "$type" - 字符串,检查文档字段的类型

数组相关运算符

  • "$in" - JSON值数组,文档字段必须存在于提供的列表中
  • "$nin" - JSON值数组,文档字段不得存在于提供的列表中
  • "$size" - 整数,特殊条件,用于匹配文档中数组字段的长度。非数组字段无法匹配此条件。

其他相关运营商

  • "$mod" - [Divisor,Remainder],其中Divisor和Remainder都是正整数(即大于0)。匹配文档where(field%Divisor == Remainder)为true。对于任何非整数字段,这都是错误的
  • "$regex" - 字符串,与文档字段匹配的正则表达式模式。仅当字段为字符串值并与提供的匹配项匹配时才匹配

最简单的选择器

{"selector":{"name":"tom"}}

其中

{"name":"tom"}

匹配 name 为 tom 的文档(如果存在)。使用其他字段扩展此示例可能如下所示:

{"name": "tom", "location": "Boston"}

这将匹配一个 name 叫 tom 的文件和拥有 Boston 的 location 值。

如果选择器中的对象键有两个特殊的语法元素(句号或简称)字符表示文档中的子字段。例如,这是两个相同的例子:

{"location": {"city": "Omaha"}}
{"location.city": "Omaha"}

如果对象的键包含句号,则可以使用反斜杠进行转义,即

{"location\\.city": "Omaha"}

请注意,这里需要双反斜杠来编码实际的单反斜杠。

第二个重要的语法元素是使用美元符号($)前缀来表示运算符。例如:

{"age": {"$gt": 21}}

在这个例子中创建了布尔表达式 age > 21 。

大多数形式是隐式运算符

在大多数情况下,每个操作员必须具有该形式{"$operator": argument}。虽然选择器有两个隐式运算符。

首先,任何不是条件运算符参数的JSON对象都是 $and 每个字段的隐式运算符。例如,这两个例子是相同的:

{"foo": "bar", "baz": true}
{"$and": [{"foo": {"$eq": "bar"}}, {"baz": {"$eq": true}}]}

所以任何包含没有运算符的JSON值的字段都是相等的条件。例如,这些是等价的:

{"foo": "bar"}
{"foo": {"$eq": "bar"}}

需要明确的是,这些也是等效的:

{"foo": {"bar": "baz"}}
{"foo": {"$eq": {"bar": "baz"}}}

虽然,前面的例子实际上会在内部标准化为:

{"foo.bar": {"$eq": "baz"}}

CouchDB 使用了MongoDB 的查询语言 Mango ,具体可以查看https://github.com/cloudant/mango

一个Couchdb丰富查询实战的chaincode

package main

import (
	"fmt"
	/*导入 chaincode shim 包和 peer protobuf 包*/
	"github.com/hyperledger/fabric/core/chaincode/shim"
	"github.com/hyperledger/fabric/protos/peer"

	"encoding/json"
	"time"
	"strconv"
	"bytes"
	"strings"
)

//参考: https://github.com/cloudant/mango

const prefix = "jonluo"

type CloudCertificateChaincode struct {
}

// 云证
type CloudCertificate struct {
	CloudCardNumber   string `json:"cloudCardNumber"`   //云证编号
	CloudCardPerson   string `json:"cloudCardPerson"`   //存证方
	CloudCardPlatform string `json:"cloudCardPlatform"` //传证平台
	Time              int64  `json:"time"`              //存证时间
	BlockNumber       string `json:"blockNumber"`       //存证区块号
	CloudCardHash     string `json:"cloudCardHash"`     //存证hash
	FileType          string `json:"fileType"`          //文件类型
	FileLabel         string `json:"fileLabel"`         //文件标签
	FileName          string `json:"fileName"`          //文件名
	FileAddress       string `json:"fileAddress"`       //下载地址
}

//初始化方法
func (s *CloudCertificateChaincode) Init(stub shim.ChaincodeStubInterface) peer.Response {

	return shim.Success(nil)
}

//调用Chaincode
func (s *CloudCertificateChaincode) Invoke(stub shim.ChaincodeStubInterface) peer.Response {

	//获取要调用的方法名和方法参数
	fn, args := stub.GetFunctionAndParameters()

	fmt.Printf("方法: %s  参数 : %s \n", fn, args)

	if fn == "addCard" {
		return s.addCard(stub, args)
	} else if fn == "getList" {
		return s.getList(stub, args)
	} else if fn == "get" {
		return s.get(stub, args)
	}

	return shim.Error("方法不存在")
}

func (s *CloudCertificateChaincode) addCard(stub shim.ChaincodeStubInterface, args []string) peer.Response {

	if len(args) != 1 {
		return shim.Error("参数出错")
	}

	cardStr := args[0]

	var card CloudCertificate
	//这里就是实际的解码和相关的错误检查
	if err := json.Unmarshal([]byte(cardStr), &card); err != nil {
		return shim.Error("json反序列化失败")
	}

	t := time.Now()
	id := prefix + strconv.FormatInt(t.UnixNano(), 10)
	card.CloudCardNumber = id
	card.Time = t.Unix()

	bys, err := json.Marshal(card)
	fmt.Println("json:" + string(bys))

	if err != nil {
		return shim.Error("json序列化失败")
	}

	err = stub.PutState(id, bys)
	if err != nil {
		return shim.Error(err.Error())
	}
	return shim.Success(nil)
}

func (s *CloudCertificateChaincode) getList(stub shim.ChaincodeStubInterface, args []string) peer.Response {

	if len(args) != 3 {
		return shim.Error("要输入一个键")
	}

	page, err := strconv.Atoi(args[0])
	if err != nil {
		return shim.Error("page 出错")
	}
	size, err := strconv.Atoi(args[1])
	if err != nil {
		return shim.Error("size 出错")
	}
	index := (page - 1) * size

	pmap := map[string]string{}
	if err := json.Unmarshal([]byte(args[2]), &pmap); err != nil {
		return shim.Error("json反序列化失败")
	}
	//封装条件
	selector := selectionCriteria(pmap)
	fmt.Println(selector)

	queryIterator, err := stub.GetQueryResult(selector)
	defer queryIterator.Close()

	var list = make([]CloudCertificate, 0)

	if err != nil {
		return shim.Error("GetQueryResult 出错")
	} else {
		var next = 0

		for queryIterator.HasNext() {

			if next == page*size {
				break
			}

			if next >= index {

				item, err := queryIterator.Next()
				if err != nil {
					return shim.Error("queryIterator.Next 出错")
				}

				var c CloudCertificate
				err = json.Unmarshal(item.Value, &c)
				if err != nil {
					return shim.Error("json反序列化失败")
				}
				list = append(list, c)
			}

			next++

		}
	}

	msg, err := json.Marshal(list)
	fmt.Println("json:" + string(msg))

	if err != nil {
		return shim.Error("json序列化失败")
	}

	return shim.Success(msg)

}

func (s *CloudCertificateChaincode) get(stub shim.ChaincodeStubInterface, args []string) peer.Response {

	if len(args) != 1 {
		return shim.Error("要输入一个键")
	}
	//读出
	value, err := stub.GetState(args[0])

	if err != nil {
		return shim.Error(err.Error())
	}

	return shim.Success(value)
}

func selectionCriteria(pmap map[string]string) string {

	var buffer bytes.Buffer
	buffer.WriteString(`{"selector":{`)
	buffer.WriteString(`"cloudCardNumber":{"$regex": "^` + prefix + `.*"},`)

	for k, v := range pmap {

		switch k {

		case "startTime":
			if v != "" {
				buffer.WriteString(`"time":{"$gte": ` + v + `},`)
			}
		case "endTime":
			if v != "" {
				buffer.WriteString(`"time":{"$lte": ` + v + `},`)
			}
		case "startTime-endTime":
			if v != "" {
				args := strings.Split(v,"-")
				buffer.WriteString(`"time":{"$gte": ` + args[0] + `,"$lte": ` + args[1] + `},`)
			}

		case "fileType":
			if v != "" && v != "," {
				types := `"fileType":{"$or":[`

				args := strings.Split(v,",")
				for i,tyv := range args  {
					if i != 0 {
						types += `,`
					}
					types +=`{"$eq":"`+tyv+`"}`
				}
				types += `]},`

				buffer.WriteString(types)
			}

		default:
			if k != "" && v != "" {
				buffer.WriteString(`"` + k + `":{"$eq": "` + v + `"},`)
			}

		}
	}
	buffer.Truncate(buffer.Len()-1)

	buffer.WriteString("}}")

	return buffer.String()
}

func main() {

	if err := shim.Start(new(CloudCertificateChaincode)); err != nil {
		fmt.Println("CloudCertificateChaincode start error")
	}
}
展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部