Spring data repository MongoDB - spring-data

I user Spring Data and will search entries by firstname, surname, email or username. Do I have to set the parameters four times or is it possible to only set the parameter once?
Thanks!
userRepository.findUsersByFirstnameOrSurnameOrEmailOrUsername("Mustermann", "Mustermann",
"Mustermann", "Mustermann");

If you want the query to be automatically generated from the method name, then yes, you would have to pass 4 parameters and if all of them can have the same value in some cases, that same value will have to be passed 4 times.
If however you are willing to trade-off ease of passing parameters against automatic query generation, you can use the #Query annotation to generate your own query. This annotation will take any valid MongoDB query so for your case, the MongoDB query will be:
> db.user.find({ $or : [{"firstName" : "Mustermann"}, {"surname" : "Mustermann"}, {"email" : "Mustermann"}, {"userName" : "Mustermann"}] });
You can therefore code the query as:
#Query("$or : [{'firstName' : ?1}, {'surname' : ?1}, {'email' : ?1}, {'userName' : ?1}]")
List<User> findUsersByFirstnameOrSurnameOrEmailOrUsername(String keyword);

Related

Next.js/MongoDB - Query Optimization

I am building a website using Next.js and MongoDB. On one of my website page, I have implemented filters to help search for products. To retrieve and update the filters (update item count each time a filter is changing), I have an api endpoint which query my MongoDB Collection. This specific collection contains ~200.000 items. Each item have several fields such as brand, model, place etc...
I have 9 fields which I use to filter and thus must fetch through my api each time there's a change. Therefore I have 9 queries running through my api, on for each field/filter and the query on MongoDB looks like :
var models = await db_collection
.aggregate([
{
$match: {
$and: [filter],
},
},
{
$group: { _id: '$model', count: { $sum: 1 } },
},
{ $sort: { _id: 1 } },
])
.toArray();
The problem is that, as 9 queries are running, the update of the page (mainly due to the queries) takes ~4secs which is too long. I would like to reach <1sec. I would like to now if there is a good practice I am missing such as doing one query instead of one for each filter or maybe a database optimization on my database.
Thank you,
I have tried using a $project argument before $groupon aggregate pipeline for the query to reduce the number of field returned, using distinct and then sorting instead of aggregate but none of these solutions seem to improve efficiency.
EDIT :
As suggested by R2D2, I am posting the structure of a document on MongoDB in my collection :
{
_id : ObjectId('example_id')
source : string
date : date
brand : string
family : string
model : string
size : string
color : string
condition : string
contact : string
SKU : string
}
Depending on the pages, I query unique values of each field of interest (source, date, brand, family, model, size, color, condition, contact) and their count depending on filters (e.g. Number for each unique values of model for selected brands, I also query documents based on specific values of these fields.
As mentioned, you indexes are important and if you are querying by those field I recomand to create compound indexes, see here for indexes optimisation : https://learnmongodbthehardway.com/schema/indexes/
As far as the aggregation pipeline goes, nothing is out of the ordinary, but this specific aggregation just return the number of items per model matching the criteria, not the matching document. If it is all the data you need you might find it usefull to create a new collection when you perform pre-caculation for common search daily (how many items have the color black, ...) this way, when the page loads, you don't have to look in you 200k+ items, but just in your pre-calculated statistical collection. Schedule a cron task or use a lambda function to invoke a route on your api that will calculate all your stats once a day and upsert them in a new collection.
Also I believe the "and" is useless useless since you can use the implicit $and. You can look for an object like :
{
color : {$in : ['BLACK', 'BLUE']},
size : 3
}
rather than :
[{color : 'BLACK'}, {color : 'BLUE'}, {size : 3}]
Reserve the explicit $and for when you really need it.

Issue with cosmos DB collection order

I'm trying to order my collection using the following query:
db.getCollection('trip').find().sort({'itinerary.0.timestamp': 1})
The result is not being correctly sorted, however I exported the full collection to a local mongoDB database and the same query works like a charm. In order to perform that sort in cosmos DB I had to create the index 'itinerary.0.timestamp'.
data example:
{
"_id" : ObjectId("6087104ca68f171ce7715448"),
"tripId" : NumberLong(38533184),
"itinerary" : [
{
"transId" : NumberLong(39800097),
"timestamp" : NumberLong(1619372446291)
},
{
"transId" : NumberLong(39800576),
"timestamp" : NumberLong(1619372446321)
},
],
"results" : [],
"tripTimeSent" : ISODate("2021-04-29T14:44:53.253Z")
}
What am I missing?
Thanks!!
The solution was to create a new field, itiTimestamp, outside the array containing the value 'itinerary.0.timestamp'. Then just order by itiTimestamp
It's true that you need to create an index for the sort field. Here's the doc related:
To apply a sort to a query, you must create an index on the fields
used in the sort operation.
==========================================
I've tested in my side, after creating wildcard index on itinerary, sort query could be executed but has no luck. I also refer to this answer(new BasicDBObject("labels.0.value", 1)) and this one(db.testCollection.find().sort({"someArray.0": 1})), they all don't work for the date format Op provided.
But when I added a properity "score":[20,55,80] in each item in the collection, I found it can be sorted by the first item when sort by score directly.
I assume that this feature hasn't supported.

Regarding querying in cloud firestore

I'm doing some research on cloud firestore using flutter. Currently I'm looking into querying in cloud firestore. I got a basic idea of how to query like say in the screenshot of the database given below :
The 2nd (Some Dumb Shit) and 3rd (Some Good Shit) projects belong to the field "Hardware" ..... So if I want to search a project with respect to its field ..... I'll do something like this :
databaseReference.collection("Projects").where("Field",isEqualTo: "Hardware")
But say if I want to search projects based on the name of the members ( Referring to the screenshot above ..... I need to search of a project where a name "Sarvesh Dalvi" is present inside the "Members" field). How am I supposed to write a query in this case.
Note :
Name ("Sarvesh Dalvi") is present inside this heirarchy :
DocumentID(eg : "Some Dumb Shit") / Array("Members") / Map({Name : __ , MemberRef :__});
Thanks in advance for the help.
Update :
I learned how to access an array by doing something like this :
Future<dynamic> getUserProjectFromDatabase(String username)
{
return databaseReference.collection("Projects").where("Members",arrayContains: {"Name" : username}).getDocuments().then((value){
print(value.documents.length);
value.documents.forEach((element) {print(element.data);});
});
}
But this works if the Map only contains :
{"Name" : username};
But in my case my Map is something like this :
{
"Name" : username,
"MemberRef" : /*Reference to a Document*/
};
[Refer to the screenshot posted above]
I only want to query for the Name inside the map and not the MemberRef...... so how can I query something like :
Future<dynamic> getUserProjectFromDatabase(String username)
{
return databaseReference.collection("Projects").where("Members",arrayContains: {"Name" : username,"MemberRef" : /* can be anything */}).getDocuments().then((value){
print(value.documents.length);
value.documents.forEach((element) {print(element.data);});
});
}
There is no way to query for just the member name in your current data structure.
You have two main options:
If you also know the MemberRef for the member, you can query with array-contains: where('Members', arrayContains: {'Name':'New User','MemberRef':'value of memberref'}). This works because array-contains can check if the array contains the complete value that you specify.
If you don't know the MemberRef subbfield, then you'll need to change your data model to allow the query. I typically recommend creating an additional field with just the member names: MemberNames: ["New User", "Sarvesh Dalvi"]. Then you can use the same array-contains operator, but now on this new field with the simple type: where('MemberNames', arrayContains: 'New User').

How to update multiple users with different value in mongoose?

I have two users. test1 and test2. I need to update language for that users in single query.
Now i am looping them
db.collection.update({name : test1}, {lang: node})
db.collection.update({name : test2}, {lang: php})
But i don't want that, how can i do that in single query?
is there any way like
db.collection.update({[{name: test1}, {lang: node}], [{name : test2}, {lang: php}]}, {multi:true})
or
if (name: test1){update lang: node} elseif (name: test2){update lang : php}
sample document
{
_id: 231123,
name : test1,
lang : java
}
Try the Bulk API: https://docs.mongodb.com/v3.2/reference/method/Bulk/
Normal update API doesn't provide what you want since the query for the update operations are not the same. Bulk is the only API that comes to mind for your use case.

Query object within object

I'm using passport.js to store my users into my mongodb. A user object looks like this
{
"_id" : ObjectId("54893faf0907a100006341ee"),
"local" : {
"password" : [encrypted password],
"email" : "johnsmith#domain.com"
},
"__v" : 0
}
In a mongodb shell how would I go about listing all the emails? I'm finding it difficult to do this as my data sits two level deep within the object. Cheers!
You can use distinct to get a list of a field's distinct values in the collection, using dot notation to reference the embedded field:
db.users.distinct('local.email')