func newViewPrimaryIndex(v *viewIndexer, name string) (*primaryIndex, error) { ddoc := newPrimaryDDoc(name) doc := expression.NewIdentifier(v.keyspace.Name()) meta := expression.NewMeta(doc) mdid := expression.NewField(meta, expression.NewFieldName("id")) inst := primaryIndex{ viewIndex{ name: name, using: datastore.VIEW, on: datastore.IndexKey{mdid}, ddoc: ddoc, keyspace: v.keyspace, view: v, }, } err := inst.putDesignDoc() if err != nil { return nil, err } err = inst.WaitForIndex() if err != nil { return nil, err } return &inst, nil }
func loadViewIndexes(v *viewIndexer) ([]*datastore.Index, error) { b := v.keyspace rows, err := b.cbbucket.GetDDocs() if err != nil { return nil, err } inames := make([]string, 0, len(rows.Rows)) nonUsableIndexes := make([]string, 0) for _, row := range rows.Rows { cdoc := row.DDoc id := cdoc.Meta["id"].(string) if strings.HasPrefix(id, "_design/ddl_") { iname := strings.TrimPrefix(id, "_design/ddl_") inames = append(inames, iname) } else if strings.HasPrefix(id, "_design/dev_") { // append this to the list of non-usuable indexes iname := strings.TrimPrefix(id, "_design/dev_") for _, name := range v.nonUsableIndexes { if iname == name { continue } } nonUsableIndexes = append(nonUsableIndexes, iname) } else if strings.HasPrefix(id, "_design/") { iname := strings.TrimPrefix(id, "_design/") for _, name := range v.nonUsableIndexes { if iname == name { continue } } nonUsableIndexes = append(nonUsableIndexes, iname) } } indexes := make([]*datastore.Index, 0, len(inames)) for _, iname := range inames { ddname := "ddl_" + iname jdoc, err := getDesignDoc(b, ddname) if err != nil { return nil, err } jview, ok := jdoc.Views[iname] if !ok { nonUsableIndexes = append(nonUsableIndexes, iname) logging.Errorf("Missing view for index %v ", iname) continue } exprlist := make([]expression.Expression, 0, len(jdoc.IndexOn)) for _, ser := range jdoc.IndexOn { if iname == PRIMARY_INDEX { doc := expression.NewIdentifier(b.Name()) meta := expression.NewMeta(doc) mdid := expression.NewField(meta, expression.NewFieldName("id")) exprlist = append(exprlist, mdid) } else { expr, err := parser.Parse(ser) if err != nil { nonUsableIndexes = append(nonUsableIndexes, iname) logging.Errorf("Cannot unmarshal expression for index %v", iname) continue } exprlist = append(exprlist, expr) } } if len(exprlist) != len(jdoc.IndexOn) { continue } ddoc := designdoc{ name: ddname, viewname: iname, mapfn: jview.Map, reducefn: jview.Reduce, } if ddoc.checksum() != jdoc.IndexChecksum { nonUsableIndexes = append(nonUsableIndexes, iname) logging.Errorf("Warning - checksum failed on index %v", iname) continue } var index datastore.Index logging.Infof("Found index name %v keyspace %v", iname, b.Name()) if iname == PRIMARY_INDEX { index = &viewIndex{ name: iname, keyspace: b, view: v, using: datastore.VIEW, ddoc: &ddoc, on: exprlist, } indexes = append(indexes, &index) } else { index = &viewIndex{ name: iname, keyspace: b, view: v, using: datastore.VIEW, ddoc: &ddoc, on: exprlist, } indexes = append(indexes, &index) } } v.nonUsableIndexes = nonUsableIndexes if len(indexes) == 0 { return nil, nil } return indexes, nil }
func TestConverter(t *testing.T) { s1 := NewJSConverter().Visit( expression.NewLT(constant("a"), constant("b"))) s2 := "(\"a\" < \"b\")" if s1 != s2 { t.Errorf(" mismatch s1 %s s2 %s", s1, s2) } s1 = NewJSConverter().Visit( expression.NewBetween(constant("a"), constant("b"), constant("c"))) s2 = "(\"a\" > \"b\" && \"a\" < \"c\")" if s1 != s2 { t.Errorf(" mismatch s1 %s s2 %s", s1, s2) } s1 = NewJSConverter().Visit(expression.NewAdd( expression.NewSub(constant("a"), constant("b")), expression.NewDiv(constant("a"), constant("b")))) s2 = "((\"a\" - \"b\") + (\"a\" / \"b\"))" if s1 != s2 { t.Errorf(" mismatch s1 %s s2 %s", s1, s2) } s1 = NewJSConverter().Visit(expression.NewLength(constant("abc"))) s2 = "(\"abc\".length)" if s1 != s2 { t.Errorf(" mismatch s1 %s s2 %s", s1, s2) } s1 = NewJSConverter().Visit(expression.NewUpper(constant("abc"))) s2 = "(\"abc\".toUpperCase())" if s1 != s2 { t.Errorf(" mismatch s1 %s s2 %s", s1, s2) } s1 = NewJSConverter().Visit(expression.NewStrToMillis(constant("Wed, 09 Aug 1995 00:00:00"))) s2 = "(Date.parse(\"Wed, 09 Aug 1995 00:00:00\"))" if s1 != s2 { t.Errorf(" mismatch s1 %s s2 %s", s1, s2) } s1 = NewJSConverter().Visit(expression.NewContains(constant("dfgabc"), constant("abc"))) s2 = "(\"dfgabc\".indexOf(\"abc\"))" if s1 != s2 { t.Errorf(" mismatch s1 %s s2 %s", s1, s2) } s1 = NewJSConverter().Visit(expression.NewSubstr(constant("dfgabc"), constant(1), constant(4))) s2 = "(\"dfgabc\".substring(1,4))" if s1 != s2 { t.Errorf(" mismatch s1 %s s2 %s", s1, s2) } s1 = NewJSConverter().Visit(expression.NewAdd(expression.NewContains(constant("dfgabc"), constant("abc")), expression.NewSubstr(constant("dfgabc"), constant(1), constant(4)))) s2 = "((\"dfgabc\".indexOf(\"abc\")) + (\"dfgabc\".substring(1,4)))" if s1 != s2 { t.Errorf(" mismatch s1 %s s2 %s", s1, s2) } doc := expression.NewIdentifier("bucket") m1 := expression.NewField(doc, expression.NewFieldName("id")) m2 := expression.NewField(doc, expression.NewFieldName("type")) s1 = NewJSConverter().Visit(expression.NewOr( expression.NewUpper(m1), expression.NewLower(m2))) s2 = "((`bucket`.`id`.toUpperCase()) || (`bucket`.`type`.toLowerCase()))" if s1 != s2 { t.Errorf(" mismatch s1 %s s2 %s", s1, s2) } doc = expression.NewIdentifier("bucket") m1 = expression.NewField(doc, expression.NewFieldName("geo")) m2 = expression.NewField(m1, expression.NewFieldName("accuracy")) s1 = NewJSConverter().Visit(m2) s2 = "`bucket`.`geo`.`accuracy`" if s1 != s2 { t.Errorf(" mismatch s1 %s s2 %s", s1, s2) } doc = expression.NewIdentifier("bucket") m1 = expression.NewField(doc, expression.NewElement(expression.NewFieldName("address"), constant(0))) s1 = NewJSConverter().Visit(m1) s2 = "`bucket`.`address`[0]" if s1 != s2 { t.Errorf(" mismatch s1 %s s2 %s", s1, s2) } s1 = NewJSConverter().Visit(expression.NewLength(expression.NewElement(doc, expression.NewFieldName("type")))) s2 = "(`bucket`[`type`].length)" if s1 != s2 { t.Errorf(" mismatch s1 %s s2 %s", s1, s2) } }
func (this *builder) selectScan(keyspace datastore.Keyspace, node *algebra.KeyspaceTerm) (Operator, error) { if this.where == nil { return this.selectPrimaryScan(keyspace, node) } nnf := planner.NewNNF() where := this.where.Copy() where, err := nnf.Map(where) if err != nil { return nil, err } formalizer := expression.NewFormalizer() formalizer.Keyspace = node.Alias() primaryKey := expression.NewField( expression.NewMeta(expression.NewConstant(node.Alias())), expression.NewFieldName("id")) indexers, err := keyspace.Indexers() if err != nil { return nil, err } indexes := make([]datastore.Index, 0, len(indexers)*16) primaryIndexes := make(map[datastore.Index]bool, len(indexers)*2) for _, indexer := range indexers { idxs, err := indexer.Indexes() if err != nil { return nil, err } indexes = append(indexes, idxs...) primaryIdxs, err := indexer.PrimaryIndexes() if err != nil { return nil, err } for _, p := range primaryIdxs { primaryIndexes[p] = true } } unfiltered := make(map[datastore.Index]expression.Expression, len(indexes)) filtered := make(map[datastore.Index]expression.Expression, len(indexes)) for _, index := range indexes { state, _, er := index.State() if er != nil { return nil, er } if state != datastore.ONLINE { continue } var key expression.Expression if primaryIndexes[index] { key = primaryKey } else { rangeKey := index.RangeKey() if len(rangeKey) == 0 || rangeKey[0] == nil { // Index not rangeable continue } key := rangeKey[0].Copy() key, err = formalizer.Map(key) if err != nil { return nil, err } key, err = nnf.Map(key) if err != nil { return nil, err } } if !planner.SargableFor(where, key) { // Index not applicable continue } indexCond := index.Condition() if indexCond == nil { unfiltered[index] = key continue } indexCond = indexCond.Copy() indexCond, err = formalizer.Map(indexCond) if err != nil { return nil, err } indexCond, err = nnf.Map(indexCond) if err != nil { return nil, err } if planner.SubsetOf(where, indexCond) { // Index condition satisfies query condition filtered[index] = key break } } var indexMap map[datastore.Index]expression.Expression if len(filtered) > 0 { indexMap = filtered } else if len(unfiltered) > 0 { indexMap = unfiltered } for index, key := range indexMap { spans := planner.SargFor(where, key) var scan Operator scan = NewIndexScan(index, node, spans, false, math.MaxInt64) if len(spans) > 1 { // Use UnionScan to de-dup multiple spans scan = NewUnionScan(scan) } return scan, err } return this.selectPrimaryScan(keyspace, node) }