Combine AND/OR in Mongoddb Critreia based on some Condition - mongodb

Have Criteria Lists as follows
List<Criteria> filterCriteria = new ArrayList<>();
List<Criteria> searchCriteria = new ArrayList<>();
List<Criteria> chgCriteria = new ArrayList<>();
Want to combine them into a single query Something like following
Criteria criteriaDefinition = new Criteria();
if (filterCriteria.size() > 0)
criteriaDefinition.andOperator(filterCriteria.toArray(new Criteria[filterCriteria.size()]));
if (searchCriteria.size() > 0)
criteriaDefinition.orOperator(searchCriteria.toArray(new Criteria[searchCriteria.size()]));
if (chgCriteriaList.size() > 0)
criteriaDefinition.orOperator(chgCriteriaList.toArray(new Criteria[chgCriteriaList.size()]));
query.addCriteria(criteriaDefinition);
But it doesnot work. Suggest what is the right approach.
I am using Spring data with Mongodb.

Related

Dynamic JPA OR Expression

How can I construct Criteria Query to update status of multiple items?
final CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaUpdate<Item> query = builder.createCriteriaUpdate(Item.class);
Root<Item> root = query.from(Item.class);
List<Predicate> predicates = new ArrayList<Predicate>();
for(Long id : itemIds)
{
predicates.add(builder.or(builder.equal(root.get(Item_.id), id)));
}
query.set(root.get(Item_.status), status)
.where(predicates.toArray(new Predicate[predicates.size()]));
entityManager.createQuery(query).executeUpdate();
The above query only work if itemIds. size() == 1. If the itemIds.size() > 2 then the entities will not be updated. Can anyone help to construct correct predicate for OR expression please.
Thank in advance!

How to use DBObject Query in distinct function of mongo template?

i want to find distinct value of a field with some Query criteria. my code is..
public List searchservice(String th_type) {
Query query = new Query();
query.addCriteria(Criteria.where("th_type").regex(th_type));
List list = operations.getCollection("doclist").distinct("th_type", query);
return list;
}
in mongo template a distinct function is defined
mongoTemplate.getCollection(collection).distinct(key, query)
bu my code is giving error because i am using simple Query object instead of DBObject Query. how can i use DBObject Query here?
Use a BasicDBObject for this:
public List searchservice(String th_type) {
BasicDBObject dbObject = new BasicDBObject();
dbObject.append("th_type", th_type);
DBCollection dBCollection = operations.getCollection("doclist");
List list = dBCollection.distinct("th_type", dbObject);
return list;
}
UPDATE:
With regex:
BasicDBObject regexQuery = new BasicDBObject();
regexQuery.put("th_type", new BasicDBObject("$regex", th_type));
List list = operations.getCollection("doclist").distinct("th_type",regexQuery);

how rebuild lucene indexes over hibernate search jpa with mongodb

I have accidentally deleted my index directory, now i am trying to rebuild all indexes.
I am using the hibernate search with JPA, lucene and MONGODB.
the following method is returning no results
public void rebuildIndex()throws Exception{
org.hibernate.search.jpa.FullTextEntityManager fem = org.hibernate.search.jpa.Search.getFullTextEntityManager(entityManager);
org.hibernate.search.query.dsl.QueryBuilder queryBuilder = fem.getSearchFactory().buildQueryBuilder().forEntity(Person.class).get();
org.apache.lucene.search.Query query = queryBuilder.all().createQuery();
FullTextQuery fullTextQuery = fem.createFullTextQuery(query, Person.class);
//fullTextQuery.initializeObjectsWith(ObjectLookupMethod.SKIP, DatabaseRetrievalMethod.FIND_BY_ID);
System.out.println(fullTextQuery.toString());
List<Person> results = fullTextQuery.getResultList();
fem.clear();
System.out.println(results.size());
for(Person p : results){
fem.index( p );
fem.flushToIndexes();
fem.clear();
}
//fem.createIndexer().startAndWait();
}
the method is returning no result. how should I get all data from mongoDb to rebuild index?
as hibernate search didn't work with criteria neither with JPQL and has his own JP-QL parser.
I couldn't create a findAll method to retrieve all objetcs
the only way was use a native mongoDb query:
org.hibernate.search.jpa.FullTextEntityManager fem = org.hibernate.search.jpa.Search.getFullTextEntityManager(entityManager);
Mongo mongo = new Mongo("127.0.0.1", 27017);
DB db = mongo.getDB("mainBase");
DBCollection dbCollection = db.getCollection("Persons");
DBCursor cursor = dbCollection.find();
Collection<String> ids = new ArrayList<String>();
String id = "";
while (cursor.hasNext()) {
id = cursor.next().get("_id").toString();
System.out.println(id);
ids.add(id);
}
System.out.println(">"+ids.size());
Person pes;
for(String p : ids){
pes = new Person();
pes.setId(p);
pes = find(pes);
System.out.println("indexing: "+pes.getId());
fem.index( pes );//index each element
fem.flushToIndexes();//apply changes to indexes
fem.clear();//free memory since the queue is processed
}

How to implement search with multiple filters using lucene.net

