Merge lp:~gary-wzl77/scope-aggregator/fix_1490828 into lp:scope-aggregator

Proposed by Gary.Wang
Status: Merged
Approved by: Kyle Nitzsche
Approved revision: 150
Merged at revision: 149
Proposed branch: lp:~gary-wzl77/scope-aggregator/fix_1490828
Merge into: lp:scope-aggregator
Diff against target: 292 lines (+142/-52)
4 files modified
README.md (+27/-0)
include/query.h (+2/-0)
src/query.cpp (+89/-51)
src/utils.cpp (+24/-1)
To merge this branch: bzr merge lp:~gary-wzl77/scope-aggregator/fix_1490828
Reviewer Review Type Date Requested Status
Kyle Nitzsche (community) Approve
Review via email: mp+269709@code.launchpad.net

Commit message

load correct keyword for a scope with multiple keywords when dept changed.
Add link_to_child in keyword struct to make it possible to navigateto child scope.

Description of the change

load correct keyword for a scope with multiple keywords when dept changed.
Add link_to_child in keyword struct to make it possible to navigateto child scope.

To post a comment you must log in.
Revision history for this message
Kyle Nitzsche (knitzsche) wrote :

Gary: Nice work.

A couple comments:

1) lines 30-41: This removes the code in handle_child_results() that skips a scope when it is not in the current department. I understand you added code to do the same thing for *keyword* scopes (lines 193 to 198). Also, the main lambda function that handles each child result *does* filter out results when they are not in the current department in all cases. So even with your MR the user should not see the scope if it is in the wrong department. But, there is no need to process results for scopes that are not in the current department with the lambda function, so the approach of removing lines 30-41 seems to result in unnecessary code execution (unless I have misunderstood something :) So it seems to me that lines 30-41 should not be removed. Thoughts?

2) You have added support for a declared "keyword" to have a link_to_child. I did not add this previously because a "keyword" by definition may include any number of child scopes (including zero). There is no guarantee that any particular child is installed. And there is no single child that should obviously be linked to given the dynamic and unpredictable nature or keyword aggregation. Are you sure we have a use case for this?

review: Needs Information
149. By Gary.Wang

Add department id checker when using departments.

Revision history for this message
Gary.Wang (gary-wzl77) wrote :

1.Indeed, it might cause unnecessary code execution, which is sth I didn't take into account. I've re-submitted and move code[30-41] below scope registry list, since that's more reasonable.
2.Yes, I'm sure. To take a trivial instance, for all child scopes in bollywood aggregator, one of the requirement is there is a need for them to linked to corresponding child scope by clicking the header(child scope name). And as you said, not every child scope should obviously be linked to a given keyword aggregation. In such a case, I think developer could isolate the special one by declaring another keyword or scope json object since link_to_child property in keyword object is applied for all child scopes.

Revision history for this message
Gary.Wang (gary-wzl77) wrote :

Regarding question2, There is one issue related on this
e.g. amazon scope has keyword "books", if we simply use keyword as following
...
        {
            "keyword":
            {
                "keyword": "books",
                "link_to_child" : "true"
            }
        }
...

There're some results returned from amazon scope, but these results items have nth to do with "books" keywords. Reason for this is developer didn't handle "books" keyword in source code although scope supports the keyword.

So if we isolate amazon by declaring scope json object with default_query_string property as following
        {
            "scope":
            {
                "id": "com.canonical.scopes.amazon",
                "local_id": "amazon",
                "_category_title": "amazon",
                "default_query_string": "best selling books"
            }
        },
        {
            "keyword":
            {
                "keyword": "books",
                "link_to_child" : "true",
            }
        }

We can see there two amazon scope exists since keyword object is applied for all child scopes.First one is explicit declared amazon scope, second one is amazon scope with "books" keywords. That looks bad.
So two ways to fix the duplicated issue.

1. Fix in child scope(amazon)
2. Fix in scope aggregator with adding new property exclude_scopes
   e.g.
        {
            "scope":
            {
                "id": "com.canonical.scopes.amazon",
                "local_id": "amazon",
                "_category_title": "amazon",
                "default_query_string": "best selling books
            }
        },
        {
            "keyword":
            {
                "keyword": "books",
                "link_to_child" : "true",
                "exclude_scopes" :[
                    {
                        "id": "com.canonical.scopes.amazon"
                    }
                ]
            }
        }

So in this way, we keep the first one and second one will be ignored since it's excluded in keyword scope list. I've added this new property in scope agg and have a quick testing, It works fine.

