Couchbase : How to maintain arrays without duplicate elements? - couchbase

We have a Couchbase store which has the Customer data.
Each customer has exactly one document in this bucket.
Daily transactions will result in making updates to this customer data.
Sample document. Let's focus on the purchased_product_ids array.
{
"customer_id" : 1000
"purchased_product_ids" : [1, 2, 3, 4, 5 ]
# in reality this is a big array - hundreds of elements
...
... many other elements ...
...
}
Existing purchased_product_ids :
[1, 2, 3, 4, 5]
products purchased today :
[1, 2, 3, 6] // 6 is a new entry, others existing already
Expected result after the update:
[1, 2, 3, 4, 5, 6]
I am using Subdocument API to avoid large data transfer between server and clients.
Option1 "arrayAppend" :
customerBucket.mutateIn(customerKey)
.arrayAppend("purchased_product_ids", JsonObject for [1,2,3,6] )
.execute();
It results in duplicate elements.
"purchased_product_ids" : [1, 2, 3, 4, 5, 1, 2, 3, 6]
Option2 "arrayAddUnique" :
customerBucket.mutateIn(customerKey)
.arrayAddUnqiue("purchased_product_ids", 1 )
.arrayAddUnqiue("purchased_product_ids", 2 )
.arrayAddUnqiue("purchased_product_ids", 3 )
.arrayAddUnqiue("purchased_product_ids", 6 )
.execute();
It throws exception for most of the times,
because those elements already existing.
Is there any better way to do this update ?

You could use N1QL, and the ARRAY_APPEND() and ARRAY_DISTINCT() functions.
UPDATE customer USE KEYS "foo"
SET purchased_product_ids = ARRAY_DISTINCT(ARRAY_APPEND(purchased_product_ids, 9))
Presumably this would be a prepared statement and the key itself and the new value would be supplied as parameters.
Also, if you want to add multiple elements to the array at once, ARRAY_CONCAT() would be a better choice. More here:
https://docs.couchbase.com/server/6.0/n1ql/n1ql-language-reference/arrayfun.html

