Updating in MongoDB without first having to pull a Mongoid object? - ruby

When performing queries I can go through Mongoid:
product_obj = Product.where(
_id: "58f876f683c336eec88e9db5"
).first # => #<Product _id: 58f876f683c336eec88e9db5, created_at: nil, updated_at: nil, sku: "123", name: "Some text", ...)
​
or I can circumvent it:
product_hsh = Product.collection.find( {
_id: BSON::ObjectId.from_string("58f876f683c336eec88e9db5")
}, {
projection: {
_id: 1,
name: 1
}
} ).first # => {"_id"=>BSON::ObjectId('58f876f683c336eec88e9db5'), "name"=>"Some text"}
I prefer to circumvent, because it performs better; I can limit which fields to get in the response.
My problem, however, is how to further work with the returned product. The response is a hash, not an object, so if I have to update it I need to pull it through Mongoid anyway, and thereby the performance gains are gone:
Product.find(product_hsh["_id"]).update_attribute(:name, "Some other text")
My question is: how do I update without first having to pull a Mongoid object?

You don't need to pull/fetch at all. You can just send the $set commands directly:
Product.where(id: product_hsh["_id"]).update_all(name: "Some other text")

Related

How to update embedded object of an array inside mongodb document using mongoid with ruby?

I want to update an embedded object of an array inside of mongodb collection using ruby and mongoid. I have an schema like this
{
_id: 23234342,
age: 23,
customer_name: 'alex',
email: 'alex#gmail.com',
orders: [
{
reference_number: '10',
amount: 2300,
ordered_at: '01-01-2023 11:12:00'
},
{
reference_number: '12',
amount: 2300,
ordered_at: '01-01-2023 11:12:00'
},
]
}
Now i want to update the value of amount of orders object with reference_number = 12
I know i can do like this
Customer.where(_id: "23234342").elem_match(orders: { reference_number: 12 }).update("$set" => {"orders.$.amount" => 4000})
But i want the correct syntax in ruby mongoid for this query.
How can i achieve using ruby mongoid?

update elasticcache record without totally replacing it

I'm using this to add a bunch of records to the elasticcache index:
def self.index_trends(trend_hashes)
formatted_trends = trend_hashes.map do |trend_hash|
{
index: {
_index: 'trends',
_type: 'trend',
_id: trend_hash["id"],
data: trend_hash
}
}
end
elasticsearch.bulk({
body: formatted_trends
})
end
Now I need to update a record (given its id). I want to just add one key-val to the data hash, not replace the whole state. How can I do this using elasticsearch-ruby?
I dont know how to do this with ruby, but in ES there is update API.
Which look like this
POST test/type1/1/_update
{
"script" : "ctx._source.new_field = \"value_of_new_field\""
}
From example with ruby should be
client.update index: 'myindex', type: 'mytype', id: '1',
body: { script: 'ctx._source.tags += tag', params: { tag: 'x' } }

Creating a GraphQLObjectType with an indexable field signature?

I'm currently in the process of transforming a REST API into GraphQL, but I've hit a bit of a snag in one of the endpoints.
Currently, this endpoint returns an object who's keys can be an unlimited set of strings, and whos values all match a certain shape.
So, as a rudimentary example, I have this situation...
// response
{
foo: { id: 'foo', count: 3 },
bar: { id: 'bar', count: 6 },
baz: { id: 'baz', count: 1 },
}
Again, the keys are not known at runtime and can be an unlimited set of strings.
In TypeScript, for example, this sort of situation is handled by creating an interface using an indexable field signature, like so...
interface Data {
id: string;
count: number;
}
interface Response {
[key: string]: Data;
}
So, my question is: Is this sort of thing possible with graphql? How would I go about creating a type/schema for this?
Thanks in advance!
I think that one solution can be usage of JSON.stringify() method
exampleQuery: {
type: GraphQLString,
resolve: (root, args, context) => {
let obj = {
foo: { id: 'foo', count: 3 },
bar: { id: 'bar', count: 6 },
baz: { id: 'baz', count: 1 }
};
return JSON.stringify(obj);
}
}
Then, after retrieving the result of GraphQL query you could use JSON.parse(result) (in case the part performing the query is also written in JavaScript - otherwise you would have to use equivalent method of other language to parse the incoming JSON response).
Disadvantage of such a solution is that you do not have the possibility to choose what fields of obj you want to retrieve from the query, but, as you said, the returning object can have unlimited set of strings that probably are not known on the front end of the application, so there is no need to choose it's keys, am I right?

Create a Model Scope using hash attribute

I am using mongoid-history gem and mongoid in Ruby. I am actually linking the mongo history to a model called SocialPost, so i can make something like.
history = current_user.social_posts.history_tracks
Now i need to filter this 'history' with a scope or a method that filter attribute 'association_chain' from history tracker model, but 'history_tracks' attributes are made in this way:
<HistoryTracker _id: 57bdc1cb65e59325ae000001, created_at: 2016-08-24 15:48:27 UTC, updated_at: 2016-08-24 15:48:27 UTC, association_chain: [{"name"=>"SocialPost", "id"=>BSON::ObjectId('57ac8b0f65e5930944000000')}], modified: {"facebook_likes_count"=>2594213, "tweeter_followers_count"=>0}, original: {}, version: 1, action: "update", scope: "social_post", modifier_id: nil>
So, how i can create a search in my HistoryTracker model that allow me to search a specific group of ids inside association_chain, something like this:
HistoryTracker.where(:association_chain.in => {"name"=>"SocialPost", "id"=>[GROUPS OF IDS TO SEARCH]}
UPDATE using $elemMatch
#test case multiple social_id: empty result
HistoryTracker.any_in({:association_chain.elem_match => [{name: 'SocialPost', :id.in => social_ids}] })
#test case 1 social_id: match result
HistoryTracker.any_in({:association_chain.elem_match => [{name: 'SocialPost', :id => social_ids.first}] })
I found already a posible solution. The elemMatch should be constructed grouped with ids you need to collect, so this is what i did:
social_ids = [BSON::ObjectId('...1'), BSON::ObjectId('...2'), BSON::ObjectId('...3')]
#reorder the ids with the extra hash elements
a = []
social_ids.each do |id_bson|
a << {name: 'SocialPost', id: id_bson}
end
And now you create the 'QUERY'
HistoryTracker.any_in({:association_chain.elem_match => a})

How do you display all values in an object?

This is a Mongoid object, but I think it applies for all objects. How do I split up #product into key, value pairs when #product looks like this:
#<Product _id: 4e7cd178e66de72d70000010, _type: nil,
created_at: 2011-09-23 18:35:36 UTC,
updated_at: 2011-09-23 18:35:36 UTC, brand: "crystalrock",
name: "Crazy Fool Specialty Tank", retailer: nil, model: nil,
sku: nil store: {"id"=>"36033", "name"=>"Ed Hardy"},
product_id: "684", sku_number: "D9WAFDCR", category_primary: "Tanks">
I would think it would be a object method, but I don't see one that works.
It looks like #product.attributes is a Mongoid method that works.

Resources