Ejemplo n.º 1
0
Archivo: parse.go Proyecto: deoxxa/qs
// parseExpr3 handles NOT expressions
//
//   expr3 = {"NOT"} expr4
func (p *Parser) parseExpr3(ctx context) (tokType, bleve.Query, error) {

	tok := p.next()
	if tok.typ != tNOT {
		p.backup()
		// just let the lower, non-NOT expression bubble up with its prefix
		return p.parseExpr4(ctx)
	}

	prefix, q, err := p.parseExpr4(ctx)
	if err != nil {
		return tEOF, nil, err
	}

	// KLUDGINESS - prefixes on terms in NOT expressions:
	// `NOT -bob`  => `bob`
	// `NOT +bob`  => `NOT bob`
	if prefix != tMINUS {
		q = bleve.NewBooleanQuery(
			[]bleve.Query{},
			[]bleve.Query{},
			[]bleve.Query{q}, // mustNot
		)
	}
	return tEOF, q, nil
}
Ejemplo n.º 2
0
Archivo: parse.go Proyecto: deoxxa/qs
// starting point
//   exprList = expr1*
func (p *Parser) parseExprList(ctx context) (bleve.Query, error) {
	// <empty>
	if p.peek().typ == tEOF {
		return bleve.NewMatchNoneQuery(), nil
	}

	must := []bleve.Query{}
	mustNot := []bleve.Query{}
	should := []bleve.Query{}

	for {
		tok := p.peek()
		if tok.typ == tEOF {
			break
		}
		// slightly kludgy...
		if tok.typ == tRPAREN {
			break
		}

		prefix, q, err := p.parseExpr1(ctx)
		if err != nil {
			return nil, err
		}

		switch prefix {
		case tPLUS:
			must = append(must, q)
		case tMINUS:
			mustNot = append(mustNot, q)
		default:
			if p.DefaultOp == AND {
				must = append(must, q)
			} else { // OR
				should = append(should, q)
			}
		}
	}

	total := len(must) + len(mustNot) + len(should)
	if total == 0 {
		return bleve.NewMatchNoneQuery(), nil
	}
	if total == 1 && len(must) == 1 {
		return must[0], nil
	}
	if total == 1 && len(should) == 1 {
		return should[0], nil
	}

	return bleve.NewBooleanQuery(must, should, mustNot), nil
}
Ejemplo n.º 3
0
Archivo: parse.go Proyecto: deoxxa/qs
// parseExpr2 handles AND expressions
//
//   expr2 = expr3 {"AND" expr3}
func (p *Parser) parseExpr2(ctx context) (tokType, bleve.Query, error) {

	queries := []bleve.Query{}
	prefixes := []tokType{}

	for {
		prefix, q, err := p.parseExpr3(ctx)
		if err != nil {
			return tEOF, nil, err
		}

		prefixes = append(prefixes, prefix)
		queries = append(queries, q)

		tok := p.next()
		if tok.typ != tAND {
			p.backup()
			break
		}
	}

	// let single, non-AND expressions bubble upward, prefix intact
	if len(queries) == 1 {
		return prefixes[0], queries[0], nil
	}

	// KLUDGINESS - prefixes on terms in AND expressions
	// we'll ignore "+" and treat "-" as NOT
	// eg:
	// `+alice AND -bob AND chuck`  => `alice AND (NOT bob) AND chuck`
	for i, _ := range queries {
		if prefixes[i] == tMINUS {
			queries[i] = bleve.NewBooleanQuery(
				[]bleve.Query{},
				[]bleve.Query{},
				[]bleve.Query{queries[i]}, // mustNot
			)
		}
	}

	return tEOF, bleve.NewConjunctionQuery(queries), nil
}
Ejemplo n.º 4
0
Archivo: main.go Proyecto: d4l3k/campus
// search executes a search for rooms or buildings.
func (s *Server) search(w http.ResponseWriter, r *http.Request) {
	query := r.URL.Query()

	q := query.Get("q")
	typeFilter := query.Get("type")

	results := []*models.Index{}
	if idx, ok := s.idIndex[q]; ok {
		results = append(results, idx)
	} else {
		query := bleve.NewBooleanQuery()
		if len(q) > 0 {
			/*fuzzy_query := bleve.NewFuzzyQuery(q)
			fuzzy_query.FuzzinessVal = 3
			queryShould = append(queryShould, fuzzy_query)
			queryShould = append(queryShould, bleve.NewRegexpQuery("[a-zA-Z0-9_]*"+q+"[a-zA-Z0-9_]*"))
			queryShould = append(queryShould, bleve.NewQueryStringQuery(q))*/
			query.AddShould(bleve.NewQueryStringQuery(q))
		}

		if typeFilter != "all" {
			termQuery := bleve.NewTermQuery(typeFilter)
			query.AddMust(termQuery)
		}

		searchRequest := bleve.NewSearchRequest(query)
		searchRequest.Size = 25
		searchResult, err := s.index.Search(searchRequest)
		if err != nil {
			http.Error(w, err.Error(), 500)
			return
		}

		for _, result := range []*search.DocumentMatch(searchResult.Hits) {
			results = append(results, s.idIndex[result.ID])
		}
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(results)
}
Ejemplo n.º 5
0
// parseExpr1 handles OR expressions
//
//   expr1 = expr2 {"OR" expr2}
func (p *Parser) parseExpr1(ctx context) (tokType, query.Query, error) {

	queries := []query.Query{}
	prefixes := []tokType{}

	for {
		prefix, q, err := p.parseExpr2(ctx)
		if err != nil {
			return tEOF, nil, err
		}

		prefixes = append(prefixes, prefix)
		queries = append(queries, q)

		tok := p.next()
		if tok.typ != tOR {
			p.backup()
			break
		}
	}

	// let single, non-OR expressions bubble upward, prefix intact
	if len(queries) == 1 {
		return prefixes[0], queries[0], nil
	}

	// KLUDGINESS - prefixes on terms in OR expressions
	// we'll ignore "+" and treat "-" as NOT
	// eg:
	// `+alice OR -bob OR chuck`  => `alice OR (NOT bob) OR chuck`
	for i, _ := range queries {
		if prefixes[i] == tMINUS {
			q := bleve.NewBooleanQuery()
			q.AddMustNot(queries[i])
			queries[i] = q
		}
	}

	return tEOF, bleve.NewDisjunctionQuery(queries...), nil
}
Ejemplo n.º 6
0
func TestQuerySyntaxParserValid(t *testing.T) {
	fivePointOh := 5.0
	onePointOh := 1.0
	theTruth := true
	theFalsehood := false

	jan_01_2015 := numeric_util.Int64ToFloat64(time.Date(2015, time.January, 01, 0, 0, 0, 0, time.UTC).UnixNano())
	jan_02_2015 := numeric_util.Int64ToFloat64(time.Date(2015, time.January, 02, 0, 0, 0, 0, time.UTC).UnixNano())
	mar_15_2015 := numeric_util.Int64ToFloat64(time.Date(2015, time.March, 15, 0, 0, 0, 0, time.UTC).UnixNano())
	mar_16_2015 := numeric_util.Int64ToFloat64(time.Date(2015, time.March, 16, 0, 0, 0, 0, time.UTC).UnixNano())

	tests := []struct {
		input   string
		result  bleve.Query
		mapping *bleve.IndexMapping
	}{
		{
			input:   "test",
			mapping: NewIndexMapping(),
			result:  bleve.NewMatchPhraseQuery("test"),
		},
		{
			input:   `"test phrase 1"`,
			mapping: NewIndexMapping(),
			result:  bleve.NewMatchPhraseQuery("test phrase 1"),
		},
		{
			input:   "field:test",
			mapping: NewIndexMapping(),
			result:  bleve.NewMatchPhraseQuery("test").SetField("field"),
		},
		// - is allowed inside a term, just not the start
		{
			input:   "field:t-est",
			mapping: NewIndexMapping(),
			result:  bleve.NewMatchPhraseQuery("t-est").SetField("field"),
		},
		// + is allowed inside a term, just not the start
		{
			input:   "field:t+est",
			mapping: NewIndexMapping(),
			result:  bleve.NewMatchPhraseQuery("t+est").SetField("field"),
		},
		// > is allowed inside a term, just not the start
		{
			input:   "field:t>est",
			mapping: NewIndexMapping(),
			result:  bleve.NewMatchPhraseQuery("t>est").SetField("field"),
		},
		// < is allowed inside a term, just not the start
		{
			input:   "field:t<est",
			mapping: NewIndexMapping(),
			result:  bleve.NewMatchPhraseQuery("t<est").SetField("field"),
		},
		// = is allowed inside a term, just not the start
		{
			input:   "field:t=est",
			mapping: NewIndexMapping(),
			result:  bleve.NewMatchPhraseQuery("t=est").SetField("field"),
		},
		{
			input:   "+field1:test1",
			mapping: NewIndexMapping(),
			result:  bleve.NewMatchPhraseQuery("test1").SetField("field1"),
		},
		{
			input:   "-field2:test2",
			mapping: NewIndexMapping(),
			result: bleve.NewBooleanQuery(
				nil,
				nil,
				[]bleve.Query{
					bleve.NewMatchPhraseQuery("test2").SetField("field2"),
				}),
		},
		{
			input:   `field3:"test phrase 2"`,
			mapping: NewIndexMapping(),
			result:  bleve.NewMatchPhraseQuery("test phrase 2").SetField("field3"),
		},
		{
			input:   `+field4:"test phrase 1"`,
			mapping: NewIndexMapping(),
			result:  bleve.NewMatchPhraseQuery("test phrase 1").SetField("field4"),
		},
		{
			input:   `-field5:"test phrase 2"`,
			mapping: NewIndexMapping(),
			result: bleve.NewBooleanQuery(
				nil,
				nil,
				[]bleve.Query{
					bleve.NewMatchPhraseQuery("test phrase 2").SetField("field5"),
				}),
		},
		{
			input:   `+field6:test3 -field7:test4 field8:test5`,
			mapping: NewIndexMapping(),
			result: bleve.NewBooleanQuery(
				[]bleve.Query{
					bleve.NewMatchPhraseQuery("test3").SetField("field6"),
				},
				[]bleve.Query{
					bleve.NewMatchPhraseQuery("test5").SetField("field8"),
				},
				[]bleve.Query{
					bleve.NewMatchPhraseQuery("test4").SetField("field7"),
				}),
		},
		{
			input:   "test^3",
			mapping: NewIndexMapping(),
			result:  bleve.NewMatchPhraseQuery("test").SetBoost(3.0),
		},
		{
			input:   "test^3 other^6",
			mapping: NewIndexMapping(),
			result: bleve.NewBooleanQuery(
				nil,
				[]bleve.Query{
					bleve.NewMatchPhraseQuery("test").SetBoost(3.0),
					bleve.NewMatchPhraseQuery("other").SetBoost(6.0),
				},
				nil,
			),
		},
		{
			input:   "33",
			mapping: NewIndexMapping(),
			result:  bleve.NewMatchPhraseQuery("33"),
		},
		{
			input:   "field:33",
			mapping: NewIndexMapping(),
			result:  bleve.NewMatchPhraseQuery("33").SetField("field"),
		},
		{
			input:   "cat-dog",
			mapping: NewIndexMapping(),
			result:  bleve.NewMatchPhraseQuery("cat-dog"),
		},
		/*
			        // TODO: MatchPhraseQuery doesn't handle fuzziness...
					{
						input:   "watex~",
						mapping: NewIndexMapping(),
						result:  bleve.NewMatchPhraseQuery("watex").SetFuzziness(1),
					},

				{
					input:   "watex~2",
					mapping: NewIndexMapping(),
					result: bleve.NewBooleanQuery(
						nil,
						[]bleve.Query{
							bleve.NewMatchQuery("watex").SetFuzziness(2),
						},
						nil),
				},
				{
					input:   "watex~ 2",
					mapping: NewIndexMapping(),
					result: bleve.NewBooleanQuery(
						nil,
						[]bleve.Query{
							bleve.NewMatchQuery("watex").SetFuzziness(1),
							bleve.NewMatchQuery("2"),
						},
						nil),
				},
				{
					input:   "field:watex~",
					mapping: NewIndexMapping(),
					result: bleve.NewBooleanQuery(
						nil,
						[]bleve.Query{
							bleve.NewMatchQuery("watex").SetFuzziness(1).SetField("field"),
						},
						nil),
				},
				{
					input:   "field:watex~2",
					mapping: NewIndexMapping(),
					result: bleve.NewBooleanQuery(
						nil,
						[]bleve.Query{
							bleve.NewMatchQuery("watex").SetFuzziness(2).SetField("field"),
						},
						nil),
				},
		*/
		{
			input:   `field:555c3bb06f7a127cda000005`,
			mapping: NewIndexMapping(),
			result:  bleve.NewMatchPhraseQuery("555c3bb06f7a127cda000005").SetField("field"),
		},
		{
			input:   `field:>5`,
			mapping: NewIndexMapping(),
			result:  bleve.NewNumericRangeInclusiveQuery(&fivePointOh, nil, &theFalsehood, nil).SetField("field"),
		},
		{
			input:   `field:>=5`,
			mapping: NewIndexMapping(),
			result:  bleve.NewNumericRangeInclusiveQuery(&fivePointOh, nil, &theTruth, nil).SetField("field"),
		},
		{
			input:   `field:<5`,
			mapping: NewIndexMapping(),
			result:  bleve.NewNumericRangeInclusiveQuery(nil, &fivePointOh, nil, &theFalsehood).SetField("field"),
		},
		{
			input:   `field:<=5`,
			mapping: NewIndexMapping(),
			result:  bleve.NewNumericRangeInclusiveQuery(nil, &fivePointOh, nil, &theTruth).SetField("field"),
		},
		{
			input:   `grapefruit AND lemon`,
			mapping: NewIndexMapping(),
			result: bleve.NewConjunctionQuery([]bleve.Query{
				bleve.NewMatchPhraseQuery("grapefruit"),
				bleve.NewMatchPhraseQuery("lemon"),
			}),
		},
		{
			input:   `grapefruit OR lemon`,
			mapping: NewIndexMapping(),
			result: bleve.NewDisjunctionQuery([]bleve.Query{
				bleve.NewMatchPhraseQuery("grapefruit"),
				bleve.NewMatchPhraseQuery("lemon"),
			}),
		},
		{
			// default operator is OR
			input:   `grapefruit lemon`,
			mapping: NewIndexMapping(),
			result: bleve.NewBooleanQuery(
				nil,
				[]bleve.Query{
					bleve.NewMatchPhraseQuery("grapefruit"),
					bleve.NewMatchPhraseQuery("lemon"),
				},
				nil,
			),
		},
		{
			input:   `grapefruit AND NOT lemon`,
			mapping: NewIndexMapping(),
			result: bleve.NewConjunctionQuery([]bleve.Query{
				bleve.NewMatchPhraseQuery("grapefruit"),
				bleve.NewBooleanQuery(nil, nil, []bleve.Query{bleve.NewMatchPhraseQuery("lemon")}),
			}),
		},
		{
			input:   `field:(grapefruit AND lemon)`,
			mapping: NewIndexMapping(),
			result: bleve.NewConjunctionQuery([]bleve.Query{
				bleve.NewMatchPhraseQuery("grapefruit").SetField("field"),
				bleve.NewMatchPhraseQuery("lemon").SetField("field"),
			}),
		},
		{
			input:   `-field:(grapefruit AND lemon)`,
			mapping: NewIndexMapping(),
			result: bleve.NewBooleanQuery(nil, nil, []bleve.Query{
				bleve.NewConjunctionQuery([]bleve.Query{
					bleve.NewMatchPhraseQuery("grapefruit").SetField("field"),
					bleve.NewMatchPhraseQuery("lemon").SetField("field"),
				}),
			}),
		},
		{
			input:   `shoesize:[1 TO 5]`,
			mapping: NewIndexMapping(),
			result:  bleve.NewNumericRangeInclusiveQuery(&onePointOh, &fivePointOh, &theTruth, &theTruth).SetField("shoesize"),
		},
		{
			input:   `shoesize:{1 TO 5}`,
			mapping: NewIndexMapping(),
			result:  bleve.NewNumericRangeInclusiveQuery(&onePointOh, &fivePointOh, &theFalsehood, &theFalsehood).SetField("shoesize"),
		},
		{
			input:   `shoesize:[1 TO 5}`,
			mapping: NewIndexMapping(),
			result:  bleve.NewNumericRangeInclusiveQuery(&onePointOh, &fivePointOh, &theTruth, &theFalsehood).SetField("shoesize"),
		},
		{
			input:   `shoesize:{1 TO 5]`,
			mapping: NewIndexMapping(),
			result:  bleve.NewNumericRangeInclusiveQuery(&onePointOh, &fivePointOh, &theFalsehood, &theTruth).SetField("shoesize"),
		},
		{
			input:   `shoesize:[ TO 5]`,
			mapping: NewIndexMapping(),
			result:  bleve.NewNumericRangeInclusiveQuery(nil, &fivePointOh, nil, &theTruth).SetField("shoesize"),
		},
		{
			input:   `shoesize:[1 TO ]`,
			mapping: NewIndexMapping(),
			result:  bleve.NewNumericRangeInclusiveQuery(&onePointOh, nil, &theTruth, nil).SetField("shoesize"),
		},
		// date ranges (note that endpoints and inclusivity might be modified by the parser)
		{
			input:   `when:[2015-01-01 TO 2015-03-15]`,
			mapping: NewIndexMapping(),
			result:  bleve.NewNumericRangeInclusiveQuery(&jan_01_2015, &mar_16_2015, &theTruth, &theFalsehood).SetField("when"),
		},
		{
			input:   `when:{2015-01-01 TO 2015-03-15]`,
			mapping: NewIndexMapping(),
			result:  bleve.NewNumericRangeInclusiveQuery(&jan_02_2015, &mar_16_2015, &theTruth, &theFalsehood).SetField("when"),
		},
		{
			input:   `when:[2015-01-01 TO 2015-03-15}`,
			mapping: NewIndexMapping(),
			result:  bleve.NewNumericRangeInclusiveQuery(&jan_01_2015, &mar_15_2015, &theTruth, &theFalsehood).SetField("when"),
		},
		{
			input:   `when:>2015-03-15`,
			mapping: NewIndexMapping(),
			result:  bleve.NewNumericRangeInclusiveQuery(&mar_16_2015, nil, &theTruth, nil).SetField("when"),
		},
	}

	for _, test := range tests {

		q, err := Parse(test.input)
		if err != nil {
			t.Error(err)
		}
		if !reflect.DeepEqual(q, test.result) {
			t.Errorf("Expected %#v, got %#v: for `%s`", test.result, q, test.input)
			//			t.Errorf("Expected %#v, got %#v: for %s", test.result.(*booleanQuery).Should.(*disjunctionQuery).Disjuncts[0], q.(*booleanQuery).Should.(*disjunctionQuery).Disjuncts[0], test.input)
		}
	}
}
Ejemplo n.º 7
0
// starting point
//   exprList = expr1*
func (p *Parser) parseExprList(ctx context) (query.Query, error) {
	// <empty>
	if p.peek().typ == tEOF {
		return bleve.NewMatchNoneQuery(), nil
	}

	must := []query.Query{}
	mustNot := []query.Query{}
	should := []query.Query{}

	for {
		tok := p.peek()
		if tok.typ == tEOF {
			break
		}
		// slightly kludgy...
		if tok.typ == tRPAREN {
			break
		}

		prefix, q, err := p.parseExpr1(ctx)
		if err != nil {
			return nil, err
		}

		switch prefix {
		case tPLUS:
			must = append(must, q)
		case tMINUS:
			mustNot = append(mustNot, q)
		default:
			if p.DefaultOp == AND {
				must = append(must, q)
			} else { // OR
				should = append(should, q)
			}
		}
	}

	// some obvious shortcuts
	total := len(must) + len(mustNot) + len(should)
	if total == 0 {
		return bleve.NewMatchNoneQuery(), nil
	}
	if total == 1 && len(must) == 1 {
		return must[0], nil
	}
	if total == 1 && len(should) == 1 {
		return should[0], nil
	}

	// no shortcuts - go with the full-fat version
	q := bleve.NewBooleanQuery()
	if len(must) > 0 {
		q.AddMust(must...)
	}
	if len(should) > 0 {
		q.AddShould(should...)
	}
	if len(mustNot) > 0 {
		q.AddMustNot(mustNot...)
	}
	return q, nil
}
Ejemplo n.º 8
0
func getJobSearchRequest(r *http.Request) (*bleve.SearchRequest, error) {
	musts := []query.Query{}
	// mustNots := []query.Query{}
	// shoulds := []query.Query{}

	tagShoulds := []query.Query{}
	for _, tag := range r.URL.Query()["tags"] {
		tagShoulds = append(tagShoulds, bleve.NewMatchQuery(tag))
	}
	if len(tagShoulds) > 0 {
		booleanQuery := bleve.NewBooleanQuery()
		booleanQuery.AddShould(tagShoulds...)
		musts = append(musts, booleanQuery)
	}

	value1 := 0.0
	if len(r.URL.Query().Get("price_from")) != 0 {
		intValue1, err := strconv.ParseInt(r.URL.Query().Get("price_from"), 10, 64)
		if err != nil {
			return nil, err
		}
		value1 = float64(intValue1)
	}
	value2 := math.MaxFloat64
	if len(r.URL.Query().Get("price_to")) != 0 {
		intValue2, err := strconv.ParseInt(r.URL.Query().Get("price_to"), 10, 64)
		if err != nil {
			return nil, err
		}
		value2 = float64(intValue2)
	}

	inclusiveValue1 := true
	inclusiveValue2 := false
	numericRangeIncludiveQuery := bleve.NewNumericRangeInclusiveQuery(
		&value1,
		&value2,
		&inclusiveValue1,
		&inclusiveValue2,
	)
	numericRangeIncludiveQuery.SetField("price")
	musts = append(musts, numericRangeIncludiveQuery)

	period := int64(30)
	if len(r.URL.Query().Get("period")) != 0 {
		periodTemp, err := strconv.ParseInt(r.URL.Query().Get("period"), 10, 64)
		if err != nil {
			return nil, err
		}
		if periodTemp > 0 && periodTemp <= 365 {
			period = periodTemp
		}
	}

	now := time.Now()
	dateTo := time.Now().Add(time.Duration(24*period) * time.Hour)
	dateRangeQuery := bleve.NewDateRangeQuery(now, dateTo)
	dateRangeQuery.SetField("startDate")
	musts = append(musts, dateRangeQuery)

	query := bleve.NewBooleanQuery()
	query.AddMust(musts...)
	// query.AddMustNot(mustNots...)
	// query.AddShould(shoulds...)

	searchRequest := bleve.NewSearchRequest(query)
	searchRequest.Fields = []string{"*"}

	return searchRequest, nil
}