func (l location) Predicate(ctx context.Context, args []string) (*Constraint, error) { where := args[0] rects, err := geocode.Lookup(ctx, where) if err != nil { return nil, err } if len(rects) == 0 { return nil, fmt.Errorf("No location found for %q", where) } var c *Constraint for i, rect := range rects { loc := &LocationConstraint{ West: rect.SouthWest.Long, East: rect.NorthEast.Long, North: rect.NorthEast.Lat, South: rect.SouthWest.Lat, } permLoc := &Constraint{ Permanode: &PermanodeConstraint{ Location: loc, }, } if i == 0 { c = permLoc } else { c = orConst(c, permLoc) } } return c, nil }
func parseLocationAtom(ctx *context.Context, word string) (*Constraint, error) { if strings.HasPrefix(word, "loc:") { where := strings.TrimPrefix(word, "loc:") rects, err := geocode.Lookup(ctx, where) if err != nil { return nil, err } if len(rects) == 0 { return nil, fmt.Errorf("No location found for %q", where) } var c *Constraint for i, rect := range rects { loc := &LocationConstraint{ West: rect.SouthWest.Long, East: rect.NorthEast.Long, North: rect.NorthEast.Lat, South: rect.SouthWest.Lat, } fileLoc := permOfFile(&FileConstraint{ IsImage: true, Location: loc, }) permLoc := &Constraint{ Permanode: &PermanodeConstraint{ Location: loc, }, } rectConstraint := orConst(fileLoc, permLoc) if i == 0 { c = rectConstraint } else { c = orConst(c, rectConstraint) } } return c, nil } if word == "has:location" { c := permOfFile(&FileConstraint{ IsImage: true, Location: &LocationConstraint{ Any: true, }, }) return c, nil } return nil, errors.New(fmt.Sprintf("Not an location-atom: %v", word)) }
// parseExpression parses a search expression (e.g. "tag:funny // near:portland") and returns a SearchQuery for that search text. The // Constraint field will always be set. The Limit and Sort may also be // set. func parseExpression(ctx *context.Context, exp string) (*SearchQuery, error) { base := &Constraint{ Permanode: &PermanodeConstraint{ SkipHidden: true, }, } sq := &SearchQuery{ Constraint: base, } exp = strings.TrimSpace(exp) if exp == "" { return sq, nil } andNot := false // whether the next and(x) is really a and(!x) and := func(c *Constraint) { old := sq.Constraint if andNot { c = &Constraint{ Logical: &LogicalConstraint{ Op: "not", A: c, }, } } sq.Constraint = &Constraint{ Logical: &LogicalConstraint{ Op: "and", A: old, B: c, }, } } permOfFile := func(fc *FileConstraint) *Constraint { return &Constraint{ Permanode: &PermanodeConstraint{ Attr: "camliContent", ValueInSet: &Constraint{File: fc}, }, } } orConst := func(a, b *Constraint) *Constraint { return &Constraint{ Logical: &LogicalConstraint{ Op: "or", A: a, B: b, }, } } andFile := func(fc *FileConstraint) { and(permOfFile(fc)) } andWHRatio := func(fc *FloatConstraint) { andFile(&FileConstraint{ IsImage: true, WHRatio: fc, }) } words := strings.Fields(exp) for _, word := range words { andNot = false if strings.HasPrefix(word, "-") { andNot = true word = word[1:] } if m := tagExpr.FindStringSubmatch(word); m != nil { and(&Constraint{ Permanode: &PermanodeConstraint{ Attr: "tag", SkipHidden: true, Value: m[1], }, }) continue } if m := titleExpr.FindStringSubmatch(word); m != nil { and(&Constraint{ Permanode: &PermanodeConstraint{ Attr: "title", SkipHidden: true, ValueMatches: &StringConstraint{ Contains: m[1], CaseInsensitive: true, }, }, }) continue } if word == "is:image" { and(&Constraint{ Permanode: &PermanodeConstraint{ Attr: "camliContent", ValueInSet: &Constraint{ File: &FileConstraint{ IsImage: true, }, }, }, }) continue } if word == "is:landscape" { andWHRatio(&FloatConstraint{Min: 1.0}) continue } if word == "is:portrait" { andWHRatio(&FloatConstraint{Max: 1.0}) continue } if word == "is:pano" { andWHRatio(&FloatConstraint{Min: 1.6}) continue } if word == "has:location" { andFile(&FileConstraint{ IsImage: true, Location: &LocationConstraint{ Any: true, }, }) continue } if strings.HasPrefix(word, "format:") { andFile(&FileConstraint{ MIMEType: &StringConstraint{ Equals: mimeFromFormat(strings.TrimPrefix(word, "format:")), }, }) continue } if strings.HasPrefix(word, "width:") { m := whRangeExpr.FindStringSubmatch(strings.TrimPrefix(word, "width:")) if m == nil { return nil, errors.New("bogus width range") } andFile(&FileConstraint{ IsImage: true, Width: whIntConstraint(m[1], m[2]), }) continue } if strings.HasPrefix(word, "height:") { m := whRangeExpr.FindStringSubmatch(strings.TrimPrefix(word, "height:")) if m == nil { return nil, errors.New("bogus height range") } andFile(&FileConstraint{ IsImage: true, Height: whIntConstraint(m[1], m[2]), }) continue } if strings.HasPrefix(word, "before:") || strings.HasPrefix(word, "after:") { before := false when := "" if strings.HasPrefix(word, "before:") { before = true when = strings.TrimPrefix(word, "before:") } else { when = strings.TrimPrefix(word, "after:") } base := "0000-01-01T00:00:00Z" if len(when) < len(base) { when += base[len(when):] } t, err := time.Parse(time.RFC3339, when) if err != nil { return nil, err } tc := &TimeConstraint{} if before { tc.Before = types.Time3339(t) } else { tc.After = types.Time3339(t) } and(&Constraint{ Permanode: &PermanodeConstraint{ Time: tc, }, }) continue } if strings.HasPrefix(word, "loc:") { where := strings.TrimPrefix(word, "loc:") rects, err := geocode.Lookup(ctx, where) if err != nil { return nil, err } if len(rects) == 0 { return nil, fmt.Errorf("No location found for %q", where) } var locConstraint *Constraint for i, rect := range rects { rectConstraint := permOfFile(&FileConstraint{ IsImage: true, Location: &LocationConstraint{ West: rect.SouthWest.Long, East: rect.NorthEast.Long, North: rect.NorthEast.Lat, South: rect.SouthWest.Lat, }, }) if i == 0 { locConstraint = rectConstraint } else { locConstraint = orConst(locConstraint, rectConstraint) } } and(locConstraint) continue } log.Printf("Unknown search expression word %q", word) // TODO: finish. better tokenization. non-operator tokens // are text searches, etc. } return sq, nil }