How to combine multiple criteria in OR query dynamically in Play Morphia - playframework-1.x

I am trying to use a kind of builder pattern to build an OR query using multiple criteria depending upon the scenario. An example is
public class Stylist extends Model {
public String firstName;
public String lastName;
public String status;
...
}
I would like to search Stylist collection if the first name or last name matches a given string and also status matches another string. I am writing the query as follows:
MorphiaQuery query = Stylist.q();
if (some condition) {
query.or(query.criteria("status").equal("PendingApproval"), query.criteria("status").equal(EntityStatus.ACTIVE));
}
if (some other condition as well) {
query.or(query.criteria("firstName").containsIgnoreCase(name), query.criteria("lastName").containsIgnoreCase(name));
}
When both the conditions are met, I see that query contains only the criteria related to firstName and lastName i.e. different OR criteria are not added/appended but overwritten. It's quite different from filter criteria where all the different filter conditions are appended and you can easily build queries containing multiple AND conditions.
I can solve the problem by putting my conditions differently and building my queries differently but doesn't seem to be an elegant way. Am I doing something wrong ?
I am using Play! Framework 1.2.4 and Play Morphia module version 1.2.5a
Update
To put it more clearly, I would like to AND multiple OR queries. Concretely, in the above mentioned scenario, I would like to
I would like to search for Stylists where :
firstName or lastName contains supplied name AND
status equals ACTIVE or PENDING_APPROVAL.
I have been able to construct the query directly on Mongo shell through :
db.stylists.find({$and: [{$or : [{status: "PENDING_APPROVAL"}, {status : "ACTIVE"}]},{$or : [{firstName : { "$regex" : "test" , "$options" : "i"}}, {lastName : { "$regex" : "test" , "$options" : "i"}}]}] }).pretty();
But have not able to achieve the same through Query API methods. Here is my attempt :
Query<Stylist> query = MorphiaPlugin.ds().find(Stylist.class);
CriteriaContainer or3 = query.or(query.criteria("firstName").containsIgnoreCase(name), query.criteria("lastName").containsIgnoreCase(name));
CriteriaContainer or4 = query.or(query.criteria("status").equal("PENDING_APPROVAL"), query.criteria("status").equal("ACTIVE"));
query.and(or3, or4);
query.toString() results in following output : { "$or" : [ { "status" : "PENDING_APPROVAL"} , { "status" : "ACTIVE"}]}
Not sure, where am I missing ?

