RethinkDB - how to filter records where attribute matches a list / array - arrays

RethinkDB
I want to fetch all the records which have a particular field/attribute matching one of 3 different possible values.
In the example below, I want to see all the records who have status equal to 'Ready', 'Active', or 'Something else'
I would expect to be able to do something like this :
r.db('db').table('table').filter(
function (rec) {
return [ 'Ready', 'Active', 'Something else' ].includes( rec('status') );
}
);
That doesn't error, nor does it return me all the records that have status equal to 'Ready', 'Active', or 'Something else' (it returns no results).
How do I find all records with a field matching any one of the values in an array?

Another solution to this which is closer to your initial hunch is using .contains(...):
r.db('db').table('table').filter(
function (rec) {
return r.expr(['Ready', 'Active', 'Something else']).contains( rec('status'));
}
);
This is likely more efficient than using regex.

Ok, I figured this out after reading a bit more about the ReQL query language.
We don't have a JS function in there, instead it is ReQL, which basically just means you can use .match() and a regular expression.
The regular expression to match a list is simply to seperate the matching strings with bars "|".
So the solution to the above example is
r.db('db').table('table').filter(
function (rec) {
return rec('status').match( "Ready|Active|Something else" );
}
);

Related

Get nested document within document in FaunaDB

I'm fairly new to Fauna so please forgive me. I'd like to make one query that returns a nested document within a document. I access the initial document that contains the nested document by the index I created below:
Get(Match(Index("account-user_by_sub"), "google-oauth2|10233470761")
That index returns this:
{
ref: Ref(Collection("account-users"), "325230990747238466"),
ts: 1646423292780000,
data: {
userAccount: Ref(Collection("user-accounts"), "325134359745003585"),
firstName: "firstName",
lastName: "lastName",
sub: "google-oauth2|10233470761",
}
}
I'd like to make one query that returns the above response along with the nested userAccount document. I found similar questions on Stackoverflow but didn't have any luck with their solutions. I tried this below but it just returned the block of code you see above:
Get(Match(Index("account-user_by_sub"), "google-oauth2|10233470761")),
Lambda("X",
Let(
{
accountUserDoc: Get(Var("X")),
userAccountRef: Get(Select(["data", "userAccount"], Var("accountUserDoc")))
},
{
accountUser: Var("accountUserDoc"),
userAccount: Get(Var("userAccountRef"))
}
)
)
The query you tried won't work: it tries to chain two FQL expressions together with a comma, and there's no connection between the two, so the Lambda doesn't receive a parameter.
You're pretty close to the solution though. Try this:
Let(
{
accountUserDoc: Get(
Match(
Index("account-user_by_sub"),
"google-oauth2|10233470761"
)
),
userAccountDoc: Get(
Select(["data", "userAccount"], Var("accountUserDoc"))
)
},
{
accountUser: Var("accountUserDoc"),
userAccount: Var("userAccountDoc")
}
)
Basically, this places the initial Get inside the Let, rather than trying to pass the document as a parameter.
Since you are calling Get on the value in the userAccount field, you didn't need to do it again when composing the desired result; that would have failed anyway because Get takes a reference, not a document.

Saving conditions whether a user has done something in MongoDB: which field type should I use?

So I have this scenario where I want to save a condition whether if a user "has joined the website onboarding tour". So the question I want to ask is not how to save the field to MongoDB, but rather what field type should I use for it?
I want it to be reusable for another certain condition such as "has joined a campaign A", or "has visited special page B". Now I have 3 cases that you can look:
Case 1: Just a single boolean field for every case: One field for one condition, such as hasJoinedNewOnboarding: true or hasJoinedCampaignA: false. All I need to do is search by key: true.
Case 2: Use an array: One array for many conditions, such as ['hasJoinedNewOnboarding','hasJoinedCampaignA']. Let's say the field name is meta_data. All I need to do is search using $elemMatch like { meta_data: { $elemMatch: { 'hasJoinedNewOnboarding' } } }
Case 3: Use an object: One object for many conditions, such as Case 2: Use an array: One array for many conditions, such as { hasJoinedNewOnboarding: true, hasJoinedCampaignA: false }. Let's say the field name is meta_data. All I need to do is search it like { 'meta_data.hasJoinedNewOnboarding': true }
With that said, which one do you think is the best way to store the conditions in the database? Or do you have something in mind that is better than these 3 cases?
Thanks
There's not much difference when you want to query your data, it will always be either:
db.col.find({hasJoinedNewOnboarding: true})
or for the second approach:
db.col.find({arrayName: "hasJoinedNewOnboarding"})
Both ways are easy however I would recommend storing such events in an array because it's easier to aggregate the data when you don't need to refer to multiple key names in MongoDB,
For example, if you have a document like:
{
events: [
"hasJoinedNewOnboarding",
"hasJoinedCampaignA"
]
}
You can dynamically count how many users have done something by running following query:
db.collection.aggregate([
{
$unwind: "$events"
},
{
$group: {
_id: "$events",
count: { $sum: 1 }
}
}
])
Mongo Playground
Alternatively if you decide to use first or third approach the name of the event is represented by the name of the key in MongoDB's document so you can still easily count single event occurances but if you want to group all events dynamically you need to use $objectToArray operator which becomes more cumbersome.
So the recommended approach would be to keep them as an array of strings or an array of objects like:
{ events: [ { eventType: "NewOnboarding", date: ... } ] }

Updating firestore array of maps shows error : Unsupported field value: undefined

i have a document in firestore like below
what i want to do is update this fields time and stage
i tried these with update and set methods
db.collection("record").doc("user"+user).collection("datas").doc("roadmap").update({
first:[{
0:[ stage:"new stage",
time:"new time,
done:false
}]
}]
})
and
db.collection("startups").doc("user"+user).collection("datas").doc("roadmap").update({
first:{[
stage:"new stage",
time:"new time",
done:false
}]
})
but it always shows error such as :[![FirebaseError: Function DocumentReference.set() called with invalid data. Unsupported field value: undefined]
The following should do the trick:
db.collection("record").doc("user"+user).collection("datas").doc("roadmap")
.update({ first: [{ stage: 'new stage', time: 'new time', done: false }] });
This will work because your array has only one element (a map).
Note that if you want to modify one specific element of an array with several elements, you will need to first read the array in your front-end, modify the array and then write back the modified array to Firestore.
Even if it is not what you are looking for, it's worth noting the arrayUnion() and arrayRemove() methods, see here for more details.

Override sails update to return just one object

When I update a model, waterlock .update() always return an array of objects, even if I set on criteria a primaryKey.
on my code
Ad.update({ id: req.param('id') }, {
// desired attributed to be updated
}).exec(function(err, updatedRecord) {
// updatedRecord is always an array of objects
});
And in order to use the updatedRecord, I have to point out to 0 index like updatedRecord[0] which is something I consider not very clean. According to docs update() in sails, this is a common escenario.
Knowing that, I have 2 questions:
Wouldn't be better that when you find one model return just a updated object for that model, not an array?
If that is a convention, how could be overrided this function in order to return just an object instead of an array when .update() have only affected one record?
it is a convention that it will update all the records that matches the find criteria, but as you are probably using a unique validation on model, it will probably return an array of 1 or 0. You need to do it on hand.
You can override methods in model, by implementing a method with same name as waterline default. But as you will need to completely rewrite the code, it is not viable. Neither changing waterline underlying code.
A solution will be creating a new function on your Ad model:
module.exports = {
attributes: {
adid: {
unique: true,
required: true
},
updateMe: {
}
},
updateOne: function(adid, newUpdateMe, cb){
Ad.update({ id: req.param('id') }, {
// desired attributed to be updated
}).exec(function(err, updatedRecord) {
// updatedRecord is always an array of objects
if (updatedRecord.length == 1){
return cb(null, updatedRecord[0]);
}
return cb(null, {}); //also can error if not found.
});
}
};
Also. Avoid using id as an model attribute (use other name), as some databases like mongodb already add this attribute as default and may cause conflicts with your model.
I dont think its possible with waterline. Its because update method is a generalized one, passing a primary key in where condition is always not the case.

mongodb - retrieve array subset

what seemed a simple task, came to be a challenge for me.
I have the following mongodb structure:
{
(...)
"services": {
"TCP80": {
"data": [{
"status": 1,
"delay": 3.87,
"ts": 1308056460
},{
"status": 1,
"delay": 2.83,
"ts": 1308058080
},{
"status": 1,
"delay": 5.77,
"ts": 1308060720
}]
}
}}
Now, the following query returns whole document:
{ 'services.TCP80.data.ts':{$gt:1308067020} }
I wonder - is it possible for me to receive only those "data" array entries matching $gt criteria (kind of shrinked doc)?
I was considering MapReduce, but could not locate even a single example on how to pass external arguments (timestamp) to Map() function. (This feature was added in 1.1.4 https://jira.mongodb.org/browse/SERVER-401)
Also, there's always an alternative to write storedJs function, but since we speak of large quantities of data, db-locks can't be tolerated here.
Most likely I'll have to redesign the structure to something 1-level deep, like:
{
status:1,delay:3.87,ts:138056460,service:TCP80
},{
status:1,delay:2.83,ts:1308058080,service:TCP80
},{
status:1,delay:5.77,ts:1308060720,service:TCP80
}
but DB will grow dramatically, since "service" is only one of many options which will append each document.
please advice!
thanks in advance
In version 2.1 with the aggregation framework you are now able to do this:
1: db.test.aggregate(
2: {$match : {}},
3: {$unwind: "$services.TCP80.data"},
4: {$match: {"services.TCP80.data.ts": {$gte: 1308060720}}}
5: );
You can use a custom criteria in line 2 to filter the parent documents. If you don't want to filter them, just leave line 2 out.
This is not currently supported. By default you will always receive the whole document/array unless you use field restrictions or the $slice operator. Currently these tools do not allow filtering the array elements based on the search criteria.
You should watch this request for a way to do this: https://jira.mongodb.org/browse/SERVER-828
I'm attempting to do something similar. I tried your suggestion of using the GROUP function, but I couldn't keep the embedded documents separate or was doing something incorrectly.
I needed to pull/get a subset of embedded documents by ID. Here's how I did it using Map/Reduce:
db.parent.mapReduce(
function(parent_id, child_ids){
if(this._id == parent_id)
emit(this._id, {children: this.children, ids: child_ids})
},
function(key, values){
var toReturn = [];
values[0].children.forEach(function(child){
if(values[0].ids.indexOf(product._id.toString()) != -1)
toReturn.push(child);
});
return {children: toReturn};
},
{
mapparams: [
"4d93b112c68c993eae000001", //example parent id
["4d97963ec68c99528d000007", "4debbfd5c68c991bba000014"] //example embedded children ids
]
}
).find()
I've abstracted my collection name to 'parent' and it's embedded documents to 'children'. I pass in two parameters: The parent document ID and an array of the embedded document IDs that I want to retrieve from the parent. Those parameters are passed in as the third parameter to the mapReduce function.
In the map function I find the parent document in the collection (which I'm pretty sure uses the _id index) and emit its id and children to the reduce function.
In the reduce function, I take the passed in document and loop through each of the children, collecting the ones with the desired ID. Looping through all the children is not ideal, but I don't know of another way to find by ID on an embedded document.
I also assume in the reduce function that there is only one document emitted since I'm searching by ID. If you expect more than one parent_id to match, than you will have to loop through the values array in the reduce function.
I hope this helps someone out there, as I googled everywhere with no results. Hopefully we'll see a built in feature soon from MongoDB, but until then I have to use this.
Fadi, as for "keeping embedded documents separate" - group should handle this with no issues
function getServiceData(collection, criteria) {
var res=db[collection].group({
cond: criteria,
initial: {vals:[],globalVar:0},
reduce: function(doc, out) {
if (out.globalVar%2==0)
out.vals.push({doc.whatever.kind.and.depth);
out.globalVar++;
},
finalize: function(out) {
if (vals.length==0)
out.vals='sorry, no data';
return out.vals;
}
});
return res[0];
};

Resources