#Document
public class Product extends Item {
private Date onlineDate;
private Date offlineDate;
}
Data
Product {
onlineDate : some date
offlineDate : null
}
below query return 0 hits
query.addCriteria(Criteria.where("onlineDate").lte(date).and("offlineDate").gte(date))
but below query returns results
query.addCriteria(Criteria.where("onlineDate").lte(date))
is this something that I am not allowed to compare with null date.
You basically need to include conditions to accept the null in the results. Right now you are asking "is greater than" a supplied date, and null is not "greater than", therefore it is exluded.
This means adding an $or condition to test for both possible conditions on that field:
Criteria criteria = Criteria.where("onlinedate").lte(date).orOperator(
Criteria.where("offlinedate").gte(date),
Criteria.where("offlinedate").is(null)
);
Query query = new Query().addCriteria(criteria);
System.out.println(query.getQueryObject());
Which would give you:
{
"onlinedate" : { "$lte" : date } ,
"$or" : [
{ "offlinedate" : { "$gte" : date } } ,
{ "offlinedate" : null }
]
}
That is the correct translation of altering your query to allow the null value, but it does make me think that your basic thinking on the query is incorrect since this would return results that basically say "still online". That may be what you want, but then again you might have been intending to ask something different.
If in fact you were looking for "all" document that did not have the supplied date value "betweeen" the two values in the document, then you would instead "invert" the range expressions to match documents "between" and then "invert" the result with $nor instead:
Criteria criteria = new Criteria().norOperator(
Criteria.where("onlinedate").gte(date)
.and("offlinedate").lte(date)
);
Query query = new Query().addCriteria(criteria);
System.out.println(query.getQueryObject());
Which results in a query like this:
{
"$nor" : [
{
"onlinedate" : { "$gte" : date },
"offlinedate" : { "$lte" : date }
}
]
}
Which is returning results both "before" and "after" the range between the two date properties, or where the "offline" date was null and the range is not "closed".
It depends on which set of results you actually want, being either that you include the null values to find the data that is "still open" or you just "exclude the range" to find the data that falls outside of the range instead.
Related
I have entity of this type saved to elasticSearch:
class Value {
private Integer priority;
private Date dueDate;
}
For priority possible values are 1 and 2(I know that enum should be used but let's not concentrate on that).
Now using elastic search I want to get the following result:
How many values I have with priority 1 that have dueDate today or in the past
How many values I have with priority 1 that have dueDate null or in the future
How many values I have with priority 2 that have dueDate today or in the past
How many values I have with priority 2 that have dueDate null or in the future
What I tried doing is the following:
SearchRequest request = new SearchRequest.Builder()
.index(MY_INDEX)
.query(getQuery(someKeys))
.aggregations(ELASTIC_AGG_PRIORITY, Aggregation.of(a ->
a.terms(terms -> terms.field(ELASTIC_AGG_PRIORITY).missing(""))))
.build();
This will get me aggregations that are grouped by priority and will know the number of values with priority 1 and with priority 2. But I need to add nested aggregations and here is my issue. How to separate these buckets that I am getting into one with dueDate in past or today and with dueDate in future or null. Additionally I am having issue with writing the range query:
public static Query getRangeQuery(String field) {
return QueryBuilders.range().field(field).lte(JsonData.of(new Date())).build()._toQuery();
}
I am not able to pass the year and I don't know how to handle the nulls when I need to select everything in the future and null values. I tried the following but not getting the result I need:
SearchRequest request = new SearchRequest.Builder()
.index(MY_INDEX)
.query(getQuery(someKeys))
.aggregations(ELASTIC_AGG_ASSIGNEE, Aggregation.of(a ->
a.terms(terms -> terms.field(ELASTIC_AGG_ASSIGNEE).missing(""))))
.aggregations(ELASTIC_AGG_PRIORITY, Aggregation.of(a ->
a.terms(terms -> terms.field(ELASTIC_AGG_PRIORITY).missing(""))
.aggregations(ELASTIC_AGG_PRIORITY, Aggregation.of(filterAgg -> filterAgg
.filter(QueryUtil.getRangeQuery("dueDate"))))
)
).build();
where QueryUtil.getRangeQuery( is the method described above.
So to summarise my question is how to get the 4 above mentioned results?
You're close but you need to implement the "date_range_aggregation" to get the results you are looking for.
Your terms aggregation is ok, all you need to do is add the date_range_aggregation as a subaggregation to the terms aggregation.
For your "null" values, set a "missing" value as something in the past.That way you can pick that up as a third bucket.
Sample[in json]:
{
"aggs": {
"range": {
"date_range": {
"field": "date",
"missing": "1976/11/30",
"ranges": [
{
"key": "Oldest",
"to" : "1976/11/30"
},
{
"key": "Past",
"from" : "1976/11/30"
"to": "now"
},
{
"key": "Future",
"from": "now"
}
]
}
}
}
}
Let me know if this works for you.
I have a more complex aggregation query when I try to bind my result using projections, I need to check that Array contains a date (day).
MongoDB query works fine
{ "$project" : { "days" : { "day" : "$_id" , "signatureVerified" : {"$in": ["$_id", "$signatureVerified"] } }}}
spring-data-code
project().and(DAYS).nested(bind(DAY, _ID)and(Field field));
I've tried preparing a match array checker using an ArrayOperators class but there is no possibility to return a Field object. Any ideas on how it can be achieved?
ArrayOperators.arrayOf(SIGNATURE_VERIFIED).containsValue(Fields.field(_ID));
I have a node called quotes in Firebase. I'm facing issues while fetching data in Android for a particular range. I want to fetch 3 continues quotes id starting from 2. Here is my query database:
"quotes" : {
"-L75elQJaD3EYPsd4oWS" : {
"authorName" : "Hellen v",
"famousQuote" : "When one door of happiness closes, another opens; but often we look so long at the closed door that we do not see the one which has been opened for us.",
"id" : "1",
"uploadedBy" : "Admin"
},
"-L7GOvDNI-o_H8RvNwoN" : {
"authorName" : "Rocky Balboa",
"famousQuote" : "It's not about how hard you can hit; it's about how hard you can get hit and keep moving forward.",
"id" : "2",
"uploadedBy" : "Admin"
},
"-L7GP9oBv5NR1T6HlDd4" : {
"authorName" : "African proverb",
"famousQuote" : "If you want to go fast, go alone. If you want to go far, go together.",
"id" : "3",
"uploadedBy" : "Admin"
},
"-L7GPjM1F3_7Orcz0Q1q" : {
"authorName" : "A.P.J Abdul Kalam",
"famousQuote" : "Don’t take rest after your first victory because if you fail in second, more lips are waiting to say that your first victory was just luck.",
"id" : "4",
"uploadedBy" : "Admin"
},
Below is the rule which I'm using for quotes
"quotes": {
".indexOn": ".value"
}
How can I get quotes which has id 2,3 and 4?
If you have more than 4 records in your database, to solve this, you can use a query in which you should combine startAt() and endAt() methods to limit both ends of your query like this:
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
Query query = rootRef.child("quotes").orderByChild("id").startAt("2").endAt("4");
query.addListenerForSingleValueEvent(/* ... */);
Please see here more informations about Firebase Query's startAt() method:
Create a query constrained to only return child nodes with a value greater than or equal to the given value, using the given orderBy directive or priority as default.
And here are more informations about Firebase Query's endAt() method:
Create a query constrained to only return child nodes with a value less than or equal to the given value, using the given orderBy directive or priority as default.
Edit: According to your comment, if you only want the items that have the id property set to 2, 3 and 4, you should use nested queries like this:
Query queryTwo = rootRef.child("quotes").orderByChild("id").equalsTo("2");
queryTwo.addListenerForSingleValueEvent(
List<Item> list = new ArrayList();
list.add(itemTwo);
Query queryThree = rootRef.child("quotes").orderByChild("id").equalsTo("3");
queryThree.addListenerForSingleValueEvent(
list.add(itemThree);
Query queryFour = rootRef.child("quotes").orderByChild("id").equalsTo("4");
queryFour.addListenerForSingleValueEvent(
list.add(itemFour);
//Do what you need to do with the list that contains three items
);
);
);
I have few json document with the following format :-
_source: {
userId: "A1A1",
customerId: "C1",
component: "comp_1",
timestamp: 1408986553,
}
I want to query the document based on the following :-
(( userId == currentUserId) OR ( customerId== currentCustomerId) OR (currentRole ==ADMIN) ) AND component= currentComponent)
I tried using the SearchSourceBuilder and QueryBuilders.matchQuery, but I wasnt able to put multiple sub queries with AND and OR operators.
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.matchQuery("userId",userId)).sort("timestamp", SortOrder.DESC).size(count);
How we query elasticsearch using OR and AND operators?
I think in this case the Bool query is the best shot.
Something like :
{
"bool" : {
"must" : { "term" : { "component" : "comp_1" } },
"should" : [
{ "term" : { "userId" : "A1A1" } },
{ "term" : { "customerId" : "C1" } },
{ "term" : { "currentRole" : "ADMIN" } }
],
"minimum_should_match" : 1
}
}
Which gives in Java:
QueryBuilder qb = QueryBuilders
.boolQuery()
.must(termQuery("component", currentComponent))
.should(termQuery("userId", currentUserId))
.should(termQuery("customerId", currentCustomerId))
.should(termQuery("currentRole", ADMIN))
.minimumNumberShouldMatch(1)
The must parts are ANDs, the should parts are more or less ORs, except that you can specify a minimum number of shoulds to match (using minimum_should_match), this minimum being 1 by default I think (but you could set it to 0, meaning that a document matching no should condition would be returned as well).
If you want to do more complex queries involving nested ANDs and ORs, simply nest other bool queries inside must or should parts.
Also, as you're looking for exact values (ids and so on), maybe you can use term queries instead of match queries, which spare you the analysis phase (if those fields are analyzed at all, which doesn't necessarily make sense for ids). If they are analyzed, you still can do that, but only if you know exactly how your terms are stored (standard analyzer stores them lower cased for instance).
If you use a query_string query, your ANDs and ORs will be interpreted as such by the Lucene library.
This allows you to search for
(currentUserId OR currentCustomerId) AND currentComponent
for instance. By default, the values will be searched for in all fields.
In a project I'm working on, at one point I read a query to mongodb from a string. I've been using com.mongodb.util.JSON.parse(querystring) to read the query, which worked fine until I started reading queries that contained operators like $max and $min. At that point, rather than using mongodb's $max operator, the parser instead creates a "$max" field. For instance,
the input string:
{ $query : { state : "AL" } , $max : { pop : 9058 } }
is parsed to the DBObject:
{ "$query" : { "state" : "AL"} , "$max" : { "pop" : 9058}}
When I then look for a DBCursor with that query document, I get a cursor of size 0 (no matching document found in the databse), presumably because there are no documents with "$query" or "$max" fields.
Is there something I can use besides JSON.parse()? I'm not averse to writing my own function for it, but how can I get a DBObject that recognizes the $ operators as operators and not fields?
Any advice would be appreciated!
The following code snippet using query modification operator $max seems to work fine.
/* {$query:{state:"AL"}, "$max":{pop:10000}}*/
String s = "{$query:{state:\"AL\"}, \"$max\":{pop:10000}}";
DBObject dbObject = (DBObject) JSON.parse(s);
System.out.println("\nFind all: ");
DBCursor cursor = collection.find(dbObject);
try {
while (cursor.hasNext()) {
DBObject cur = cursor.next();
System.out.println(cur);
}
} finally {
cursor.close();
}
Make sure you have specified index on pop.
db.zips.getIndexes()
[
{
"v" : 1,
"key" : {
"_id" : 1
},
"ns" : "test.zips",
"name" : "id"
},
{
"v" : 1,
"key" : {
"pop" : 1
},
"ns" : "test.zips",
"name" : "pop_1"
}
]
See the following link for detail.
http://docs.mongodb.org/manual/reference/operator/max/
Just in case you are interested in using aggregation operators $max or $min, the following link provide details and sample code.
http://docs.mongodb.org/ecosystem/tutorial/use-aggregation-framework-with-java-driver/
So it turns out the DBObject as given up there worked out fine. It returns a cursor with a size of 0, true, but the DBCursor's length is actually the thing I was looking for. (Previously, I had been checking whether the cursor's size was 0, and if it was, returning null.)
I'm not quite sure what the difference between size and length is in a DBCursor (the difference between size and count is apparent, but I'm not sure what length is supposed to be), but it works now. In the case above, size and count were both 0 but length was the desired number.