In the algolia documentation, they specify that you can manipulate arrays like this:
// adding
index.partialUpdateObject({
myfield: {
value: 'myvalue',
_operation: 'Add'
},
objectID: 'myID'
})
/removing
index.partialUpdateObject({
myfield: {
value: 'myvalue',
_operation: 'Remove'
}
})
This works well when the array is a string or number. However imagine that I have this document structure, where arrays are actually nested objects:
{
first_name: String,
last_name: String,
subjects: [
{
itemId: String,
title: String,
randomField: String,
dateAdded: Date
}
]
}
In this case the algolia documentation is very unclear. For example, imagine the following scenarios:
I want to update the randomField field of a particular array item. I want to be able to update a nested array item by itemId.
I want to be able to add or remove nested array items. In this case, what do I pass into the "value" field when doing a partialUpdateObject.
Is this kind of thing possible in Algolia? What would be your recommendations?
It's not possible to add/update/remove a specific attribute of a nested array using the partilUpdateObject function.
You get it right by fetching the object, modifying and updating it after. :)
It’s still true that you can’t update individual key value with in a nested object
Related
This is the mongo schema-
{
name: String
description: String,
array1: [{prop1: Number, prop2: String}],
referencingArray: {prop1: String, prop2_array1ref: <array1 element ref>},
}
Array elements of type object. One of the properties in referencingArray object need to refer to an element from array1. The UI will later use the referenced array element to annotate referencingArray info. The UI also needs to update referencingArray based on changes to array1. Currently array1 objects do not have any uniquely identifying property.
One idea I have is to add a guid property to array1 objects to uniquely identify them and make the referencingArray object's prop2_array1ref to use this guid. But that adds an extra property on the array1 objects.
Referring to array1 objects via index will need to handle delete from array1. If prop2_array1ref is referring to index 0 element of array1, deleting the element at index 0 should put referencingArray in a bad state instead of silently referring to the new object at index 0.
Mongo assigns _id property to array objects. I thought of using the _id property of array objects in prop2_array1ref but I am not sure how to handle new entries when the array objects don't have an id yet.
Question:
What's the best way to refer array1 elements from referencingArray?
Edit to add example:
{
name: 'John',
description: 'Best guy',
array1: [
{prop1: 12, prop2: 'lorem'},
{prop1: 55, prop2: 'ipsum'}
],
referencingArray: {prop1: 'domur', prop2_array1ref: <array1 element 1 ref>},
// User can change prop2_array1ref to point to different element of array1 in UI.
// Question is what's a good value for prop2_array1ref?
// Options are-
// 1. Add guid to array1 elements and use that as value of prop2_array1ref. (e.g. 'fl3-42mnj-348hn-83ngns')
// 2. index of array1 elements. (e.g. 1)
// 3. mongo assigned _id of array1 elements. (e.g. 98702398420398)
}
A simple normalization will do the job:
your main model
{
name: String
description: String,
array1: [{type: Schema.Types.ObjectId, ref: 'Array1Model'}],
referencingArray: {prop1: String, prop2_array1ref: {type: Schema.Types.ObjectId, ref: 'Array1Model'}},
}
Array1Model
{
prop1: Number
prop2: String
}
As you said yourself, mongo will implicitly add the primary key. Later on, if you want to render them nicely on the frontend, you do a simple "join"-like operation using $lookup. Here's an example.
I'm trying to update an array that sits inside another array in a document. The schema is like this:
const projectSchema = new mongoose.Schema({
stakeholders: [{
stakeholderTitle: {
type: String,
},
...
subgroup: [{
subgroupTitle: {
type: String
},
subgroupPercent: {
type: Number,
}
}]
}],
and I'm trying to update the 'subgroup' array. I have got the query to work on its parent (the stakeholder array) with the positional $ operator, using the answer to this question I asked previously. So my query looks like this.....
await db.findOneAndUpdate({ find by the id }, { "stakeholders.$.stakeholderTitle": req.body.stakeholderTitle, ... "stakeholders.$.subgroup": req.body.subgroup })
However, this query doesn't work for the 'stakeholders subgroup' array, and makes it null. Looking through the mongo docs for the positional operator it states that 'The positional $ operator cannot be used for queries which traverse more than one array, such as queries that traverse arrays nested within other arrays, because the replacement for the $ placeholder is a single value', which I guess might be my problem.
So how can I do this with a findOneAndUpdate query?
From what I see is you have to specify the object you want to update inside the subgroup array. Try this - (i.e I'm updating the subgroupTitle of the subgroup array);
await db.findOneAndUpdate(
{
_id: userId,
"stakeholders.stakeholderTitle": req.body.stakeholderTitle,
"stakeholders.stakeholderTitle.subgroup.subgroupTitle": req.body.subgroupTitle
},
{$set: {
"stakeholders.stakeholderTitle.subgroup.$.subgroupPercent": somePercentValue,
}
},
);
Also note, it's only the array that you find that you can update. It might not be exactly what you want, but its a step closer
We're using collection2 (obviously with simple schema) and trying to save an array of objects to a single property on the Meteor.users collection. For example our data might be:
[
{name: "paul"},
{name: "darryn"},
{name: "tom"}
]
in reality our object is more complex
when trying to do this with $set in an update on the users collection we've either gotten 500's or managed to delete the user object entirely when turning off validation.
we've also gotten the following error a number of times:
Validation object must have at least one operator / meteor mongo
This StackOverflow Question mentions it but doesn't offer a solution that makes sense in our context.
My question is two fold. How should the schema be defined for this as we've been trying with type: [Object] which I'm not sure is right, and secondly how should the update statement be created in the method.
Any thoughts, or help would be amazing.
First define the schema for your complex object. Here I've just added age as a key:
Schema.Foo = new SimpleSchema({
name: { type: String },
age: { type: Number, min: 0 }
});
Then augment the user schema with a key whose type is an array of the type you just defined
Schema.User = new SimpleSchema({
foo: { type: [Schema.foo] },
etc...
});
I have the following mongo entry:
et = {
languages: [{
code: String,
title: String,
tools: [{
description: String,
mds: [ObjectId]
}],
}]
//some more stuff
}
I now need to update this object and add an new ObjectId to the mds array. I need to specify the language element via the code element and the tools entry via the description parameter.
So far I came up with the following update method with which I can update some element of the correct language entry:
ETs.find({
'_id':mdAttributes.etID,
'languages':{'$elemMatch':{'code':mdAttributes.language}}
},{
'$set':{
'languages.$.title':'update2.jpg'
}
});
However I do not know how add an query for the correct tool.
So what my set should make should be something like this:
ETs.find({
'_id':mdAttributes.etID,
'languages':{'$elemMatch':{'code':mdAttributes.language}}
},{
'$set':{
'languages.$.tools.$.mds': ["newId"]
}
});
Is there a way to achieve this in mongo?
Short answer, no. The positional operator doesn't currently work with nested arrays (https://jira.mongodb.org/browse/server-831).
You can do it nonetheless by setting the whole tools array entry (like you do on your first example, but for the tools array instead of the title field).
I have documents like this:
db.planet.insert({
name: 'Earth',
stuff: {
'grass': 'green',
'sky': 'blue',
'sea': 'blue'
}
})
db.planet.insert({
name: 'Mars',
stuff: {
'dust': 'red',
'sky': 'yellow'
}
})
I want to find all planets that have at least some blue stuff (only earth in this case). How can I do that?
Catch: I tried solving this by using an array (instead of object) for stuff (stuff: [ { k: 'grass', v: 'green'},... ]) but I also need to be able to update (upsert to be exact) value of some stuff. For instance I must be able to do this:
db.planet.update({ name: 'Mars' }, {
'$set': {
'stuff.canals': 'brown',
'stuff.dust': 'reddish'
}
})
So, how can I find the planets with something blue on them? :)
Use the $or operator. The $or operator value should be an array, with the array containing conditions of which at least one must be true.
In your case, given the field names you have mentioned:
db.planet.find($or:[
{"stuff.sky":"blue"},
{"stuff.grass":"blue"},
{"stuff.sea":"blue"},
{"stuff.dust":"blue"},
{"stuff.canals":"blue")
])
See http://docs.mongodb.org/manual/reference/operator/or/
As there are a quite a number of different fields you want to query, you may want to keep an array of all these field names somewhere so you can generate the $or array programmatically.
I don't think there's a way to make the query without knowing the field names in advance. See mongodb query without field name .