I'm new to lucene.net. I want to implement search functionality on a client database. I have the following scenario:
Users will search for clients based on the currently selected city.
If the user wants to search for clients in another city, then he has to change the city and perform the search again.
To refine the search results we need to provide filters on Areas (multiple), Pincode, etc. In other words, I need the equivalent lucene queries to the following sql queries:
SELECT * FROM CLIENTS
WHERE CITY = N'City1'
AND (Area like N'%area1%' OR Area like N'%area2%')
SELECT * FROM CILENTS
WHERE CITY IN ('MUMBAI', 'DELHI')
AND CLIENTTYPE IN ('GOLD', 'SILVER')
Below is the code I've implemented to provide search with city as a filter:
private static IEnumerable<ClientSearchIndexItemDto> _search(string searchQuery, string city, string searchField = "")
{
// validation
if (string.IsNullOrEmpty(searchQuery.Replace("*", "").Replace("?", "")))
return new List<ClientSearchIndexItemDto>();
// set up Lucene searcher
using (var searcher = new IndexSearcher(_directory, false))
{
var hits_limit = 1000;
var analyzer = new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_30);
// search by single field
if (!string.IsNullOrEmpty(searchField))
{
var parser = new QueryParser(Lucene.Net.Util.Version.LUCENE_30, searchField, analyzer);
var query = parseQuery(searchQuery, parser);
var hits = searcher.Search(query, hits_limit).ScoreDocs;
var results = _mapLuceneToDataList(hits, searcher);
analyzer.Close();
searcher.Dispose();
return results;
}
else // search by multiple fields (ordered by RELEVANCE)
{
var parser = new MultiFieldQueryParser(Lucene.Net.Util.Version.LUCENE_30, new[]
{
"ClientId",
"ClientName",
"ClientTypeNames",
"CountryName",
"StateName",
"DistrictName",
"City",
"Area",
"Street",
"Pincode",
"ContactNumber",
"DateModified"
}, analyzer);
var query = parseQuery(searchQuery, parser);
var f = new FieldCacheTermsFilter("City",new[] { city });
var hits = searcher.Search(query, f, hits_limit, Sort.RELEVANCE).ScoreDocs;
var results = _mapLuceneToDataList(hits, searcher);
analyzer.Close();
searcher.Dispose();
return results;
}
}
}
Now I have to provide more filters on Area, Pincode, etc. in which Area is multiple. I tried BooleanQuery like below:
var cityFilter = new TermQuery(new Term("City", city));
var areasFilter = new FieldCacheTermsFilter("Area",areas); -- where type of areas is string[]
BooleanQuery filterQuery = new BooleanQuery();
filterQuery.Add(cityFilter, Occur.MUST);
filterQuery.Add(areasFilter, Occur.MUST); -- here filterQuery.Add not have an overloaded method which accepts string[]
If we perform the same operation with single area then it's working fine.
I've tried with ChainedFilter like below, which doesn't seems to satisfy the requirement. The below code performs or operation on city and areas. But the requirement is to perform OR operation between the areas provided in the given city.
var f = new ChainedFilter(new Filter[] { cityFilter, areasFilter });
Can anybody suggest to me how to achieve this in lucene.net? Your help will be appreciated.
You're looking for the BooleanFilter. Almost any query object has a matching filter object.
Look into TermsFilter (from Lucene.Net.Contrib.Queries) if your indexing doesn't match the requirements of FieldCacheTermsFilter. From the documentation of the later; "this filter requires that the field contains only a single term for all documents".
var cityFilter = new FieldCacheTermsFilter("CITY", new[] {"MUMBAI", "DELHI"});
var clientTypeFilter = new FieldCacheTermsFilter("CLIENTTYPE", new [] { "GOLD", "SILVER" });
var areaFilter = new TermsFilter();
areaFilter.AddTerm(new Term("Area", "area1"));
areaFilter.AddTerm(new Term("Area", "area2"));
var filter = new BooleanFilter();
filter.Add(new FilterClause(cityFilter, Occur.MUST));
filter.Add(new FilterClause(clientTypeFilter, Occur.MUST));
filter.Add(new FilterClause(areaFilter, Occur.MUST));
IndexSearcher searcher = null; // TODO.
Query query = null; // TODO.
Int32 hits_limit = 0; // TODO.
var hits = searcher.Search(query, filter, hits_limit, Sort.RELEVANCE).ScoreDocs;
What you are looking for is nested boolean queries so that you have an or (on your cities) but that whole group (matching the or) is itself matched as an and
filter1 AND filter2 AND filter3 AND (filtercity1 OR filtercity2 OR filtercity3)
There is already a good description of how to do this here:
How to create nested boolean query with lucene API (a AND (b OR c))?

MongoDB query in C#

I'd like to get certain documents that match a specific clause, but don't know how to achieve that WHERE effect in relational databases. I have a simple database with words and their translations (objects with 2 fields) and use this code
var words = database.GetCollection<Word>("Dictionary")
to get them. But this gets the whole collection. What if there were thousands of records in the collection? How to get just the records I want?
Use regular expressions matching as below. The 'i' shows case insensitivity.
var collections = mongoDatabase.GetCollection("Abcd");
var queryA = Query.And(
Query.Matches("strName", new BsonRegularExpression("ABCD", "i")),
Query.Matches("strVal", new BsonRegularExpression("4121", "i")));
var queryB = Query.Or(
Query.Matches("strName", new BsonRegularExpression("ABCD","i")),
Query.Matches("strVal", new BsonRegularExpression("33156", "i")));
var getA = collections.Find(queryA);
var getB = collections.Find(queryB);
For Using 'And' or 'Or' in your query, if you want to search over multiple fields.
This assumes you have a class called Word that is modled like you collection.
MongoServer _server = new MongoClient(connectionString).GetServer();
MongoDatabase _database = _server.GetDatabase(database);
MongoCollection _collection = _database.GetCollection(collection);
var results = _collection.FindAs<Word>(Query.EQ("MyField","WordToFind"));