func (pages *Pages) QueryMatchPhrase(field string, phrase string) *Pages { q := bleve.NewMatchPhraseQuery(phrase) q.SetField(field) pages.queries = append(pages.queries, q) pages.clear() return pages }
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) } } }
func resultsHandler(w http.ResponseWriter, r *http.Request) { var ( pageHTML = "results-search.html" pageInclude = "results-search.include" ) urlQuery := r.URL.Query() err := r.ParseForm() if err != nil { responseLogger(r, http.StatusBadRequest, err) w.Header().Set("Content-Type", "text/plain") w.WriteHeader(http.StatusBadRequest) w.Write([]byte(fmt.Sprintf("error in POST: %s", err))) return } // Collect the submissions fields. submission := make(map[string]interface{}) // Basic Search results if r.Method == "GET" { for k, v := range urlQuery { if k == "all_ids" { if b, err := strconv.ParseBool(strings.Join(v, "")); err == nil { submission[k] = b } } else if k == "from" || k == "size" || k == "total" { if i, err := strconv.Atoi(strings.Join(v, "")); err == nil { submission[k] = i } } else if k == "q" || k == "q_exact" || k == "q_excluded" || k == "q_required" { submission[k] = strings.Join(v, "") } } } // Advanced Search results if r.Method == "POST" { for k, v := range r.Form { if k == "all_ids" { if b, err := strconv.ParseBool(strings.Join(v, "")); err == nil { submission[k] = b } } else if k == "from" || k == "size" || k == "total" { if i, err := strconv.Atoi(strings.Join(v, "")); err == nil { submission[k] = i } } else if k == "q" || k == "q_exact" || k == "q_excluded" || k == "q_required" { submission[k] = strings.Join(v, "") } } } q, err := mapToSearchQuery(submission) if err != nil { responseLogger(r, http.StatusBadRequest, err) w.WriteHeader(http.StatusBadRequest) w.Write([]byte(fmt.Sprintf("%s", err))) return } // // Note: Add logic to handle basic and advanced search... // // q NewQueryStringQuery // q_required NewQueryStringQuery with a + prefix for each strings.Fields(q_required) value // q_exact NewMatchPhraseQuery // q_excluded NewQueryStringQuery with a - prefix for each strings.Feilds(q_excluded) value // var conQry []bleve.Query if q.Q != "" { conQry = append(conQry, bleve.NewQueryStringQuery(q.Q)) } if q.QExact != "" { conQry = append(conQry, bleve.NewMatchPhraseQuery(q.QExact)) } var terms []string for _, s := range strings.Fields(q.QRequired) { terms = append(terms, fmt.Sprintf("+%s", strings.TrimSpace(s))) } for _, s := range strings.Fields(q.QExcluded) { terms = append(terms, fmt.Sprintf("-%s", strings.TrimSpace(s))) } if len(terms) > 0 { qString := strings.Join(terms, " ") conQry = append(conQry, bleve.NewQueryStringQuery(qString)) } qry := bleve.NewConjunctionQuery(conQry) if q.Size == 0 { q.Size = 10 } searchRequest := bleve.NewSearchRequestOptions(qry, q.Size, q.From, q.Explain) if searchRequest == nil { responseLogger(r, http.StatusBadRequest, fmt.Errorf("Can't build new search request options %+v, %s", qry, err)) w.WriteHeader(http.StatusBadRequest) w.Write([]byte(fmt.Sprintf("%s", err))) return } searchRequest.Highlight = bleve.NewHighlight() searchRequest.Highlight.AddField("title") searchRequest.Highlight.AddField("content_description") searchRequest.Highlight.AddField("subjects") searchRequest.Highlight.AddField("subjects_function") searchRequest.Highlight.AddField("subjects_topical") searchRequest.Highlight.AddField("extents") subjectFacet := bleve.NewFacetRequest("subjects", 3) searchRequest.AddFacet("subjects", subjectFacet) subjectTopicalFacet := bleve.NewFacetRequest("subjects_topical", 3) searchRequest.AddFacet("subjects_topical", subjectTopicalFacet) subjectFunctionFacet := bleve.NewFacetRequest("subjects_function", 3) searchRequest.AddFacet("subjects_function", subjectFunctionFacet) // Return all fields searchRequest.Fields = []string{ "title", "identifier", "content_description", "content_condition", "resource_type", "access_restrictions", "access_restrictions_note", "use_restrictins", "use_restrictons_note", "dates", "date_expression", "extents", "subjects", "subjects_function", "subjects_topical", "linked_agents_creators", "linked_agents_subjects", "link_agents_sources", "digital_objects.title", "digital_objects.file_uris", "related_resources", "deaccessions", "accession_date", "created", } searchResults, err := index.Search(searchRequest) if err != nil { responseLogger(r, http.StatusInternalServerError, fmt.Errorf("Bleve results error %v, %s", qry, err)) w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(fmt.Sprintf("%s", err))) return } // q (ciat.SearchQuery) performs double duty as both the structure for query submission as well // as carring the results to support paging and other types of navigation through // the query set. Results are a query with the bleve.SearchReults merged q.AttachSearchResults(searchResults) pageHTML = "results-search.html" pageInclude = "results-search.include" // Load my templates and setup to execute them tmpl, err := tmplfn.Assemble(tmplFuncs, path.Join(templatesDir, pageHTML), path.Join(templatesDir, pageInclude)) if err != nil { responseLogger(r, http.StatusInternalServerError, fmt.Errorf("Template Errors: %s, %s, %s\n", pageHTML, pageInclude, err)) w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(fmt.Sprintf("Template errors: %s", err))) return } // Render the page w.Header().Set("Content-Type", "text/html") var buf bytes.Buffer err = tmpl.Execute(&buf, q) //err = tmpl.Execute(w, q) if err != nil { responseLogger(r, http.StatusInternalServerError, fmt.Errorf("Can't render %s, %s/%s, %s", templatesDir, pageHTML, pageInclude, err)) w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("Template error")) return } //NOTE: This bit of ugliness is here because I need to allow <mark> elements and ellipis in the results fragments w.Write(bytes.Replace(bytes.Replace(bytes.Replace(buf.Bytes(), []byte("<mark>"), []byte("<mark>"), -1), []byte("</mark>"), []byte("</mark>"), -1), []byte(`…`), []byte(`…`), -1)) }
func TestBeerSearchAll(t *testing.T) { defer os.RemoveAll("beer-search-test.bleve") mapping, err := buildIndexMapping() if err != nil { t.Fatal(err) } index, err := bleve.New("beer-search-test.bleve", mapping) if err != nil { t.Fatal(err) } defer index.Close() for jf := range walkDirectory("data/", t) { docId := jf.filename[0:strings.LastIndex(jf.filename, ".")] err = index.Index(docId, jf.contents) if err != nil { t.Error(err) } } expectedCount := uint64(7303) actualCount := index.DocCount() if actualCount != expectedCount { t.Errorf("expected %d documents, got %d", expectedCount, actualCount) } // run a term search termQuery := bleve.NewTermQuery("shock").SetField("name") termSearchRequest := bleve.NewSearchRequest(termQuery) termSearchResult, err := index.Search(termSearchRequest) if err != nil { t.Error(err) } expectedResultCount := uint64(1) if termSearchResult.Total != expectedResultCount { t.Errorf("expected %d hits, got %d", expectedResultCount, termSearchResult.Total) } else { expectedResultId := "anheuser_busch-shock_top" if termSearchResult.Hits[0].ID != expectedResultId { t.Errorf("expected top hit ID: %s, got %s", expectedResultId, termSearchResult.Hits[0].ID) } } // run a match phrase search matchPhraseQuery := bleve.NewMatchPhraseQuery("spicy mexican food") matchPhraseSearchRequest := bleve.NewSearchRequest(matchPhraseQuery) matchPhraseSearchResult, err := index.Search(matchPhraseSearchRequest) if err != nil { t.Error(err) } expectedResultCount = uint64(1) if matchPhraseSearchResult.Total != expectedResultCount { t.Errorf("expected %d hits, got %d", expectedResultCount, matchPhraseSearchResult.Total) } else { expectedResultId := "great_divide_brewing-wild_raspberry_ale" if matchPhraseSearchResult.Hits[0].ID != expectedResultId { t.Errorf("expected top hit ID: %s, got %s", expectedResultId, matchPhraseSearchResult.Hits[0].ID) } } // run a syntax query syntaxQuery := bleve.NewQueryStringQuery("+name:light +description:water -description:barley") syntaxSearchRequest := bleve.NewSearchRequest(syntaxQuery) syntaxSearchResult, err := index.Search(syntaxSearchRequest) if err != nil { t.Error(err) } expectedResultCount = uint64(1) if syntaxSearchResult.Total != expectedResultCount { t.Errorf("expected %d hits, got %d", expectedResultCount, syntaxSearchResult.Total) } else { expectedResultId := "iron_city_brewing_co-ic_light" if syntaxSearchResult.Hits[0].ID != expectedResultId { t.Errorf("expected top hit ID: %s, got %s", expectedResultId, syntaxSearchResult.Hits[0].ID) } } // run a numeric range search queryMin := 50.0 numericRangeQuery := bleve.NewNumericRangeQuery(&queryMin, nil).SetField("abv") numericSearchRequest := bleve.NewSearchRequest(numericRangeQuery) numericSearchResult, err := index.Search(numericSearchRequest) if err != nil { t.Error(err) } expectedResultCount = uint64(1) if numericSearchResult.Total != expectedResultCount { t.Errorf("expected %d hits, got %d", expectedResultCount, numericSearchResult.Total) } else { expectedResultId := "woodforde_s_norfolk_ales-norfolk_nog_old_dark_ale" if numericSearchResult.Hits[0].ID != expectedResultId { t.Errorf("expected top hit ID: %s, got %s", expectedResultId, numericSearchResult.Hits[0].ID) } } // run a date range search queryStartDate := "2011-10-04" dateRangeQuery := bleve.NewDateRangeQuery(&queryStartDate, nil).SetField("updated") dateSearchRequest := bleve.NewSearchRequest(dateRangeQuery) dateSearchResult, err := index.Search(dateSearchRequest) if err != nil { t.Error(err) } expectedResultCount = uint64(2) if dateSearchResult.Total != expectedResultCount { t.Errorf("expected %d hits, got %d", expectedResultCount, dateSearchResult.Total) } else { expectedResultId := "brasserie_du_bouffay-ambr" if dateSearchResult.Hits[0].ID != expectedResultId { t.Errorf("expected top hit ID: %s, got %s", expectedResultId, dateSearchResult.Hits[0].ID) } } // run a prefix search prefixQuery := bleve.NewPrefixQuery("adir").SetField("name") prefixSearchRequest := bleve.NewSearchRequest(prefixQuery) prefixSearchResult, err := index.Search(prefixSearchRequest) if err != nil { t.Error(err) } expectedResultCount = uint64(1) if prefixSearchResult.Total != expectedResultCount { t.Errorf("expected %d hits, got %d", expectedResultCount, prefixSearchResult.Total) } else { expectedResultId := "f_x_matt_brewing-saranac_adirondack_lager" if prefixSearchResult.Hits[0].ID != expectedResultId { t.Errorf("expected top hit ID: %s, got %s", expectedResultId, prefixSearchResult.Hits[0].ID) } } }
func TestBeerSearchAll(t *testing.T) { defer os.RemoveAll("beer-search-test.bleve") mapping, err := buildIndexMapping() if err != nil { t.Fatal(err) } index, err := bleve.New("beer-search-test.bleve", mapping) if err != nil { t.Fatal(err) } defer index.Close() // open the directory dirEntries, err := ioutil.ReadDir("data/") if err != nil { t.Fatal(err) } indexBatchSize := 100 batch := index.NewBatch() batchCount := 0 for _, dirEntry := range dirEntries { filename := dirEntry.Name() // read the bytes jsonBytes, err := ioutil.ReadFile("data/" + filename) if err != nil { t.Fatal(err) } // // shred them into a document ext := filepath.Ext(filename) docId := filename[:(len(filename) - len(ext))] batch.Index(docId, jsonBytes) batchCount++ if batchCount >= indexBatchSize { err = index.Batch(batch) if err != nil { t.Fatal(err) } batch = index.NewBatch() batchCount = 0 } } // flush the last batch if batchCount > 0 { err = index.Batch(batch) if err != nil { t.Fatal(err) } } expectedCount := uint64(7303) actualCount, err := index.DocCount() if err != nil { t.Error(err) } if actualCount != expectedCount { t.Errorf("expected %d documents, got %d", expectedCount, actualCount) } // run a term search termQuery := bleve.NewTermQuery("shock").SetField("name") termSearchRequest := bleve.NewSearchRequest(termQuery) termSearchResult, err := index.Search(termSearchRequest) if err != nil { t.Error(err) } expectedResultCount := uint64(1) if termSearchResult.Total != expectedResultCount { t.Errorf("expected %d hits, got %d", expectedResultCount, termSearchResult.Total) } else { expectedResultId := "anheuser_busch-shock_top" if termSearchResult.Hits[0].ID != expectedResultId { t.Errorf("expected top hit ID: %s, got %s", expectedResultId, termSearchResult.Hits[0].ID) } } // run a match phrase search matchPhraseQuery := bleve.NewMatchPhraseQuery("spicy mexican food") matchPhraseSearchRequest := bleve.NewSearchRequest(matchPhraseQuery) matchPhraseSearchResult, err := index.Search(matchPhraseSearchRequest) if err != nil { t.Error(err) } expectedResultCount = uint64(1) if matchPhraseSearchResult.Total != expectedResultCount { t.Errorf("expected %d hits, got %d", expectedResultCount, matchPhraseSearchResult.Total) } else { expectedResultId := "great_divide_brewing-wild_raspberry_ale" if matchPhraseSearchResult.Hits[0].ID != expectedResultId { t.Errorf("expected top hit ID: %s, got %s", expectedResultId, matchPhraseSearchResult.Hits[0].ID) } } // run a syntax query syntaxQuery := bleve.NewQueryStringQuery("+name:light +description:water -description:barley") syntaxSearchRequest := bleve.NewSearchRequest(syntaxQuery) syntaxSearchResult, err := index.Search(syntaxSearchRequest) if err != nil { t.Error(err) } expectedResultCount = uint64(1) if syntaxSearchResult.Total != expectedResultCount { t.Errorf("expected %d hits, got %d", expectedResultCount, syntaxSearchResult.Total) } else { expectedResultId := "iron_city_brewing_co-ic_light" if syntaxSearchResult.Hits[0].ID != expectedResultId { t.Errorf("expected top hit ID: %s, got %s", expectedResultId, syntaxSearchResult.Hits[0].ID) } } // run a numeric range search queryMin := 50.0 numericRangeQuery := bleve.NewNumericRangeQuery(&queryMin, nil).SetField("abv") numericSearchRequest := bleve.NewSearchRequest(numericRangeQuery) numericSearchResult, err := index.Search(numericSearchRequest) if err != nil { t.Error(err) } expectedResultCount = uint64(1) if numericSearchResult.Total != expectedResultCount { t.Errorf("expected %d hits, got %d", expectedResultCount, numericSearchResult.Total) } else { expectedResultId := "woodforde_s_norfolk_ales-norfolk_nog_old_dark_ale" if numericSearchResult.Hits[0].ID != expectedResultId { t.Errorf("expected top hit ID: %s, got %s", expectedResultId, numericSearchResult.Hits[0].ID) } } // run a date range search queryStartDate := "2011-10-04" dateRangeQuery := bleve.NewDateRangeQuery(&queryStartDate, nil).SetField("updated") dateSearchRequest := bleve.NewSearchRequest(dateRangeQuery) dateSearchResult, err := index.Search(dateSearchRequest) if err != nil { t.Error(err) } expectedResultCount = uint64(2) if dateSearchResult.Total != expectedResultCount { t.Errorf("expected %d hits, got %d", expectedResultCount, dateSearchResult.Total) } else { expectedResultId := "brasserie_du_bouffay-ambr" if dateSearchResult.Hits[0].ID != expectedResultId { t.Errorf("expected top hit ID: %s, got %s", expectedResultId, dateSearchResult.Hits[0].ID) } } // run a prefix search prefixQuery := bleve.NewPrefixQuery("adir").SetField("name") prefixSearchRequest := bleve.NewSearchRequest(prefixQuery) prefixSearchResult, err := index.Search(prefixSearchRequest) if err != nil { t.Error(err) } expectedResultCount = uint64(1) if prefixSearchResult.Total != expectedResultCount { t.Errorf("expected %d hits, got %d", expectedResultCount, prefixSearchResult.Total) } else { expectedResultId := "f_x_matt_brewing-saranac_adirondack_lager" if prefixSearchResult.Hits[0].ID != expectedResultId { t.Errorf("expected top hit ID: %s, got %s", expectedResultId, prefixSearchResult.Hits[0].ID) } } }
// part = lit {"~" number} | range | "(" exprList ")" func (p *Parser) parsePart(ctx context) (query.Query, error) { tok := p.next() // lit if tok.typ == tLITERAL { var q query.Query if strings.ContainsAny(tok.val, "*?") { q = bleve.NewWildcardQuery(tok.val) } else { if p.peek().typ == tFUZZY { fuzziness, err := p.parseFuzzySuffix() if err != nil { return nil, err } fuzz := bleve.NewFuzzyQuery(tok.val) fuzz.SetFuzziness(fuzziness) q = fuzz } else { q = bleve.NewMatchPhraseQuery(tok.val) } } if ctx.field != "" { if fieldable, ok := q.(query.FieldableQuery); ok { fieldable.SetField(ctx.field) } else { return nil, ParseError{ctx.fieldPos, "unexpected field"} } } return q, nil } if tok.typ == tQUOTED { // strip quotes (ugh) txt := string(tok.val[1 : len(tok.val)-1]) /* if strings.ContainsAny(txt, "*?") { return nil, ParseError{tok.pos, "wildcards not supported in phrases"} } */ q := bleve.NewMatchPhraseQuery(txt) if ctx.field != "" { q.SetField(ctx.field) } return q, nil } // | "(" exprList ")" if tok.typ == tLPAREN { q, err := p.parseExprList(ctx) if err != nil { return nil, err } tok = p.next() if tok.typ != tRPAREN { return nil, ParseError{tok.pos, "missing )"} } return q, nil } // | range if tok.typ == tLSQUARE || tok.typ == tLBRACE { p.backup() q, err := p.parseRange(ctx) if err != nil { return nil, err } return q, nil } // | relational if tok.typ == tGREATER || tok.typ == tLESS { p.backup() q, err := p.parseRelational(ctx) if err != nil { return nil, err } return q, nil } if tok.typ == tERROR { return nil, ParseError{tok.pos, tok.val} } return nil, ParseError{tok.pos, fmt.Sprintf("unexpected %s", tok.val)} }
// part = lit | range | "(" exprList ")" func (p *Parser) parsePart(ctx context) (bleve.Query, error) { tok := p.next() // lit if tok.typ == tLITERAL { q := bleve.NewMatchPhraseQuery(tok.val) if ctx.field != "" { q.SetField(ctx.field) } return q, nil } if tok.typ == tQUOTED { // strip quotes (ugh) txt := string(tok.val[1 : len(tok.val)-1]) q := bleve.NewMatchPhraseQuery(txt) if ctx.field != "" { q.SetField(ctx.field) } return q, nil } // | "(" exprList ")" if tok.typ == tLPAREN { q, err := p.parseExprList(ctx) if err != nil { return nil, err } tok = p.next() if tok.typ != tRPAREN { return nil, ParseError{tok.pos, "missing )"} } return q, nil } // | range if tok.typ == tLSQUARE || tok.typ == tLBRACE { p.backup() q, err := p.parseRange(ctx) if err != nil { return nil, err } return q, nil } // | relational if tok.typ == tGREATER || tok.typ == tLESS { p.backup() q, err := p.parseRelational(ctx) if err != nil { return nil, err } return q, nil } if tok.typ == tERROR { return nil, ParseError{tok.pos, tok.val} } return nil, ParseError{tok.pos, fmt.Sprintf("unexpected %s", tok.val)} }