Skip to content

Commit b6ae2fb

Browse files
author
Burak Serdar
committed
Complete saved search work: fix metadata, add tests
1 parent 570d9f8 commit b6ae2fb

File tree

9 files changed

+567
-16
lines changed

9 files changed

+567
-16
lines changed

misc/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@
3838
<artifactId>lightblue-core-config</artifactId>
3939
<version>2.9.0-SNAPSHOT</version>
4040
</dependency>
41+
<dependency>
42+
<groupId>org.slf4j</groupId>
43+
<artifactId>slf4j-simple</artifactId>
44+
<scope>test</scope>
45+
</dependency>
4146
</dependencies>
4247
<build>
4348
</build>

misc/src/main/java/com/redhat/lightblue/savedsearch/FindRequestBuilder.java

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import java.util.Iterator;
2222
import java.util.Map;
23+
import java.util.HashMap;
2324

2425
import java.io.IOException;
2526

@@ -38,14 +39,75 @@
3839

3940
import com.redhat.lightblue.crud.FindRequest;
4041

42+
import com.redhat.lightblue.metadata.TypeResolver;
43+
import com.redhat.lightblue.metadata.Type;
44+
import com.redhat.lightblue.metadata.types.StringType;
45+
4146
import com.redhat.lightblue.query.*;
4247

4348
import com.redhat.lightblue.util.JsonUtils;
49+
import com.redhat.lightblue.util.Error;
4450

