| 109 | } |
| 110 | |
| 111 | Result<GroupResults> SQLEngineImpl::execute_group_by( |
| 112 | CollectionSchema::Ptr collection, const GroupByVectorQuery &group_by_query, |
| 113 | const std::vector<Segment::Ptr> &segments) { |
| 114 | if (segments.empty()) { |
| 115 | return GroupResults{}; |
| 116 | } |
| 117 | |
| 118 | SearchQuery query = from_group_by(group_by_query); |
| 119 | |
| 120 | // Check filter satisfiability once before the loop (result depends only on |
| 121 | // the query, not on segment data). |
| 122 | auto first_query_info = build_query_info( |
| 123 | collection, query, |
| 124 | std::make_shared<GroupBy>(group_by_query.group_by_field_name_, |
| 125 | group_by_query.topk_per_group_, |
| 126 | group_by_query.group_count_)); |
| 127 | if (!first_query_info) { |
| 128 | return tl::make_unexpected(first_query_info.error()); |
| 129 | } |
| 130 | if (first_query_info.value()->is_filter_unsatisfiable()) { |
| 131 | LOG_WARN("filter is unsatisfiable: %s", |
| 132 | first_query_info.value()->to_string().c_str()); |
| 133 | return {}; |
| 134 | } |
| 135 | |
| 136 | // Keep a copy, the planner will move elements out of query_infos. |
| 137 | QueryInfo::Ptr group_by_query_info = first_query_info.value(); |
| 138 | |
| 139 | // Build a separate QueryInfo per segment so the optimizer can mutate each |
| 140 | // independently. Reuse first_query_info for segment 0. |
| 141 | std::vector<QueryInfo::Ptr> query_infos; |
| 142 | query_infos.reserve(segments.size()); |
| 143 | query_infos.emplace_back(std::move(first_query_info.value())); |
| 144 | for (size_t i = 1; i < segments.size(); ++i) { |
| 145 | auto query_info = build_query_info( |
| 146 | collection, query, |
| 147 | std::make_shared<GroupBy>(group_by_query.group_by_field_name_, |
| 148 | group_by_query.topk_per_group_, |
| 149 | group_by_query.group_count_)); |
| 150 | if (!query_info) { |
| 151 | return tl::make_unexpected(query_info.error()); |
| 152 | } |
| 153 | query_infos.emplace_back(std::move(query_info.value())); |
| 154 | } |
| 155 | auto reader = search_by_query_info(collection, segments, &query_infos); |
| 156 | if (!reader) { |
| 157 | return tl::make_unexpected(Status::InternalError( |
| 158 | "Execute plan failed (group_by): ", reader.error().c_str())); |
| 159 | } |
| 160 | return fill_group_by_result(*group_by_query_info, reader.value().get()); |
| 161 | } |
| 162 | |
| 163 | Result<FtsCondInfo::Ptr> SQLEngineImpl::parse_fts_query( |
| 164 | CollectionSchema::Ptr collection, const std::string &field_name, |