If we own source code, I prefer 1st option.
In the future if scopes which we don't own source code have the same issue that amazon has. I think the second one is better one. At the same time, I think it's also applied for link_to_child property. If there is no need for one certain scope to navigate to child scope, we can write it as following
   e.g
        {
            "scope":
            {
                "id": "com.canonical.scopes.myscope",
                "local_id": "myscope",
                "link_to_child" : "false"
            }
        },
        {
            "keyword":
            {
                "keyword": "books",
                "link_to_child" : "true",
                "exclude_scopes" :[
                    {
                        "id": "com.canonical.scopes.myscope"
                    }
                ]
            }
        }

I'd like to hear you option on this and will submit my MR if this suggestion looks good to you.
Thanks

Revision history for this message
Kyle Nitzsche (knitzsche) wrote :

Hi Gary,

We already have "link_to_child_specified" in "category" json objects. Category objects, like keyword objects, have no obvious scope to link to so the dev can declare one.

So, how about if we add "link_to_child_specified" to category objects *and* add a new key to keyword (and category) objects: "link_to_child_default_query_string".

This way the keyword can link to a specific child *with* a default query string.

Note that "link_to_child_specified" should *only* be used for pre-installed scopes (to ensure the scope really is present). We may want to only add the link to the specified child when the registry reports it is actually installed.

Revision history for this message
Gary.Wang (gary-wzl77) wrote :

   Personally, I trend to prefer to add "exclude_scopes" property in keyword object, reasons as following:
   1. if add "link_to_child_default_query_string" property, query_string will be applied for all child scope, If one or *more* child scopes match keyword in keyword json object are not supposed to use "default_query_string"?
      let me take an instance I meet during my development
      e.g. there're 5 book child scopes [amazon, ebay, douban, open library, viva reader], all of them supports "books" keyword
        {
            "keyword":
            {
                "keyword": "books",
                "link_to_child" : "true",
                "default_query_string" : "best selling books"
            }
        }

       * In this case, amazon, ebay, open library works fine, but no results returned for douban, viva reader since both two Chinese child scope only support Chinese querying. However both child scope can give correct results when loading default department without query string just as open as usual.

     2. The main point for adding "exclude_scopes" is we can get rid of one or *more* scopes in keyword object and drop them or isolate them to display in another ways[category, scope] to avoid duplicated display issue for one or *more* scope in scope aggregator.

Revision history for this message
Kyle Nitzsche (knitzsche) wrote :

Hi Gary,

Regarding the proposal for keyword category link_to_child_specified and link_to_child_default_query_string:

Link_to_child_query_string would only be passed to the link_to_child scope, not to all scopes included by keyword in the keyword json obkect. (That's why I propose the name: link_to_child_query_string.)

        {
            "keyword":
            {
                "keyword": "books",
                "link_to_child_specified" : "SOMESCOPEID",
                "link_to_child_query_string" : "best selling books" <-- is only passed to link_to_child
            }
        }

Because this approach expands the use of an existing feature (link_to_child_specified), I prefer it, unless I am misunderstanding something. :)

On the topic of exclude_scopes. in your opinion, is this needed if we use my proposal (or is it a choice between the two approaches).

Revision history for this message
Gary.Wang (gary-wzl77) wrote :

I think your approach does work for the case with some limitation as following
1.scopes: [amazon, ebay, open_library, douban, viva] ----> all of them support "books" keyword
2.all of them are supposed to link to child scopes.

limitation:
1.amazon gives us items which completely unrelated to "books" keyword since developer didn't deal with "books" keywords in source code.
2.english query string (default_query_string)doesn't work for douban and viva scopes, since these two scope only support Chinese querying.

Could you please write your json code based on my requirement as above ?

Thanks.

Revision history for this message
Kyle Nitzsche (knitzsche) wrote :

Hi Gary,

I did not understand that you want *every* keyword child scope to have a link to child.

So, I think we have discussed this enough. :)

You understand my preference to reuse and expand existing features and json keys where possible.

But if that does not meet the particular requirements, then please go ahead and complete the implementation as you have described, including the exclude_scopes key and I will approve after normal review.

cheers,
Kyle

150. By Gary.Wang

1.Add exclude_list key for keywords json object to make it possible to not
2.Merge from trunk

Revision history for this message
Gary.Wang (gary-wzl77) wrote :

Basically, at least for all the scope aggregator I implement "link to child" for every scope is required.
I've submitted latest MR.
Please review.

Revision history for this message
Kyle Nitzsche (knitzsche) wrote :

