Spring mongo query collection on property with underscore char - java

I'm building a query to retrieve elements from a mongo collection, using MongoTemplate. The query criteria contains a property with an underscore, that somehow is replaced with '._', making the query always return 0 elements.
Criteria matchingCriteria = Criteria
.where("entries").elemMatch(Criteria.where("app_id").is(appId))
Looking to the logs I can see the generated query as follows:
o.s.data.mongodb.core.MongoTemplate: find using query: { "entries" : { "$elemMatch" : { "app._id" : "5834718ab0"}}} fields: null for class: Ranking in collection: ranking
I've already tried with BasicQuery, slashing underscore with '\\', and using the unicode “app\u005Fid". None of them worked. It's important to note that a collection with name "app" exists in my database.
The behaviour doesn't look standard. When I use another property with an underscore the value is not replaced:
Criteria matchingCriteria = Criteria .where("entries").elemMatch(Criteria.where("unique_app_id").‌​is(appId))
The logs:
o.s.data.mongodb.core.MongoTemplate find using query: { "entries" : { "$elemMatch" : { "unique_app_id" : "1131706359"}}} fields: null for class: class Ranking in collection: ranking
entries is an array with collection with the following format:
{
"instanceId" : "654ba2d16579e",
"app_id" : "583471adb0",
"unique_app_id" : "554577506",
"value" : 169
}
It's worth mentioning that the same query (without the underscore replacement) works fine in a mongo IDE (Robomongo in this case).
I'm using spring-boot-starter-data-mongodb 1.4.1.RELEASE.
I'm really out of ideas right now.
Any suggestion ?

Per section 3.4.3 of this Spring Data Commons documentation:
As we treat underscore as a reserved character we stongly advise to
follow standard Java naming conventions (i.e. not using underscores in
property names but camel case instead).
I don't believe you can use an underscore character in the middle of an element's name using Spring. Manual references are named after the referenced collection. Use the document type (collection name in singular) followed by _id ( <document>_id ). This is the only case where you can use underscore in the middle.
Update: Here is an existing pull request for the exact behavior you're seeing, as well as Spring's bug tracker for it.
From the Mongo shell, I can execute the following query with success:
> db.app.findOne({ "entries" : { "$elemMatch" : { "app_id" : "1"}}})
{
"_id" : ObjectId("58a5bc6afa8dd4ae3097d5f7"),
"name" : "Keith",
"entries" : [
{
"instanceId" : "654ba2d16579e",
"app_id" : "1"
}
]
}
So, perhaps the Spring API doesn't split when it finds multiple _ tokens when parsing a criteria, but does split for traversal when parsing one.

Related

Escape MongoDB regex character in Spring SpEL - getting SpelParseException

I'm trying to override the MongoRepository::findAll() method to define the custom #Query. I am following the SpEL JPA Query support docs to have similar logic in MongoRepository to check for user role and create a dynamic query to fetch the authorized records.
I've the following method setup in my BlogRepository which extends MongoRepository:
#Query("{ user_id : ?#{ hasRole('ROLE_ADMIN') ? /./ : principal.userId } } ")
#Override
Page<Blog> findAll(Pageable pageable);
I get the following error from this method:
org.springframework.expression.spel.SpelParseException: EL1049E:(pos 31): Unexpected data after '.': 'div(/)'
at org.springframework.expression.spel.standard.InternalSpelExpressionParser.raiseInternalException(InternalSpelExpressionParser.java:988)
at org.springframework.expression.spel.standard.InternalSpelExpressionParser.eatDottedNode(InternalSpelExpressionParser.java:407)
at org.springframework.expression.spel.standard.InternalSpelExpressionParser.maybeEatNode(InternalSpelExpressionParser.java:360)
at org.springframework.expression.spel.standard.InternalSpelExpressionParser.eatPrimaryExpression(InternalSpelExpressionParser.java:345)
at org.springframework.expression.spel.standard.InternalSpelExpressionParser.eatUnaryExpression(InternalSpelExpressionParser.java:337)
As the below mongo query runs and retuns the desired values, I believe, I just need to escape the /./ character so that SpEL doesn't try to parse it.
{ 'user_id' : /./ }
I couldn't find a escape character for SpEL but played with the following options and still similar kind of parse exception is coming up:
'/./'
#{/./} -- wraping with SpEL expression
.
/.*.*/
Could you help me out?
I got it working :
#Query(" ?#{ hasRole('ROLE_ADMIN') ? '{}' : {'user_id' : principal.userId } } ")
probably you can use escape function like
%?#{escape(...)}

How can I save this JSON in mongoDB?

