I have a result of a db query in java.sql.ResultSet that needs to be converted to hierarchical data structure. It looks a bit like so:
name|version|pname|code|count
n1|1.1|p1|c1|3
n1|1.1|p1|c2|2
n1|1.1|p2|c1|1
n1|1.2|p1|c1|0
n2|1.0|p1|c1|5
I need that converted into a hierarchical data structure:
N1
+ 1.1
+ p1
+ c1(3)
+ c2(2)
+ p2
+ c1(1)
+ 1.2
+ p1
+ c1(0)
N2
+ 1.0
+ p1
+ c1(5)
So my data structure can look something like this
Name {
String name
List<Version> versions
}
Version {
String version
List<PName> pnames
}
PName {
String pName
List<CodeCount> codeCounts
}
CodeCount {
String code
Integer count
}
Anyone have suggestions/code snippets on the best way to do this?
There are a few ways, and how you do it depends on how robust your solution needs to be.
One would be to just write a couple of objects that had the attributes in the database. Then you could get the result set, and iterate over it, creating a new object each time the key field (for example, "name") changed, and adding it to a list of that object. Then you'd set the attributes appropriately. That is the "quick and dirty" solution.
A slightly more robust way would be to use something like Hibernate to do the mapping.
If you do decide to do that, I would also suggest redoing your tables so that they accurately reflect your object structure. It may not be needed if you just want a fast solution. But if you are seeking a robust solution for commercial or enterprise software, it's probably a good idea.
Related
When using MarkLogic 7 with the Java Client API, I am currently in the process of moving my query definitions from a StringQueryDefinition to a StructuredQueryDefinition allowing me to construct and manipulate the query programmatically.
With the string query I was able to use the sort operator with sort:{my-sort-order} successfully which in turn referred to names of predefined orders as specified in the query options (https://docs.marklogic.com/guide/search-dev/query-options#id_30002), but cannot find the API docs a related method allowing me to specify the sort order with the structured query builder.
What is the recommended way on how to specify the sort order when using a StructuredQueryDefinition?
UPDATE
Based on Erik's proposal, this is how the code snippet currently looks like, but it doesn't solve the problem, since the operator-state has to go as child on the query element and not as child on the search element:
RawStructuredQueryDefinition queryDef = qb.build(qb.and(qb.term(..), qb.rangeConstraint(...)));
String sorting = "<operator-state><operator-name>sort</operator-name><state-name>" + orderBy + "</state-name></operator-state>";
String combi = "<search xmlns='http://marklogic.com/appservices/search'>" + queryDef.toString() + sorting + "</search>";
RawCombinedQueryDefinition combinedQueryDef = queryManager.newRawCombinedQueryDefinition(new StringHandle(combi), OPTIONS);
// DOES NOT WORK, but will lead to MarkLogicIOException "Could not construct search results: parser error"
// Possible solution is to modify the queryDef DOM your own
We used the <options> tag of the <search> element sent to the server.
This requires an ugly string concatenation, but it does not require anything server-side other than an index on the sort property.
For the format of the XML, see this link or the search.xsd :
http://docs.marklogic.com/guide/rest-dev/appendixb#id_33716
The idea is to generate an XML like this one :
<search xmlns='http://marklogic.com/appservices/search'>
<query xmlns="http://marklogic.com/appservices/search">
<collection-query>
<uri>a_collection</uri>
</collection-query>
</query>
<options>
<sort-order type="xs:dateTime" direction="descending">
<json-property>a_field</json-property>
</sort-order>
</options>
</search>
We did it like this :
First, build your StructuredQueryDefinition
StructuredQueryDefinition queryDef = sb.collection("my_collection);
Build the <options> element seen above
String xmlSortNode =
" <options>" +
" <sort-order type=\"xs:dateTime\" direction=\"descending\">" +
" <json-property>a_field</json-property>" +
" </sort-order>" +
" </options>";
Build the full search element
String searchXml="<search xmlns='http://marklogic.com/appservices/search'>"
+ queryDef.serialize()
+ xmlSortNode
+ "</search>";
Execute the query
queryManager.newRawCombinedQueryDefinition(new StringHandle(searchXml),"all");
At present, StructuredQueryBuilder only builds criteria. You would have to refer to persisted query options with the sort order or send a combined search with both the criteria and options.
We’re tracking this on GitHub. Please feel free to add information there.
This is the solution I ended up with, basically I use the programmatic way on building a query with StructuredQueryBuilder and then inject the operator-state, referring to a pre-defined sort order in my options (OPTIONS_ALL):
org.jdom2.Document doc = new SAXBuilder().build(new StringReader(queryDef.serialize()));
if (!StringUtils.isEmpty(orderBy)) {
Element operatorState = new Element("operator-state", NAMESPACE_SEARCH);
operatorState.addContent(new Element("operator-name", NAMESPACE_SEARCH).setText("sort"));
operatorState.addContent(new Element("state-name", NAMESPACE_SEARCH).setText(orderBy));
doc.getRootElement().addContent(operatorState);
}
RawStructuredQueryDefinition rawQueryDef =
queryManager.newRawStructuredQueryDefinition(new JDOMHandle(doc), OPTIONS_ALL);
// ~~
SearchHandle resultsHandle = new SearchHandle();
queryManager.search(rawQueryDef, resultsHandle, start);
With
public static final Namespace NAMESPACE_SEARCH = Namespace.getNamespace("http://marklogic.com/appservices/search");
Sort order is considered an operator. Take a look at operator-state and you'll find an example of sorting in a structured query.
Quick question... so in Hybris, I have a query similar to this:
"SELECT {CPR:pk} FROM {CategoryProductRelation as CPR}, ...."
Basically, I need to extract the Product Code and Category Code from Java which I think are available as source / target respectively but my question is, just like there's ProductModel, CategoryModel, etc. is there anything like that for CategoryProductRelation?, probably something like a generic RelationModel to simply extract source / target and go from there?
You'll need to JOIN in the entities like this
SELECT {CPR:pk}, {c.code} FROM {CategoryProductRelation as CPR
JOIN Category AS c on {CPR.source} = {c.PK} } WHERE ...
Also, you can do that in the Service Layer by simply calling your query and accessing the properties right from the relation type:
..
CategoryProductRelationModel model = result.get(0)
String categoryCode = ((CategoryModel)model.getSource()).getCode()
Depending on your amount of data, this could be pretty ineffecient.
I have a MemoryIndex created like this.
```
Version version = Version.LUCENE_47;
Analyzer analyzer = new SimpleAnalyzer(version);
MemoryIndex index = new MemoryIndex();
index.addField("text", "Readings about Salmons and other select Alaska fishing Manuals", analyzer);
```
Then, I have a query containing a number of sub-query which is created from a set of concepts (including id, name, description). Right now I have to loop for every concept, generate a query, and finally check if it is matched => if it is, I append it to a string which is used to store matches
```
for (Concept concept : concepts) {
Query query = queryGenerator.getQueryForConcept(concept);
float score = query != null ? index.search(query) : 0.0f;
if (score > 0) {
matches.append(sep + concept.getId() + "|" + concept.getName());
sep = "|";
}
}```
The problem is: the number of concepts is growing larger and larger, which affects the performance. Is there anyway that I can create a one single query and compare to a document, and find out what concepts have been hit the document?
I tried using BooleanQuery as a whole, then add all subquery which derrived from concept into it. It matches but don't know which subquery hits, and even if we do, how do we put the details like "id", and "name" of a concept into it?
Much appreciate all answers
In my (java) Controller in a Play2 project I'm saving some data to an object.
So entity here is an instance of a Model subclass.
I do stuff like this
log.debug("Saving title=" + title + ", tags=" + tags);
entity.title = title;
entity.tags = tags;
entity.save();
// verify:
ModelClass m = ModelClass.find.byId(entity.id);
log.debug("Saved title=" + m.title + ", tags=" + m.tags);
Where title is a String and tags is a List<String>. The debug log says
Saving title=foo, tags=[bar, quux]
Saved title=foo, tags=null
So data is coming in, I'm not getting any warnings, but the list of strings is just lost somewhere along the way. I'm just using an in-memory h2 db, maybe it works when I'm really persisting it, but... what's up with this?
Edit: The generated SQL create syntax doesn't contain "tags" at all. So there's obviously something wrong with that.
Edit: see How to persist a property of type List<String> in JPA?
In JPA you must declare a List as #ElementCollection for it to be persisted. It seems that EBean do not support this feature.
One way to do it should be to declare your List tags as #Transient (ie. not persisted) and have methods to manage it while keeping up to date a simple String that contains your tags comma separated. That would be this String that gets persisted in a single column.
I am new to hibernate and still learning the basics. I'd appreciate if someone can point me in the right direction.
I have a class:
Destination
id
name
longitude
latitude
I can read destinations based on id with something like this:
List result = session.createQuery("from Destination as d where d.id=2").list();
However, I want to read destinations from database using name. I can perhaps write something like this as a query:
String name; // name set somewhere else, say a function argument
List result = session.createQuery("from Destination as d where d.name LIKE %"+name).list();
I believe this will yield all destinations with names similar to (variable) name.
Is there something inbuilt in hibernate for such use cases or is there a better way to handle this ?
EDIT:
One thing that follows from my thought process is: name column on destination db table will have an index setup. Can I map this index in some way to the Destination class using hibernate ?
You could build your query by concatenating strings. A more elegant solution would be to use the Hibernate Criteria API.
You query would then look something like:
List result = session.createCriteria(Destination.class)
.add(Restrictions.like("name", "%" + name)
.list();