Do you need purchased_product_ids to be ordered? If not you can convert it to a map, e.g.
{
"customer_id" : 1000
"purchased_product_ids" : {1: {}, 3: {}, 5: {}, 2: {}, 4: {}}
}
and then write to that map with subdoc, knowing you won't be conflicting (assuming product IDs are unique):
customerBucket.mutateIn(customerKey)
.upsert("purchased_product_ids.1", JsonObject.create()) // already exists
.upsert("purchased_product_ids.6", JsonObject.create()) // new product
.execute();
which will result in:
{
"customer_id" : 1000
"purchased_product_ids" : {1: {}, 3: {}, 6: {}, 5: {}, 2: {}, 4: {}}
}
(I've used JsonObject.create() as a placeholder here in case you need to associate additional information for each customer-order paid, but you could equally just write null. If you do need purchased_product_ids to be ordered, you can write the timestamp of the order, e.g. 1: {date: <TIMESTAMP>}, and then order it in code when you fetch.)

Related

Karate : Matching data between nested array

Is there a way to match the response data from API which contain a nested array for a key where key-value pair are in different order inside the nested array in karate?
Scenario: Verify original data contains expected data
def original = [{ a:1, b: [{c:2},{d:3}]}]
def expected = [{ b: [{d:3},{c:2}], a:1 }]
Using contains deep method will solve the issue but I am expecting original data from a API response so in some point of time if one more field gets added to the API response, then my scenario will still get passed
Don't try to do everything in one-line. Split your matches, and there is more explanation in the docs:
* def inner = [{ c: 2 }, { d: 3 }]
* def response = [{ a: 1, b: [{ d: 3 }, { c: 2 }]}]
* match each response contains { b: '#(^^inner)' }
* match each response == { a: 1, b: '#(^^inner)' }
* match response[0] == { a: 1, b: '#(^^inner)' }
* match response == [{ a: 1, b: '#(^^inner)' }]
You don't need to use all of these, I'm showing the possible options.

Serializing Multiple API Fields into one. Django

I have a pre-defined API, like:
{
time : some_time,
height : {1: 154, 2: 300, 3: 24},
color : {1: 'red', 2: 'blue', 3: 'green'},
age : {1: 27, 2: 324, 3: 1},
... many, many more keys.
}
I have no control of this API, so cannot change its structure.
Each integer key inside the sub dictionaries are linked and part of one record. For example the object that is 154 in height, is also colour: red and age: 27.
I am aware one strategy to work with this is to have separate serialisers for each field.
class MySerializer(serializers.ModelSerializer):
# Nested serializers
height = HeightSerializer()
colour = ColourSerializer()
age = AgeSerializer()
etc, etc, etc
But that still gives me messy data to work with, that requires lots of update() logic in the serializer.
What I instead want to do is have one nested serializer that has access to the full request data, and can work with height, colour and age simultaneously and return me something like from the to_internal_value() method:
{
['record' : 1, 'height': 154, 'colour' : 'red', 'age' : 27],
['record' : 2, 'height': 300, 'colour' : 'blue', 'age' : 324],
['record' : 3, 'height': 24, 'colour' : 'green', 'age' : 2],
}
But unfortunately the height serializer only seems to have access to information on fields called height. I am aware I can user source="foo" in the init call, but then it only has access to a field called "foo". I want it to have access to all fields.
I noticed there is a source='*' option, but it doesn't work. My init method of the serializer never gets called unless there is a key "height" in the api call.
Any ideas how I can have a nested serialiser that has access to all the data in the request?
Thanks
Joey

MySQL 5.7 - Increment a number in JSON

Is it possible to mass increment a specific JSON attribute?
For example, my JSON column is called metadata and looks like this:
{"counter": 0, ...}
Can I update multiple rows so that the counter increments by 1 with a single query?
Edit:
Since this is getting flagged for too broad, let me give an exact example.
Data on table x, the only column on it is metadata and it has 3 rows:
{"counter": 0}, {"counter": 1}, {"counter": 0, "something": "somethign"}
Desired result is one query that would update those 3 to this:
{"counter": 1}, {"counter": 2}, {"counter": 1, "something": "somethign"}
It goes without saying that I'm not an expert with MySQL and I didn't manage to get together a query that would work for me from the MySQL documentation. Help would be appreciated.
You could use JSON_EXTRACT along with JSON_SET:
UPDATE table SET col = JSON_SET(col, '$.counter', JSON_EXTRACT(col, '$.counter') + 1)

How to define a function that adds the values in HashMaps in Python?

The question is : create a function which will calculate the total stock worth in the cafe. You will need to remember to loop through the appropriate maps and lists to do this.
What I have so far :
menu = ("Coffee", "Tea", "Cake", "Cookies")
stock = {
"Coffee" : 10,
"Tea" : 17,
"Cake" : 15,
"Cookies" : 5,
}
price = {
"Coffee" : 'R 12',
"Tea" : 'R 11',
"Cake" : 'R 20',
"Cookies" : 'R 8',
}
def totalstock(stock):
Now I'm stuck, I know there should be a loop and a sum function, but I don't know how to convert the strings to ints so I can add them?
In this case your price dictionary doesn't just have numbers so you'll have to separate the R from the number. Example:
coffee_price = int(price['Coffee'].split(' ')[1])
To explain, take the string at price['Coffee'] and split it, giving a list with 2 values. Return the second value to the int() function to be converted to an integer and stored in coffee_price.

How to use an array value or variable as a key with scriptDB?

Is this possible? If I have an array like this
[[1,1,5000],[2,1,3000],[5,6,1000]]
is it possible to put it into scriptDB in a format like this?
{
customerName: 'Steve',
.
.
.
.
.
1:
{
itemID: 1,
qty: 1,
cost: 5000
},
2:
{
itemID: 2,
qty: 1,
cost: 3000
},
5:
{
itemID: 5,
qty: 6,
cost: 1000
}
}
If this doesn't happen to be possible what is the best option to store these so that I could query by itemID?
Its possible but NOT desired for many reasons: 1) there is a limit on how big a value csn be in a key,value pair so eventually it will bust when a costumer has many items. 2) you cant query those items without getting the entire customer and manually looping all items.
Much better normalize it so each 'row' (key,value) is an item which a customer property.