I need save this JSON: {edbff2886c8ca7aa1bd02b092aa03930.zip=T001.zip}. It's from a Map<String,String>. Why isn't possible?
com.mongodb.WriteConcernException: { "serverUsed" : "127.0.0.1:27017" , "ok" : 1 , "n" : 0 , "updatedExisting" : false , "err" : "The dotted field 'edbff2886c8ca7aa1bd02b092aa03930.zip' in 'filenameMap.edbff2886c8ca7aa1bd02b092aa03930.zip' is not valid for storage." , "code" : 57}
Query
public Validacao update(String id, Validacao validacao) {
mongoCollection.update("{_id : #}", id)
.with("{$set: #}", validacao);
return validacao;
}
This:
{edbff2886c8ca7aa1bd02b092aa03930.zip=T001.zip}
isn't valid JSON to start with. You'd need quotes around the field name and the value.
However, even that wouldn't be enough. From the MongoDB documentation:
Field names cannot contain dots (i.e. .) or null characters, and they must not start with a dollar sign (i.e. $). See Dollar Sign Operator Escaping for an alternate approach.
Now, depending on what other characters your field name might have, you could potentially replace all dots with something else before storing - or you may need some sort of escaping mechanism.

elasticsearch match/term query not returning exact match

I am using elasticsearch in my project in Java, with the document format like
/index/type/_mapping
{
"my_id" : "string"
}
Now, suppose the my_id values are
A01, A02, A01.A1, A012.AB0
For the query,
{
"query" : {
"term" : {
"my_id" : "a01"
}
}
}
Observed : the documents returned are for A01, A01.A1, A012.AB0
Expected : I need the A01 document only.
I looked for the solution and found that i would have to use a custom analyzer for my_id field. I do not want to change my mapping for the document.
Also, I used "index": "not_analyzed" in the query but there was no change in the output.
Yes, you could use 'not_analyzed' analyzer, but try to use term filter instead of term query
Also check current mapping of the document

Elasticsearch multiple fields autosuggestion

I want to implement autosuggestion functionality using elastic search. I can use nGram filters to match partial words on multiple fields and its working fine as expected. Output of the search returns full document with multiple fields as required. Now my problem is, how do I give autosuggestion to the user based on the matching field. e.g. I have got 5 fields:
{userId:'rakesh',firstName:'Rakesh','lastName':'Goyal','mobileNo':'123-123-1234','alternativeMobileNo':'123-123-1235'}
{userId:'goyal',firstName:'Goyal','lastName':'Rakshit','mobileNo':'123-123-1236','alternativeMobileNo':'123-123-1237'}
In the above example if user types 123, I want to return 123-123-1234, 123-123-1235, 123-123-1236, 123-123-1237 (4 auto suggestions).
Similarly if user types Rak, I want to return Rakesh, Rakshit (2 auto suggestions).
How do I know match exists in mobileNo and alternativeMobileNo field for first example and return results accordingly?
How do I know match exists in firstName and lastName field for second example and return results accordingly?
How do I give autosuggestion to the user based on the matching field?
When user types 123, store it in a Java variable, prepare a query like below inserting that variable into and send a request to ElasticSearch.
{
"query" : {
"query_string" : {
"query" : "*123*"
}
}
}
The above query will manage to check it in both fields mobileNo and alternativeMobileNo.
Similarly, if user types Rak, the query will be similar to the previous one,
{
"query" : {
"query_string" : {
"query" : "*Rak*"
}
}
}
And I think you want to use highlighter api to answer your last how questions, which allows to highlight search results on one or more fields.
A screenshot of highlight example in es :

Update an existing collection in MongoDB using Java-Hadoop connector

Is it possible to update existing MongoDB collection with new data. I am using hadoop job to read write data to Mongo. Required scenario is :-
Say first collection in Mongo is
{
"_id" : 1,
"value" : "aaa"
"value2" : null
}
after reading data from Mongo and processing data, MongoDB should contain
{
"_id" : 1,
"value" : "aaa"
"value2" : "bbb"
}
If possible, please provide some dummy code.
BasicBSONObject query=new BasicBSONObject();
query.append("fieldname", value);
BasicBSONObject update=new BasicBSONObject();
update.append("$set", new BasicBSONObject().append("newfield",value));
MongoUpdateWritable muw=new MongoUpdateWritable(query,update,false,true);
contex.write(key, muw);
query : is used for providing condition(matching condition).
update : is used for adding new field and value in existing collection.
MongoUpdateWritable:
3rd parameter is upsert value(same as mongodb)
4th parameter is multiple update in many documents in a collection.
Set in Driver class
job.setOutputValueClass(MongoUpdateWritable.class);
I have done it by extending org.apache.hadoop.mapreduce.RecordWriter and overriding write method of this class.
The Mongo-Hadoop Connector doesn't currently suppor this feature. You can open a feature request in the MongoDB Jira if you like.
I have done it by the stratio, if you are using spark, you can check it out!

Categories

Resources