sailsjs array query Exact match - sails.js

i want to query mongodb in sailsjs.
this is structure of my db
{
"users": [
"52ed09e1d015533c124015d5",
"52ed4bc75ece1fb013fed7f5"
],
"user_msgs": [
{
"sender": "52ed09e1d015533c124015d5",
"sendTo": "52ed4bc75ece1fb013fed7f5",
"msg": "ss"
}
],
"createdAt": ISODate("2014-02-06T16:12:17.751Z"),
"updatedAt": ISODate("2014-02-06T16:12:17.751Z"),
"_id": ObjectID("52f3b461f46da23c111582f6")
}
I want to search those "users" who who match array [
"52ed09e1d015533c124015d5",
"52ed4bc75ece1fb013fed7f5"
]
Message.find({user: ["52ed09e1d015533c124015d5","52ed4bc75ece1fb013fed7f5"]})
this query returns all objects which contains 1 OR 2 ..but i need only those which exacly match 1 AND 2,
i have also tried $all, and etc.. but did not worked
please tell me how to write query with sailsjs supported syntex to get those user

You'll need to use the native Mongo adapter for this:
Message.native(function(err, collection) {
collection.find({users:{'$all':["52ed09e1d015533c124015d5","52ed4bc75ece1fb013fed7f5"]}}).toArray(function(err, results) {
// Do something with results
});
});

Message.find()
.where({users: "52ed09e1d015533c124015d5", "52ed4bc75ece1fb013fed7f5"})
.exec(function(){
// do something
});
While the above code may work to pull in just those users. I think a better solution would be to define your user ID's in your message model.
I would add the following attributes to your messages model:
senderID: {
type: 'string'
},
receiverID: {
type: 'string'
}
Now you can make that query more efficient by using the following query:
Message.find()
.where({senderID: "52ed09e1d015533c124015d5"})
.where({receiverID: "52ed4bc75ece1fb013fed7f5"})
.exec(function(){
// do something
});
This is the route I would take.

Related

How to build a MongoDB query that combines two field temporarily?

