I got a warning from Atlas Mongo saying:
Do not use the $regex operator when using a case-insensitive index for
your query. The $regex implementation is not collation-aware and
cannot utilize case-insensitive indexes. Instead, we recommend Atlas
Search queries that use the $search aggregation pipeline stage.
Source: https://www.mongodb.com/docs/atlas/schema-suggestions/case-insensitive-regex/
In my Java code i'm using this:
import org.springframework.data.mongodb.core.query.Criteria;
Criteria criteria = new Criteria();
...
criteria.and("search").regex(data.getText(), "i"); // i means case-insensitive
...
return Query.query(criteria);
Obviously this is slow performance as I get a warning. How to I apply the collation?
What is the best performance approach for this so I remove the i option from the regex?
The best practice to query data with case-insensitive option is using text index and search operator instead of regex
Create index-case-insensitive for fields you want to query:
TextIndexDefinition textIndex = new TextIndexDefinitionBuilder()
.onField("title", 2F)
.onField("content")
.build();
MongoTemplate template = … // obtain MongoTemplate
template.indexOps(CookingRecipe.class).ensureIndex(textIndex);
Then use text index to query data:
TextCriteria criteria = TextCriteria.forDefaultLanguage()
.matchingAny("coffee", "cake");
Query query = TextQuery.queryText(criteria)
.sortByScore()
.with(new PageRequest(0, 5)); //top 5 documents matching “coffee” or “cake”
Kindly refer $text $search your Documents with Spring Data MongoDB for more information
Related
Hi I am trying to do query on elastic search by following the sql query and I want to implement same logic using Java API
select * from log , web where l.loghost = w.webhost and #datetime between '2016-05-20' AND '2016-05-25'
log and web are different types, and indices are set to logstash-log-* and logstash-web*, #timestamp format looks like "2016-05-20T17:14:01.037Z"
Now I have the following Java code but i don't know how to set between two dates ,so it does not return expected output
SearchResponse response = client.prepareSearch("logstash-log-*","logstash-web-*")
.setTypes("log","web")
.setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
.setFetchSource(new String[]{"*"}, null)
.setQuery(QueryBuilders.queryStringQuery("1.2.3.4").field("*_host"))// Query
.execute()
.actionGet();
Please guide I am new to Elastic search. Thanks in advance.
You need to combine a range query with your query_string query inside a bool/filter query:
QueryStringQueryBuilder qs = QueryBuilders.queryStringQuery("1.2.3.4").field("*_host");
RangeQueryBuilder range = QueryBuilders.rangeQuery("#timestamp")
.gte("2016-05-20T00:00:00.000Z")
.lte("2016-05-25T00:00:00.000Z");
and then
...
.setQuery(QueryBuilders.boolQuery().filter(qs).filter(range))
...
How to get all the document under array in mongodb java. My Database is as below. Want to retrieve all the data under array 198_168_1_134.
below is some of What i tried,
eventlist.find(new BasicDBObject("$match","192_168_10_17"))
eventlist.find(new BasicDBObject("$elemMatch","192_168_10_17"))
eventlist.find(null, new BasicDBObject("$192_168_10_17", 1))
You have two options:
using .find() with cherry-picking which document you have to have fetched.
using the aggregation framework by projecting the documents.
By using .find() , you can do:
db.collection.find({}, { 192_168_10_17 : 1 })
By using the aggregation framework, you can do:
db.collection.aggregate( { $project : { 192_168_10_17 : 1 } } )
which will fetch only the 192_168_10_17 document data.
Of course, in order to get this working in Java, you have to translate these queries to a corresponding chain of BasicDBObject instances.
By using mongo java driver you can do this by following query -
eventlist.find(new BasicDBObject(), new BasicDBObject("198_168_1_134", 1))
What seems almost natural in simple SQL is impossible in mongodb.
Given a simple document:
{
"total_units" : 100,
"purchased_unit" : 60
}
I would like to query the collection, using spring data Criteria class, where "total_units > purchased_units".
To my understanding it should be as trivial as any other condition.
Found nothing to support this on Spring api.
You can use the following pattern:
Criteria criteria = new Criteria() {
#Override
public DBObject getCriteriaObject() {
DBObject obj = new BasicDBObject();
obj.put("$where", "this.total_units > this.purchased_units");
return obj;
}
};
Query query = Query.query(criteria);
I don't think Spring Data API supports this yet but you may need to wrap the $where query in your Java native DbObject. Note, your query performance will be fairly compromised since it evaluates Javascript code on every record so combine with indexed queries if you can.
Native Mongodb query:
db.collection.find({ "$where": "this.total_units > this.purchased_units" });
Native Java query:
DBObject obj = new BasicDBObject();
obj.put( "$where", "this.total_units > this.purchased_units");
Some considerations you have to look at when using $where:
Do not use global variables.
$where evaluates JavaScript and cannot take advantage of indexes.
Therefore, query performance improves when you express your query
using the standard MongoDB operators (e.g., $gt, $in). In general, you
should use $where only when you can’t express your query using another
operator. If you must use $where, try to include at least one other
standard query operator to filter the result set. Using $where alone
requires a table scan. Using normal non-$where query statements
provides the following performance advantages:
MongoDB will evaluate non-$where components of query before $where
statements. If the non-$where statements match no documents, MongoDB
will not perform any query evaluation using $where. The non-$where
query statements may use an index.
As far as I know you can't do
query.addCriteria(Criteria.where("total_units").gt("purchased_units"));
but would go with your suggestion to create an additional computed field say computed_units that is the difference between total_units and purchased_units which you can then query as:
Query query = new Query();
query.addCriteria(Criteria.where("computed_units").gt(0));
mongoOperation.find(query, CustomClass.class);
Thanks #Andrew Onischenko for the historic good answer.
On more recent version of spring-data-mongodb (ex. 2.1.9.RELEASE), I had to write the same pattern like below:
import org.bson.Document;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
// (...)
Criteria criteria = new Criteria() {
#Override
public Document getCriteriaObject() {
Document doc = new Document();
doc.put("$where", "this.total_units > this.purchased_units");
return doc;
}
};
Query query = Query.query(criteria);
One way is this:
Criteria c = Criteria.where("total_units").gt("$purchased_unit");
AggregationOperation matchOperation = Aggregation.match(c);
Aggregation aggregation = Aggregation.newAggregation(matchOperation);
mongoTemplate.aggregate(aggregation, "collectionNameInStringOnly", ReturnTypeEntity.class);
Remember to put collection name in string so as to match the spellings of fields mentioned in criteria with fields in database collection.
Currently I am using java to connect to MONGODB,
I want to write this sql query in mongodb using java driver:
select * from tableA where name like("%ab%")
is their any solution to perform the same task through java,
the query in mongodb is very simple i know, the query is
db.collection.find({name:/ab/})
but how to perform same task in java
Current I am using pattern matching to perform the task and code is
DBObject A = QueryBuilder.start("name").is(Pattern.compile("ab",
Pattern.CASE_INSENSITIVE)).get();
but it makes query very slow I think , does a solution exist that does not use pattern matching?
Can use Regular Expressions. Take a look at the following:
http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-RegularExpressions
Make sure you understand the potential performance impacts!
DBObject A = QueryBuilder.start("name").is(Pattern.compile("ab",
Pattern.CASE_INSENSITIVE)).get();
I think this is one of the possible solution, you need to create index to achieve those.
Why do you fear the regular expressions? Once the expression is compiled they are very fast, and if the expression is "ab" the result is similar to a function that search a substring in a string.
However to do what you need you have 2 possibilities:
The first one, using regular expression, as you mention in your question. And I believe this is the best solution.
The second one, using the $where queries.
With $where queries you can specify expression like these
db.foo.find({"$where" : "this.x + this.y == 10"})
db.foo.find({"$where" : "function() { return this.x + this.y == 10; }"})
and so you can use the JavaScript .indexOf() on string fields.
Code snippet using the $regex clause (as mentioned by mikeycgto)
String searchString = "ab";
DBCollection coll = db.getCollection("yourCollection");
query.put("name",
new BasicDBObject("$regex", String.format(".*((?i)%s).*", searchString)) );
DBCursor cur = coll.find(query);
while (cur.hasNext()) {
DBObject dbObj = cur.next();
// your code to read the DBObject ..
}
As long as you are not opening and closing the connection per method call, the query should be fast.
I have an object that was stored via mongo-java-driver. Object uses java.util.UUID for its _id field. Following is presentation of object via mongo shell:
> db.b.find()
{ "_id" : BinData(3,"zUOYY2AE8WZqigtb/Tqztw==") }
I have a requirement to process searching via $where clause. I use following code to do it:
Mongo m = new Mongo();
DBCollection coll = m.getDB("a").getCollection("b");
coll.save(new BasicDBObject("_id", UUID.randomUUID()));
// ??? - don't know what should be specified
DBObject query = new BasicDBObject("$where", "this[\"_id\"] == " + ???);
coll.find(query).count()
The question is what should I specify instead of ??? to make it work?
Thanks for any help.
My invesigation shown that only one way to do it is rewriting a query in object based way (I mean migration of $where clause part to BasicDBObject based query). In such case mongo-java-driver supports java.util.UUID without any additional effort.