Looks good, thanks.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'README.md'
2--- README.md 2015-07-15 03:34:23 +0000
3+++ README.md 2015-09-14 03:04:52 +0000
4@@ -759,3 +759,30 @@
5 }
6 }
7 ]
8+
9+
10+#### `exclude_scopes` key (optional)
11+
12+Value: json list of objects.
13+
14+Suppose you want some scopes which supports one certain keywords not to display results, you can specify scope id in the exclude_scopes and scope aggregator will not displays results of specified scopes.
15+
16+Note:This key is only supposed to be declared in a `keywords` json object, which doesn't not use a shared category.
17+e.g. amazon supports 'books' keyword, but in most cases it returns something unrelated to 'books' keyword. we can get rid of its results in the way as following.
18+ "order":
19+ [
20+ {
21+ "keyword":
22+ {
23+ "keyword": "books",
24+ "link_to_child" : "true",
25+ "exclude_scopes" :[
26+ {
27+ "id": "com.canonical.scopes.amazon"
28+ }
29+ ]
30+ }
31+ }
32+ ]
33+
34+
35
36=== modified file 'include/query.h'
37--- include/query.h 2015-08-04 19:12:04 +0000
38+++ include/query.h 2015-09-14 03:04:52 +0000
39@@ -153,6 +153,8 @@
40 int cardinality = -1;
41 std::string dept_id;
42 std::string dept_title_msgid;
43+ bool link_to_child = false;
44+ std::vector<std::string> exclude_scopes; // id of exclude scopes
45 };
46 std::vector<std::shared_ptr<keyword>> keywords;// pulled from child_sccopes.json
47 std::map<std::string, std::shared_ptr<keyword>> id_keyword_map;//keyword id (the keyword string) to keyword instance
48
49=== modified file 'src/query.cpp'
50--- src/query.cpp 2015-09-04 20:12:11 +0000
51+++ src/query.cpp 2015-09-14 03:04:52 +0000
52@@ -289,27 +289,27 @@
53 us::CannedQuery canned_query = query();
54 std::string dept_id = canned_query.department_id();
55
56+ // skip to next if scope is not registered in the system
57+ auto reg_scopes = registry_->list();
58+ us::MetadataMap::const_iterator scope_it;
59+ scope_it = reg_scopes.find(localId_id_m[local_id]);
60+ if (scope_it == reg_scopes.end()) {
61+ qWarning () << QString("%1: scope is NOT REGISTERED, skipping: %2").arg(qstr(SCOPE_ID), qstr(localId_id_m[local_id]));
62+ continue;
63+ }
64+
65 //TODO: add to docs note that when declaring a scope more than once, all must be in different depts
66 if (using_departments)
67 {
68 if (dept_id == "") //is root dept
69 {
70 if(scope->department() != dept_id_of_root)
71- continue;
72+ continue;
73 }
74 else if(scope->department() != query().department_id())
75 continue;
76 }
77-
78- // skip to next if scope is not registered in the system
79- auto reg_scopes = registry_->list();
80- us::MetadataMap::const_iterator scope_it;
81- scope_it = reg_scopes.find(localId_id_m[local_id]);
82- if (scope_it == reg_scopes.end()) {
83- qWarning () << QString("%1: scope is NOT REGISTERED, skipping: %2").arg(qstr(SCOPE_ID), qstr(localId_id_m[local_id]));
84- continue;
85- }
86-
87+
88 // if scope is not in the currently enabled list, skip it
89 bool found_as_enabled = false;
90 for (auto cs : current_child_scopes)//FQ ids
91@@ -322,12 +322,33 @@
92 }
93 }
94 }
95+
96 if (!found_as_enabled)
97 {
98 qWarning () << QString("%1: scope is NOT ENABLED, skipping: %2").arg(qstr(SCOPE_ID), qstr(localId_id_m[local_id]));
99 continue;
100 }
101
102+ // if scope is in exclude scopes list, skip it
103+ bool found_as_exclude = false;
104+ for (auto kw : keywords)
105+ {
106+ if (kw->id == scope->keyword()) {
107+ auto iter = std::find(kw->exclude_scopes.begin(), kw->exclude_scopes.end(), scope->id());
108+ if (iter != kw->exclude_scopes.end())
109+ {
110+ found_as_exclude = true;
111+ qWarning () << QString("==== FOUND AS EXCLUDE: %1: scope is in exclude scopes of keyword: %2")
112+ .arg(qstr(scope->id()), qstr(scope->keyword()));
113+ }
114+ }
115+ }
116+
117+ if (found_as_exclude) {
118+ continue;
119+ }
120+
121+
122 if (query_string.empty() && scope->only_in_search() == true)
123 {
124 continue;
125@@ -612,13 +633,33 @@
126 }
127 if (!upstream_reply->lookup_category(cat_id))
128 {
129- scope->set_category(upstream_reply->register_category
130- (
131- cat_id,
132- cat_title,
133- "",
134- us::CategoryRenderer(rdr)
135- ));
136+ if (scope->category_link_to_child())
137+ {
138+ scope->set_category
139+ (
140+ upstream_reply->register_category
141+ (
142+ cat_id,
143+ cat_title,
144+ "",
145+ us::CannedQuery(scope->id(), query_string, scope->child_department()),
146+ us::CategoryRenderer(rdr)
147+ )
148+ );
149+ }
150+ else
151+ {
152+ scope->set_category
153+ (
154+ upstream_reply->register_category
155+ (
156+ cat_id,
157+ cat_title,
158+ "",
159+ us::CategoryRenderer(rdr)
160+ )
161+ );
162+ }
163 }
164 }
165 res.set_category(scope->category());
166@@ -1049,39 +1090,36 @@
167 {
168 if (!rdr.empty()) // should not ever be empty!
169 {
170- if (scope->category_link_to_child())
171- {
172- if (!upstream_reply->lookup_category(cat_id))
173- {
174- scope->set_category
175- (
176- upstream_reply->register_category
177- (
178- cat_id,
179- cat_title,
180- "",
181- us::CannedQuery(scope->id(), query_string, scope->child_department()),
182- us::CategoryRenderer(rdr)
183- )
184- );
185- }
186- }
187- else
188- {
189- if (!upstream_reply->lookup_category(cat_id))
190- {
191- scope->set_category
192- (
193- upstream_reply->register_category
194- (
195- cat_id,
196- cat_title,
197- "",
198- us::CategoryRenderer(rdr)
199- )
200- );
201- }
202- }
203+ if (!upstream_reply->lookup_category(cat_id))
204+ {
205+ if (scope->category_link_to_child())
206+ {
207+ scope->set_category
208+ (
209+ upstream_reply->register_category
210+ (
211+ cat_id,
212+ cat_title,
213+ "",
214+ us::CannedQuery(scope->id(), query_string, scope->child_department()),
215+ us::CategoryRenderer(rdr)
216+ )
217+ );
218+ }
219+ else
220+ {
221+ scope->set_category
222+ (
223+ upstream_reply->register_category
224+ (
225+ cat_id,
226+ cat_title,
227+ "",
228+ us::CategoryRenderer(rdr)
229+ )
230+ );
231+ }
232+ }
233 }
234 else
235 qWarning() << QString("Warning: Scope %1 renderer is empty.").arg(qstr(scope->id()));
236
237=== modified file 'src/utils.cpp'
238--- src/utils.cpp 2015-08-04 19:12:04 +0000
239+++ src/utils.cpp 2015-09-14 03:04:52 +0000
240@@ -648,6 +648,23 @@
241 QJsonDocument rdr_d(renderer_o);
242 keyword_catname_search_renderer[category] = sstr(rdr_d.toJson());
243 }
244+ if (keyword_o.contains("link_to_child"))
245+ {
246+ if (keyword_o["link_to_child"].toString() == "true")
247+ keyword_.link_to_child = true;
248+ }
249+ if (keyword_o.contains("exclude_scopes"))
250+ {
251+ QJsonArray scope_a = keyword_o["exclude_scopes"].toArray();
252+
253+ for (const auto item : scope_a)
254+ {
255+ QJsonObject scope_o = item.toObject();
256+ std::string id = scope_o["id"].toString().toStdString();
257+ keyword_.exclude_scopes.emplace_back(id);
258+ }
259+ }
260+
261 std::shared_ptr<keyword> kw_ptr = std::make_shared<keyword>(keyword_);
262 keywords.emplace_back(kw_ptr);
263 id_keyword_map[keyword_id] = kw_ptr;
264@@ -812,7 +829,6 @@
265 AggChildScope keych(keyword_child.id);
266 QString time_str = QDateTime::currentDateTimeUtc().toString();
267 keych.set_local_id(keyword_child.id + time_str.toStdString());
268- keych.set_category_link_to_child(true);
269 keych.set_keyword_scope(true);
270 dept d;
271 d.id = keyword_child.id;
272@@ -837,6 +853,7 @@
273 d.keyword = kw;
274 keych.set_department(kw);
275 keych.set_keyword(kw);
276+ keych.set_category_link_to_child(id_keyword_map[kw]->link_to_child);
277 qWarning() << "==== ID KW 1: " << qstr(keych.keyword());
278
279 bool a_dup = false;
280@@ -844,6 +861,12 @@
281 if (using_departments)
282 {
283 std::string curr_dept_id = query().department_id();
284+
285+ //make sure we load correct dept id if we specify department property in keyword object
286+ if (!curr_dept_id.empty() && keyword_deptId[kw] != curr_dept_id) {
287+ continue;
288+ }
289+
290 for (std::shared_ptr<child_scope> cs : child_scopes)
291 {
292 if (keyword_child.id != cs->id)

Subscribers

People subscribed via source and target branches

to all changes: