MongoDB with Spring Data - Avoid $and operator - mongodb

I want to create the following query using MongoDB api for Spring Data:
{ "someField": "someValue", otherField: "otherValue"}
The only way I found to do this is:
Criteria someFieldCriteria = Criteria.where("someField").is("someValue");
Criteria otherFieldCriteria = Criteria.where("otherField").is("otherValue");
Criteria andCriteria = new Criteria.andOperator(someFieldCriteria, otherFieldCriteria)
But this compiles to:
{ $and: [{ "someField": "someValue"}, { otherField: "otherValue"}] }
Anyone knows? Thanks!

Use and(String key).
Something like
Criteria andCriteria = Criteria.where("someField").is("someValue").and("otherField").is("otherValue");

Related

Upserting entity in MongoDB with adding element to nested array in one operation

I have some entities like that:
{
"id": 1,
...
"items": [
{
"name": "name_1",
...
}
]
}
I need to upsert that document in MongoDB, that means:
if there is no documents with id == 1, then save a new document
if there is document with id == 1, then I need to add 'item' from given document to persisted.
It is neccessary to do this by atomic operation.
I use SpringData and MongoTemplate and tried to use aggregate operations, but didn't manage to achieve expected behavior.
Maybe somenopne knows how to do it with Spring Data?
Your query will look something like this.
db.coll.updateMany(
{"id": 1},
{$push: {
"items": {"name": "d"}
}},
{upsert: true}
);
In spring boot,
Query query = Query.query(Criteria.where("id").is(1));
Update update = new Update();
update.push("items", <item object>);
UpdateResult updateResult = mongoTemplate.upsert(query, update, <class on which update has to run>.class);

Morphia Aggregation Lookup syntax?