I have a schema which has one field named ownerId and a field which is an array named participantIds. In the frontend users can select participants. I'm using these ids to filter documents by querying the participantIds with the $all operator and the list of participantsIds from the frontend. This is perfect except that the participantsIds in the document don't include the ownerId. I thought about using aggregate to add a new field which consists of a list like this one: [participantIds, ownerId] and then querying against this new field with $all and after that delete the field again since it isn't need in the frontend.
How would such a query look like or is there any better way to achieve this behavior? I'm really lost right now since I'm trying to implement this with mongo_dart for the last 3 hours.
This is how the schema looks like:
{
_id: ObjectId(),
title: 'Title of the Event',
startDate: '2020-09-09T00:00:00.000',
endDate: '2020-09-09T00:00:00.000',
startHour: 1,
durationHours: 1,
ownerId: '5f57ff55202b0e00065fbd10',
participantsIds: ['5f57ff55202b0e00065fbd14', '5f57ff55202b0e00065fbd15', '5f57ff55202b0e00065fbd13'],
classesIds: [],
categoriesIds: [],
roomsIds: [],
creationTime: '2020-09-10T16:42:14.966',
description: 'Some Desc'
}
Tl;dr I want to query documents with the $all operator on the participantsIds field but the ownerId should be included in this query.
What I want is instead of querying against:
participantsIds: ['5f57ff55202b0e00065fbd14', '5f57ff55202b0e00065fbd15', '5f57ff55202b0e00065fbd13']
I want to query against:
participantsIds: ['5f57ff55202b0e00065fbd14', '5f57ff55202b0e00065fbd15', '5f57ff55202b0e00065fbd13', '5f57ff55202b0e00065fbd10']
Having fun here, by the way, it's better to use Joe answer if you are doing the query frequently, or even better a "All" field on insertion.
Additional Notes: Use projection at the start/end, to get what you need
https://mongoplayground.net/p/UP_-IUGenGp
db.collection.aggregate([
{
"$addFields": {
"all": {
$setUnion: [
"$participantsIds",
[
"$ownerId"
]
]
}
}
},
{
$match: {
all: {
$all: [
"5f57ff55202b0e00065fbd14",
"5f57ff55202b0e00065fbd15",
"5f57ff55202b0e00065fbd13",
"5f57ff55202b0e00065fbd10"
]
}
}
}
])
Didn't fully understand what you want to do but maybe this helps:
db.collection.find({
ownerId: "5f57ff55202b0e00065fbd10",
participantsIds: {
$all: ['5f57ff55202b0e00065fbd14',
'5f57ff55202b0e00065fbd15',
'5f57ff55202b0e00065fbd13']
})
You could use the pipeline form of update to either add the owner to the participant list or add a new consolidated field:
db.collection.update({},[{$set:{
allParticipantsIds: {$setUnion: [
"$participantsIds",
["$ownerId"]
]}
}}])

Mongodb: Update a field with data from a sub-sub field?

I'm trying to update a field in a collection with data from the same collection, but from a sub-sub field in it, and either can't get the syntax right, or I'm just doing it wrong.
I've spent quite some time now digging around here, but can't seem to get anywhere.
Here's the example structure of the users collection:
{
"_id": "12345qwerty",
"services": {
"oauth": {
"CharacterID": 12345678,
"CharacterName": "Official Username",
},
},
"name": "what I want to change",
"username": "OfficialUsername"
}
What I'm trying to do would be pretty trivial with SQL, ie: update all the display names to match a trusted source...
update users
set name = services.oauth.CharacterName;
...but I'm having trouble getting in MongoDB, and I have a feeling im doing it wrong.
Here's what i have so far, but it doesn't work as expected.
db.users.find().snapshot().forEach(
function (elem) {
db.users.update(
{ _id: elem._id },
{ $set: { name: elem.services.oauth.CharacterName } }
);
}
);
I can set the name to be anything at the base level, but can't set it to be something from the sublevel, as it doesn't recognise the sub-fields.
Any help would be greatly appreciated!
db.users.update({"services.oauth.CharacterName": {$exists: true}},{$set: {"name": "services.oauth.CharacterName"}},{multi:true})
I am setting name at the root of your document to be equal to the value in services.oauth.CharacterName in the sub sub document. multi = true will update multiple document, I am only updating documents that have the services.oauth.CharacterName value.

How do I find an element in Meteor.user() and return other element in that same array in mongodb with meteor

How do I find an element in Meteor.user() and return other element in that same array in mongodb with meteor. How do I find course_id and get classes_id array?
this is in meteor.user
{
"_id": "RoFFcaAfXBeR2napZ",
"emails": [
{
"address": "tong#gmail.com",
"verified": false
}
],
"classes": [
{
"course_id": "svesvinfdsgrvnekuktndvsk",
"classes_id": ["myclass1", "myclass2"]
},
{
"course_id": "cegtrtcrtvw5cgekrgecec",
"classes_id": ["myclass3", "myclass4"]
},
{
"course_id": "rvxwefdsrvyjvyccrhnkik",
"classes_id": ["myclass5", "myclass6"]
},
],
"courses": [
"qwmZdgQbrZ3rmHdN8"
]
}
You can do it on the server using the Mongo's $ projection operator, but you cannot get the same functionality using MiniMongo out of the box, as it does not support this operator.
Let courseId hold the desired course id.
On the server, using:
c.find({"classes.course_id": courseId},{fields:{"courses.$": 1}})
should yield the desired result.
You will have to refactor your collection or use other tricks in order to get the same result on the client side.
One trick that comes to mind is using a mapping to leave only the desired courses:
function getClasses(courseId){
return myCollection.find({"classes.course_id": courseId}).map((student) => {
student.classes = _.filter(student.classes, (el) => el.course_id === courseId)
return student;
});
}
which also keeps your query reactive.
By default Meteor subscribes only the _id and profile object to the client. so you'll not be able to fetch your class records. you need to make a custom publish which returns your desired records and subscribe to that publication.

Morphia query to filter and fetch an embedded list element

I am new to NoSQL and morphia. I am using Morphia to query MongoDB.
I have a sample collection as below:
[
{
"serviceId": "id1",
"serviceName": "ding",
"serviceVersion": "1.0",
"files": [
{
"fileName": "b.html",
"fileContents": "contentsA"
},
{
"fileName": "b.html",
"fileContents": "contentsB"
}
]
},
{
"serviceId": "id2",
"serviceName": "ding",
"serviceVersion": "2.0",
"files": [
{
"fileName": "b.html",
"fileContents": "contentsA"
},
{
"fileName": "b.html",
"fileContents": "contentsB"
}
]
}
]
I would like to fetch an element in "files" List , given service name, service version and filename., using Morphia.
I was able to get what I want using the query below:
db.ApiDoc.find({ serviceName: "ding", serviceVersion: "2.0"}, { files: { $elemMatch: { fileName: "b.html" } } }).sort({ "_id": 1}).skip(0).limit(30);
What I tried so far :
I tried using "elemmatch" api that morphia has, but no luck.
query = ...createQuery(
Result.class);
query.and(query.criteria("serviceName").equal("ding"),
query.criteria("serviceVersion").equal(
"2.0"));
query.filter("files elem",BasicDBObjectBuilder.start("fileName", "a.html").get());
I seem to get the entire Result collection with all the files. I would like to get only the matched files(by filename).
can some one help me how I can get this to work?
Thanks
rajesh
I don't believe it's possible to get just the matching sub element. You can request just to have the 'files' array returned but all elements will be included in the result set and you will have to refilter in your code.
The other option is to make Files a collection of its own with a serviceId field and then you'll have more power to load only certain files.
It's possible to do that.
the filter doesn't really work like projection.
try this :
datastore.createQuery(Result.class)
.field("serviceName").equal("dong")
.field("serviceVersion").equal("2.0")
.field("files.filename").equal("a.html")
.project("files.$.filename", true);

Pull and addtoset at the same time with mongo

I have a collection which elements can be simplified to this:
{tags : [1, 5, 8]}
where there would be at least one element in array and all of them should be different. I want to substitute one tag for another and I thought that there would not be a problem. So I came up with the following query:
db.colll.update({
tags : 1
},{
$pull: { tags: 1 },
$addToSet: { tags: 2 }
}, {
multi: true
})
Cool, so it will find all elements which has a tag that I do not need (1), remove it and add another (2) if it is not there already. The problem is that I get an error:
"Cannot update 'tags' and 'tags' at the same time"
Which basically means that I can not do pull and addtoset at the same time. Is there any other way I can do this?
Of course I can memorize all the IDs of the elements and then remove tag and add in separate queries, but this does not sound nice.
The error is pretty much what it means as you cannot act on two things of the same "path" in the same update operation. The two operators you are using do not process sequentially as you might think they do.
You can do this with as "sequential" as you can possibly get with the "bulk" operations API or other form of "bulk" update though. Within reason of course, and also in reverse:
var bulk = db.coll.initializeOrderedBulkOp();
bulk.find({ "tags": 1 }).updateOne({ "$addToSet": { "tags": 2 } });
bulk.find({ "tags": 1 }).updateOne({ "$pull": { "tags": 1 } });
bulk.execute();
Not a guarantee that nothing else will try to modify,but it is as close as you will currently get.
Also see the raw "update" command with multiple documents.
If you're removing and adding at the same time, you may be modeling a 'map', instead of a 'set'. If so, an object may be less work than an array.
Instead of data as an array:
{ _id: 'myobjectwithdata',
data: [{ id: 'data1', important: 'stuff'},
{ id: 'data2', important: 'more'}]
}
Use data as an object:
{ _id: 'myobjectwithdata',
data: { data1: { important: 'stuff'},
data2: { important: 'more'} }
}
The one-command update is then:
db.coll.update(
'myobjectwithdata',
{ $set: { 'data.data1': { important: 'treasure' } }
);
Hard brain working for this answer done here and here.
Starting in Mongo 4.4, the $function aggregation operator allows applying a custom javascript function to implement behaviour not supported by the MongoDB Query Language.
And coupled with improvements made to db.collection.update() in Mongo 4.2 that can accept an aggregation pipeline, allowing the update of a field based on its own value,
We can manipulate and update an array in ways the language doesn't easily permit:
// { "tags" : [ 1, 5, 8 ] }
db.collection.updateMany(
{ tags: 1 },
[{ $set:
{ "tags":
{ $function: {
body: function(tags) { tags.push(2); return tags.filter(x => x != 1); },
args: ["$tags"],
lang: "js"
}}
}
}]
)
// { "tags" : [ 5, 8, 2 ] }
$function takes 3 parameters:
body, which is the function to apply, whose parameter is the array to modify. The function here simply consists in pushing 2 to the array and filtering out 1.
args, which contains the fields from the record that the body function takes as parameter. In our case, "$tag".
lang, which is the language in which the body function is written. Only js is currently available.
In case you need replace one value in an array to another check this answer:
Replace array value using arrayFilters