Subversion Repositories SmartDukaan

Rev

Rev 37638 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
22346 amit.gupta 1
package com.spice.profitmandi.common.solr;
2
 
30080 amit.gupta 3
import com.spice.profitmandi.common.enumuration.SchemeType;
4
import com.spice.profitmandi.common.exception.ProfitMandiBusinessException;
5
import com.spice.profitmandi.common.web.client.RestClient;
25380 amit.gupta 6
import org.apache.commons.lang3.StringUtils;
7
import org.apache.http.conn.HttpHostConnectException;
28221 amit.gupta 8
import org.apache.logging.log4j.LogManager;
9
import org.apache.logging.log4j.Logger;
25380 amit.gupta 10
import org.json.JSONArray;
11
import org.json.JSONObject;
27030 amit.gupta 12
import org.springframework.beans.factory.annotation.Autowired;
26736 amit.gupta 13
import org.springframework.beans.factory.annotation.Value;
25380 amit.gupta 14
import org.springframework.stereotype.Service;
15
 
30080 amit.gupta 16
import java.util.*;
31712 amit.gupta 17
import java.util.stream.Collectors;
25380 amit.gupta 18
 
25386 amit.gupta 19
@Service("solrServiceCommon")
22346 amit.gupta 20
public class SolrService {
28221 amit.gupta 21
 
22
	private static final Logger logger = LogManager.getLogger(SolrService.class);
23
 
26736 amit.gupta 24
	@Value("${new.solr.url}")
25
	private String solrUrl;
28221 amit.gupta 26
 
27030 amit.gupta 27
	@Autowired
28
	private RestClient restClient;
28221 amit.gupta 29
 
31330 tejbeer 30
	public String getContent(String queryTerm, List<Integer> categoryIds, List<String> brands, int limit,
31
			boolean activeOnly) throws Exception {
32
 
33
		JSONArray docs = this.getContentDocs(queryTerm, categoryIds, brands, limit, activeOnly);
34
		return docs.toString();
35
	}
36
 
37
	public JSONArray getContentDocs(String queryTerm, List<Integer> categoryIds, List<String> brands, int limit,
38
			boolean activeOnly) throws Exception {
22346 amit.gupta 39
		Map<String, String> params = new HashMap<>();
25380 amit.gupta 40
		List<String> mandatoryQ = new ArrayList<>();
41
		if (queryTerm != null && !queryTerm.equals("null")) {
28221 amit.gupta 42
			mandatoryQ.add(String.format("+(%s)", "*" + queryTerm + "*"));
25380 amit.gupta 43
		} else {
44
			queryTerm = null;
45
		}
27325 amit.gupta 46
		params.put("q", StringUtils.join(mandatoryQ, " "));
30267 amit.gupta 47
		if (categoryIds != null && categoryIds.size() > 0) {
30262 amit.gupta 48
			params.put("q", params.get("q") + " +filter(categoryId_i:(" + StringUtils.join(categoryIds, " ") + "))");
27601 amit.gupta 49
		}
28221 amit.gupta 50
		if (brands.size() > 0) {
31714 amit.gupta 51
			brands = brands.stream().map(x -> x.replaceAll(" ", "\\\\ ")).collect(Collectors.toList());
27601 amit.gupta 52
			params.put("q", params.get("q") + " AND brand_ss:(" + StringUtils.join(brands, " ") + ")");
53
		}
30080 amit.gupta 54
		if (activeOnly) {
55
			params.put("q", params.get("q") + " AND active_b:true");
56
		}
25380 amit.gupta 57
		params.put("fl", "*");
58
		if (queryTerm == null) {
59
			params.put("sort", "create_s desc");
60
		}
22346 amit.gupta 61
		params.put("start", String.valueOf(0));
28221 amit.gupta 62
		if (limit == 0) {
37619 amit 63
			params.put("fl", "catalogId_i, title_s, active_b");
27603 amit.gupta 64
			params.put("rows", String.valueOf(5000));
65
		} else {
28653 amit.gupta 66
			params.put("rows", String.valueOf(limit));
27603 amit.gupta 67
		}
22346 amit.gupta 68
		params.put("wt", "json");
25380 amit.gupta 69
		String response = null;
22346 amit.gupta 70
		try {
27030 amit.gupta 71
			response = restClient.get(SchemeType.HTTP, solrUrl, 8984, "solr/demo/select", params);
25380 amit.gupta 72
		} catch (HttpHostConnectException e) {
73
			throw new ProfitMandiBusinessException("", "", "Could not connect to host");
22346 amit.gupta 74
		}
25380 amit.gupta 75
		JSONObject solrResponseJSONObj = new JSONObject(response).getJSONObject("response");
76
		JSONArray docs = solrResponseJSONObj.getJSONArray("docs");
31330 tejbeer 77
		return docs;
22346 amit.gupta 78
	}
25380 amit.gupta 79
 
36923 ranu 80
	/**
81
	 * Same query as {@link #getContentDocs} but also surfaces Solr's
82
	 * <code>response.numFound</code> so callers can show "showing X of Y" totals.
83
	 * Returns a small wrapper with the docs array (still capped by {@code limit})
84
	 * and the unlimited match total.
85
	 */
86
	public ContentSearchResult getContentWithTotal(String queryTerm, List<Integer> categoryIds, List<String> brands,
87
			int limit, boolean activeOnly) throws Exception {
88
		Map<String, String> params = new HashMap<>();
89
		List<String> mandatoryQ = new ArrayList<>();
90
		if (queryTerm != null && !queryTerm.equals("null")) {
91
			mandatoryQ.add(String.format("+(%s)", "*" + queryTerm + "*"));
92
		} else {
93
			queryTerm = null;
94
		}
95
		params.put("q", StringUtils.join(mandatoryQ, " "));
96
		if (categoryIds != null && categoryIds.size() > 0) {
97
			params.put("q", params.get("q") + " +filter(categoryId_i:(" + StringUtils.join(categoryIds, " ") + "))");
98
		}
99
		if (brands.size() > 0) {
100
			brands = brands.stream().map(x -> x.replaceAll(" ", "\\\\ ")).collect(Collectors.toList());
101
			params.put("q", params.get("q") + " AND brand_ss:(" + StringUtils.join(brands, " ") + ")");
102
		}
103
		if (activeOnly) {
104
			params.put("q", params.get("q") + " AND active_b:true");
105
		}
106
		params.put("fl", "*");
107
		if (queryTerm == null) {
108
			params.put("sort", "create_s desc");
109
		}
110
		params.put("start", String.valueOf(0));
111
		if (limit == 0) {
112
			params.put("fl", "catalogId_i, title_s");
113
			params.put("rows", String.valueOf(5000));
114
		} else {
115
			params.put("rows", String.valueOf(limit));
116
		}
117
		params.put("wt", "json");
118
		String response;
119
		try {
120
			response = restClient.get(SchemeType.HTTP, solrUrl, 8984, "solr/demo/select", params);
121
		} catch (HttpHostConnectException e) {
122
			throw new ProfitMandiBusinessException("", "", "Could not connect to host");
123
		}
124
		JSONObject solrResponseJSONObj = new JSONObject(response).getJSONObject("response");
125
		JSONArray docs = solrResponseJSONObj.getJSONArray("docs");
126
		long numFound = solrResponseJSONObj.optLong("numFound", docs.length());
127
		return new ContentSearchResult(docs, numFound);
128
	}
129
 
130
	/** Wrapper for {@link #getContentWithTotal}. */
131
	public static class ContentSearchResult {
132
		private final JSONArray docs;
133
		private final long totalCount;
134
 
135
		public ContentSearchResult(JSONArray docs, long totalCount) {
136
			this.docs = docs;
137
			this.totalCount = totalCount;
138
		}
139
 
140
		public JSONArray getDocs() { return docs; }
141
		public long getTotalCount() { return totalCount; }
142
	}
143
 
26606 amit.gupta 144
	public Map<Integer, JSONObject> getContentByCatalogIds(List<Integer> catalogIds) throws Exception {
145
		Map<Integer, JSONObject> documentMap = new HashMap<>();
146
		Map<String, String> params = new HashMap<>();
147
		params.put("q", "catalogId_i:" + StringUtils.join(catalogIds, " "));
148
		params.put("fl", "*");
149
		params.put("start", String.valueOf(0));
28221 amit.gupta 150
		params.put("rows", String.valueOf(100));
26606 amit.gupta 151
		params.put("wt", "json");
152
		String response = null;
153
		try {
27030 amit.gupta 154
			response = restClient.get(SchemeType.HTTP, solrUrl, 8984, "solr/demo/select", params);
26606 amit.gupta 155
		} catch (HttpHostConnectException e) {
156
			throw new ProfitMandiBusinessException("", "", "Could not connect to host");
157
		}
158
		JSONObject solrResponseJSONObj = new JSONObject(response).getJSONObject("response");
159
		JSONArray docs = solrResponseJSONObj.getJSONArray("docs");
160
		for (int i = 0; i < docs.length(); i++) {
161
			JSONObject doc = docs.getJSONObject(i);
162
			documentMap.put(doc.getInt("catalogId_i"), doc);
163
		}
164
		return documentMap;
165
	}
28221 amit.gupta 166
 
28653 amit.gupta 167
	// This method is the used to pull docs based on search and shall be used
28221 amit.gupta 168
	// interchangably for both the things
37142 amit 169
	public static String brandExclusionFq(List<String> excludeBrands) {
170
		if (excludeBrands == null || excludeBrands.isEmpty()) {
171
			return null;
172
		}
173
		return "-brand_ss:(" + excludeBrands.stream().map(x -> "\"" + x + "\"").collect(Collectors.joining(" OR "))
174
				+ ")";
175
	}
176
 
37259 amit 177
	/**
37638 amit 178
	 * Category facet over the hot-deal models: which categoryId_i values are present (and
179
	 * how many models each) among the docs the hot-deal listing can show. Drives the
180
	 * page's category filter chips.
37259 amit 181
	 */
182
	public Map<Integer, Integer> getHotDealCategoryFacet(List<Integer> hotDealCatalogIds) throws Exception {
183
		Map<Integer, Integer> counts = new LinkedHashMap<>();
37665 amit 184
		for (Map.Entry<String, Integer> bucket
185
				: hotDealFacet(hotDealCatalogIds, "categoryId_i", null).entrySet()) {
37638 amit 186
			counts.put(Integer.parseInt(bucket.getKey()), bucket.getValue());
187
		}
188
		return counts;
189
	}
190
 
191
	/**
192
	 * Brand facet over the hot-deal models, driving the page's brand chips.
193
	 *
194
	 * <p>Facets on oem_brand_s, NOT brand_ss. Every hot-deal doc reads "Hot Deal" in
195
	 * brand_ss - that is the whole point of the brand - so faceting there would yield a
196
	 * single useless bucket. oem_brand_s carries the brand the SKU had before the move
197
	 * (Samsung, OnePlus, ...), which is what a partner actually wants to narrow by. It is
198
	 * surfaced to partners as plain "Brand"; "OEM" is internal vocabulary.
199
	 */
37665 amit 200
	/**
201
	 * @param categoryId when set, counts only models in that category, so the chips match
202
	 *                   the tab the partner is standing on. Without it the row is global:
203
	 *                   the LED TVs tab would offer Samsung and Vivo chips even though
204
	 *                   neither has a hot-deal TV, and tapping one returns nothing.
205
	 */
206
	public Map<String, Integer> getHotDealBrandFacet(List<Integer> hotDealCatalogIds, Integer categoryId)
207
			throws Exception {
208
		return hotDealFacet(hotDealCatalogIds, "oem_brand_s", categoryId);
37638 amit 209
	}
210
 
211
	/**
212
	 * One facet query over the hot-deal doc set, keyed by raw bucket value. Shared by the
37665 amit 213
	 * category and brand facets, which differ only in the field they count and whether
214
	 * they narrow to a category.
37638 amit 215
	 */
37665 amit 216
	private Map<String, Integer> hotDealFacet(List<Integer> hotDealCatalogIds, String facetField,
217
			Integer categoryId) throws Exception {
37638 amit 218
		Map<String, Integer> counts = new LinkedHashMap<>();
37259 amit 219
		if (hotDealCatalogIds == null || hotDealCatalogIds.isEmpty()) {
220
			return counts;
221
		}
222
		String idClause = hotDealCatalogIds.stream().map(x -> "catalog" + x)
223
				.collect(Collectors.joining(" OR "));
37665 amit 224
		// categoryId_i lives on the parent (catalog) doc, same as the id clause, so it
225
		// narrows the facet without needing a child-document join.
226
		String categoryClause = categoryId == null || categoryId <= 0
227
				? "" : " +categoryId_i:" + categoryId;
37259 amit 228
		Map<String, String> params = new HashMap<>();
37665 amit 229
		params.put("q", "+id:(" + idClause + ") +show_default_b:true +eol_no_stock_b:false"
230
				+ categoryClause);
37259 amit 231
		params.put("rows", "0");
232
		params.put("facet", "true");
37638 amit 233
		params.put("facet.field", facetField);
37259 amit 234
		params.put("facet.mincount", "1");
235
		params.put("wt", "json");
236
		String response = restClient.get(SchemeType.HTTP, solrUrl, 8984, "solr/demo/select", params);
237
		JSONArray buckets = new JSONObject(response).getJSONObject("facet_counts")
37638 amit 238
				.getJSONObject("facet_fields").getJSONArray(facetField);
37259 amit 239
		for (int i = 0; i + 1 < buckets.length(); i += 2) {
37638 amit 240
			counts.put(buckets.getString(i), buckets.getInt(i + 1));
37259 amit 241
		}
242
		return counts;
243
	}
244
 
33573 amit.gupta 245
	public JSONArray getSolrDocs(String queryTerm, String categoryId, int offset, int limit, String sort,
35369 ranu 246
			String brand, int subCategoryId, boolean hotDeal, boolean group, boolean eol_filter) throws Throwable {
37142 amit 247
		return this.getSolrDocs(queryTerm, categoryId, offset, limit, sort, brand, subCategoryId, hotDeal, group,
248
				eol_filter, null);
249
	}
250
 
251
	public JSONArray getSolrDocs(String queryTerm, String categoryId, int offset, int limit, String sort,
252
			String brand, int subCategoryId, boolean hotDeal, boolean group, boolean eol_filter,
253
			List<String> excludeBrands) throws Throwable {
37245 amit 254
		return getSolrDocs(queryTerm, categoryId, offset, limit, sort, brand, subCategoryId, hotDeal, group,
255
				eol_filter, excludeBrands, null);
256
	}
257
 
258
	public JSONArray getSolrDocs(String queryTerm, String categoryId, int offset, int limit, String sort,
259
			String brand, int subCategoryId, boolean hotDeal, boolean group, boolean eol_filter,
260
			List<String> excludeBrands, List<Integer> hotDealCatalogIds) throws Throwable {
27030 amit.gupta 261
		List<String> parentFilter = new ArrayList<>();
37259 amit 262
		// Hot-deal listings are cross-category by default (the curated catalogId filter
263
		// is the scope); categoryId is applied there only when the caller explicitly
264
		// passes one (the page's category filter chips)
265
		if (hotDealCatalogIds == null || hotDealCatalogIds.isEmpty()
266
				|| (categoryId != null && !categoryId.trim().isEmpty())) {
37253 amit 267
			parentFilter.add("categoryId_i:" + categoryId);
268
		}
31596 amit.gupta 269
		parentFilter.add("show_default_b:true");
27030 amit.gupta 270
		List<String> childFilter = new ArrayList<>();
271
		childFilter.add("itemId_i:*");
272
 
273
		Map<String, String> params = new HashMap<>();
274
		if (queryTerm == null || queryTerm.equals("null")) {
275
			queryTerm = "";
276
		} else {
277
			queryTerm = "(" + queryTerm + ")";
278
		}
37638 amit 279
		// True when this query is scoped to hot deals by EITHER route below. The brand
280
		// filter keys off this rather than the hotDeal flag alone, because the caller that
281
		// resolves the catalog ids itself passes hotDeal = false - and that listing still
282
		// needs its brand chips routed to oem_brand_s.
283
		boolean hotDealScoped = hotDeal || (hotDealCatalogIds != null && !hotDealCatalogIds.isEmpty());
284
 
37245 amit 285
		if (hotDealCatalogIds != null && !hotDealCatalogIds.isEmpty()) {
37638 amit 286
			// Caller already resolved the hot-deal set from the brand: restrict parents to
37259 amit 287
			// those models; items stay the normal active set
37245 amit 288
			String idClause = hotDealCatalogIds.stream().map(x -> "catalog" + x)
289
					.collect(java.util.stream.Collectors.joining(" OR "));
290
			parentFilter.add("id:(" + idClause + ")");
291
			childFilter.add("active_b:true");
292
		} else if (hotDeal) {
37638 amit 293
			// hot_deal_b is derived from the SKU's brand at index time (FofoSolr)
27030 amit.gupta 294
			childFilter.add("hot_deal_b:true");
295
		} else {
296
			childFilter.add("active_b:true");
297
		}
298
		if (subCategoryId != 0) {
299
			parentFilter.add("subCategoryId_i:" + subCategoryId);
300
		}
35369 ranu 301
 
302
		if(eol_filter){
303
			parentFilter.add("eol_no_stock_b:false");
304
		}
27030 amit.gupta 305
		if (StringUtils.isNotBlank(brand)) {
37638 amit 306
			// Inside the hot-deal listing the brand chips carry the ORIGINAL brand, which
307
			// lives in oem_brand_s - every one of those docs reads "Hot Deal" in brand_ss,
308
			// so filtering there would match nothing.
309
			String brandField = hotDealScoped ? "oem_brand_s" : "brand_ss";
310
			parentFilter.add(brandField + ":" + "\\\"" + brand + "\\\"");
27030 amit.gupta 311
		}
312
		if (queryTerm == "") {
28221 amit.gupta 313
			params.put("sort", (sort == "" ? "" : sort + ", ") + "create_s desc");
27030 amit.gupta 314
		} else {
315
			parentFilter.addAll(Arrays.asList(queryTerm.split(" ")));
316
		}
317
		String parentFilterString = "\"" + String.join(" AND ", parentFilter) + "\"";
318
		String childFilterString = String.join(" AND ", childFilter);
319
		params.put("q", String.format("{!parent which=%s}%s", parentFilterString, childFilterString));
320
		params.put("fl",
321
				String.format("*, [child parentFilter=id:catalog* childFilter=%s]", "\"" + childFilterString + "\""));
322
		params.put("start", String.valueOf(offset));
323
		params.put("rows", String.valueOf(limit));
324
		params.put("wt", "json");
34023 vikas.jang 325
 
37142 amit 326
		String exclusionFq = brandExclusionFq(excludeBrands);
327
		if (exclusionFq != null) {
328
			params.put("fq", exclusionFq);
329
		}
330
 
34023 vikas.jang 331
		String groupByField = null;
332
 
333
		if (group) {
334
			groupByField = "superCatalog_s";
335
			params.put("group", String.valueOf(group));
336
			params.put("group.field", groupByField);
337
			params.put("group.limit", "1");
338
			if (!sort.isEmpty()) {
339
				params.put("group.sort", sort);
340
			}
341
		}
342
logger.info("groupByField {}", groupByField);
27030 amit.gupta 343
		String response = null;
34023 vikas.jang 344
 
27030 amit.gupta 345
		try {
346
			response = restClient.get(SchemeType.HTTP, solrUrl, 8984, "solr/demo/select", params);
347
		} catch (HttpHostConnectException e) {
348
			throw new ProfitMandiBusinessException("", "", "Could not connect to host");
349
		}
28221 amit.gupta 350
 
34023 vikas.jang 351
		/*JSONObject solrResponseJSONObj = new JSONObject(response).getJSONObject("response");
352
		JSONArray docs = solrResponseJSONObj.getJSONArray("docs");*/
353
 
354
		JSONObject solrResponseJSONObj = new JSONObject(response);
355
		JSONArray docs;
356
 
357
		if (group) {
358
			logger.info("Reached in if condition {}",groupByField);
359
			JSONObject grouped = solrResponseJSONObj.getJSONObject("grouped");
360
			JSONArray groups = grouped.getJSONObject(groupByField).getJSONArray("groups");
361
 
362
			docs = new JSONArray();
363
			for (int i = 0; i < groups.length(); i++) {
364
				JSONObject groupObj = groups.getJSONObject(i);
365
				JSONArray groupDocs = groupObj.getJSONObject("doclist").getJSONArray("docs");
366
				for (int j = 0; j < groupDocs.length(); j++) {
367
					docs.put(groupDocs.getJSONObject(j));
368
				}
369
			}
370
		} else {
371
			logger.info("Reached in else condition {}",groupByField);
372
			docs = solrResponseJSONObj.getJSONObject("response").getJSONArray("docs");
373
		}
34187 vikas.jang 374
 
34186 vikas.jang 375
		logger.info("Reached at end {}",docs);
34023 vikas.jang 376
 
27030 amit.gupta 377
		return docs;
378
	}
379
 
22346 amit.gupta 380
}