Compare two fields in the document with criteria in mongodb - mongodb-query

I would like to understand how do I compare two fields in the document. Has anyone ever needed to do this type of comparison? I'm using the criteria.
criteria.add(Criteria.where("this.campoC == this.campoD"));
Query query = new Query();
List<Criteria> criteria = new ArrayList<>();
criteria.add(Criteria.where("campoA").is(123));
criteria.add(Criteria.where("campoB").is("N"));
criteria.add(Criteria.where("this.campoC == this.campoD")); // <---- How do you compare two fields???
query.addCriteria(new Criteria().andOperator(criteria.toArray(new Criteria[criteria.size()]))).limit(1);
return query;

Try this one
Criteria matchCriteria = new Criteria() {
#Override
public Document getCriteriaObject() {
Document doc = new Document();
doc.put( "$where", "this.campoC == this.campoD");
return doc;
}
};
criteria.add(matchCriteria );

Related

abas-ERP: Copy TN field into another TN field with edp/epi

I want to create an invoice and fill the field pftext (TN2) with the content of another TN2 field called ytdescription.
My problem is, that I always have the path of my textfield in pftext but not the content of my textfield.
1st try of edp header:
kunde;artex;pftext;mge
'M|ytkdnr';'M|ytartnr';'M|ytdescription';'M|ytmge'
2nd try:
kunde;artex;*pftext;mge;
'M|ytkdnr';'M|ytartnr';'M|ytdescription';'M|ytmge'
Of course I could create a T254 field and store the content of M|ytdescription in the new field, but then I am stuck to max 3000 chars for the content.
Many other tries followed but with no success :-(
Any help is highly appreciated!
I Don't know the options to do this per EDP, but there is a solution to do this per AJO:
public int run(DbContext dbContext, String[] args) {
/*
* Get the object to copy data from, in this case a customer with idno 70001
*/
String idno = "70001";
SelectionBuilder<Customer> selectionBuilder = SelectionBuilder.create(Customer.class);
selectionBuilder.add(Conditions.eq(Customer.META.idno, idno));
Customer customer = QueryUtil.getFirst(dbContext, selectionBuilder.build());
VendorEditor vendorEditor = null;
// Read the original content to a StringWriter
StringWriter originalFreeText = new StringWriter();
try {
customer.getFreeText(originalFreeText);
// Create a new object to write the values to. In example a Supplier
vendorEditor = dbContext.newObject(VendorEditor.class);
vendorEditor.setSwd("Searchword");
vendorEditor.setAddr("Vendor Name");
if (!originalFreeText.toString().equals("")) {
vendorEditor.setFreeText(new StringReader(originalFreeText.toString()));
}
vendorEditor.commit();
} catch (IOException e) {
dbContext.out().println(e.getMessage());
} finally {
if (vendorEditor != null) {
if (vendorEditor.active()) {
vendorEditor.abort();
}
}
}
return 0;
}

(pure) Lucene: Counting documents having a timestamp in longfield, grouped by year

My documents structure is:
[text:TextField,date:LongField]
I am looking for a 'statistic' query on my documents, based on a precision level on the dateTime field. This means counting documents grouped by the LongField date, ignoring some bytes at the right of the date.
For a given precision, I am looking for how many documents match for each different values of this precision.
Assuming the precision 'year' is grouping by "date/10000"
With the following data:
{text:"text1",dateTime:(some timestamp where year is 2015 like 20150000)}
{text:"text2",dateTime:(some timestamp where year is 2010 like 20109878)}
{text:"text3",dateTime:(some timestamp where year is 2015 like 20150024)}
{text:"text14,dateTime:(some timestamp where year is 1997 like 19970987)}
The result should be:
[{bracket:1997, count:1}
{bracket:2010, count:1}
{bracket:2015, count:2}]
While NumericRangeQuery allow to create 1 (or some) range, is it possible for lucene to generate the ranges based on a precision step?
I can handle this by creating a new field for each precision level that I need, but maybe this kind of things allready exists.
It's a kind of faceted search where the facet is the time. The use case should be:
-give me document count for each milleniums,
-then give me document count for each centuries (inside a millenium)
-then give me document count for each year (inside a century)
-then give me document count for each days (inside a year)
when 0 documents exists inside a bucket, the result should not be in the results.
Regards
Collector can do this without any trick, here is the working code:
public class GroupByTest1 {
private RAMDirectory directory;
private IndexSearcher searcher;
private IndexReader reader;
private Analyzer analyzer;
private class Data {
String text;
Long dateTime;
private Data(String text, Long dateTime) {
this.text = text;
this.dateTime = dateTime;
}
}
#Before
public void setUp() throws Exception {
directory = new RAMDirectory();
analyzer = new WhitespaceAnalyzer();
IndexWriter writer = new IndexWriter(directory, new IndexWriterConfig(analyzer));
Data datas[] = {
new Data("A", 2012L),
new Data("B", 2012L),
new Data("C", 2012L),
new Data("D", 2013L),
};
Document doc = new Document();
for (Data data : datas) {
doc.clear();
doc.add(new TextField("text", data.text, Field.Store.YES));
doc.add(new LongField("dateTime", data.dateTime, Field.Store.YES));
writer.addDocument(doc);
}
writer.close();
reader = DirectoryReader.open(directory);
searcher = new IndexSearcher(reader);
}
#Test
public void test1() throws Exception {
final Map<Integer, Long> map = new HashMap<>();
Collector collector = new SimpleCollector() {
int base = 0;
#Override
public void collect(int doc) throws IOException {
String year = reader.document(doc + base).get("dateTime");
if (!map.containsKey(Integer.valueOf(year))) {
map.put(Integer.valueOf(year), 1L);
} else {
long l = map.get(Integer.valueOf(year));
map.put(Integer.valueOf(year), ++l);
}
}
#Override
public boolean needsScores() {
return false;
}
#Override
protected void doSetNextReader(LeafReaderContext context) throws IOException {
base = context.docBase;
}
};
searcher.search(new MatchAllDocsQuery(), collector);
for (Integer integer : map.keySet()) {
System.out.print("year = " + integer);
System.out.println(" count = " + map.get(integer));
}
}
}
Output I get is following:
year = 2012 count = 3
year = 2013 count = 1
This may run slow depending on how many records you have. It loads every single document to know what is the year on it and groups based on that. There is also grouping module which you may also look into it.

How can I make my lucene search "global" (i.e. for all groups) in liferay?

I use the following in a search container to find the projects in a given group:
SearchContext searchContext = SearchContextFactory.getInstance(request);
searchContext.setStart(searchContainer.getStart());
searchContext.setKeywords(keywords);
searchContext.setEnd(searchContainer.getEnd());
results = ProjectLocalServiceUtil.getSearchedProjects(searchContext);
total = ProjectLocalServiceUtil.getSearchedProjectsCount(searchContext);
The methods getSearchedProjects translates from search results to a list of projects:
public List<Project> getSearchedProjects(SearchContext context) throws SearchException {
Indexer indexer = IndexerRegistryUtil.getIndexer(Project.class);
Hits results = indexer.search(context);
List<Project> projects = new ArrayList<Project>();
for (int i = 0; i < results.getDocs().length; i++) {
com.liferay.portal.kernel.search.Document doc = results.doc(i);
long projectId=GetterUtil.getLong(doc.get(Field.ENTRY_CLASS_PK));
Project project = null;
try {
project = ProjectLocalServiceUtil.getProject(projectId);
projects.add(project);
} catch (Exception e) {
e.printStackTrace();
continue;
}
}
return projects;
}
The results are as I expect when the search portlet is placed in the group referenced in the group I set in my projectIndexer's doGetDocument method:
document.addKeyword(Field.GROUP_ID, groupId);
document.addKeyword(Field.SCOPE_GROUP_ID, groupId);
but I would like to use the portlet to search from another group.
I tried to set the list of groupIds in the portlet, using
searchContext.setGroupIds(new long[] {projectHolder.getGroupId()});
but I still don't get any results in the search.
Any pointers on how I can search 'across groups' ?
Alain
I found the answer to my question. In order for
searchContext.setGroupIds(new long[] {group1ID, group2Id});
to have an effect the searchContext should NOT have groupId as one of its attributes.
SearchContextFactory adds GroupId in the attributes (see the extract below), so after the call to getInstance, you need to remove groupId from the attributes :
SearchContext searchContext = SearchContextFactory.getInstance(request);
Map<String, Serializable> attributes = searchContext.getAttributes();
attributes.remove("groupId");
Hope this helps someone someday.
Alain
Below, the extract from SearchContextFactory.getInstance(HttpServletRequest request) that (I believe - didn't debug it) sets the groupId attribute from the parameters :
// Extract from SearchContextFactory.java
Map<String, String[]> parameters = request.getParameterMap();
for (Map.Entry<String, String[]> entry : parameters.entrySet()) {
String name = entry.getKey();
String[] values = entry.getValue();
if ((values != null) && (values.length > 0)) {
if (values.length == 1) {
attributes.put(name, values[0]);
}
else {
attributes.put(name, values);
}
}
}
searchContext.setAttributes(attributes);

Combining two different jTables and adding button into jTable

I try to do for forum using java swing. Here are my codes for table :
public void SetUpJTable() {
DefaultTableModel tableModel = (DefaultTableModel) jTable.getModel();
String[] data = new String[4];
db.setUp("IT Innovation Project");
String sql = "Select topic_title,topic_description,topic_by from forumTopics WHERE topic_id = "
+ topicId + "";
ResultSet resultSet = null;
resultSet = db.readRequest(sql);
try {
while (resultSet.next()) {
data[0] = resultSet.getString("topic_title");
data[1] = resultSet.getString("topic_description");
data[2] = resultSet.getString("topic_by");
tableModel.addRow(data);
}
resultSet.close();
} catch (Exception e) {
System.out.println(e);
}
}
I set up this table to retrieve the topic details which user select certain thread from main page. And I set up another table to store for the replies by users. Here is it :
public void SetUpJTableComment() {
DefaultTableModel tableModel1 = (DefaultTableModel) jTableComment
.getModel();
String[] data = new String[3];
db.setUp("IT Innovation Project");
String sql = "Select reply_content,reply_by from forumReplies WHERE reply_topic = "
+ topicId + "";
ResultSet resultSet = null;
resultSet = db.readRequest(sql);
try {
while (resultSet.next()) {
data[0] = resultSet.getString("reply_content");
data[1] = resultSet.getString("reply_by");
tableModel1.addRow(data);
}
resultSet.close();
} catch (Exception e) {
System.out.println(e);
}
}
And this is how I set up the table :
private JTable getJTableComment() {
String header[] = { "Comment", "Reply By" };
if (jTableComment == null) {
jTableComment = new JTable() {
public boolean isCellEditable(int nRow, int nCol) {
return false;
}
};
}
DefaultTableModel tableModel1 = (DefaultTableModel) jTableComment
.getModel();
tableModel1.setColumnIdentifiers(header);
jTableComment.getColumnModel().getColumn(0).setMinWidth(700);
jTableComment.getColumnModel().getColumn(0).setMaxWidth(800);
jTableComment.getColumnModel().getColumn(1).setMinWidth(97);
jTableComment.getColumnModel().getColumn(1).setMaxWidth(100);
jTableComment.getTableHeader().setFont(
new Font("Dialog", Font.PLAIN, 20));
jTableComment.getTableHeader().setForeground(Color.white);
jTableComment.getTableHeader().setBackground(new Color(102, 102, 102));
jTableComment.setRowHeight(50);
jTableComment.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
jTableComment.setFont(new Font("Dialog", Font.PLAIN, 18));
return jTableComment;
}
It works perfectly with two separating tables. I wonder if there is some way to combine both of these tables into one table? And how can I customize the table to make it look less-liked a table because my current one is just .. solid-table and my teacher asked me to improve it but I have no idea to do so. And I tried to add button into the table but I realized that I cannot add it from the try statement because that is is retrieve data from database directly. Any guides? Thanks in advance.
You can use SQL join construct and have one table with more columns:
select topic_title,topic_description,topic_by,
reply_content,reply_by
from forumTopics join forumReplies
on (forumTopics.topic_id=forumReplies.topic_id) WHERE topic_id = 1234
then build the model from the five column result set as you are already doing.
But surely if there is more than one reply to a forum topic, the topic part will be repeated in the table.
To make a table not to look like a table, try JTreeTable from Swing Labs maybe, it allows to have tree-like subsections, exactly that is required. It is not part of the system library however, you will need to download it. Some source code on how to just JTreeTable can be found here.
On how JTreeTable looks is Swing Labs, you can see in they web-startable demo. It also shows the code sample automatically.

Lucene - How can you modify a Query object's terms?

I want to add new fields to my Lucene-based search engine site, however I want to be able to intercept queries and modify them before I pass them on to the Searcher.
For example each document has the field userid so you can search for documents authored by a particular user by their ID, e.g. foo bar userid:123 however I want to add the ability to search by username.
I'd like to add a field user:RonaldMcDonald to queries (not to documents), however I want to be able to intercept that term and replace it with an equivalent userid:123 term (my own code would be responsible for converting "RonaldMcDonald" to "123").
Here's the simple code I'm using right now:
Int32 get = (pageIndex + 1) * pageSize;
Query query;
try {
query = _queryParser.Parse( queryText );
} catch(ParseException pex) {
log.Add("Could not parse query.");
throw new SearchException( "Could not parse query text.", pex );
}
log.Add("Parsed query.");
TopDocs result = _searcher.Search( query, get );
I've had a look at the Query class, but I can't see any way to retrieve, remove, or insert terms.
You can subclass the QueryParser and override NewTermQuery.
QP qp = new QP("user", new SimpleAnalyzer());
var s = qp.Parse("user:RonaldMcDonald data:[aaa TO bbb]");
Where s is will be userid:123 data:[aaa TO bbb]
public class QP : QueryParser
{
Dictionary<string, string> _dict =
new Dictionary<string, string>(new MyComparer()) {{"RonaldMcDonald","123"} };
public QP(string field, Analyzer analyzer) : base(field, analyzer)
{
}
protected override Query NewTermQuery(Term term)
{
if (term.Field() == "user")
{
//Do your username -> userid mapping
return new TermQuery(new Term("userid", _dict[term.Text()]));
}
return base.NewTermQuery(term);
}
//Case insensitive comparer
class MyComparer : IEqualityComparer<string>
{
public bool Equals(string x, string y)
{
return String.Compare(x, y, true, CultureInfo.InvariantCulture)==0;
}
public int GetHashCode(string obj)
{
return obj.ToLower(CultureInfo.InvariantCulture).GetHashCode();
}
}
}