RethinkDB - how to get a single document by query? - javascript

How can I get one document only by query in RethinkDB?
for an example, I want to get the document by this query below:
let searchQuery = {
name: 'rob'
}
var cursor = await r.table('users').filter(searchQuery).run(connection)
var user = await cursor.toArray()
console.log(user)
Result:
[ { email: 'fooz#bar.com',
id: '00e18124-714b-4298-aa34-5126ebda8828',
name: 'rob' } ]
Filter method actually returns a list of users with the same name - 'rob'.
I want the result like get method:
r.table('posts').get('a9849eef-7176-4411-935b-79a6e3c56a74').run(conn, callback);
Result:
{ email: 'fooz#bar.com',
id: '00e18124-714b-4298-aa34-5126ebda8828',
name: 'rob' }
But this method only takes document id.
Any ideas?

get (by id) fetches a single row selection by design, but filter is a streaming operation that processes multiple values (like arrays and streams). You might want to use nth that's designed to query for a stream/array element by its position, or its close alternative (). Here is an example:
.filter({ name: 'rob' })`
will return an array like
[{"email":"fooz#bar.com","id":"230de013-5d6a-436e-a7e9-a891c01913e5","name":"rob"}]
But the following queries
.filter({ name: 'rob' }).nth(0)
.filter({ name: 'rob' })(0)
will both return
{"email":"fooz#bar.com","id":"230de013-5d6a-436e-a7e9-a891c01913e5","name":"rob"}
Note that both also throw ReqlNonExistenceError: Index out of bounds: 0 in case if there are no rows found. However, you can easily fix it using the default operator:
.filter({ name: 'not-rob' }).nth(0).default(null)
Tested using RethinkDB data explorer with the "Raw view" tab active since both "Tree view" and "Table view" display single-element array as a single object. I'm almost sure the RethinkDB query engine is smart enough to make using nth(0) much more efficient rather than fetching the whole cursor to an array and taking the latter's first element in your code.

Related

Handling updates on nested screens using Context and React Navigation in React Native

Let's say I am building a shopping list app. I have the ability to create different shopping lists. I have three screens:
-- Shopping Lists: displays all the lists
---- List Details: displays all the items from a list
------ List Item Details: displays all the info about an item
To store the state and avoid prop drilling I use Context. My state could look like:
shoppingLists = [
{
title: 'Groceries',
items: [{
name: 'Apples',
quantity: 3
... (other info)
},
items: [{
name: 'Oranges',
quantity: 6,
...
}]
},
{
title: 'Office Supplies',
items: [{
name: 'Paper',
quantity: 2
... (other info)
},
items: [{
name: 'Pens',
quantity: 25,
...
}]
}]
When I tap on a Shopping List on the Shopping Lists screen I do
navigation.navigate('ListDetails', {params: listItem})
In the List Details screen I have de ability to change the quantity of an item or delete them. If I do any of this actions I have to make an API call to my server to update the value on the database. Here are my questions:
Currently I store the values in a local variable in my screen for example:
const [title, setTitle] = useState(props.route.params.title)
const [items, setItems] = useState(prop.route.params.items)
And if I make a change in the quantity I use setState to update my local array and then make an API call to update the context. This results problematic when adding more nested screens.
For example if I would give the user the ability to change the item quantity in the List Item Details screen, when the user goes back to the List Details screen, the values would not be updated.
My question is, which is the correct way to grab the state of the context and update it locally?
For example should I do something like:
navigation.navigate('ListDetails', {params: { listName: 'Groceries' })
And then in my List Details screen grab the correct list from the array like:
const {lists} = useContext(ShoppingLists)
const list = lists.filter(l => l.title === props.route.params.title)
What is the correct way?
The other question I have is a more general question. I want to use optimistic responses: when the user updates the quantity of an item in the list I update it locally and send the request to the server. If there's an error rollback the change. Which would be the correct way of doing this?
Thanks!
I would advise against updating context data in such a way. You could create a setList() method in the context instead, or updateList() to aggregate new data to the existing data.
This is related to the previous one. You could wrap the logic of POSTing to the server in a try-catch block and throw an error when it's not successful. Then you can set the new value only when it's successful. Another option would be to return the promise, so you could handle errors from outside, like setList().then(response => {}).catch(error => {}) though I don't think this looks that good.
Think you're over complicating it and also in your list you're not showing an id for each item. You should build your database with all the items and their unique id then call upon that when needed. The id should never change and that's easier to pass around and reference.
For example you can build an object in context with just the id and quantity and use useReducer for this:
https://reactjs.org/docs/hooks-reference.html#usereducer

Second Like in Find Query

How to use second LIKE in typeorm find query? I try like below, but not work and always return empty array
return await this.conn.getRepository(Article).find({
where: {
body: Like(`%${query}%`),
//title: Like(`%${query}%`), //NOT WORK WITH SECOND
},
order: {
lastUpdate: 'DESC'
},
relations: ['author']
});
thanks for any help!
I have to make a lot assumptions about your data and use case.
I suspect that the problem you are having has to do with the boolean operation that gets generated by TypeORM for your where condition(s).
Based on the way you have specified your request the resulting boolean operation is an AND. This means it will only return TRUE if both body AND title contain the search string.
I suspect this is NOT what you intended. I am assuming that what you really want is for the where to return TRUE if body or title or both contain the search string.
If all my assumptions are all correct, then you should be able to get the result you want by changing your request to the following:
return await this.conn.getRepository(Article).find({
where: [
{body: Like(`%${query}%`)},
{title: Like(`%${query}%`)}
],
order: {
lastUpdate: 'DESC'
},
relations: ['author']
});
Using this form of the request should result in a where clause that use an OR operator between the two Like tests.

How to append to a list/array in dynamodb?

For an 'id' in my DynamoDB table (e.g. e5eb02ae-04d5-4331-91e6-11efaaf12ea5), i have a column called Weather
['sun', 'rain', 'snow', etc...]
I need to update Weather when, say ['hail'] arrives. Right now, my update() below, replaces the the entire array. All i want to do is append 'hail' to the end of that list.
const updateinfo = {
id: "e5eb02ae-04d5-4331-91e6-11efaaf12ea5",
Weather: "hail"
}
try {
await API.graphql(graphqlOperation (UpdateInfo, { input: updateinfo })) //mutation
console.log('success')
}
catch (err) {
console.log(err)
}
How do i do this, ensuring that my list is just appended to with the info, WITHOUT having to get() and pull down ['sun', 'rain', 'snow', etc...], then adding 'hail' and creating a new array? Thats then a GetInfo() and an UpdateInfo() which is double the effort/call/expense.
I posted a response to a similar question in another question. The answer to this is to use a DynamoDB function called list_append.
list_append (operand, operand)
This function evaluates to a list with a new element added to it. You can append the new element to the start or the end of the list by reversing the order of the operands.
You can look here for a detailed response on how to do it using AWS AppSync and AWS Amplify.
https://stackoverflow.com/a/57088993/11681626

mongoose - how do I get the elements removed from $pull

I'm using $pull to pull a subdocument within an array of a document.
Don't know if it matters but in my case the subdocuments contain _id so they are indexed.
Here are JSONs that describes the schemas:
user: {
_id: String,
items: [UserItem]
}
UserItem: {
_id: String,
score: Number
}
Now my problem is this: I am using $pull to remove certain UserItem's from a User.
var delta = {$pull:{}};
delta.$pull.items={};
delta.$pull.items._id = {$in: ["mongoID1", "mongoID2" ...]};
User.findOneAndUpdate(query, delta, {multi: true}, function(err, data){
//whatever...
});
What i get in data here is the User object after the change, when what i wish to get is the items that were removed from the array (satellite data).
Can this be done with one call to the mongo or do I have to do 2 calls: 1 find and 1 $pull?
Thanks for the help.
You really cannot do this, or at least there is nothing that is going to return the "actual" elements that were "pulled" from the array in any response, even with the newer WriteResponse objects available to the newer Bulk Operations API ( which is kind of the way forward ).
The only way you can really do this is by "knowing" the elements you are "intending" to "pull", and then comparing that to the "original" state of the document before it was modified. The basic MongoDB .findAndModify() method allows this, as do the mongoose wrappers of .findByIdAndUpdate() as well and .findOneAndUpdate().
Basic usage premise:
var removing = [ "MongoId1", "MongoId2" ];
Model.findOneAndUpdate(
query,
{ "$pull": { "items._id": { "$in": removing } } },
{ "new": false },
function(err,doc) {
var removed = doc.items.filter(function(item) {
return removing.indexOf(item) != -1;
});
if ( removed.length > 0 )
console.log(removed);
}
);
Or something along those lines. You basically "turn around" the default mongoose .findOneAndUpdate() ( same for the other methods ) behavior and ask for the "original" document before it was modified. If the elements you asked to "pull" were present in the array then you report them, or inspect / return true or whatever.
So the mongoose methods differ from the reference implementation by returning the "new" document by default. Turn this off, and then you can "compare".
Further notes: "multi" is not a valid option here. The method modifies "one" document by definition. Also you make a statement that the array sub-documents conatain an _id. This is done by "mongoose" by default. But those _id values in the array are "not indexed" unless you specifically define an index on that field. The only default index is the "primary document" _id.

Setting unique id with Meteor insert

I'm linking the FB Graph API to Meteor so that I can retrieve a users photos and I'm having trouble setting the Meteor id to the Facebook id for each photo. Right now when the function is called it will return the same photo multiple times in the database since Meteor assigns a new _id to each photo each time.
For example, one entry might look like this:
Object {_id: "cnMsxSkmMXTjnhwRX", id: "1015160259999999", from: Object, picture: "https://photoSmall.jpg", source: "https://photoBig.jpg"…}
And a second, after the call has been performed again, like this:
Object {_id: "acMegKenftmnaefSf", id: "1015160259999999", from: Object, picture: "https://photoSmall.jpg", source: "https://photoBig.jpg"…}
Thereby creating two id fields in MongoDB.
The code I am using is below. I've tried a number of things to fix the code to no avail.
Meteor.methods({
getUserData: function() {
var fb = new Facebook(Meteor.user().services.facebook.accessToken);
var data = fb.getUserData();
_.forEach(data.data, function(photo) {
Photos.insert(photo, function(err) {
if(err) console.error(err);
});
});
}
});
Thanks in advance!
Check if the photo exists prior to inserting it
...
_.forEach(data.data, function(photo) {
if(Photos.findOne({id: photo.id})) return;
...
Another option is to add a unique key index to the id field. Or even use the _id field to store the id value. (be sure to use try catch to ensure it doesn't cause an error on the second insert).
Wouldn't there be something different to each of these with different ids?
You could also clean up the uniques before you run them with _.uniq

Categories