I guess there could be 2 ways to handle your case:
first, use List<Criteria>
MorphiaQuery query = Stylist.q();
List<Criteria> l = new ArrayList<Criteria>()
if (some condition) {
l.add(query.criteria("status").equals("PendingApproval");
l.add(query.criteria("status").equal(EntityStatus.ACTIVE));
}
if (some other conditional as well) {
l.add(query.criteria("firstName").containsIgnoreCase(name));
l.add(query.criteria("lastName").containsIgnoreCase(name));
}
query.or(l.toArray());
Second, use CritieriaContainer
MorphiaQuery query = Stylist.q();
CriteriaContainer cc = null;
if (some condition) {
cc = query.or(query.criteria("status").equal("PendingApproval"), query.criteria("status").equal(EntityStatus.ACTIVE));
}
if (some other condition) {
if (null != cc) query.or(cc, query.criteria("firstName").containsIgnoreCase(name), query.criteria("lastName").containsIgnoreCase(name));
else query.or(query.criteria("firstName").containsIgnoreCase(name), query.criteria("lastName").containsIgnoreCase(name));
}

Related

How to indent a region by more than one indent?

I have the following grammar:
Department:
'Department:' name = ID
'Company:' companyName = STRING
'Persons:' persons += Person+
;
Person:
firstName = STRING
':'
surname = STRING
':'
address = STRING
;
And I would like to have formatting like this:
Department: Department1
Company: "Company1"
Persons:
"Person1FirstName" : "Person1LastName" : "Person1Address"
"Person2FirstName" : "Person2LastName" : "Person2Address"
But when I implement the formatter code I cannot seem to be able to use indent twice since the white space formatting gets merged instead of getting concatinated.
class TestsFormatter extends AbstractFormatter2
{
def dispatch void format(Department department, extension IFormattableDocument document)
{
department.interior[indent]
department.regionFor.keyword("Department:").prepend[setNewLines(2)]
department.regionFor.keyword("Department:").append[oneSpace]
department.regionFor.keyword("Company:").prepend[newLine]
department.regionFor.keyword("Company:").append[oneSpace]
department.regionFor.keyword("Persons:").prepend[newLine]
for (Person person : department.persons)
{
person.format
}
}
def dispatch void format(Person person, extension IFormattableDocument document)
{
person.prepend[indent]
person.prepend[indent]
}
}
I found out that there is an example with "increaseIndentation/decreaseIndentation" in the documentation of AbstractFormatter2. But when I try to use them, it cannot resolve them.
you can achive this by using a custom replacer similar as described in How to define different indentation levels in the same document with Xtext formatter

Testing criteria creation in a Grails service

I have a Grails service that creates a criteria query with optional parameters like this:
List<Car> search(String make = "%", String model = "%", Integer year = null) {
def c = Car.createCriteria()
return c.list() {
if(make) {
like("make", make)
}
if(model) {
like("model", model)
}
if(year) {
eq("year", year)
}
}
}
(Also, is this the idiomatic way to do this in grails? I'm quite new to the framework and I'm trying to find the right way to do things)
I'd like to test that the proper criteria filters are set according to the values of the parameters of the search method but I'm having no success.
I tried some variations of this:
#TestFor(CarService)
#Mock(Car)
class CarServiceSpec extends Specification {
def car = Mock(Car)
void "empty filters"() {
when: service.search()
then:
with(car.createCriteria()) {
0 * like(*_)
0 * eq(*_)
}
}
}
But I can't seem to find a way to do assertions about the interactions between the CarService and the criteria object.
What am I missing?
The Grails Where query instead of the Criteria query seems to be better choice for an idiomatic way to do this in Grails:
Gorm Where Query

Neo4j Spring Data Query Builder

Is there a way of dynamically building a cypher query using spring data neo4j?
I have a cypher query that filters my entities similar to this one:
#Query("MATCH (n:Product) WHERE n.name IN {0} return n")
findProductsWithNames(List<String> names);
#Query("MATCH (n:Product) return n")
findProductsWithNames();
When the names list is empty or null i just want to return all products. Therefore my service impl. checks the names array and calls the correct repository method. The given example is looks clean but it really gets ugly once the cypher statements are more complex and the code starts to repeat itself.
You can create your own dynamic Cypher queries and use Neo4jOperations to execute them. Here is it an example (with a query different from your OP) that I think can ilustrate how to do that:
#Autowired
Neo4jOperations template;
public User findBySocialUser(String providerId, String providerUserId) {
String query = "MATCH (n:SocialUser{providerId:{providerId}, providerUserId:{providerUserId}})<-[:HAS]-(user) RETURN user";
final Map<String, Object> paramsMap = ImmutableMap.<String, Object>builder().
put("providerId", providerId).
put("providerUserId", providerUserId).
build();
Map<String, Object> result = template.query(query, paramsMap).singleOrNull();
return (result == null) ? null : (User) template.getDefaultConverter().convert(result.get("user"), User.class);
}
Hope it helps
Handling paging is also possible this way:
#Test
#SuppressWarnings("unchecked")
public void testQueryBuilding() {
String query = "MATCH (n:Product) return n";
Result<Map<String, Object>> result = neo4jTemplate.query(query, Collections.emptyMap());
for (Map<String, Object> r : result.slice(1, 3)) {
Product product = (Product) neo4jTemplate.getDefaultConverter().convert(r.get("n"), Product.class);
System.out.println(product.getUuid());
}
}

How to find matches that occur within a specified string with regex?

I have a unique situation where I need to query a mongo database to find the names of people who occur in a body of text. The query must specify the body of text and find records with values that occur in the body of text. How can I do this with a regular expression?
I need to write a query where this would match:
/Jonathan is a handsome guy/.test('Jonathan')
The problem is that the text inside "test" is the value of a mongo field, so this query must be written such that the body of text is provided as input, and it matches on names that occur within (are substrings of) the body of text.
A more concrete example:
db.test.find();
{ "_id" : ObjectId("547e9b79f2b519cd1657b21e"), "name" : "Jonathan" }
{ "_id" : ObjectId("547e9b88f2b519cd1657b21f"), "name" : "Sandy" }
db.test.find({name: { $in: [/Jonathan has the best queries/]} } );
I need to construct a query that would return "Jonathan" when provided the input "Jonathan has the best queries"
This $where may do the trick, though can be very slow:
db.test.find({$where: function() {
var mystr = '/Jonathan has the best queries/';
var patt = new RegExp(this.name);
if (patt.test(mystr)) return true;
return false;
}})

Why does my XML ASP.NET web service return results which repeats itself?

I have written an ASP.NET web service.
It looks like this:
WebServices.logic pLogic = new WebServices.logic();
WebServices.manager[] pManager = new PowerManager[1];
pManager[0] = new PowerManager();
pManager[0].CustomerId = "sjsjshd";
pManager[0].state = pLogic.getState("sasj");
return pManager[0];
The pManager class looks like this:
public string _CustomerId;
public int PowerStatus;
public List<ArrayList> _Power;
public string CustomerId
{
get
{
return _CustomerId;
}
set
{
_CustomerId = value;
}
}
public List<ArrayList> Power
{
get
{
return _Power;
}
set
{
_Power = value;
}
}
When I run it, I get a repetition of the results, like so:
<p>
<_CustomerId>sjsjshd</_CustomerId>
<pStatus>0</PowerStatus>
−
<_p>
−
<ArrayOfAnyType>
<anyType xsi:type="xsd:int">1</anyType>
</ArrayOfAnyType>
<ArrayOfAnyType/>
</_p>
<CustomerId>sjsjshd</CustomerId>
−
<p>
−
<ArrayOfAnyType>
<anyType xsi:type="xsd:int">1</anyType>
</ArrayOfAnyType>
<ArrayOfAnyType/>
</p>
</pManager>
However, there is no duplicate values stored (Eg. I store client name in a collection, but only once - count of 1). There are no duplicates stored when I call getState(). This method returns a collection and it contains one value, but the results in XML has a repetition of this.
How comes the results appear to repeat themselves? When running the system, I only get one error.
Thanks
OK, looks like your XML serialization is giving you all the public members of your PowerManager class. Based on the naming convention of starting with an underscore, those members should be private, like this:
private string _CustomerId;
private List<ArrayList> _Power;
You also state "When running the system, I only get one error." What error are you getting?