Ticket #50: opsynonym4.patch
File opsynonym4.patch, 24.1 KB (added by , 17 years ago) |
---|
-
matcher/Makefile.mk
17 17 matcher/remotesubmatch.h\ 18 18 matcher/scaleweight.h\ 19 19 matcher/selectpostlist.h\ 20 matcher/synonympostlist.h\ 20 21 matcher/valuerangepostlist.h\ 21 22 matcher/xorpostlist.h 22 23 … … 53 54 matcher/scaleweight.cc\ 54 55 matcher/selectpostlist.cc\ 55 56 matcher/stats.cc\ 57 matcher/synonympostlist.cc\ 56 58 matcher/tradweight.cc\ 57 59 matcher/valuerangepostlist.cc\ 58 60 matcher/weight.cc\ -
matcher/multimatch.cc
374 374 } 375 375 Assert(!postlists.empty()); 376 376 377 #ifdef XAPIAN_DEBUG_VERBOSE 378 { 379 DEBUGLINE(MATCH, "termfreqandwts:"); 380 map<string, Xapian::MSet::Internal::TermFreqAndWeight>::const_iterator tfwi; 381 for (tfwi = termfreqandwts.begin(); tfwi != termfreqandwts.end(); ++tfwi) 382 { 383 DEBUGLINE(MATCH, "termfreqandwt[" << tfwi->first << "] = " << tfwi->second.termfreq << ", " << tfwi->second.termweight); 384 } 385 } 386 #endif 387 377 388 // Get a single combined postlist 378 389 PostList *pl; 379 390 if (postlists.size() == 1) { … … 794 805 DEBUGLINE(MATCH, "denom = " << denom << " percent_scale = " << percent_scale); 795 806 Assert(percent_scale <= denom); 796 807 denom *= greatest_wt; 808 if (denom == 0) { 809 percent_scale = 1.0 / greatest_wt; 810 } else { 797 811 Assert(denom > 0); 798 812 percent_scale /= denom; 813 } 799 814 } else { 800 815 // If all the terms match, the 2 sums of weights cancel 801 816 percent_scale = 1.0 / greatest_wt; -
matcher/localmatch.cc
32 32 #include "omqueryinternal.h" 33 33 #include "queryoptimiser.h" 34 34 #include "scaleweight.h" 35 #include "stats.h" 36 #include "synonympostlist.h" 35 37 #include "weightinternal.h" 36 #include "stats.h"37 38 38 39 #include <cfloat> 39 40 #include <cmath> … … 114 115 } 115 116 116 117 PostList * 118 LocalSubMatch::make_synonym_postlist(PostList * or_pl, MultiMatch * matcher) 119 { 120 DEBUGCALL(MATCH, PostList *, "LocalSubMatch::make_synonym_postlist", 121 "[or_pl]"); 122 DEBUGLINE(MATCH, "or_pl->get_termfreq() = " << or_pl->get_termfreq_est()); 123 AutoPtr<SynonymPostList> res(new SynonymPostList(or_pl, matcher)); 124 AutoPtr<Xapian::Weight> wt; 125 126 // FIXME:1.1: create the Xapian::Weight::Internal directly, and hold it in 127 // an AutoPtr until supplying it to wt_factory->create() in case of an 128 // exception. 129 Xapian::Weight::Internal * wt_internal(stats->create_weight_internal()); 130 wt_internal->termfreq = or_pl->get_termfreq_est(); 131 wt_internal->reltermfreq = 0; // FIXME - calculate this. 132 wt = wt_factory->create(wt_internal, qlen, 1, ""); 133 134 res->set_weight(wt.release()); 135 RETURN(res.release()); 136 } 137 138 PostList * 117 139 LocalSubMatch::postlist_from_op_leaf_query(const Xapian::Query::Internal *query, 118 140 double factor) 119 141 { 120 142 DEBUGCALL(MATCH, PostList *, "LocalSubMatch::postlist_from_op_leaf_query", 121 query << ", " << factor);143 query->get_description() << ", " << factor); 122 144 Assert(query); 123 145 AssertEq(query->op, Xapian::Query::Internal::OP_LEAF); 124 146 Assert(query->subqs.empty()); … … 142 164 Xapian::doccount tf = stats->get_termfreq(query->tname); 143 165 Xapian::weight weight = boolean ? 0 : wt->get_maxpart(); 144 166 Xapian::MSet::Internal::TermFreqAndWeight info(tf, weight); 167 DEBUGLINE(MATCH, "Setting term_info[" << query->tname << "] to (" << tf << ", " << weight << ")"); 145 168 term_info.insert(make_pair(query->tname, info)); 146 169 } else if (!boolean) { 147 170 i->second.termweight += wt->get_maxpart(); 171 AssertEq(stats->get_termfreq(query->tname), i->second.termfreq); 172 DEBUGLINE(MATCH, "Increasing term_info[" << query->tname << "] to (" << i->second.termfreq << ", " << i->second.termweight << ")"); 148 173 } 149 174 150 175 LeafPostList * pl = db->open_post_list(query->tname); -
matcher/localmatch.h
86 86 PostList * get_postlist_and_term_info(MultiMatch *matcher, 87 87 std::map<string, Xapian::MSet::Internal::TermFreqAndWeight> *termfreqandwts); 88 88 89 /** Convert a postlist into a synonym postlist. 90 */ 91 PostList * make_synonym_postlist(PostList * or_pl, MultiMatch * matcher); 92 89 93 /** Convert an OP_LEAF query to a PostList. 90 94 * 91 95 * This is called by QueryOptimiser when it reaches an OP_LEAF query. -
matcher/synonympostlist.h
1 /* synonympostlist.h: Combine subqueries, weighting as if they are synonyms 2 * 3 * Copyright 2007 Lemur Consulting Ltd 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 */ 19 20 #ifndef XAPIAN_INCLUDED_SYNONYMPOSTLIST_H 21 #define XAPIAN_INCLUDED_SYNONYMPOSTLIST_H 22 23 #include "multimatch.h" 24 #include "postlist.h" 25 #include "stats.h" 26 #include <vector> 27 28 /** A postlist comprising several postlists SYNONYMed together. 29 * 30 * This postlist returns all postings in the OR of the sub postlists, but 31 * returns weights as if they represented a single term. The term frequency 32 * portion of the weight is approximated. 33 */ 34 class SynonymPostList : public PostList { 35 private: 36 /** The subtree, which starts as an OR of all the sub-postlists being 37 * joined with Synonym, but may decay into something else. 38 */ 39 PostList * subtree; 40 41 /** The object which is using this postlist to perform 42 * a match. This object needs to be notified when the 43 * tree changes such that the maximum weights need to be 44 * recalculated. 45 */ 46 MultiMatch *matcher; 47 48 /** Weighting object used for calculating the synonym weights. 49 */ 50 const Xapian::Weight * wt; 51 52 /** Flag indicating whether the weighting object needs the doclength. 53 */ 54 bool want_doclength; 55 56 public: 57 SynonymPostList(PostList *subtree_, MultiMatch * matcher_); 58 ~SynonymPostList(); 59 60 /** Set the weight object to be used for the synonym postlist. 61 * 62 * Ownership of the weight object passes to the synonym postlist - the 63 * caller must not delete it after use. 64 */ 65 void set_weight(const Xapian::Weight * wt_); 66 67 PostList *next(Xapian::weight w_min); 68 PostList *skip_to(Xapian::docid did, Xapian::weight w_min); 69 70 Xapian::weight get_weight() const; 71 Xapian::weight get_maxweight() const; 72 Xapian::weight recalc_maxweight(); 73 74 // The following methods just call through to the subtree. 75 Xapian::termcount get_wdf() const; 76 Xapian::doccount get_termfreq_min() const; 77 Xapian::doccount get_termfreq_est() const; 78 Xapian::doccount get_termfreq_max() const; 79 Xapian::docid get_docid() const; 80 Xapian::doclength get_doclength() const; 81 PositionList * read_position_list(); 82 PositionList * open_position_list() const; 83 bool at_end() const; 84 85 std::string get_description() const; 86 }; 87 88 #endif /* XAPIAN_INCLUDED_SYNONYMPOSTLIST_H */ -
matcher/queryoptimiser.cc
Property changes on: matcher/synonympostlist.h ___________________________________________________________________ Name: svn:eol-style + native
51 51 QueryOptimiser::do_subquery(const Xapian::Query::Internal * query, double factor) 52 52 { 53 53 DEBUGCALL(MATCH, PostList *, "QueryOptimiser::do_subquery", 54 query << ", " << factor);54 query->get_description() << ", " << factor); 55 55 56 56 // Handle QueryMatchNothing. 57 57 if (!query) RETURN(new EmptyPostList()); … … 99 99 RETURN(do_subquery(query->subqs[0], sub_factor)); 100 100 } 101 101 102 case Xapian::Query::OP_SYNONYM: { 103 RETURN(do_synonym(query, factor)); 104 } 105 102 106 default: 103 107 Assert(false); 104 108 RETURN(NULL); 105 109 } 106 110 } 107 111 112 PostList * 113 QueryOptimiser::do_leaf(const Xapian::Query::Internal * query, double factor) { 114 return localsubmatch.postlist_from_op_leaf_query(query, factor); 115 } 116 108 117 struct PosFilter { 109 118 PosFilter(Xapian::Query::Internal::op_t op_, size_t begin_, size_t end_, 110 119 Xapian::termcount window_) … … 122 131 QueryOptimiser::do_and_like(const Xapian::Query::Internal *query, double factor) 123 132 { 124 133 DEBUGCALL(MATCH, PostList *, "QueryOptimiser::do_and_like", 125 query << ", " << factor);134 query->get_description() << ", " << factor); 126 135 127 136 list<PosFilter> pos_filters; 128 137 vector<PostList *> plists; … … 172 181 list<PosFilter> & pos_filters) 173 182 { 174 183 DEBUGCALL(MATCH, void, "QueryOptimiser::do_and_like", 175 query << ", " << factor << ", [and_plists], [pos_filters]"); 184 query->get_description() << ", " << 185 factor << ", [and_plists], [pos_filters],"); 176 186 177 187 Xapian::Query::Internal::op_t op = query->op; 178 188 Assert(is_and_like(op)); … … 251 261 QueryOptimiser::do_or_like(const Xapian::Query::Internal *query, double factor) 252 262 { 253 263 DEBUGCALL(MATCH, PostList *, "QueryOptimiser::do_or_like", 254 query << ", " << factor);264 query->get_description() << ", " << factor); 255 265 256 266 // FIXME: we could optimise by merging OP_ELITE_SET and OP_OR like we do 257 267 // for AND-like operations. 258 268 Xapian::Query::Internal::op_t op = query->op; 259 269 Assert(op == Xapian::Query::OP_ELITE_SET || op == Xapian::Query::OP_OR || 260 op == Xapian::Query::OP_XOR );270 op == Xapian::Query::OP_XOR || op == Xapian::Query::OP_SYNONYM); 261 271 272 // We build an OR tree for OP_SYNONYM. (The resulting tree will then be 273 // passed into a SynonymPostList, from which the weightings will come.) 274 if (op == Xapian::Query::OP_SYNONYM) { 275 op = Xapian::Query::OP_OR; 276 } 277 262 278 const Xapian::Query::Internal::subquery_list &queries = query->subqs; 263 279 AssertRel(queries.size(), >=, 2); 264 280 … … 333 349 ComparePostListTermFreqAscending()); 334 350 } 335 351 } 352 353 PostList * 354 QueryOptimiser::do_synonym(const Xapian::Query::Internal *query, double factor) 355 { 356 DEBUGCALL(MATCH, PostList *, "QueryOptimiser::do_synonym", 357 query->get_description() << ", " << factor); 358 359 if (factor == 0.0) { 360 // If we have a factor of 0, we don't care about the weights, so 361 // we're just like a normal OR query. 362 RETURN(do_or_like(query, 0.0)); 363 } 364 365 AssertEq(query->wqf, 0); // FIXME - should we be doing something with the wqf? 366 367 // Build a postlist tree which we'll use to get the frequencies. 368 AutoPtr<PostList> freq_pl(do_or_like(query, 0.0)); 369 370 RETURN(localsubmatch.make_synonym_postlist(do_or_like(query, 0.0), 371 matcher)); 372 } -
matcher/queryoptimiser.h
63 63 * 64 64 * @return A PostList. 65 65 */ 66 PostList * do_leaf(const Xapian::Query::Internal * query, double factor) { 67 return localsubmatch.postlist_from_op_leaf_query(query, factor); 68 } 66 PostList * do_leaf(const Xapian::Query::Internal * query, double factor); 69 67 70 68 /** Optimise an AND-like Xapian::Query::Internal subtree into a PostList 71 69 * subtree. … … 101 99 */ 102 100 PostList * do_or_like(const Xapian::Query::Internal *query, double factor); 103 101 102 /** Optimise a synonym Xapian::Query::Internal subtree into a PostList 103 * 104 * @param query The subtree to optimise. 105 * @param factor How much to scale weights for this subtree by. 106 * 107 * @return A PostList subtree. 108 */ 109 PostList * do_synonym(const Xapian::Query::Internal *query, double factor); 110 104 111 public: 105 112 QueryOptimiser(const Xapian::Database::Internal & db_, 106 113 LocalSubMatch & localsubmatch_, -
matcher/synonympostlist.cc
1 /* synonympostlist.cc: Combine subqueries, weighting as if they are synonyms 2 * 3 * Copyright 2007 Lemur Consulting Ltd 4 * 5 * This program is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU General Public License as 7 * published by the Free Software Foundation; either version 2 of the 8 * License, or (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 18 * USA 19 */ 20 21 #include <config.h> 22 23 #include "synonympostlist.h" 24 #include "branchpostlist.h" 25 #include "omassert.h" 26 #include "omdebug.h" 27 28 SynonymPostList::SynonymPostList(PostList *subtree_, 29 MultiMatch * matcher_) 30 : subtree(subtree_), 31 matcher(matcher_), 32 wt(NULL), 33 want_doclength(false) 34 { 35 } 36 37 SynonymPostList::~SynonymPostList() 38 { 39 delete wt; 40 delete subtree; 41 } 42 43 void 44 SynonymPostList::set_weight(const Xapian::Weight * wt_) 45 { 46 delete(wt); 47 wt = wt_; 48 want_doclength = wt_->get_sumpart_needs_doclength(); 49 } 50 51 PostList * 52 SynonymPostList::next(Xapian::weight w_min) 53 { 54 DEBUGCALL(MATCH, PostList *, "SynonymPostList::next", w_min); 55 next_handling_prune(subtree, w_min, matcher); 56 RETURN(NULL); 57 } 58 59 PostList * 60 SynonymPostList::skip_to(Xapian::docid did, Xapian::weight w_min) 61 { 62 DEBUGCALL(MATCH, PostList *, "SynonymPostList::skip_to", did << ", " << w_min); 63 skip_to_handling_prune(subtree, did, w_min, matcher); 64 RETURN(NULL); 65 } 66 67 Xapian::weight 68 SynonymPostList::get_weight() const 69 { 70 return wt->get_sumpart(get_wdf(), want_doclength ? get_doclength() : 0); 71 } 72 73 Xapian::weight 74 SynonymPostList::get_maxweight() const 75 { 76 return wt->get_maxpart(); 77 } 78 79 Xapian::weight 80 SynonymPostList::recalc_maxweight() 81 { 82 return SynonymPostList::get_maxweight(); 83 } 84 85 Xapian::termcount 86 SynonymPostList::get_wdf() const { 87 return subtree->get_wdf(); 88 } 89 90 Xapian::doccount 91 SynonymPostList::get_termfreq_min() const { 92 return subtree->get_termfreq_min(); 93 } 94 95 Xapian::doccount 96 SynonymPostList::get_termfreq_est() const { 97 return subtree->get_termfreq_est(); 98 } 99 100 Xapian::doccount 101 SynonymPostList::get_termfreq_max() const { 102 return subtree->get_termfreq_max(); 103 } 104 105 Xapian::docid 106 SynonymPostList::get_docid() const { 107 return subtree->get_docid(); 108 } 109 110 Xapian::doclength 111 SynonymPostList::get_doclength() const { 112 return subtree->get_doclength(); 113 } 114 115 PositionList * 116 SynonymPostList::read_position_list() { 117 return subtree->read_position_list(); 118 } 119 120 PositionList * 121 SynonymPostList::open_position_list() const { 122 return subtree->open_position_list(); 123 } 124 125 bool 126 SynonymPostList::at_end() const { 127 return subtree->at_end(); 128 } 129 130 std::string 131 SynonymPostList::get_description() const 132 { 133 return "(Synonym " + subtree->get_description() + ")"; 134 } -
tests/api_db.cc
Property changes on: matcher/synonympostlist.cc ___________________________________________________________________ Name: svn:eol-style + native
1171 1171 return true; 1172 1172 } 1173 1173 1174 // Check a synonym search 1175 DEFINE_TESTCASE(synonym1, backend) { 1176 Xapian::Database db(get_database("etext")); 1177 Xapian::doccount lots = 214; 1178 vector<vector<Xapian::Query> > subqueries_list; 1179 1180 vector<Xapian::Query> subqueries; 1181 subqueries.push_back(Xapian::Query("date")); 1182 subqueries_list.push_back(subqueries); 1183 1184 subqueries.clear(); 1185 subqueries.push_back(Xapian::Query("sky")); 1186 subqueries.push_back(Xapian::Query("date")); 1187 subqueries_list.push_back(subqueries); 1188 1189 subqueries.clear(); 1190 subqueries.push_back(Xapian::Query("date")); 1191 subqueries.push_back(Xapian::Query(Xapian::Query::OP_OR, 1192 Xapian::Query("sky"), 1193 Xapian::Query("glove"))); 1194 subqueries_list.push_back(subqueries); 1195 1196 subqueries.clear(); 1197 subqueries.push_back(Xapian::Query("sky")); 1198 subqueries.push_back(Xapian::Query("date")); 1199 subqueries.push_back(Xapian::Query("stein")); 1200 subqueries.push_back(Xapian::Query("ally")); 1201 subqueries_list.push_back(subqueries); 1202 1203 subqueries.clear(); 1204 subqueries.push_back(Xapian::Query("sky")); 1205 subqueries.push_back(Xapian::Query(Xapian::Query::OP_PHRASE, 1206 Xapian::Query("date"), 1207 Xapian::Query("stein"))); 1208 subqueries_list.push_back(subqueries); 1209 1210 for (vector<vector<Xapian::Query> >::const_iterator 1211 qlist = subqueries_list.begin(); 1212 qlist != subqueries_list.end(); ++qlist) 1213 { 1214 // Run two queries, one joining the subqueries with OR and one joining them 1215 // with SYNONYM. 1216 Xapian::Enquire enquire(db); 1217 enquire.set_query(Xapian::Query(Xapian::Query::OP_OR, qlist->begin(), qlist->end())); 1218 Xapian::MSet ormset = enquire.get_mset(0, lots); 1219 Xapian::Query synquery(Xapian::Query::OP_SYNONYM, qlist->begin(), qlist->end()); 1220 tout << synquery << "\n"; 1221 enquire.set_query(synquery); 1222 Xapian::MSet mset = enquire.get_mset(0, lots); 1223 1224 // Check that the queries return some results. 1225 TEST_NOT_EQUAL(mset.size(), 0); 1226 // Check that the queries return the same number of results. 1227 TEST_EQUAL(mset.size(), ormset.size()); 1228 map<Xapian::docid, Xapian::weight> values_or; 1229 map<Xapian::docid, Xapian::weight> values_synonym; 1230 for (Xapian::doccount i = 0; i < mset.size(); ++i) { 1231 values_or[*ormset[i]] = ormset[i].get_weight(); 1232 values_synonym[*mset[i]] = mset[i].get_weight(); 1233 } 1234 TEST_EQUAL(values_or.size(), values_synonym.size()); 1235 1236 /* Check that the weights for each item in the or mset are different from 1237 * those in the synonym mset. (Note, it's technically possible that some 1238 * might be equal, but unlikely, so for now we just check that none are. 1239 * If this causes problems, we can change to just checking that most 1240 * differ.) */ 1241 for (map<Xapian::docid, Xapian::weight>::const_iterator 1242 j = values_or.begin(); 1243 j != values_or.end(); ++j) 1244 { 1245 Xapian::docid did = j->first; 1246 // Check that all the results in the or tree make it to the synonym tree. 1247 TEST(values_synonym.find(did) != values_synonym.end()); 1248 if (qlist->size() == 1) { 1249 // Check that the weights are the same. 1250 TEST_EQUAL(values_or[did], values_synonym[did]); 1251 } else { 1252 // Check that the weights differ. 1253 TEST_NOT_EQUAL(values_or[did], values_synonym[did]); 1254 } 1255 } 1256 } 1257 return true; 1258 } 1259 1174 1260 // tests that specifying a nonexistent input file throws an exception. 1175 1261 DEFINE_TESTCASE(quartzdatabaseopeningerror1, quartz) { 1176 1262 mkdir(".quartz", 0755); -
include/xapian/query.h
111 111 /** Select an elite set from the subqueries, and perform 112 112 * a query with these combined as an OR query. 113 113 */ 114 OP_ELITE_SET 114 OP_ELITE_SET, 115 116 /** Treat a set of queries as synonyms. 117 * 118 * This returns all results which match at least one of the 119 * queries, but weighting as if all the sub-queries are instances 120 * of the same term: so multiple matching terms for a document 121 * increase the wdf value used, and the term frequency is based on 122 * the number of documents which would match an OR of all the 123 * subqueries. 124 * 125 * The term frequency used will usually be an approximation, 126 * because calculating the precise combined term frequency would 127 * be overly expensive. 128 * 129 * Identical to OP_OR, except for the weightings returned. 130 */ 131 OP_SYNONYM 115 132 } op; 116 133 117 134 /** Copy constructor. */ -
common/remoteprotocol.h
38 38 // 30.3: New MSG_GETMSET which passes check_at_least parameter. 39 39 // 30.4: New query operator OP_SCALE_WEIGHT. 40 40 // 30.5: New MSG_GETMSET which expects MSet's percent_factor to be returned. 41 // 30.6: Add synonym queries (add operator to the serialised form of queries) 41 42 #define XAPIAN_REMOTE_PROTOCOL_MAJOR_VERSION 30 42 #define XAPIAN_REMOTE_PROTOCOL_MINOR_VERSION 543 #define XAPIAN_REMOTE_PROTOCOL_MINOR_VERSION 6 43 44 44 45 /* When we move to version 31: 45 46 * + Remove MSG_DELETEDOCUMENT_PRE_30_2 -
api/omqueryinternal.cc
59 59 case Xapian::Query::OP_PHRASE: 60 60 case Xapian::Query::OP_ELITE_SET: 61 61 case Xapian::Query::OP_VALUE_RANGE: 62 case Xapian::Query::OP_SYNONYM: 62 63 return 0; 63 64 case Xapian::Query::OP_SCALE_WEIGHT: 64 65 return 1; … … 91 92 case Xapian::Query::OP_NEAR: 92 93 case Xapian::Query::OP_PHRASE: 93 94 case Xapian::Query::OP_ELITE_SET: 95 case Xapian::Query::OP_SYNONYM: 94 96 return UINT_MAX; 95 97 default: 96 98 Assert(false); … … 187 189 result += "."; 188 190 result += str_parameter; // serialise_double(get_dbl_parameter()); 189 191 break; 192 case Xapian::Query::OP_SYNONYM: 193 result += "="; 194 break; 190 195 } 191 196 } 192 197 return result; … … 213 218 case Xapian::Query::OP_ELITE_SET: name = "ELITE_SET"; break; 214 219 case Xapian::Query::OP_VALUE_RANGE: name = "VALUE_RANGE"; break; 215 220 case Xapian::Query::OP_SCALE_WEIGHT: name = "SCALE_WEIGHT"; break; 221 case Xapian::Query::OP_SYNONYM: name = "SYNONYM"; break; 216 222 } 217 223 return name; 218 224 } … … 492 498 return qint_from_vector(Xapian::Query::OP_SCALE_WEIGHT, 493 499 subqs, 0, param); 494 500 } 495 default: 501 case '=': { 502 return qint_from_vector(Xapian::Query::OP_SYNONYM, subqs); 503 } 504 default: 496 505 DEBUGLINE(UNKNOWN, "Can't parse remainder `" << p - 1 << "'"); 497 506 throw Xapian::InvalidArgumentError("Invalid query string"); 498 507 } … … 662 671 case OP_ELITE_SET: 663 672 case OP_OR: 664 673 case OP_XOR: 674 case OP_SYNONYM: 665 675 // Doing an "OR" type operation - if we've got any MatchNothing 666 676 // subnodes, drop them; except that we mustn't become an empty 667 677 // node due to this, so we never drop a MatchNothing subnode … … 746 756 } 747 757 } 748 758 break; 749 case OP_OR: case OP_AND: case OP_XOR: 759 case OP_OR: case OP_AND: case OP_XOR: case OP_SYNONYM: 750 760 // Remove duplicates if we can. 751 761 if (subqs.size() > 1) collapse_subqs(); 752 762 break; … … 790 800 void 791 801 Xapian::Query::Internal::collapse_subqs() 792 802 { 793 Assert(op == OP_OR || op == OP_AND || op == OP_XOR );803 Assert(op == OP_OR || op == OP_AND || op == OP_XOR || op == OP_SYNONYM); 794 804 typedef set<Xapian::Query::Internal *, SortPosName> subqtable; 795 805 subqtable sqtab; 796 806 … … 865 875 Assert(!is_leaf(op)); 866 876 if (subq == 0) { 867 877 subqs.push_back(0); 868 } else if (op == subq->op && (op == OP_AND || op == OP_OR || op == OP_XOR )) {878 } else if (op == subq->op && (op == OP_AND || op == OP_OR || op == OP_XOR || op == OP_SYNONYM)) { 869 879 // Distribute the subquery. 870 880 for (subquery_list::const_iterator i = subq->subqs.begin(); 871 881 i != subq->subqs.end(); i++) {