Beispiel #1
0
// parseExpr1 handles OR expressions
//
//   expr1 = expr2 {"OR" expr2}
func (p *Parser) parseExpr1(ctx context) (tokType, bleve.Query, error) {

	queries := []bleve.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 {
			queries[i] = bleve.NewBooleanQuery(
				[]bleve.Query{},
				[]bleve.Query{},
				[]bleve.Query{queries[i]}, // mustNot
			)
		}
	}

	return tEOF, bleve.NewDisjunctionQuery(queries), nil
}
Beispiel #2
0
func bleveQueryFromExpr(expr query.Expr) (bleve.Query, error) {
	if e, ok := expr.(*query.FieldExpr); ok {
		return bleve.NewPhraseQuery([]string{e.Term}, e.Field), nil
	} else if e, ok := expr.(*query.BinaryExpr); ok {
		lhs, err := bleveQueryFromExpr(e.LHS)
		if err != nil {
			return nil, err
		}
		rhs, err := bleveQueryFromExpr(e.RHS)
		if err != nil {
			return nil, err
		}
		if e.Op == query.AND {
			return bleve.NewConjunctionQuery([]bleve.Query{lhs, rhs}), nil
		} else if e.Op == query.OR {
			return bleve.NewDisjunctionQuery([]bleve.Query{lhs, rhs}), nil
		}
	}

	return nil, nil
}
Beispiel #3
0
func query(term, highlight string, index bleve.Index, u content.User, feedIds []data.FeedId, paging ...int) (ua []content.UserArticle, err error) {
	var query bleve.Query

	query = bleve.NewQueryStringQuery(term)

	if len(feedIds) > 0 {
		queries := make([]bleve.Query, len(feedIds))
		conjunct := make([]bleve.Query, 2)

		for i, id := range feedIds {
			q := bleve.NewTermQuery(strconv.FormatInt(int64(id), 10))
			q.SetField("FeedId")

			queries[i] = q
		}

		disjunct := bleve.NewDisjunctionQuery(queries)

		conjunct[0] = query
		conjunct[1] = disjunct

		query = bleve.NewConjunctionQuery(conjunct)
	}

	searchRequest := bleve.NewSearchRequest(query)

	if highlight != "" {
		searchRequest.Highlight = bleve.NewHighlightWithStyle(highlight)
	}

	limit, offset := pagingLimit(paging)
	searchRequest.Size = limit
	searchRequest.From = offset

	searchResult, err := index.Search(searchRequest)

	if err != nil {
		return
	}

	if len(searchResult.Hits) == 0 {
		return
	}

	articleIds := []data.ArticleId{}
	hitMap := map[data.ArticleId]*search.DocumentMatch{}

	for _, hit := range searchResult.Hits {
		if articleId, err := strconv.ParseInt(hit.ID, 10, 64); err == nil {
			id := data.ArticleId(articleId)
			articleIds = append(articleIds, id)
			hitMap[id] = hit
		}
	}

	ua = u.ArticlesById(articleIds)
	if u.HasErr() {
		return ua, u.Err()
	}

	for i := range ua {
		data := ua[i].Data()

		hit := hitMap[data.Id]

		if len(hit.Fragments) > 0 {
			data.Hit.Fragments = hit.Fragments
			ua[i].Data(data)
		}
	}
	return
}
Beispiel #4
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)
		}
	}
}