51+
/**
52+
* Builds a find request from a saved search
53+
*/
4554
public class FindRequestBuilder {
4655

56+
public static final String ERR_SAVED_SEARCH_INVALID_TYPE="crud:saved-search:invalidType";
57+
public static final String ERR_SAVED_SEARCH_NO_DEFAULT_VALUE="crud:saved-search:noDefaultValue";
58+
public static final String ERR_SAVED_SEARCH_MISSING_PARAM="crud:saved-search:missing";
59+
4760
private static final Logger LOGGER = LoggerFactory.getLogger(FindRequestBuilder.class);
4861

62+
/**
63+
* Given the parameter values supplied by the caller, returns a
64+
* map containing default values for missing parameters. If there
65+
* are missing required parameters, throws an Error with missing
66+
* parameters.
67+
*/
68+
public static Map<String,String> fillDefaults(JsonNode savedSearch,
69+
Map<String,String> parameterValues,
70+
TypeResolver types) {
71+
Map<String,String> ret=new HashMap<>();
72+
JsonNode parametersNode=savedSearch.get("parameters");
73+
if(parametersNode instanceof ArrayNode) {
74+
for(Iterator<JsonNode> itr=parametersNode.elements();itr.hasNext();) {
75+
JsonNode parameterNode=itr.next();
76+
if(parameterNode instanceof ObjectNode) {
77+
Type type=null;
78+
JsonNode x=parameterNode.get("name");
79+
String name=x.asText();
80+
x=parameterNode.get("type");
81+
if(x!=null) {
82+
type=types.getType(x.asText());
83+
if(type==null) {
84+
throw Error.get(ERR_SAVED_SEARCH_INVALID_TYPE,x.asText());
85+
}
86+
} else {
87+
type=StringType.TYPE;
88+
}
89+
boolean optional=false;
90+
x=parameterNode.get("optional");
91+
if(x!=null) {
92+
optional=x.asBoolean();
93+
}
94+
if(parameterValues.containsKey(name)) {
95+
ret.put(name,(String)StringType.TYPE.cast(type.cast(parameterValues.get(name))));
96+
} else if(optional) {
97+
// Fill it in using default value
98+
JsonNode defaultValue=parameterNode.get("defaultValue");
99+
if(defaultValue==null)
100+
throw Error.get(ERR_SAVED_SEARCH_NO_DEFAULT_VALUE,name);
101+
ret.put(name,(String)StringType.TYPE.cast(type.fromJson(defaultValue)));
102+
} else {
103+
throw Error.get(ERR_SAVED_SEARCH_MISSING_PARAM,name);
104+
}
105+
}
106+
}
107+
}
108+
return ret;
109+
}
110+
49111
/**
50112
* Builds a find request from the saved search by rewriting the
51113
* search bound parameters using parameter values. The search

misc/src/main/java/com/redhat/lightblue/savedsearch/SavedSearchCache.java

Lines changed: 59 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
*/
1919
package com.redhat.lightblue.savedsearch;
2020

21-
import java.util.Map;
2221
import java.util.Iterator;
2322
import java.util.Objects;
2423
import java.util.List;
@@ -60,12 +59,16 @@
6059
import com.redhat.lightblue.util.Path;
6160
import com.redhat.lightblue.util.JsonUtils;
6261

62+
/**
63+
* This class is the main access point to saved searches. It loads
64+
* saved searches from the db, and keeps them in a weak cache.
65+
*/
6366
public class SavedSearchCache {
6467
private static final Logger LOGGER = LoggerFactory.getLogger(SavedSearchCache.class);
6568

6669
public static final String ERR_SAVED_SEARCH="crud:saved-search";
6770

68-
private Cache<Key,ObjectNode> cache;
71+
Cache<Key,ObjectNode> cache;
6972

7073
private static final Path P_NAME=new Path("name");
7174
private static final Path P_ENTITY=new Path("entity");
@@ -108,6 +111,11 @@ public boolean equals(Object o) {
108111
public int hashCode() {
109112
return (version==null?1:version.hashCode())*searchName.hashCode()*entity.hashCode();
110113
}
114+
115+
@Override
116+
public String toString() {
117+
return searchName+":"+entity+":"+version;
118+
}
111119

112120
public Key(String searchName,String entity,String version) {
113121
this.searchName=searchName;
@@ -142,10 +150,21 @@ private void initializeCache(String spec) {
142150

143151

144152
/**
145-
* Retrieves a saved search from the database. The returned JsonNode can be an ObjectNode, or an ArrayNode containing
146-
* zero or more documents. If there are more than one documents, only one of them is for the requested version, and the
147-
* other is for the null version that applies to all versions. It returns null or empty array if nothing is found.
148-
* In case of retrieval error, a RetrievalError is thrown containing the errors.
153+
* Retrieves a saved search from the database.
154+
*
155+
* @param m Mediator instance
156+
* @param clid The client id
157+
* @param searchName name of the saved search
158+
* @param entit Name of the entity
159+
* @param version Entity version the search should run on
160+
*
161+
* The returned JsonNode can be an ObjectNode, or an ArrayNode
162+
* containing zero or more documents. If there are more than one
163+
* documents, only one of them is for the requested version, and
164+
* the other is for the null version that applies to all
165+
* versions. It returns null or empty array if nothing is found.
166+
* In case of retrieval error, a RetrievalError is thrown
167+
* containing the errors.
149168
*/
150169
public JsonNode retrieveFromDB(Mediator m,
151170
ClientIdentification clid,
@@ -181,21 +200,29 @@ public JsonNode retrieveFromDB(Mediator m,
181200
findRequest.setQuery(q);
182201
findRequest.setProjection(FieldProjection.ALL);
183202
Response response=m.find(findRequest);
184-
if(response.getErrors()!=null&&response.getErrors().isEmpty())
203+
if(response.getErrors()!=null&&!response.getErrors().isEmpty())
185204
throw new RetrievalError(response.getErrors());
205+
LOGGER.debug("Found {}",response.getEntityData());
186206
return response.getEntityData();
187207
}
188208

189209
/**
190210
* Either loads the saved search from the db, or from the
191211
* cache.
212+
*
213+
* @param m Mediator instance
214+
* @param clid The client id
215+
* @param searchName name of the saved search
216+
* @param entit Name of the entity
217+
* @param version Entity version the search should run on
218+
*
219+
* @return The saved search document, or null if not found
192220
*/
193221
public JsonNode getSavedSearch(Mediator m,
194222
ClientIdentification clid,
195223
String searchName,
196224
String entity,
197-
String version,
198-
Map<String,String> parameterValues) {
225+
String version) {
199226
LOGGER.debug("Loading {}:{}:{}",searchName,entity,version);
200227
ObjectNode doc=null;
201228
String loadVersion;
@@ -221,9 +248,11 @@ public JsonNode getSavedSearch(Mediator m,
221248
LOGGER.debug("Loading {} from DB",searchName);
222249
JsonNode node=retrieveFromDB(m,clid,searchName,entity,loadVersion);
223250
if(node instanceof ObjectNode) {
251+
LOGGER.debug("Loaded a single search");
224252
doc=(ObjectNode)node;
225253
store(doc);
226254
} else if(node instanceof ArrayNode) {
255+
LOGGER.debug("Loaded an array of searches");
227256
store((ArrayNode)node);
228257
doc=findDocForVersion((ArrayNode)node,loadVersion);
229258
}
@@ -235,13 +264,16 @@ public JsonNode getSavedSearch(Mediator m,
235264
}
236265

237266
private ObjectNode findDocForVersion(ArrayNode node,String version) {
267+
LOGGER.debug("Searching {} in the array",version);
238268
ObjectNode ret=null;
239269
String matchedVersion=null;
240270
for(Iterator<JsonNode> itr=node.elements();itr.hasNext();) {
241271
JsonNode searchNode=itr.next();
242272
if(searchNode instanceof ObjectNode) {
243273
JsonNode versionsNode=searchNode.get("versions");
244-
if(versionsNode instanceof ArrayNode) {
274+
LOGGER.debug("Versions in search:{}",versionsNode);
275+
if(versionsNode instanceof ArrayNode && versionsNode.size()>0) {
276+
LOGGER.debug("Looking up in versions array");
245277
for(Iterator<JsonNode> vitr=versionsNode.elements();vitr.hasNext();) {
246278
JsonNode versionNode=vitr.next();
247279
if(versionNode instanceof NullNode) {
@@ -257,11 +289,16 @@ private ObjectNode findDocForVersion(ArrayNode node,String version) {
257289
}
258290
}
259291
}
260-
} else if(version==null) {
261-
ret=(ObjectNode)searchNode;
292+
} else {
293+
if(ret==null) {
294+
ret=(ObjectNode)searchNode;
295+
matchedVersion=null;
296+
}
262297
}
298+
LOGGER.debug("Current best match after {}:{}",searchNode,ret);
263299
}
264300
}
301+
LOGGER.debug("Best match for version {}:{}",version,ret);
265302
return ret;
266303
}
267304

@@ -283,6 +320,8 @@ private boolean betterMatch(String searchedVersion,String newVersion,String matc
283320
// if newVersion is a longer prefix of searchVersion than matchedVersion is, then it is a better match
284321
int newMatchingPrefix=getMatchingPrefix(searchedVersion,newVersion);
285322
int oldMatchingPrefix=getMatchingPrefix(searchedVersion,matchedVersion);
323+
LOGGER.debug("Comparing to {}: {}: prefix={} {}: prefix={}",searchedVersion,newVersion,newMatchingPrefix,
324+
matchedVersion,oldMatchingPrefix);
286325
return newMatchingPrefix>oldMatchingPrefix;
287326
}
288327
}
@@ -321,18 +360,22 @@ private synchronized void store(ObjectNode doc) {
321360
String name=doc.get("name").asText();
322361
String entity=doc.get("entity").asText();
323362
ArrayNode arr=(ArrayNode)doc.get("versions");
363+
Key key=null;
324364
if(arr!=null) {
325365
for(Iterator<JsonNode> itr=arr.elements();itr.hasNext();) {
326366
JsonNode version=itr.next();
327367
if(version instanceof NullNode) {
328-
cache.put(new Key(name,entity,null),doc);
368+
key=new Key(name,entity,null);
329369
} else {
330-
cache.put(new Key(name,entity,version.asText()),doc);
370+
key=new Key(name,entity,version.asText());
331371
}
332372
}
333-
} else {
334-
cache.put(new Key(name,entity,null),doc);
335373
}
374+
if(key==null) {
375+
key=new Key(name,entity,null);
376+
}
377+
LOGGER.debug("Adding {} to cache",key);
378+
cache.put(key,doc);
336379
}
337380

338381
private synchronized void store(ArrayNode arr) {

0 commit comments

Comments
 (0)