I'm trying to achieve a join on three String fields between two collections on MongoDB v. 4.4.3: one containing the original documents, the other the translations.
Both document types look like this:
{
"_id" : ObjectId("60644367b521563be8044f07"),
"dsId" : "2051918",
"lcId" : "data_euscreenXL_EUS_15541BBE705033639D4E06691D7A5D2E",
"pgId" : "1",
(...)
This MongoDB query does what I need, embedding the Translations in the result:
db.Original.aggregate([
{ $match: { query parameters } },
{ $lookup:
{
from: "Translation",
let: { "origDsId": "$dsId", origLcId: "$lcId", "origPgId": "$pgId" },
pipeline: [
{ $match:
{ $expr:
{ $and:
[
{ $eq: [ "$dsId", "$$origDsId" ] },
{ $eq: [ "$lcId", "$$origLcId" ] },
{ $eq: [ "$pgId", "$$origPgId" ] }
]
}
}
},
{ $project: { dsId: 0, _id: 0 } }
],
as: "translations"
}
}])
However, I can't figure out how to write the equivalent Morphia query. I updated to Morphia v.2.2, which adds the required features, but it's all very new and hasn't yet been documented on morphia.dev; I couldn't find much more on Javadoc either. This Morphia unit test on Github looked interesting and I tried copying that approach:
Aggregation<Original> query = datastore.aggregate(Original.class)
.match(eq("dsId", datasetId), eq("lcId", localId))
.lookup(Lookup.lookup(Translation.class)
.let("origDsId", value("$dsId"))
.let("origLcId", value("$lcId"))
.let("origPgId", value("$pgId"))
.pipeline(Match.match(expr(Expressions.of()
.field("$and",
array(Expressions
.of().field("$eq",
array(field("dsId"),
field("$origDsId"))),
Expressions
.of().field("$eq",
array(field("lcId"),
field("$origLcId"))),
Expressions
.of().field("$eq",
array(field("pgId"),
field("$origPgId"))))))))
.as("translations"));
...
This returns the Original documents, but fails to join the Translations.
The problem is that the syntax of the pipeline stage is rather puzzling. I wonder if anyone can shed some light on this?
the unit test example does not use (or need?) the double-$ form seen in "$$origDsId"? From the MongoDB documentation I understand that this form is used to refer to externally defined variables (eg in the "let" assignment before the "pipeline") but they don't work in the quoted example either;
what is the role of the static ArrayExpression "array"? It looks as if it's a kind of assignment container, where Expressions.of().field("$eq", array(field("dsId"), field("$origDsId"))) might mean something like "dsId" = "$origDsId" - which would be what I need (if it would work ;) )
I tried all sorts of combinations, using field("$origDsId"), value("$origDsId"), field("$$origDsId"), value("$$origDsId"), etcetera, but having no luck so far.
Thanks in advance!

mongo db nested query with wildcards and equality condition

Trying to run a mongo db find query with equality condition in a nested VARIABLE key value
Bonus points for pymongo solution
db.utestmongo.insert({'name':'test','doc':{'heading':{'title':'awesome'},'body':{'title':'content'}}})
# want: 'title'=='awesome' => {'name':'test','doc':{'heading':{'title':'awesome'},'body':{'title':'content'}}}
# ideally
db.utestmongo.find( { "doc.*.title": "awesome" } )
# tried
db.utestmongo.find( { doc: { $elemMatch: { title: 'awesome' } } } )
Also tried links below, got very confused, maybe its because its not an equality query
https://jira.mongodb.org/browse/SERVER-2989
https://docs.mongodb.com/manual/tutorial/query-embedded-documents/
Nested mongo query
Mongo nested query with keys
if you wish to leave it as a find query you need to use the where clause and give it a function, however i'm not sure how efficient this approach is.
another alternative is using aggregate with objectToArray
and then iterating over all the keys.
it should look like this:
db.utestmongo.aggregate([
{
$addFields: { array: { $objectToArray: "$doc" }}
},
{
$match: {'array.v.title': "awesome"}
}])
pymongo solution
import pymongo
db_cnxn = pymongo.MongoClient()
db = db_cnxn['db']
pipeline = [
{"$addFields":{"array": { "$objectToArray": "$doc" }}},
{"$match": {'array.v.title': "awesome"}}
]
list(db['utestmongo'].aggregate(pipeline))

How can i query on embedded document using Spring data mongodb?

My document structure looks like
{
"users":[
{
"email":"user#company.com"
"messages":[
{
"id":"1",
"text":"A",
"from":"Jessy",
"isDeleted":"false"
},{
"id":"2",
"text":"B",
"from":"Jessy",
"isDeleted":"false"
},{
"id":"3",
"text":"C",
"from":"Alan",
"isDeleted":"false"
},{
"id":"4",
"text":"D",
"from":"Amy",
"isDeleted":"true"
},{
"id":"5",
"text":"E",
"from":"Amy",
"isDeleted":"false"
}
]
}
]
}
I want to query on sub document using or & and operations.So my query can be as follow.
select all those messages from the users collection having from as "Amy" and text is "E" or some other Criteria.
I have an idea about the Aggregation Framework supported by Spring data
AggregationOperation match =Aggregation.match(Criteria.where("from").is("Amy").and("text").is("E"));
AggregationOperation unwind = Aggregation.unwind("messages");
Aggregation aggregation = Aggregation.newAggregation(match);
AggregationResults<User> result = this.mongoTemplate.aggregate(aggregation, "users", User.class);
But my problem is that i want to dynamically populate the Criteria like Criteria is at clients end he or she can pass any query to the database i gonna parse that query into Criteria. Any help will be appreciated.

MongoDb Distinct with query C# driver

I am trying to use the db.collection.distinct(field, query) command, documented here. I am trying to call this with the C# driver, documented here.
Currently I am using the code:
_repository.Search(item.searchCriteria).Select(i => i.messageId).Distinct().ToList()
where messageId is a string and the Search function does:
//Create search accross all properties of type.
public IQueryable<SearchType> Search(SearchType entity)
{
Type entityType = entity.GetType();
var propertiesToSearch = entityType.GetProperties(BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public);
query = _collection.AsQueryable();
query = query.WhereAnd(
query.ElementType,
propertiesToSearch.Select(p => new SearchCriteria()
{
Column = p.Name,
Value = p.GetValue(entity),
Operation = WhereOperation.Equal
}).ToArray());
return query;
}
So this should get converted to:
db.collection.distinct("messageId", { $and: [ { prop1: "" }, { prop2: "" } ] })
I am getting the following error when this is run though:
"Distinct is only supported for a single field. Projections used with Distinct must resolve to a single field in the document."
I am using Mongo 2.4.9 and the official C# driver 1.8.3
The .distinct() method is an older implementation that is more of a convenience method wrapping mapReduce. For anything more involved that simple operations you should use .aggregate().
So the shell equivalent:
db.collection.aggregate([
{ "$match": { "$and": [ { "prop1": "" }, { "prop2": "" } ] } },
{ "$group": { "_id": "$messageId" } }
])
The documents are just formed as a chain of BSON documents. There are various examples here.