A search for “top kimchi near charlotte” contains more than words to match: “top” can affect ranking, while “near charlotte” can specify a location. A query tree keeps those interpretations as typed nodes with metadata, so later processing can act on them. Despite its name, this implementation represents the tree as an ordered list of JSON-like node objects rather than a visibly nested structure. section 7.4.1
To construct it, start with extracted entity tags, their character offsets, and candidate entity records. Walk through the tags in query order. Add untagged spans as keyword nodes; for each tag, add the highest-popularity candidate entity. Append any remaining text as a final keyword node. The resulting parsed query retains surface forms, canonical forms, types, and relevant metadata that a simpler tagged-query string cannot carry. section 7.4.1
From words to search operations
For “top kimchi near charlotte”, the initial nodes represent a popularity function, the keyword “kimchi”, a location-distance function, and a city with coordinates. Enrichment can replace “top” with a rating-based operation, expand “kimchi” with related terms and a category, and combine “near” with the city into a geographic filter. The transformed nodes then hold search-engine-specific query syntax. section 7.4.1 section 7.4.2 section 7.4.4
Transformation handles nodes according to type and leaves already transformed nodes alone. In the default Solr implementation, each completed node carries `type: transformed` and `syntax: solr`; the nodes can then be converted into a query string. Some semantic functions produce Solr syntax during enrichment rather than waiting for transformation—a simplifying choice, not a requirement of the representation. section 7.4.4
Listing 7.8 Generating a typed query tree from a user query
def generate_query_tree(extracted_entities):
query = extracted_entities["query"]
entities = {entity["id"]: entity for entity #1
in extracted_entities["entities"]} #1
query_tree = []
last_end = 0
for tag in extracted_entities["tags"]:
best_entity = entities[tag["ids"][0]] #2
for entity_id in tag["ids"]: #2
if (entities[entity_id]["popularity"] > #2
best_entity["popularity"]): #2
best_entity = entities[entity_id] #2
next_text = query[last_end:tag["startOffset"]].strip()
if next_text:
query_tree.append({"type": "keyword", #3
"surface_form": next_text, #3
"canonical_form": next_text}) #3
query_tree.append(best_entity) #4
last_end = tag["endOffset"]
if last_end < len(query): #5
final_text = query[last_end:len(query)].strip() #5
if final_text: #5
query_tree.append({"type": "keyword", #5
"surface_form": final_text, #5
"canonical_form": final_text}) #5
return query_tree
parsed_query = generate_query_tree(query_entities)
display(parsed_query)
The Python implementation shows how entity offsets, candidate popularity, and intervening text become an ordered list of typed nodes. section 7.4.1
