def test_geo(): # Geo location search is not supported yet. # Links: # - https://cloud.google.com/appengine/docs/standard/python/search/query_strings#Python_Queries_on_geopoint_fields (GAE) # - https://lucene.apache.org/solr/guide/7_6/spatial-search.html (Solr). prepare_solr_query('distance(location, geopoint(35.2, 40.5)) < 100', FIELDS, GROUPED_FIELDS)
def test_bool_operator_priorities(): solr_query_options = prepare_solr_query('NOT cat AND dogs OR horses', FIELDS, GROUPED_FIELDS) assert solr_query_options.query_string == \ '(NOT "cat" AND ("dogs" OR "horses"))' solr_query_options = prepare_solr_query('NOT cat OR dogs AND horses', FIELDS, GROUPED_FIELDS) assert solr_query_options.query_string == \ '((NOT "cat" OR "dogs") AND "horses")'
def test_field_match_any_value(): # Value like number OR value like date solr_query_options = prepare_solr_query( 'description:(-123.6 OR 1999-08-15)', FIELDS, GROUPED_FIELDS) assert solr_query_options.query_string == ( '((description_txt_en:"-123.6" OR description_txt_fr:"-123.6" OR' ' description_number:"-123.6") OR ' '(description_txt_en:"1999-08-15" OR description_txt_fr:"1999-08-15" OR' ' description_date:"1999-08-15"))') assert solr_query_options.query_fields == []
def test_single_word(): solr_query_options = prepare_solr_query('foo', FIELDS, GROUPED_FIELDS) assert solr_query_options.query_string == '"foo"' assert set(solr_query_options.query_fields) == { 'tag_atom', 'name_atom', 'description_txt_en', 'description_txt_fr', } assert solr_query_options.def_type == 'edismax'
def test_word_and_word(): solr_query_options = prepare_solr_query('hello AND world', FIELDS, GROUPED_FIELDS) assert solr_query_options.query_string == '("hello" AND "world")' assert set(solr_query_options.query_fields) == { 'tag_atom', 'name_atom', 'description_txt_en', 'description_txt_fr', }
def test_single_number(): solr_query_options = prepare_solr_query('-123.4', FIELDS, GROUPED_FIELDS) assert solr_query_options.query_string == '"-123.4"' assert set(solr_query_options.query_fields) == { 'tag_atom', 'name_atom', 'description_txt_en', 'description_txt_fr', 'description_number', 'price_number', }
def test_quoted(): solr_query_options = prepare_solr_query( '"tag:hello AND whateve OR (it does NOT matter)"', FIELDS, GROUPED_FIELDS) assert solr_query_options.query_string == \ '"tag:hello AND whateve OR (it does NOT matter)"' assert set(solr_query_options.query_fields) == { 'tag_atom', 'name_atom', 'description_txt_en', 'description_txt_fr', }
def test_word_and_word_or_number(): solr_query_options = prepare_solr_query('hello AND world OR 321.6', FIELDS, GROUPED_FIELDS) assert solr_query_options.query_string == '("hello" AND ("world" OR "321.6"))' assert set(solr_query_options.query_fields) == { 'tag_atom', 'name_atom', 'description_txt_en', 'description_txt_fr', 'description_number', 'price_number', }
def test_field_compare(): solr_query_options = prepare_solr_query( 'description>100.2 description<=200 price<999.9 price>=100 ' 'created_at>=1990-06-13', FIELDS, GROUPED_FIELDS) assert solr_query_options.query_string == ( '(description_number:{"100.2" TO *] AND description_number:[* TO "200"]' ' AND price_number:[* TO "999.9"} AND price_number:["100" TO *]' ' AND created_at_date:["1990-06-13" TO *])' # Searching date on description_number would lead to error, # so it should be skipped. ) assert solr_query_options.query_fields == []
def test_two_words(): solr_query_options = prepare_solr_query('hello world', FIELDS, GROUPED_FIELDS) # It's currently expected to fail as we convert it to '"hello" AND "world"' # Links: # - https://cloud.google.com/appengine/docs/standard/python/search/query_strings#Python_Multi-value_queries assert solr_query_options.query_string == '"hello world"' assert set(solr_query_options.query_fields) == { 'tag_atom', 'name_atom', 'description_txt_en', 'description_txt_fr', }
def test_field_with_many_types(): # Value like number solr_query_options = prepare_solr_query('description:-123.6', FIELDS, GROUPED_FIELDS) assert solr_query_options.query_string == ( '(description_txt_en:"-123.6" OR description_txt_fr:"-123.6" OR' ' description_number:"-123.6")' # Searching number on description_date would lead to error, # so it should be skipped. ) assert solr_query_options.query_fields == [] # Value like date solr_query_options = prepare_solr_query('description:1999-08-15', FIELDS, GROUPED_FIELDS) assert solr_query_options.query_string == ( '(description_txt_en:"1999-08-15" OR description_txt_fr:"1999-08-15" OR' ' description_date:"1999-08-15")' # Searching date on description_number would lead to error, # so it should be skipped. ) assert solr_query_options.query_fields == []
def test_single_date(): solr_query_options = prepare_solr_query('2019-01-23', FIELDS, GROUPED_FIELDS) assert solr_query_options.query_string == '"2019-01-23"' assert set(solr_query_options.query_fields) == { 'tag_atom', 'name_atom', 'description_txt_en', 'description_txt_fr', 'description_date', 'created_at_date', 'modified_at_date', }
def test_date_and_word_or_number(): solr_query_options = prepare_solr_query('2019-02-02 AND world OR 321.6', FIELDS, GROUPED_FIELDS) assert solr_query_options.query_string == \ '("2019-02-02" AND ("world" OR "321.6"))' assert set(solr_query_options.query_fields) == { 'tag_atom', 'name_atom', 'description_txt_en', 'description_txt_fr', 'description_number', 'description_date', 'created_at_date', 'modified_at_date', 'price_number', }
def test_complex_composition(): solr_query_options = prepare_solr_query( 'name:(-123.6 OR 1999-08-15 AND (foo AND NOT bar dogs (hello))) ' 'AND tag:phone "some () quoted text NOT 123" ' 'OR (one two OR three) OR price>321.6', FIELDS, GROUPED_FIELDS) assert solr_query_options.query_string == ( '(((name_atom:"-123.6" OR name_atom:"1999-08-15") AND ' '(name_atom:"foo" AND NOT name_atom:"bar" AND name_atom:"dogs" AND ' 'name_atom:"hello")) AND ' 'tag_atom:"phone" AND ("some () quoted text NOT 123" OR ' '("one" AND ("two" OR "three")) OR ' 'price_number:{"321.6" TO *]))') assert set(solr_query_options.query_fields) == { 'tag_atom', 'name_atom', 'description_txt_en', 'description_txt_fr', }
async def query(self, app_id, namespace, index_name, query, projection_fields, sort_expressions, limit, offset, cursor, keys_only, auto_discover_facet_count, facet_requests, facet_refinements, facet_auto_detect_limit): """ Retrieves documents which matches query from Solr collection and converts it to unified documents. Args: app_id: a str representing Application ID. namespace: a str representing GAE namespace or None. index_name: a str representing name of Search API index. query: a str containing Search API query. projection_fields: a list of field names to return in results. sort_expressions: a list of sort expressions, e.g.: ("field1", "asc"). limit: an int specifying maximum number of results to return. offset: an int specifying number of first document to skip. cursor: a str representing query cursor. keys_only: a bool indicating if only document IDs should be returned. auto_discover_facet_count: An int - number of top facets to discover. facet_requests: A list of FacetRequest. facet_refinements: A list of FacetRefinement. facet_auto_detect_limit: An int - number of top terms to return. Returns (asynchronously): An instance of models.SearchResult. """ index_schema = await self._get_schema_info(app_id, namespace, index_name) # Convert Search API query to Solr query with a list of fields to search. query_options = query_converter.prepare_solr_query( query, index_schema.fields, index_schema.grouped_fields) # Process GAE projection fields solr_projection_fields = self._convert_projection( keys_only, projection_fields, index_schema) # Process GAE sort expressions solr_sort_fields = self._convert_sort_expressions( sort_expressions, index_schema) # Process GAE facet-related parameters refinement_filter = None if facet_refinements: # Determine if we need to filter by refinement. refinement_filter = facet_converter.generate_refinement_filter( index_schema.grouped_facet_indexes, facet_refinements) facet_items, stats_items = await self._convert_facet_args( auto_discover_facet_count, facet_auto_detect_limit, facet_requests, index_schema, query_options, refinement_filter) stats_fields = [stats_line for solr_field, stats_line in stats_items] # DO ACTUAL QUERY: solr_result = await self.solr.query_documents( collection=index_schema.collection, query=query_options.query_string, offset=offset, limit=limit, cursor=cursor, fields=solr_projection_fields, sort=solr_sort_fields, def_type=query_options.def_type, query_fields=query_options.query_fields, facet_dict=dict(facet_items) if facet_items else None, stats_fields=stats_fields or None, filter_=refinement_filter) # Convert Solr results to unified models docs = [ _from_solr_document(solr_doc) for solr_doc in solr_result.documents ] # Read stats results stats_results = [] for solr_field, stats_line in stats_items: stats_info = solr_result.stats_results[solr_field.solr_name] stats_results.append((solr_field.gae_name, stats_info)) # Convert facet results from Solr facets and stats facet_results = facet_converter.convert_facet_results( solr_result.facet_results, stats_results) result = SearchResult(num_found=solr_result.num_found, scored_documents=docs, cursor=cursor, facet_results=facet_results) return result
def test_stem_text(): solr_query_options = prepare_solr_query('~hello', FIELDS, GROUPED_FIELDS) assert solr_query_options.query_string == '"hello"~'
def test_field_quoted_value(): solr_query_options = prepare_solr_query('tag:"hello"', FIELDS, GROUPED_FIELDS) assert solr_query_options.query_string == 'tag_atom:"hello"' assert solr_query_options.query_fields == []